diff --git a/code/datums/action.dm b/code/datums/action.dm index 304aa47baa..bfc51b0411 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -228,6 +228,11 @@ button_icon_state = "fireselect_no" name = "Toggle Firemode" +/datum/action/item_action/toggle_safety + name = "Toggle Safety" + icon_icon = 'icons/hud/actions.dmi' + button_icon_state = "safety_on" + /datum/action/item_action/rcl_col name = "Change Cable Color" icon_icon = 'icons/mob/actions/actions_items.dmi' diff --git a/code/datums/components/fullauto.dm b/code/datums/components/fullauto.dm index 1882d0b511..4b9c25db91 100644 --- a/code/datums/components/fullauto.dm +++ b/code/datums/components/fullauto.dm @@ -1,4 +1,3 @@ - #define AUTOFIRE_MOUSEUP 0 #define AUTOFIRE_MOUSEDOWN 1 @@ -20,60 +19,69 @@ return COMPONENT_INCOMPATIBLE var/obj/item/gun = parent RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/wake_up) + RegisterSignal(parent, COMSIG_GUN_AUTOFIRE_SELECTED, .proc/wake_up) + RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED, COMSIG_GUN_AUTOFIRE_DESELECTED), .proc/autofire_off) if(_autofire_shot_delay) autofire_shot_delay = _autofire_shot_delay - if(autofire_stat == AUTOFIRE_STAT_IDLE && ismob(gun.loc)) + if(ismob(gun.loc)) var/mob/user = gun.loc wake_up(src, user) /datum/component/automatic_fire/Destroy() + UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED, COMSIG_GUN_AUTOFIRE_DESELECTED)) autofire_off() return ..() /datum/component/automatic_fire/process(delta_time) - if(autofire_stat != AUTOFIRE_STAT_FIRING) + if(!(autofire_stat & AUTOFIRE_STAT_FIRING)) STOP_PROCESSING(SSprojectiles, src) return + + if(!COOLDOWN_FINISHED(src, next_shot_cd)) + return + process_shot() /datum/component/automatic_fire/proc/wake_up(datum/source, mob/user, slot) SIGNAL_HANDLER - if(autofire_stat == AUTOFIRE_STAT_ALERT) + if(autofire_stat & (AUTOFIRE_STAT_ALERT)) return //We've updated the firemode. No need for more. - if(autofire_stat == AUTOFIRE_STAT_FIRING) + if(autofire_stat & AUTOFIRE_STAT_FIRING) stop_autofiring() //Let's stop shooting to avoid issues. return + + var/obj/item/gun/G = parent + if(iscarbon(user)) - var/mob/living/carbon/arizona_ranger = user - if(arizona_ranger.is_holding(parent)) - autofire_on(arizona_ranger.client) + var/mob/living/carbon/shooter = user + if(shooter.is_holding(parent) && G.fire_select == SELECT_FULLY_AUTOMATIC) + autofire_on(shooter.client) + else + autofire_off() // There is a gun and there is a user wielding it. The component now waits for the mouse click. /datum/component/automatic_fire/proc/autofire_on(client/usercli) SIGNAL_HANDLER - - if(autofire_stat != AUTOFIRE_STAT_IDLE) + if(autofire_stat & (AUTOFIRE_STAT_ALERT|AUTOFIRE_STAT_FIRING)) return autofire_stat = AUTOFIRE_STAT_ALERT - if(!QDELETED(usercli)) - clicker = usercli - shooter = clicker.mob - RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down) + clicker = usercli + shooter = clicker.mob + RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down) + RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT, .proc/autofire_off) if(!QDELETED(shooter)) - RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT, .proc/autofire_off) UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN) - RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED), .proc/autofire_off) parent.RegisterSignal(src, COMSIG_AUTOFIRE_ONMOUSEDOWN, /obj/item/gun/.proc/autofire_bypass_check) parent.RegisterSignal(parent, COMSIG_AUTOFIRE_SHOT, /obj/item/gun/.proc/do_autofire) /datum/component/automatic_fire/proc/autofire_off(datum/source) SIGNAL_HANDLER - if(autofire_stat == AUTOFIRE_STAT_IDLE) + if(autofire_stat & (AUTOFIRE_STAT_IDLE)) return - if(autofire_stat == AUTOFIRE_STAT_FIRING) + if(autofire_stat & AUTOFIRE_STAT_FIRING) stop_autofiring() autofire_stat = AUTOFIRE_STAT_IDLE @@ -82,10 +90,9 @@ UnregisterSignal(clicker, list(COMSIG_CLIENT_MOUSEDOWN, COMSIG_CLIENT_MOUSEUP, COMSIG_CLIENT_MOUSEDRAG)) mouse_status = AUTOFIRE_MOUSEUP //In regards to the component there's no click anymore to care about. clicker = null + RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN, .proc/on_client_login) if(!QDELETED(shooter)) - RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN, .proc/on_client_login) UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT) - UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED)) shooter = null parent.UnregisterSignal(parent, COMSIG_AUTOFIRE_SHOT) parent.UnregisterSignal(src, COMSIG_AUTOFIRE_ONMOUSEDOWN) @@ -129,9 +136,9 @@ source.click_intercept_time = world.time //From this point onwards Click() will no longer be triggered. - if(autofire_stat == (AUTOFIRE_STAT_IDLE)) + if(autofire_stat & (AUTOFIRE_STAT_IDLE)) CRASH("on_mouse_down() called with [autofire_stat] autofire_stat") - if(autofire_stat == AUTOFIRE_STAT_FIRING) + if(autofire_stat & AUTOFIRE_STAT_FIRING) stop_autofiring() //This can happen if we click and hold and then alt+tab, printscreen or other such action. MouseUp won't be called then and it will keep autofiring. target = _target @@ -143,7 +150,7 @@ //Dakka-dakka /datum/component/automatic_fire/proc/start_autofiring() if(autofire_stat == AUTOFIRE_STAT_FIRING) - return + return //Already pew-pewing. autofire_stat = AUTOFIRE_STAT_FIRING clicker.mouse_override_icon = 'icons/effects/mouse_pointers/weapon_pointer.dmi' @@ -181,8 +188,9 @@ /datum/component/automatic_fire/proc/stop_autofiring(datum/source, atom/object, turf/location, control, params) SIGNAL_HANDLER - if(autofire_stat != AUTOFIRE_STAT_FIRING) - return + switch(autofire_stat) + if(AUTOFIRE_STAT_IDLE, AUTOFIRE_STAT_ALERT) + return STOP_PROCESSING(SSprojectiles, src) autofire_stat = AUTOFIRE_STAT_ALERT if(clicker) @@ -218,9 +226,7 @@ /datum/component/automatic_fire/proc/process_shot() if(autofire_stat != AUTOFIRE_STAT_FIRING) - return FALSE - if(!COOLDOWN_FINISHED(src, next_shot_cd)) - return TRUE + return if(QDELETED(target) || get_turf(target) != target_loc) //Target moved or got destroyed since we last aimed. target = target_loc //So we keep firing on the emptied tile until we move our mouse and find a new target. if(get_dist(shooter, target) <= 0) @@ -239,10 +245,7 @@ // Gun procs. /obj/item/gun/proc/on_autofire_start(mob/living/shooter) - if(semicd || shooter.stat || !can_trigger_gun(shooter)) - return FALSE - if(!can_shoot()) - shoot_with_empty_chamber(shooter) + if(!can_shoot(shooter) || !can_trigger_gun(shooter) || semicd) return FALSE var/obj/item/bodypart/other_hand = shooter.has_hand_for_held_index(shooter.get_inactive_hand_index()) if(weapon_weight == WEAPON_HEAVY && (shooter.get_inactive_held_item() || !other_hand)) @@ -258,17 +261,10 @@ /obj/item/gun/proc/do_autofire(datum/source, atom/target, mob/living/shooter, params) - SIGNAL_HANDLER - if(semicd || shooter.stat) - return NONE + SIGNAL_HANDLER_DOES_SLEEP if(!can_shoot()) shoot_with_empty_chamber(shooter) return NONE - INVOKE_ASYNC(src, .proc/do_autofire_shot, source, target, shooter, params) - return COMPONENT_AUTOFIRE_SHOT_SUCCESS //All is well, we can continue shooting. - - -/obj/item/gun/proc/do_autofire_shot(datum/source, atom/target, mob/living/shooter, params) var/obj/item/gun/akimbo_gun = shooter.get_inactive_held_item() var/bonus_spread = 0 if(istype(akimbo_gun) && weapon_weight < WEAPON_MEDIUM) @@ -276,6 +272,7 @@ bonus_spread = dual_wield_spread addtimer(CALLBACK(akimbo_gun, /obj/item/gun.proc/process_fire, target, shooter, TRUE, params, null, bonus_spread), 1) process_fire(target, shooter, TRUE, params, null, bonus_spread) + return COMPONENT_AUTOFIRE_SHOT_SUCCESS //All is well, we can continue shooting. #undef AUTOFIRE_MOUSEUP #undef AUTOFIRE_MOUSEDOWN diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index ff8c52489b..dd54088f1f 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -22,6 +22,7 @@ var/ranged_attack_speed = CLICK_CD_RANGE var/melee_attack_speed = CLICK_CD_MELEE + var/gun_flags = NONE var/fire_sound = "gunshot" var/suppressed = null //whether or not a message is displayed when fired var/can_suppress = FALSE @@ -32,6 +33,7 @@ trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers var/sawn_desc = null //description change if weapon is sawn-off var/sawn_off = FALSE + var/firing_burst = 0 //Prevent the weapon from firing again while already firing /// can we be put into a turret var/can_turret = TRUE @@ -89,10 +91,12 @@ var/zoom_out_amt = 0 var/datum/action/item_action/toggle_scope_zoom/azoom - var/safety = FALSE /// Internal variable for keeping track whether the safety is on or off - var/has_gun_safety = FALSE /// Whether the gun actually has a gun safety + //gun safeties + var/safety = TRUE /// Internal variable for keeping track whether the safety is on or off + var/has_gun_safety = TRUE/// Whether the gun actually has a gun safety var/datum/action/item_action/toggle_safety/toggle_safety_action + //Firemodes var/datum/action/item_action/toggle_firemode/firemode_action /// Current fire selection, can choose between burst, single, and full auto. var/fire_select = SELECT_SEMI_AUTOMATIC @@ -107,24 +111,18 @@ /// Just 'slightly' snowflakey way to modify projectile damage for projectiles fired from this gun. var/projectile_damage_multiplier = 1 - /// directional recoil multiplier var/dir_recoil_amp = 10 -/datum/action/item_action/toggle_safety - name = "Toggle Safety" - icon_icon = 'icons/hud/actions.dmi' - button_icon_state = "safety_on" -/obj/item/gun/ui_action_click(mob/user, actiontype) - if(istype(actiontype, /datum/action/item_action/toggle_firemode)) +/obj/item/gun/ui_action_click(mob/user, action) + if(istype(action, /datum/action/item_action/toggle_firemode)) fire_select() - else if(istype(actiontype, toggle_safety_action)) + else if(istype(action, /datum/action/item_action/toggle_safety)) toggle_safety(user) else ..() - -/obj/item/gun/Initialize() +/obj/item/gun/Initialize(mapload) . = ..() if(pin) pin = new pin(src) @@ -134,7 +132,6 @@ if(zoomable) azoom = new (src) - if(has_gun_safety) safety = TRUE toggle_safety_action = new(src) @@ -152,6 +149,7 @@ firemode_action = new(src) firemode_action.button_icon_state = "fireselect_[fire_select]" firemode_action.UpdateButtonIcon() + /obj/item/gun/ComponentInitialize() . = ..() if(SELECT_FULLY_AUTOMATIC in fire_select_modes) @@ -166,6 +164,8 @@ QDEL_NULL(bayonet) if(chambered) QDEL_NULL(chambered) + if(azoom) + QDEL_NULL(azoom) if(toggle_safety_action) QDEL_NULL(toggle_safety_action) if(firemode_action) @@ -194,6 +194,8 @@ . += "[bayonet] looks like it can be unscrewed from [src]." else if(can_bayonet) . += "It has a bayonet lug on it." + if(has_gun_safety) + . += "The safety is [safety ? "ON" : "OFF"]." /obj/item/gun/proc/fire_select() var/mob/living/carbon/human/user = usr @@ -201,10 +203,10 @@ var/max_mode = fire_select_modes.len if(max_mode <= 1) - balloon_alert(user, "only one firemode!") + to_chat(user, "[src] is not capable of switching firemodes!") return - fire_select_index = 1 + fire_select_index % max_mode // Magic math to cycle through this shit! + fire_select_index = 1 + fire_select_index % max_mode //Magic math to cycle through this shit! fire_select = fire_select_modes[fire_select_index] @@ -213,43 +215,23 @@ burst_size = 1 fire_delay = 0 SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_DESELECTED, user) - balloon_alert(user, "semi-automatic") + to_chat(user, "You switch [src] to semi-automatic.") if(SELECT_BURST_SHOT) burst_size = initial(burst_size) fire_delay = initial(fire_delay) SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_DESELECTED, user) - balloon_alert(user, "[burst_size]-round burst") + to_chat(user, "You switch [src] to [burst_size]-round burst.") if(SELECT_FULLY_AUTOMATIC) burst_size = 1 SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_SELECTED, user) - balloon_alert(user, "automatic") + to_chat(user, "You switch [src] to automatic.") playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) update_appearance() firemode_action.button_icon_state = "fireselect_[fire_select]" - firemode_action.UpdateButtons() - //SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) I'll need this later + firemode_action.UpdateButtonIcon() return TRUE -/obj/item/gun/proc/toggle_safety(mob/user, override) - if(!has_gun_safety) - return - if(override) - if(override == "off") - safety = FALSE - else - safety = TRUE - else - safety = !safety - toggle_safety_action.button_icon_state = "safety_[safety ? "on" : "off"]" - toggle_safety_action.UpdateButtons() - playsound(src, 'sound/weapons/empty.ogg', 100, TRUE) - user.visible_message( - span_notice("[user] toggles [src]'s safety [safety ? "ON" : "OFF"]."), - span_notice("You toggle [src]'s safety [safety ? "ON" : "OFF"].") - ) - //SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) once again, needed later - /obj/item/gun/equipped(mob/living/user, slot) . = ..() if(zoomed && user.get_active_held_item() != src) @@ -381,6 +363,22 @@ var/stam_cost = getstamcost(user) process_fire(target, user, TRUE, params, null, bonus_spread, stam_cost) +/obj/item/gun/proc/toggle_safety(mob/user, override) + if(!has_gun_safety) + return + if(override) + if(override == "off") + safety = FALSE + else + safety = TRUE + else + safety = !safety + toggle_safety_action.button_icon_state = "safety_[safety ? "on" : "off"]" + toggle_safety_action.UpdateButtonIcon() + playsound(src, 'sound/weapons/empty.ogg', 100, TRUE) + user.visible_message("[user] toggles [src]'s safety [safety ? "ON" : "OFF"].", + "You toggle [src]'s safety [safety ? "ON" : "OFF"].") + /obj/item/gun/can_trigger_gun(mob/living/user) . = ..() if(!.) @@ -390,6 +388,9 @@ if(HAS_TRAIT(user, TRAIT_PACIFISM) && chambered?.harmful) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. to_chat(user, " [src] is lethally chambered! You don't want to risk harming anyone...") return FALSE + if(has_gun_safety && safety) + to_chat(user, "The safety is on!") + return FALSE /obj/item/gun/CheckAttackCooldown(mob/user, atom/target) if((user.a_intent == INTENT_HARM) && user.Adjacent(target)) //melee diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index f43999d62b..4d08520246 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -5,7 +5,7 @@ var/automatic_burst_overlay = TRUE can_suppress = TRUE burst_size = 3 - burst_shot_delay = 2 + fire_delay = 2 fire_select_modes = list(SELECT_SEMI_AUTOMATIC, SELECT_BURST_SHOT, SELECT_FULLY_AUTOMATIC) /obj/item/gun/ballistic/automatic/proto @@ -17,10 +17,6 @@ pin = null burst_size = 1 -/obj/item/gun/ballistic/automatic/proto/Initialize() - . = ..() - AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) - /obj/item/gun/ballistic/automatic/proto/unrestricted pin = /obj/item/firing_pin @@ -60,34 +56,6 @@ else to_chat(user, "You cannot seem to get \the [src] out of your hands!") -/obj/item/gun/ballistic/automatic/ui_action_click(mob/user, action) - if(istype(action, /datum/action/item_action/toggle_firemode)) - burst_select() - else - return ..() - -/obj/item/gun/ballistic/automatic/proc/burst_select() - var/mob/living/carbon/human/user = usr - select = !select - if(!select) - disable_burst() - to_chat(user, "You switch to semi-automatic.") - else - enable_burst() - to_chat(user, "You switch to [burst_size]-rnd burst.") - - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - update_icon() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/gun/ballistic/automatic/proc/enable_burst() - burst_size = initial(burst_size) - -/obj/item/gun/ballistic/automatic/proc/disable_burst() - burst_size = 1 - /obj/item/gun/ballistic/automatic/can_shoot() return get_ammo() @@ -141,18 +109,10 @@ knife_y_offset = 12 automatic_burst_overlay = FALSE -/obj/item/gun/ballistic/automatic/wt550/enable_burst() - . = ..() - spread = 15 - /obj/item/gun/ballistic/automatic/wt550/afterattack() . = ..() empty_alarm() -/obj/item/gun/ballistic/automatic/wt550/disable_burst() - . = ..() - spread = 0 - /obj/item/gun/ballistic/automatic/wt550/update_icon_state() icon_state = "wt550[magazine ? "-[CEILING(((get_ammo(FALSE) / magazine.max_ammo) * 20) /4, 1)*4]" : "-0"]" //Sprites only support up to 20. @@ -216,6 +176,7 @@ /obj/item/gun/ballistic/automatic/m90/update_icon_state() icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" +/* /obj/item/gun/ballistic/automatic/m90/burst_select() var/mob/living/carbon/human/user = usr switch(select) @@ -233,6 +194,7 @@ playsound(user, 'sound/weapons/empty.ogg', 100, 1) update_icon() return +*/ /obj/item/gun/ballistic/automatic/tommygun name = "\improper Thompson SMG"