diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 47b3648054..7a54b8482d 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -321,6 +321,12 @@ ///from base of mob/AltClickOn(): (atom/A) #define COMSIG_MOB_ALTCLICKON "mob_altclickon" +//Gun signals +///When a gun is switched to automatic fire mode +#define COMSIG_GUN_AUTOFIRE_SELECTED "gun_autofire_selected" +///When a gun is switched off of automatic fire mode +#define COMSIG_GUN_AUTOFIRE_DESELECTED "gun_autofire_deselected" + // Lighting: ///from base of [atom/proc/set_light]: (l_range, l_power, l_color, l_on) #define COMSIG_ATOM_SET_LIGHT "atom_set_light" diff --git a/code/__DEFINES/gun.dm b/code/__DEFINES/gun.dm new file mode 100644 index 0000000000..05ce5118a3 --- /dev/null +++ b/code/__DEFINES/gun.dm @@ -0,0 +1,3 @@ +#define SELECT_SEMI_AUTOMATIC 1 +#define SELECT_BURST_SHOT 2 +#define SELECT_FULLY_AUTOMATIC 3 diff --git a/code/datums/action.dm b/code/datums/action.dm index ac8c909bd2..304aa47baa 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -23,6 +23,8 @@ var/icon_icon = 'icons/mob/actions.dmi' //This is the file for the ACTION icon var/button_icon_state = "default" //And this is the state for the action icon var/mob/owner + ///List of all mobs that are viewing our action button -> A unique movable for them to view. + var/list/viewers = list() /datum/action/New(Target) link_to(Target) @@ -121,6 +123,11 @@ return FALSE return TRUE +/datum/action/proc/UpdateButtons(status_only, force) + for(var/datum/hud/hud in viewers) + var/atom/movable/screen/movable/button = viewers[hud] + UpdateButtonIcon(button, status_only, force) + /datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE) if(!button) return @@ -217,6 +224,8 @@ name = "Toggle Hood" /datum/action/item_action/toggle_firemode + icon_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "fireselect_no" name = "Toggle Firemode" /datum/action/item_action/rcl_col diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 41356583e6..ff8c52489b 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -89,6 +89,19 @@ 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 + var/datum/action/item_action/toggle_safety/toggle_safety_action + + 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 + var/fire_select_index = 1 + /// What modes does this weapon have? Put SELECT_FULLY_AUTOMATIC in here to enable fully automatic behaviours. + var/list/fire_select_modes = list(SELECT_SEMI_AUTOMATIC) + /// if i`1t has an icon for a selector switch indicating current firemode. + var/selector_switch_icon = FALSE + var/dualwield_spread_mult = 1 //dualwield spread multiplier /// Just 'slightly' snowflakey way to modify projectile damage for projectiles fired from this gun. @@ -97,18 +110,53 @@ /// 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/Initialize(mapload) +/obj/item/gun/ui_action_click(mob/user, actiontype) + if(istype(actiontype, /datum/action/item_action/toggle_firemode)) + fire_select() + else if(istype(actiontype, toggle_safety_action)) + toggle_safety(user) + else + ..() + + +/obj/item/gun/Initialize() . = ..() - if(no_pin_required) - pin = null - else if(pin) + if(pin) pin = new pin(src) + if(gun_light) - alight = new (src) + alight = new(src) + if(zoomable) azoom = new (src) + if(has_gun_safety) + safety = TRUE + toggle_safety_action = new(src) + + if(burst_size > 1 && !(SELECT_BURST_SHOT in fire_select_modes)) + fire_select_modes.Add(SELECT_BURST_SHOT) + else if(burst_size <= 1 && (SELECT_BURST_SHOT in fire_select_modes)) + fire_select_modes.Remove(SELECT_BURST_SHOT) + + burst_size = 1 + + sortList(fire_select_modes, /proc/cmp_numeric_asc) + + if(fire_select_modes.len > 1) + 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) + AddComponent(/datum/component/automatic_fire, fire_delay) + /obj/item/gun/Destroy() if(pin) QDEL_NULL(pin) @@ -118,6 +166,10 @@ QDEL_NULL(bayonet) if(chambered) QDEL_NULL(chambered) + if(toggle_safety_action) + QDEL_NULL(toggle_safety_action) + if(firemode_action) + QDEL_NULL(firemode_action) return ..() /obj/item/gun/examine(mob/user) @@ -143,6 +195,61 @@ else if(can_bayonet) . += "It has a bayonet lug on it." +/obj/item/gun/proc/fire_select() + var/mob/living/carbon/human/user = usr + + var/max_mode = fire_select_modes.len + + if(max_mode <= 1) + balloon_alert(user, "only one firemode!") + return + + fire_select_index = 1 + fire_select_index % max_mode // Magic math to cycle through this shit! + + fire_select = fire_select_modes[fire_select_index] + + switch(fire_select) + if(SELECT_SEMI_AUTOMATIC) + burst_size = 1 + fire_delay = 0 + SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_DESELECTED, user) + balloon_alert(user, "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") + if(SELECT_FULLY_AUTOMATIC) + burst_size = 1 + SEND_SIGNAL(src, COMSIG_GUN_AUTOFIRE_SELECTED, user) + balloon_alert(user, "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 + 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) diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index dd68ed9817..f43999d62b 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -6,7 +6,7 @@ can_suppress = TRUE burst_size = 3 burst_shot_delay = 2 - actions_types = list(/datum/action/item_action/toggle_firemode) + fire_select_modes = list(SELECT_SEMI_AUTOMATIC, SELECT_BURST_SHOT, SELECT_FULLY_AUTOMATIC) /obj/item/gun/ballistic/automatic/proto name = "\improper Nanotrasen Saber SMG" diff --git a/code/modules/projectiles/guns/ballistic/launchers.dm b/code/modules/projectiles/guns/ballistic/launchers.dm index c53366c4f5..10a6eea89d 100644 --- a/code/modules/projectiles/guns/ballistic/launchers.dm +++ b/code/modules/projectiles/guns/ballistic/launchers.dm @@ -39,7 +39,7 @@ mag_type = /obj/item/ammo_box/magazine/m75 burst_size = 1 fire_delay = 0 - actions_types = list() + fire_select_modes = list(SELECT_SEMI_AUTOMATIC) casing_ejector = FALSE /obj/item/gun/ballistic/automatic/gyropistol/update_icon_state() diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index 98b654aadb..bd6f203882 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -7,7 +7,7 @@ can_suppress = TRUE burst_size = 1 fire_delay = 0 - actions_types = list() + fire_select_modes = list(SELECT_SEMI_AUTOMATIC) automatic_burst_overlay = FALSE /obj/item/gun/ballistic/automatic/pistol/no_mag @@ -104,7 +104,7 @@ mag_type = /obj/item/ammo_box/magazine/pistolm9mm burst_size = 3 fire_delay = 2 - actions_types = list(/datum/action/item_action/toggle_firemode) + fire_select_modes = list(SELECT_SEMI_AUTOMATIC, SELECT_BURST_SHOT, SELECT_FULLY_AUTOMATIC) /obj/item/gun/ballistic/automatic/pistol/stickman name = "flat gun" @@ -137,7 +137,7 @@ burst_size = 1 can_suppress = FALSE w_class = WEIGHT_CLASS_NORMAL - actions_types = list() + fire_select_modes = list(SELECT_SEMI_AUTOMATIC) fire_sound = 'sound/weapons/noscope.ogg' spread = 20 //damn thing has no rifling. automatic_burst_overlay = FALSE diff --git a/icons/hud/actions.dmi b/icons/hud/actions.dmi new file mode 100644 index 0000000000..5d718bc9e0 Binary files /dev/null and b/icons/hud/actions.dmi differ diff --git a/icons/mob/actions/actions_items.dmi b/icons/mob/actions/actions_items.dmi index 10627cf66d..2eb92f4a45 100644 Binary files a/icons/mob/actions/actions_items.dmi and b/icons/mob/actions/actions_items.dmi differ diff --git a/tgstation.dme b/tgstation.dme index f1e5e5bf27..48c2a7d9de 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -56,6 +56,7 @@ #include "code\__DEFINES\fantasy_affixes.dm" #include "code\__DEFINES\food.dm" #include "code\__DEFINES\footsteps.dm" +#include "code\__DEFINES\gun.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\instruments.dm" #include "code\__DEFINES\integrated_electronics.dm"