From fbc80cf8292125929cec234162a7c8f2f28b1213 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 9 Sep 2022 00:29:20 -0700 Subject: [PATCH 01/12] initial port --- code/__DEFINES/combat.dm | 14 + code/_onclick/drag_drop.dm | 3 - code/datums/components/fullauto.dm | 280 ++++++++++++++++++ code/modules/cargo/centcom_podlauncher.dm | 3 +- code/modules/client/client_defines.dm | 4 + code/modules/client/client_procs.dm | 13 +- code/modules/mob/mob.dm | 6 +- code/modules/projectiles/gun.dm | 1 - .../projectiles/guns/ballistic/automatic.dm | 10 +- .../projectiles/guns/energy/laser_gatling.dm | 2 - .../effects/mouse_pointers/weapon_pointer.dmi | Bin 0 -> 234 bytes 11 files changed, 321 insertions(+), 15 deletions(-) create mode 100644 code/datums/components/fullauto.dm create mode 100644 icons/effects/mouse_pointers/weapon_pointer.dmi diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index a6d411a8dd..6dae40ce07 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -206,6 +206,19 @@ ///Time to spend without clicking on other things required for your shots to become accurate. #define GUN_AIMING_TIME (2 SECONDS) +//Autofire component +/// Compatible firemode is in the gun. Wait until it's held in the user hands. +#define AUTOFIRE_STAT_IDLE (1<<0) +/// Gun is active and in the user hands. Wait until user does a valid click. +#define AUTOFIRE_STAT_ALERT (1<<1) +/// Gun is shooting. +#define AUTOFIRE_STAT_FIRING (1<<2) + +#define COMSIG_AUTOFIRE_ONMOUSEDOWN "autofire_onmousedown" + #define COMPONENT_AUTOFIRE_ONMOUSEDOWN_BYPASS (1<<0) +#define COMSIG_AUTOFIRE_SHOT "autofire_shot" + #define COMPONENT_AUTOFIRE_SHOT_SUCCESS (1<<0) + //Object/Item sharpness #define SHARP_NONE 0 #define SHARP_EDGED 1 @@ -261,3 +274,4 @@ * a "inefficiently" prefix will be added to the message. */ #define FEEBLE_ATTACK_MSG_THRESHOLD 0.5 + diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index 58c182036d..fa529d0bfd 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -74,9 +74,6 @@ /obj/item/proc/onMouseUp(object, location, params, mob) return -/obj/item/gun/CanItemAutoclick(object, location, params) - . = automatic - /atom/proc/IsAutoclickable() . = 1 diff --git a/code/datums/components/fullauto.dm b/code/datums/components/fullauto.dm new file mode 100644 index 0000000000..2d00783e30 --- /dev/null +++ b/code/datums/components/fullauto.dm @@ -0,0 +1,280 @@ +#define AUTOFIRE_MOUSEUP 0 +#define AUTOFIRE_MOUSEDOWN 1 + +/datum/component/automatic_fire + var/client/clicker + var/mob/living/shooter + var/atom/target + var/turf/target_loc //For dealing with locking on targets due to BYOND engine limitations (the mouse input only happening when mouse moves). + var/autofire_stat = AUTOFIRE_STAT_IDLE + var/mouse_parameters + var/autofire_shot_delay = 0.3 SECONDS //Time between individual shots. + var/mouse_status = AUTOFIRE_MOUSEUP //This seems hacky but there can be two MouseDown() without a MouseUp() in between if the user holds click and uses alt+tab, printscreen or similar. + + COOLDOWN_DECLARE(next_shot_cd) + +/datum/component/automatic_fire/Initialize(_autofire_shot_delay) + . = ..() + if(!isgun(parent)) + return COMPONENT_INCOMPATIBLE + var/obj/item/gun = parent + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/wake_up) + if(_autofire_shot_delay) + autofire_shot_delay = _autofire_shot_delay + if(autofire_stat == AUTOFIRE_STAT_IDLE && ismob(gun.loc)) + var/mob/user = gun.loc + wake_up(src, user) + + +/datum/component/automatic_fire/Destroy() + autofire_off() + return ..() + +/datum/component/automatic_fire/process(delta_time) + if(autofire_stat != AUTOFIRE_STAT_FIRING) + STOP_PROCESSING(SSprojectiles, src) + return + process_shot() + +/datum/component/automatic_fire/proc/wake_up(datum/source, mob/user, slot) + SIGNAL_HANDLER + + if(autofire_stat == AUTOFIRE_STAT_ALERT) + return //We've updated the firemode. No need for more. + if(autofire_stat == AUTOFIRE_STAT_FIRING) + stop_autofiring() //Let's stop shooting to avoid issues. + return + if(iscarbon(user)) + var/mob/living/carbon/arizona_ranger = user + if(arizona_ranger.is_holding(parent)) + autofire_on(arizona_ranger.client) + +// 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) + return + autofire_stat = AUTOFIRE_STAT_ALERT + if(!QDELETED(usercli)) + clicker = usercli + shooter = clicker.mob + RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down) + if(!QDELETED(shooter)) + RegisterSignal(shooter, COMSIG_MOB_LOGOUT, .proc/autofire_off) + UnregisterSignal(shooter, COMSIG_MOB_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) + return + if(autofire_stat == AUTOFIRE_STAT_FIRING) + stop_autofiring() + + autofire_stat = AUTOFIRE_STAT_IDLE + + if(!QDELETED(clicker)) + 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 + if(!QDELETED(shooter)) + RegisterSignal(shooter, COMSIG_MOB_LOGIN, .proc/on_client_login) + UnregisterSignal(shooter, COMSIG_MOB_LOGOUT) + UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED)) + shooter = null + parent.UnregisterSignal(parent, COMSIG_AUTOFIRE_SHOT) + parent.UnregisterSignal(src, COMSIG_AUTOFIRE_ONMOUSEDOWN) + +/datum/component/automatic_fire/proc/on_client_login(mob/source) + SIGNAL_HANDLER + if(!source.client) + return + if(source.is_holding(parent)) + autofire_on(source.client) + +/datum/component/automatic_fire/proc/on_mouse_down(client/source, atom/_target, turf/location, control, params) + var/list/modifiers = params2list(params) //If they're shift+clicking, for example, let's not have them accidentally shoot. + + if(LAZYACCESS(modifiers, SHIFT_CLICK)) + return + if(LAZYACCESS(modifiers, CTRL_CLICK)) + return + if(LAZYACCESS(modifiers, MIDDLE_CLICK)) + return + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + return + if(LAZYACCESS(modifiers, ALT_CLICK)) + return + if(source.mob.throw_mode) + return + if(!isturf(source.mob.loc)) //No firing inside lockers and stuff. + return + if(get_dist(source.mob, _target) < 2) //Adjacent clicking. + return + + if(isnull(location)) //Clicking on a screen object. + if(_target.plane != CLICKCATCHER_PLANE) //The clickcatcher is a special case. We want the click to trigger then, under it. + return //If we click and drag on our worn backpack, for example, we want it to open instead. + _target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source) + if(!_target) + CRASH("Failed to get the turf under clickcatcher") + + if(SEND_SIGNAL(src, COMSIG_AUTOFIRE_ONMOUSEDOWN, source, _target, location, control, params) & COMPONENT_AUTOFIRE_ONMOUSEDOWN_BYPASS) + return + + source.click_intercept_time = world.time //From this point onwards Click() will no longer be triggered. + + if(autofire_stat == (AUTOFIRE_STAT_IDLE)) + CRASH("on_mouse_down() called with [autofire_stat] autofire_stat") + 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 + target_loc = get_turf(target) + mouse_parameters = params + start_autofiring() + + +//Dakka-dakka +/datum/component/automatic_fire/proc/start_autofiring() + if(autofire_stat == AUTOFIRE_STAT_FIRING) + return + autofire_stat = AUTOFIRE_STAT_FIRING + + clicker.mouse_override_icon = 'icons/effects/mouse_pointers/weapon_pointer.dmi' + clicker.mouse_pointer_icon = clicker.mouse_override_icon + + if(mouse_status == AUTOFIRE_MOUSEUP) //See mouse_status definition for the reason for this. + RegisterSignal(clicker, COMSIG_CLIENT_MOUSEUP, .proc/on_mouse_up) + mouse_status = AUTOFIRE_MOUSEDOWN + + RegisterSignal(shooter, COMSIG_MOB_SWAP_HANDS, .proc/stop_autofiring) + + if(isgun(parent)) + var/obj/item/gun/shoota = parent + if(!shoota.on_autofire_start(shooter)) //This is needed because the minigun has a do_after before firing and signals are async. + stop_autofiring() + return + if(autofire_stat != AUTOFIRE_STAT_FIRING) + return //Things may have changed while on_autofire_start() was being processed, due to do_after's sleep. + + if(!process_shot()) //First shot is processed instantly. + return //If it fails, such as when the gun is empty, then there's no need to schedule a second shot. + + START_PROCESSING(SSprojectiles, src) + RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDRAG, .proc/on_mouse_drag) + + +/datum/component/automatic_fire/proc/on_mouse_up(datum/source, atom/object, turf/location, control, params) + SIGNAL_HANDLER + UnregisterSignal(clicker, COMSIG_CLIENT_MOUSEUP) + mouse_status = AUTOFIRE_MOUSEUP + if(autofire_stat == AUTOFIRE_STAT_FIRING) + stop_autofiring() + return COMPONENT_CLIENT_MOUSEUP_INTERCEPT + + +/datum/component/automatic_fire/proc/stop_autofiring(datum/source, atom/object, turf/location, control, params) + SIGNAL_HANDLER + if(autofire_stat != AUTOFIRE_STAT_FIRING) + return + STOP_PROCESSING(SSprojectiles, src) + autofire_stat = AUTOFIRE_STAT_ALERT + if(clicker) + clicker.mouse_override_icon = null + clicker.mouse_pointer_icon = clicker.mouse_override_icon + UnregisterSignal(clicker, COMSIG_CLIENT_MOUSEDRAG) + if(!QDELETED(shooter)) + UnregisterSignal(shooter, COMSIG_MOB_SWAP_HANDS) + target = null + target_loc = null + mouse_parameters = null + +/datum/component/automatic_fire/proc/on_mouse_drag(client/source, atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params) + SIGNAL_HANDLER + if(isnull(over_location)) //This happens when the mouse is over an inventory or screen object, or on entering deep darkness, for example. + var/list/modifiers = params2list(params) + var/new_target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source) + mouse_parameters = params + if(!new_target) + if(QDELETED(target)) //No new target acquired, and old one was deleted, get us out of here. + stop_autofiring() + CRASH("on_mouse_drag failed to get the turf under screen object [over_object.type]. Old target was incidentally QDELETED.") + target = get_turf(target) //If previous target wasn't a turf, let's turn it into one to avoid locking onto a potentially moving target. + target_loc = target + CRASH("on_mouse_drag failed to get the turf under screen object [over_object.type]") + target = new_target + target_loc = new_target + return + target = over_object + target_loc = get_turf(over_object) + mouse_parameters = params + + +/datum/component/automatic_fire/proc/process_shot() + if(autofire_stat != AUTOFIRE_STAT_FIRING) + return FALSE + if(!COOLDOWN_FINISHED(src, next_shot_cd)) + return TRUE + 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) + target = get_step(shooter, shooter.dir) //Shoot in the direction faced if the mouse is on the same tile as we are. + target_loc = target + else if(!in_view_range(shooter, target)) + stop_autofiring() //Elvis has left the building. + return FALSE + shooter.face_atom(target) + COOLDOWN_START(src, next_shot_cd, autofire_shot_delay) + if(SEND_SIGNAL(parent, COMSIG_AUTOFIRE_SHOT, target, shooter, mouse_parameters) & COMPONENT_AUTOFIRE_SHOT_SUCCESS) + return TRUE + stop_autofiring() + return FALSE + +// 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) + 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)) + to_chat(shooter, "You need two hands to fire [src]!") + return FALSE + return TRUE + + +/obj/item/gun/proc/autofire_bypass_check(datum/source, client/clicker, atom/target, turf/location, control, params) + SIGNAL_HANDLER + if(clicker.mob.get_active_held_item() != src) + return COMPONENT_AUTOFIRE_ONMOUSEDOWN_BYPASS + + +/obj/item/gun/proc/do_autofire(datum/source, atom/target, mob/living/shooter, params) + SIGNAL_HANDLER + if(semicd || shooter.stat) + return NONE + 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) + if(akimbo_gun.weapon_weight < WEAPON_MEDIUM && akimbo_gun.can_trigger_gun(shooter)) + 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) + +#undef AUTOFIRE_MOUSEUP +#undef AUTOFIRE_MOUSEDOWN diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index e4060de1a2..557061e5a6 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -567,7 +567,8 @@ else if(picking_dropoff_turf) holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi' //Icon for when mouse is released holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi' //Icon for when mouse is pressed - holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released) + holder.mouse_override_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released) + holder.mouse_pointer_icon = holder.mouse_override_icon holder.click_intercept = src //Create a click_intercept so we know where the user is clicking else var/mob/holder_mob = holder.mob diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 294fdcc30b..c1399ce646 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -22,6 +22,8 @@ ///Contains admin info. Null if client is not an admin. var/datum/admins/holder = null var/datum/click_intercept = null // Needs to implement InterceptClickOn(user,params,atom) proc + ///Time when the click was intercepted + var/click_intercept_time = 0 var/AI_Interact = 0 var/jobbancache = null //Used to cache this client's jobbans to save on DB queries @@ -78,6 +80,8 @@ //These two vars are used to make a special mouse cursor, with a unique icon for clicking var/mouse_up_icon = null var/mouse_down_icon = null + ///used to override the mouse cursor so it doesnt get reset + var/mouse_override_icon = null var/ip_intel = "Disabled" diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 66bb714b99..9ab2be033a 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -422,7 +422,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if( (world.address == address || !address) && !GLOB.host ) GLOB.host = key world.update_status() - + if(holder) add_admin_verbs() var/admin_memo_note = get_message_output("memo") @@ -867,10 +867,17 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( ip_intel = res.intel /client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE, extra_info) - if(last_click > world.time - world.tick_lag) + /*if(last_click > world.time - world.tick_lag) return last_activity = world.time - last_click = world.time + last_click = world.time*/ + if(!control) + return + if(click_intercept_time) + if(click_intercept_time >= world.time) + click_intercept_time = 0 //Reset and return. Next click should work, but not this one. + return + click_intercept_time = 0 //Just reset. Let's not keep re-checking forever. var/list/L = params2list(params) if(L["drag"]) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 7ef26c7818..91ff5f4fbf 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -875,13 +875,15 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) L.alpha = lighting_alpha /mob/proc/update_mouse_pointer() - if (!client) + if(!client) return client.mouse_pointer_icon = initial(client.mouse_pointer_icon) - if (ismecha(loc)) + if(istype(loc, /obj/vehicle/sealed)) var/obj/vehicle/sealed/mecha/M = loc if(M.mouse_pointer) client.mouse_pointer_icon = M.mouse_pointer + if(client.mouse_override_icon) + client.mouse_pointer_icon = client.mouse_override_icon /mob/proc/is_literate() return 0 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 7f1f43efc3..06468685c5 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -92,7 +92,6 @@ /// Just 'slightly' snowflakey way to modify projectile damage for projectiles fired from this gun. var/projectile_damage_multiplier = 1 - var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds /// directional recoil multiplier var/dir_recoil_amp = 10 diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 4b871d59b2..6487b57a9e 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -304,17 +304,21 @@ slot_flags = 0 mag_type = /obj/item/ammo_box/magazine/mm712x82 weapon_weight = WEAPON_HEAVY - var/cover_open = FALSE can_suppress = FALSE - burst_size = 3 - burst_shot_delay = 1 + burst_size = 1 + actions_types = list() spread = 7 pin = /obj/item/firing_pin/implant/pindicate automatic_burst_overlay = FALSE + var/cover_open = FALSE /obj/item/gun/ballistic/automatic/l6_saw/unrestricted pin = /obj/item/firing_pin +/obj/item/gun/ballistic/automatic/l6_saw/ComponentInitialize() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) + /obj/item/gun/ballistic/automatic/l6_saw/examine(mob/user) . = ..() if(cover_open && magazine) diff --git a/code/modules/projectiles/guns/energy/laser_gatling.dm b/code/modules/projectiles/guns/energy/laser_gatling.dm index 16a977515c..65d525b638 100644 --- a/code/modules/projectiles/guns/energy/laser_gatling.dm +++ b/code/modules/projectiles/guns/energy/laser_gatling.dm @@ -101,8 +101,6 @@ slot_flags = null w_class = WEIGHT_CLASS_HUGE custom_materials = null - automatic = 0.5 - fire_delay = 2 ammo_type = list( /obj/item/ammo_casing/energy/laser ) diff --git a/icons/effects/mouse_pointers/weapon_pointer.dmi b/icons/effects/mouse_pointers/weapon_pointer.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b5070062c0bb6c7a06e95dc52b8cc1631daada63 GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxE?rg;HIQuav+;EP=v80 z$S;_|;n|HeASb;lB%;J6wK%ybv!En1KaYW-Voq>aK~d@VFTn*Lzdq6O*3~+9=6vvm zP=kxc4<6~9_t8AbP;|y;hlO#F@nvH#g*lHV9hnrO5Ugpa?%vsLth#xH#0ldkk9@Sf z&iJ-&m?|3cH|9Va6T?pyNk5zFnyWx7-Lo_BPCrEHL{A4JVaAEj;xc$FCk^t+A d7ta`37=%BFn=Y<3a|bGA@O1TaS?83{1OTnfQ!4-f literal 0 HcmV?d00001 From defbd507baec682d592ad7b2d128fbcbe8237f87 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 9 Sep 2022 01:23:14 -0700 Subject: [PATCH 02/12] Update tgstation.dme --- tgstation.dme | 1 + 1 file changed, 1 insertion(+) diff --git a/tgstation.dme b/tgstation.dme index 67c3f21514..f1e5e5bf27 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -536,6 +536,7 @@ #include "code\datums\components\field_of_vision.dm" #include "code\datums\components\footstep.dm" #include "code\datums\components\fried.dm" +#include "code\datums\components\fullauto.dm" #include "code\datums\components\gps.dm" #include "code\datums\components\honkspam.dm" #include "code\datums\components\identification.dm" From f2ed6a571c5ef9f4a4947785fb0a0123bb02c01d Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 9 Sep 2022 18:04:52 -0700 Subject: [PATCH 03/12] this gets it to the point it compiles --- code/__DEFINES/dcs/signals.dm | 12 ++- code/__HELPERS/unsorted.dm | 5 ++ code/_onclick/drag_drop.dm | 19 +++-- code/datums/components/fullauto.dm | 82 +++++++++++++------ code/modules/client/client_procs.dm | 5 +- code/modules/mob/mob.dm | 8 +- code/modules/projectiles/gun.dm | 2 + .../projectiles/guns/ballistic/automatic.dm | 14 ++-- 8 files changed, 107 insertions(+), 40 deletions(-) diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 9de0dbe7c5..47b3648054 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -348,12 +348,22 @@ #define COMSIG_ATOM_UPDATE_LIGHT_FLAGS "atom_update_light_flags" // /client signals -#define COMSIG_MOB_CLIENT_LOGIN "mob_client_login" //sent when a mob/login() finishes: (client) #define COMSIG_MOB_CLIENT_LOGOUT "mob_client_logout" //sent when a mob/logout() starts: (client) #define COMSIG_MOB_CLIENT_MOVE "mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc) #define COMSIG_MOB_CLIENT_CHANGE_VIEW "mob_client_change_view" //from base of /client/change_view(): (client, old_view, view) #define COMSIG_MOB_CLIENT_MOUSEMOVE "mob_client_mousemove" //from base of /client/MouseMove(): (object, location, control, params) +///sent when a mob/login() finishes: (client) +#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" +//from base of client/MouseDown(): (/client, object, location, control, params) +#define COMSIG_CLIENT_MOUSEDOWN "client_mousedown" +//from base of client/MouseUp(): (/client, object, location, control, params) +#define COMSIG_CLIENT_MOUSEUP "client_mouseup" + #define COMPONENT_CLIENT_MOUSEUP_INTERCEPT (1<<0) +//from base of client/MouseUp(): (/client, object, location, control, params) +#define COMSIG_CLIENT_MOUSEDRAG "client_mousedrag" + + // /mob/living signals #define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs) #define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 428784e953..151cd17540 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -673,6 +673,11 @@ Turf and target are separate in case you want to teleport some distance from a t if(final_x || final_y) return locate(final_x, final_y, T.z) +///Returns a turf based on text inputs, original turf and viewing client +/proc/parse_caught_click_modifiers(list/modifiers, turf/origin, client/viewing_client) + if(!modifiers) + return null + //Finds the distance between two atoms, in pixels //centered = FALSE counts from turf edge to edge //centered = TRUE counts from turf center to turf center diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index fa529d0bfd..a698706f1a 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -23,23 +23,31 @@ SEND_SIGNAL(src, COMSIG_MOUSEDROPPED_ONTO, dropping, user) return - -/client/MouseDown(object, location, control, params) - if (mouse_down_icon) +/client/MouseDown(datum/object, location, control, params) + if(!control) + return + if(QDELETED(object)) //Yep, you can click on qdeleted things before they have time to nullspace. Fun. + return + SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEDOWN, object, location, control, params) + if(mouse_down_icon) mouse_pointer_icon = mouse_down_icon var/delay = mob.CanMobAutoclick(object, location, params) if(delay) selected_target[1] = object selected_target[2] = params while(selected_target[1]) - Click(selected_target[1], location, control, selected_target[2], TRUE) + Click(selected_target[1], location, control, selected_target[2]) sleep(delay) active_mousedown_item = mob.canMobMousedown(object, location, params) if(active_mousedown_item) active_mousedown_item.onMouseDown(object, location, params, mob) /client/MouseUp(object, location, control, params) - if (mouse_up_icon) + if(!control) + return + if(SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEUP, object, location, control, params) & COMPONENT_CLIENT_MOUSEUP_INTERCEPT) + click_intercept_time = world.time + if(mouse_up_icon) mouse_pointer_icon = mouse_up_icon selected_target[1] = null if(active_mousedown_item) @@ -107,6 +115,7 @@ selected_target[2] = params if(active_mousedown_item) active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob) + SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEDRAG, src_object, over_object, src_location, over_location, src_control, over_control, params) /obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob) return diff --git a/code/datums/components/fullauto.dm b/code/datums/components/fullauto.dm index 2d00783e30..fb94688cd8 100644 --- a/code/datums/components/fullauto.dm +++ b/code/datums/components/fullauto.dm @@ -11,16 +11,34 @@ var/autofire_shot_delay = 0.3 SECONDS //Time between individual shots. var/mouse_status = AUTOFIRE_MOUSEUP //This seems hacky but there can be two MouseDown() without a MouseUp() in between if the user holds click and uses alt+tab, printscreen or similar. + ///windup autofire vars + ///Whether the delay between shots increases over time, simulating a spooling weapon + var/windup_autofire = FALSE + ///the reduction to shot delay for windup + var/current_windup_reduction = 0 + ///the percentage of autfire_shot_delay that is added to current_windup_reduction + var/windup_autofire_reduction_multiplier = 0.3 + ///How high of a reduction that current_windup_reduction can reach + var/windup_autofire_cap = 0.3 + ///How long it takes for weapons that have spooled-up to reset back to the original firing speed + var/windup_spindown = 3 SECONDS + ///Timer for tracking the spindown reset timings + var/timerid COOLDOWN_DECLARE(next_shot_cd) -/datum/component/automatic_fire/Initialize(_autofire_shot_delay) +/datum/component/automatic_fire/Initialize(autofire_shot_delay, windup_autofire, windup_autofire_reduction_multiplier, windup_autofire_cap, windup_spindown) . = ..() if(!isgun(parent)) return COMPONENT_INCOMPATIBLE var/obj/item/gun = parent RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/wake_up) - if(_autofire_shot_delay) - autofire_shot_delay = _autofire_shot_delay + if(autofire_shot_delay) + src.autofire_shot_delay = autofire_shot_delay + if(windup_autofire) + src.windup_autofire = windup_autofire + src.windup_autofire_reduction_multiplier = windup_autofire_reduction_multiplier + src.windup_autofire_cap = windup_autofire_cap + src.windup_spindown = windup_spindown if(autofire_stat == AUTOFIRE_STAT_IDLE && ismob(gun.loc)) var/mob/user = gun.loc wake_up(src, user) @@ -61,9 +79,9 @@ shooter = clicker.mob RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down) if(!QDELETED(shooter)) - RegisterSignal(shooter, COMSIG_MOB_LOGOUT, .proc/autofire_off) - UnregisterSignal(shooter, COMSIG_MOB_LOGIN) - RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED), .proc/autofire_off) + RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT, .proc/autofire_off) + UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN) + RegisterSignal(parent, list(COMSIG_PARENT_QDELETING, 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) @@ -82,9 +100,9 @@ mouse_status = AUTOFIRE_MOUSEUP //In regards to the component there's no click anymore to care about. clicker = null if(!QDELETED(shooter)) - RegisterSignal(shooter, COMSIG_MOB_LOGIN, .proc/on_client_login) - UnregisterSignal(shooter, COMSIG_MOB_LOGOUT) - UnregisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED)) + RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN, .proc/on_client_login) + UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT) + UnregisterSignal(parent, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED)) shooter = null parent.UnregisterSignal(parent, COMSIG_AUTOFIRE_SHOT) parent.UnregisterSignal(src, COMSIG_AUTOFIRE_ONMOUSEDOWN) @@ -97,29 +115,31 @@ autofire_on(source.client) /datum/component/automatic_fire/proc/on_mouse_down(client/source, atom/_target, turf/location, control, params) + SIGNAL_HANDLER var/list/modifiers = params2list(params) //If they're shift+clicking, for example, let's not have them accidentally shoot. - if(LAZYACCESS(modifiers, SHIFT_CLICK)) + if(LAZYACCESS(modifiers, COMSIG_CLICK_SHIFT)) return - if(LAZYACCESS(modifiers, CTRL_CLICK)) + if(LAZYACCESS(modifiers, COMSIG_CLICK_CTRL)) return - if(LAZYACCESS(modifiers, MIDDLE_CLICK)) + if(LAZYACCESS(modifiers, MOUSE_MIDDLE_BUTTON)) return - if(LAZYACCESS(modifiers, RIGHT_CLICK)) + if(LAZYACCESS(modifiers, MOUSE_RIGHT_BUTTON)) return - if(LAZYACCESS(modifiers, ALT_CLICK)) + if(LAZYACCESS(modifiers, COMSIG_CLICK_ALT)) return - if(source.mob.throw_mode) + if(source.mob.in_throw_mode) return if(!isturf(source.mob.loc)) //No firing inside lockers and stuff. return if(get_dist(source.mob, _target) < 2) //Adjacent clicking. return - if(isnull(location)) //Clicking on a screen object. + if(isnull(location) || istype(_target, /atom/movable/screen)) //Clicking on a screen object. if(_target.plane != CLICKCATCHER_PLANE) //The clickcatcher is a special case. We want the click to trigger then, under it. return //If we click and drag on our worn backpack, for example, we want it to open instead. - _target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source) + _target = parse_caught_click_modifiers(modifiers, get_turf(source.eye), source) + params = list2params(modifiers) if(!_target) CRASH("Failed to get the turf under clickcatcher") @@ -136,7 +156,7 @@ target = _target target_loc = get_turf(target) mouse_parameters = params - start_autofiring() + INVOKE_ASYNC(src, .proc/start_autofiring) //Dakka-dakka @@ -198,7 +218,8 @@ SIGNAL_HANDLER if(isnull(over_location)) //This happens when the mouse is over an inventory or screen object, or on entering deep darkness, for example. var/list/modifiers = params2list(params) - var/new_target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source) + var/new_target = parse_caught_click_modifiers(modifiers, get_turf(source.eye), source) + params = list2params(modifiers) mouse_parameters = params if(!new_target) if(QDELETED(target)) //No new target acquired, and old one was deleted, get us out of here. @@ -229,23 +250,38 @@ stop_autofiring() //Elvis has left the building. return FALSE shooter.face_atom(target) - COOLDOWN_START(src, next_shot_cd, autofire_shot_delay) + var/next_delay = autofire_shot_delay + if(windup_autofire) + next_delay = clamp(next_delay - current_windup_reduction, round(autofire_shot_delay * windup_autofire_cap), autofire_shot_delay) + current_windup_reduction = (current_windup_reduction + round(autofire_shot_delay * windup_autofire_reduction_multiplier)) + timerid = addtimer(CALLBACK(src, .proc/windup_reset, FALSE), windup_spindown, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) +/* + if(HAS_TRAIT(shooter, TRAIT_DOUBLE_TAP)) + next_delay = round(next_delay * 0.5) +*/ + COOLDOWN_START(src, next_shot_cd, next_delay) if(SEND_SIGNAL(parent, COMSIG_AUTOFIRE_SHOT, target, shooter, mouse_parameters) & COMPONENT_AUTOFIRE_SHOT_SUCCESS) return TRUE stop_autofiring() return FALSE +/// Reset for our windup, resetting everything back to initial values after a variable set amount of time (determined by var/windup_spindown). +/datum/component/automatic_fire/proc/windup_reset(deltimer) + current_windup_reduction = initial(current_windup_reduction) + if(deltimer && timerid) + deltimer(timerid) + // Gun procs. /obj/item/gun/proc/on_autofire_start(mob/living/shooter) - if(semicd || shooter.stat || !can_trigger_gun(shooter)) + if(semicd || shooter.incapacitated() || !can_trigger_gun(shooter)) return FALSE if(!can_shoot()) shoot_with_empty_chamber(shooter) 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)) - to_chat(shooter, "You need two hands to fire [src]!") + to_chat(shooter, span_warning("You need two hands to fire [src]!")) return FALSE return TRUE @@ -258,7 +294,7 @@ /obj/item/gun/proc/do_autofire(datum/source, atom/target, mob/living/shooter, params) SIGNAL_HANDLER - if(semicd || shooter.stat) + if(semicd || shooter.incapacitated()) return NONE if(!can_shoot()) shoot_with_empty_chamber(shooter) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 9ab2be033a..4f956b5964 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -867,10 +867,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( ip_intel = res.intel /client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE, extra_info) - /*if(last_click > world.time - world.tick_lag) + if(last_click > world.time - world.tick_lag) return last_activity = world.time - last_click = world.time*/ + last_click = world.time + //fullauto stuff if(!control) return if(click_intercept_time) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 91ff5f4fbf..507a94b613 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -875,13 +875,13 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) L.alpha = lighting_alpha /mob/proc/update_mouse_pointer() - if(!client) + if (!client) return client.mouse_pointer_icon = initial(client.mouse_pointer_icon) if(istype(loc, /obj/vehicle/sealed)) - var/obj/vehicle/sealed/mecha/M = loc - if(M.mouse_pointer) - client.mouse_pointer_icon = M.mouse_pointer + var/obj/vehicle/sealed/mecha/E = loc + if(E.mouse_pointer) + client.mouse_pointer_icon = E.mouse_pointer if(client.mouse_override_icon) client.mouse_pointer_icon = client.mouse_override_icon diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 06468685c5..41356583e6 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -57,6 +57,8 @@ var/burst_spread = 0 //Spread induced by the gun itself during burst fire per iteration. Only checked if spread is 0. var/randomspread = 1 //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once. var/inaccuracy_modifier = 1 + var/semicd = 0 //cooldown handler + var/dual_wield_spread = 24 //additional spread when dual wielding lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 6487b57a9e..dd68ed9817 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -15,6 +15,11 @@ fire_sound = "sound/weapons/gunshot_smg_alt.ogg" mag_type = /obj/item/ammo_box/magazine/smgm9mm 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 @@ -309,16 +314,15 @@ actions_types = list() spread = 7 pin = /obj/item/firing_pin/implant/pindicate - automatic_burst_overlay = FALSE var/cover_open = FALSE +/obj/item/gun/ballistic/automatic/l6_saw/Initialize() + . = ..() + AddElement(/datum/element/update_icon_updates_onmob) + AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) /obj/item/gun/ballistic/automatic/l6_saw/unrestricted pin = /obj/item/firing_pin -/obj/item/gun/ballistic/automatic/l6_saw/ComponentInitialize() - . = ..() - AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) - /obj/item/gun/ballistic/automatic/l6_saw/examine(mob/user) . = ..() if(cover_open && magazine) From 1130a9341e741a9393bc560bc1a8815337678c7d Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 9 Sep 2022 20:02:45 -0700 Subject: [PATCH 04/12] now it actually works --- code/__DEFINES/misc.dm | 17 +++++++ code/__HELPERS/unsorted.dm | 16 ++++++ code/datums/components/fullauto.dm | 81 +++++++++--------------------- 3 files changed, 56 insertions(+), 58 deletions(-) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index a88f40a19f..6607b91695 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -388,6 +388,23 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define BEAT_SLOW 2 #define BEAT_NONE 0 +//Mouse buttons pressed/held/released +#define RIGHT_CLICK "right" +#define MIDDLE_CLICK "middle" +#define LEFT_CLICK "left" + +//Keys held down during the mouse action +#define CTRL_CLICK "ctrl" +#define ALT_CLICK "alt" +#define SHIFT_CLICK "shift" + +//Pixel coordinates within the icon, in the icon's coordinate space +#define ICON_X "icon-x" +#define ICON_Y "icon-y" + +//Pixel coordinates in screen_loc format ("[tile_x]:[pixel_x],[tile_y]:[pixel_y]") +#define SCREEN_LOC "screen-loc" + //https://secure.byond.com/docs/ref/info.html#/atom/var/mouse_opacity #define MOUSE_OPACITY_TRANSPARENT 0 #define MOUSE_OPACITY_ICON 1 diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 151cd17540..73b2dd3006 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -678,6 +678,22 @@ Turf and target are separate in case you want to teleport some distance from a t if(!modifiers) return null + var/screen_loc = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",") + var/list/actual_view = getviewsize(viewing_client ? viewing_client.view : world.view) + var/click_turf_x = splittext(screen_loc[1], ":") + var/click_turf_y = splittext(screen_loc[2], ":") + var/click_turf_z = origin.z + + var/click_turf_px = text2num(click_turf_x[2]) + var/click_turf_py = text2num(click_turf_y[2]) + click_turf_x = origin.x + text2num(click_turf_x[1]) - round(actual_view[1] / 2) - 1 + click_turf_y = origin.y + text2num(click_turf_y[1]) - round(actual_view[2] / 2) - 1 + + var/turf/click_turf = locate(clamp(click_turf_x, 1, world.maxx), clamp(click_turf_y, 1, world.maxy), click_turf_z) + LAZYSET(modifiers, ICON_X, "[(click_turf_px - click_turf.pixel_x) + ((click_turf_x - click_turf.x) * world.icon_size)]") + LAZYSET(modifiers, ICON_Y, "[(click_turf_py - click_turf.pixel_y) + ((click_turf_y - click_turf.y) * world.icon_size)]") + return click_turf + //Finds the distance between two atoms, in pixels //centered = FALSE counts from turf edge to edge //centered = TRUE counts from turf center to turf center diff --git a/code/datums/components/fullauto.dm b/code/datums/components/fullauto.dm index fb94688cd8..1882d0b511 100644 --- a/code/datums/components/fullauto.dm +++ b/code/datums/components/fullauto.dm @@ -1,3 +1,4 @@ + #define AUTOFIRE_MOUSEUP 0 #define AUTOFIRE_MOUSEDOWN 1 @@ -11,34 +12,16 @@ var/autofire_shot_delay = 0.3 SECONDS //Time between individual shots. var/mouse_status = AUTOFIRE_MOUSEUP //This seems hacky but there can be two MouseDown() without a MouseUp() in between if the user holds click and uses alt+tab, printscreen or similar. - ///windup autofire vars - ///Whether the delay between shots increases over time, simulating a spooling weapon - var/windup_autofire = FALSE - ///the reduction to shot delay for windup - var/current_windup_reduction = 0 - ///the percentage of autfire_shot_delay that is added to current_windup_reduction - var/windup_autofire_reduction_multiplier = 0.3 - ///How high of a reduction that current_windup_reduction can reach - var/windup_autofire_cap = 0.3 - ///How long it takes for weapons that have spooled-up to reset back to the original firing speed - var/windup_spindown = 3 SECONDS - ///Timer for tracking the spindown reset timings - var/timerid COOLDOWN_DECLARE(next_shot_cd) -/datum/component/automatic_fire/Initialize(autofire_shot_delay, windup_autofire, windup_autofire_reduction_multiplier, windup_autofire_cap, windup_spindown) +/datum/component/automatic_fire/Initialize(_autofire_shot_delay) . = ..() if(!isgun(parent)) return COMPONENT_INCOMPATIBLE var/obj/item/gun = parent RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/wake_up) - if(autofire_shot_delay) - src.autofire_shot_delay = autofire_shot_delay - if(windup_autofire) - src.windup_autofire = windup_autofire - src.windup_autofire_reduction_multiplier = windup_autofire_reduction_multiplier - src.windup_autofire_cap = windup_autofire_cap - src.windup_spindown = windup_spindown + if(_autofire_shot_delay) + autofire_shot_delay = _autofire_shot_delay if(autofire_stat == AUTOFIRE_STAT_IDLE && ismob(gun.loc)) var/mob/user = gun.loc wake_up(src, user) @@ -79,9 +62,9 @@ shooter = clicker.mob RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down) if(!QDELETED(shooter)) - RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT, .proc/autofire_off) - UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN) - RegisterSignal(parent, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED), .proc/autofire_off) + 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) @@ -100,9 +83,9 @@ mouse_status = AUTOFIRE_MOUSEUP //In regards to the component there's no click anymore to care about. clicker = null if(!QDELETED(shooter)) - RegisterSignal(shooter, COMSIG_MOB_CLIENT_LOGIN, .proc/on_client_login) - UnregisterSignal(shooter, COMSIG_MOB_CLIENT_LOGOUT) - UnregisterSignal(parent, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED)) + 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) @@ -115,18 +98,17 @@ autofire_on(source.client) /datum/component/automatic_fire/proc/on_mouse_down(client/source, atom/_target, turf/location, control, params) - SIGNAL_HANDLER var/list/modifiers = params2list(params) //If they're shift+clicking, for example, let's not have them accidentally shoot. - if(LAZYACCESS(modifiers, COMSIG_CLICK_SHIFT)) + if(LAZYACCESS(modifiers, SHIFT_CLICK)) return - if(LAZYACCESS(modifiers, COMSIG_CLICK_CTRL)) + if(LAZYACCESS(modifiers, CTRL_CLICK)) return - if(LAZYACCESS(modifiers, MOUSE_MIDDLE_BUTTON)) + if(LAZYACCESS(modifiers, MIDDLE_CLICK)) return - if(LAZYACCESS(modifiers, MOUSE_RIGHT_BUTTON)) + if(LAZYACCESS(modifiers, RIGHT_CLICK)) return - if(LAZYACCESS(modifiers, COMSIG_CLICK_ALT)) + if(LAZYACCESS(modifiers, ALT_CLICK)) return if(source.mob.in_throw_mode) return @@ -135,11 +117,10 @@ if(get_dist(source.mob, _target) < 2) //Adjacent clicking. return - if(isnull(location) || istype(_target, /atom/movable/screen)) //Clicking on a screen object. + if(isnull(location)) //Clicking on a screen object. if(_target.plane != CLICKCATCHER_PLANE) //The clickcatcher is a special case. We want the click to trigger then, under it. return //If we click and drag on our worn backpack, for example, we want it to open instead. - _target = parse_caught_click_modifiers(modifiers, get_turf(source.eye), source) - params = list2params(modifiers) + _target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source) if(!_target) CRASH("Failed to get the turf under clickcatcher") @@ -156,7 +137,7 @@ target = _target target_loc = get_turf(target) mouse_parameters = params - INVOKE_ASYNC(src, .proc/start_autofiring) + start_autofiring() //Dakka-dakka @@ -218,8 +199,7 @@ SIGNAL_HANDLER if(isnull(over_location)) //This happens when the mouse is over an inventory or screen object, or on entering deep darkness, for example. var/list/modifiers = params2list(params) - var/new_target = parse_caught_click_modifiers(modifiers, get_turf(source.eye), source) - params = list2params(modifiers) + var/new_target = params2turf(modifiers["screen-loc"], get_turf(source.eye), source) mouse_parameters = params if(!new_target) if(QDELETED(target)) //No new target acquired, and old one was deleted, get us out of here. @@ -250,38 +230,23 @@ stop_autofiring() //Elvis has left the building. return FALSE shooter.face_atom(target) - var/next_delay = autofire_shot_delay - if(windup_autofire) - next_delay = clamp(next_delay - current_windup_reduction, round(autofire_shot_delay * windup_autofire_cap), autofire_shot_delay) - current_windup_reduction = (current_windup_reduction + round(autofire_shot_delay * windup_autofire_reduction_multiplier)) - timerid = addtimer(CALLBACK(src, .proc/windup_reset, FALSE), windup_spindown, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) -/* - if(HAS_TRAIT(shooter, TRAIT_DOUBLE_TAP)) - next_delay = round(next_delay * 0.5) -*/ - COOLDOWN_START(src, next_shot_cd, next_delay) + COOLDOWN_START(src, next_shot_cd, autofire_shot_delay) if(SEND_SIGNAL(parent, COMSIG_AUTOFIRE_SHOT, target, shooter, mouse_parameters) & COMPONENT_AUTOFIRE_SHOT_SUCCESS) return TRUE stop_autofiring() return FALSE -/// Reset for our windup, resetting everything back to initial values after a variable set amount of time (determined by var/windup_spindown). -/datum/component/automatic_fire/proc/windup_reset(deltimer) - current_windup_reduction = initial(current_windup_reduction) - if(deltimer && timerid) - deltimer(timerid) - // Gun procs. /obj/item/gun/proc/on_autofire_start(mob/living/shooter) - if(semicd || shooter.incapacitated() || !can_trigger_gun(shooter)) + if(semicd || shooter.stat || !can_trigger_gun(shooter)) return FALSE if(!can_shoot()) shoot_with_empty_chamber(shooter) 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)) - to_chat(shooter, span_warning("You need two hands to fire [src]!")) + to_chat(shooter, "You need two hands to fire [src]!") return FALSE return TRUE @@ -294,7 +259,7 @@ /obj/item/gun/proc/do_autofire(datum/source, atom/target, mob/living/shooter, params) SIGNAL_HANDLER - if(semicd || shooter.incapacitated()) + if(semicd || shooter.stat) return NONE if(!can_shoot()) shoot_with_empty_chamber(shooter) From bc13bdb177d141d5bac58b0732d76f2d7aa91774 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 9 Sep 2022 22:23:05 -0700 Subject: [PATCH 05/12] sort of working fire selector, still need to get the safety stuff figured out and finish getting the selector working properly --- code/__DEFINES/dcs/signals.dm | 6 + code/__DEFINES/gun.dm | 3 + code/datums/action.dm | 9 ++ code/modules/projectiles/gun.dm | 117 +++++++++++++++++- .../projectiles/guns/ballistic/automatic.dm | 2 +- .../projectiles/guns/ballistic/launchers.dm | 2 +- .../projectiles/guns/ballistic/pistol.dm | 6 +- icons/hud/actions.dmi | Bin 0 -> 392 bytes icons/mob/actions/actions_items.dmi | Bin 15268 -> 16163 bytes tgstation.dme | 1 + 10 files changed, 136 insertions(+), 10 deletions(-) create mode 100644 code/__DEFINES/gun.dm create mode 100644 icons/hud/actions.dmi 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 0000000000000000000000000000000000000000..5d718bc9e0b62dbb73800aa62c77aa6eae7abcea GIT binary patch literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!VDw>HYaZfQW60^A+84w95DXR5ET^_7#Qg9 z@88hS;Ns%)pMgQ-bxko)4P!}=UoeBivm0qZPH|O8M2SmkadJ^+K}lwQ9s@(goZzs6 zqSEhQf(t%=eWK;9t99jJaWn(XeIgchCnG~WB zywbu@ck2=(JA41mZe!KWDjp;1r zydbyC_jGX#vFJ@sSfC{!qO!oDVFD+MsKAz{M#1?ilbIMzopr06dG3djJ3c literal 0 HcmV?d00001 diff --git a/icons/mob/actions/actions_items.dmi b/icons/mob/actions/actions_items.dmi index 10627cf66d405ff8cc96bf30e8a96db26306ee4b..2eb92f4a453629868dbdcf2da59b8970626f71b2 100644 GIT binary patch literal 16163 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%0DNvQOAB3s(tOwV+6PgX60oyUT%*mT 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 X{rhAnd$|yQeg|I5C`*@08VCMgGJ5R8 literal 15268 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 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" From 91c095989a15635aad660363f4ae000c7706119e Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 16 Sep 2022 17:55:10 -0700 Subject: [PATCH 06/12] stuff --- code/datums/action.dm | 5 ++ code/datums/components/fullauto.dm | 77 +++++++++--------- code/modules/projectiles/gun.dm | 79 ++++++++++--------- .../projectiles/guns/ballistic/automatic.dm | 44 +---------- 4 files changed, 85 insertions(+), 120 deletions(-) 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" From 29d16e5f309412f5cde0b26515eb735a3fbf39e7 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 16 Sep 2022 19:24:06 -0700 Subject: [PATCH 07/12] fixes weapon safety and fire selector button. --- code/modules/projectiles/gun.dm | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index dd54088f1f..e8ab864ef4 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -93,7 +93,7 @@ //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/has_gun_safety = FALSE/// Whether the gun actually has a gun safety var/datum/action/item_action/toggle_safety/toggle_safety_action //Firemodes @@ -116,10 +116,17 @@ /obj/item/gun/ui_action_click(mob/user, action) if(istype(action, /datum/action/item_action/toggle_firemode)) + message_admins("Fireselector button hit") fire_select() else if(istype(action, /datum/action/item_action/toggle_safety)) toggle_safety(user) + message_admins("Safety button hit") + else if(istype(action, /datum/action/item_action/toggle_scope_zoom)) + zoom(user, user.dir) + else if(istype(action, alight)) + toggle_gunlight() else + message_admins("something hit wrong with full auto") ..() /obj/item/gun/Initialize(mapload) @@ -676,12 +683,6 @@ gun_light = new_light -/obj/item/gun/ui_action_click(mob/user, action) - if(istype(action, /datum/action/item_action/toggle_scope_zoom)) - zoom(user, user.dir) - else if(istype(action, alight)) - toggle_gunlight() - /obj/item/gun/proc/toggle_gunlight() if(!gun_light) return From 9ca985b769c93c3457c465d3973f36f71a8bd109 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 16 Sep 2022 19:24:21 -0700 Subject: [PATCH 08/12] adds gun safeties, remember to turn them off. --- .../projectiles/guns/ballistic/automatic.dm | 1 + code/modules/projectiles/guns/ballistic/bow.dm | 1 + .../projectiles/guns/ballistic/derringer.dm | 1 + .../projectiles/guns/ballistic/launchers.dm | 3 +++ .../projectiles/guns/ballistic/magweapon.dm | 1 + .../modules/projectiles/guns/ballistic/revolver.dm | 1 + code/modules/projectiles/guns/ballistic/shotgun.dm | 1 + code/modules/projectiles/guns/ballistic/toy.dm | 1 + code/modules/projectiles/guns/energy/dueling.dm | 1 + code/modules/projectiles/guns/energy/energy_gun.dm | 1 + .../projectiles/guns/energy/kinetic_accelerator.dm | 1 + code/modules/projectiles/guns/energy/laser.dm | 3 +++ code/modules/projectiles/guns/energy/megabuster.dm | 1 + code/modules/projectiles/guns/energy/mounted.dm | 2 ++ code/modules/projectiles/guns/energy/plasma_cit.dm | 1 + code/modules/projectiles/guns/energy/pulse.dm | 1 + code/modules/projectiles/guns/energy/special.dm | 14 ++++++++++++++ code/modules/projectiles/guns/misc/beam_rifle.dm | 1 + code/modules/projectiles/guns/misc/blastcannon.dm | 1 + code/modules/projectiles/guns/misc/chameleon.dm | 1 + code/modules/projectiles/guns/misc/chem_gun.dm | 2 ++ .../projectiles/guns/misc/grenade_launcher.dm | 1 + code/modules/projectiles/guns/misc/medbeam.dm | 1 + code/modules/projectiles/guns/misc/syringe_gun.dm | 2 ++ 24 files changed, 44 insertions(+) diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 4d08520246..150d07e745 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -7,6 +7,7 @@ burst_size = 3 fire_delay = 2 fire_select_modes = list(SELECT_SEMI_AUTOMATIC, SELECT_BURST_SHOT, SELECT_FULLY_AUTOMATIC) + has_gun_safety = TRUE /obj/item/gun/ballistic/automatic/proto name = "\improper Nanotrasen Saber SMG" diff --git a/code/modules/projectiles/guns/ballistic/bow.dm b/code/modules/projectiles/guns/ballistic/bow.dm index dbf249b3f8..714a4e10a8 100644 --- a/code/modules/projectiles/guns/ballistic/bow.dm +++ b/code/modules/projectiles/guns/ballistic/bow.dm @@ -15,6 +15,7 @@ pin = null no_pin_required = TRUE trigger_guard = TRIGGER_GUARD_NONE //so ashwalkers can use it + has_gun_safety = FALSE //bows and arrows don't have safeties /obj/item/gun/ballistic/bow/shoot_with_empty_chamber() return diff --git a/code/modules/projectiles/guns/ballistic/derringer.dm b/code/modules/projectiles/guns/ballistic/derringer.dm index 47213f17df..186b2b8edd 100644 --- a/code/modules/projectiles/guns/ballistic/derringer.dm +++ b/code/modules/projectiles/guns/ballistic/derringer.dm @@ -10,6 +10,7 @@ fire_sound = 'sound/weapons/revolvershot.ogg' casing_ejector = FALSE w_class = WEIGHT_CLASS_TINY + has_gun_safety = TRUE /obj/item/gun/ballistic/derringer/get_ammo(countchambered = FALSE, countempties = TRUE) var/boolets = 0 //legacy var name maturity diff --git a/code/modules/projectiles/guns/ballistic/launchers.dm b/code/modules/projectiles/guns/ballistic/launchers.dm index 10a6eea89d..57f6932bff 100644 --- a/code/modules/projectiles/guns/ballistic/launchers.dm +++ b/code/modules/projectiles/guns/ballistic/launchers.dm @@ -11,6 +11,7 @@ fire_sound = 'sound/weapons/grenadelaunch.ogg' w_class = WEIGHT_CLASS_NORMAL pin = /obj/item/firing_pin/implant/pindicate + has_gun_safety = FALSE /obj/item/gun/ballistic/revolver/grenadelauncher/unrestricted pin = /obj/item/firing_pin @@ -27,6 +28,7 @@ icon_state = "mecha_grenadelnchr" mag_type = /obj/item/ammo_box/magazine/internal/cylinder/grenademulti pin = /obj/item/firing_pin + has_gun_safety = FALSE /obj/item/gun/ballistic/revolver/grenadelauncher/cyborg/attack_self() return @@ -92,6 +94,7 @@ casing_ejector = FALSE weapon_weight = WEAPON_HEAVY magazine_wording = "rocket" + has_gun_safety = TRUE /obj/item/gun/ballistic/rocketlauncher/unrestricted pin = /obj/item/firing_pin diff --git a/code/modules/projectiles/guns/ballistic/magweapon.dm b/code/modules/projectiles/guns/ballistic/magweapon.dm index f3a11eb179..3150c09126 100644 --- a/code/modules/projectiles/guns/ballistic/magweapon.dm +++ b/code/modules/projectiles/guns/ballistic/magweapon.dm @@ -20,6 +20,7 @@ w_class = WEIGHT_CLASS_BULKY var/obj/item/stock_parts/cell/cell var/cell_type = /obj/item/stock_parts/cell/magnetic + has_gun_safety = TRUE /obj/item/gun/ballistic/automatic/magrifle/Initialize(mapload) . = ..() diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index f3b07aa7a4..f5dc57afa7 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -6,6 +6,7 @@ fire_sound = "sound/weapons/revolvershot.ogg" casing_ejector = FALSE recoil = 0.5 + has_gun_safety = TRUE /obj/item/gun/ballistic/revolver/Initialize(mapload) . = ..() diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index b52620e7e0..1d9ceacff5 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -15,6 +15,7 @@ var/clip_delay = CLICK_CD_MELEE weapon_weight = WEAPON_HEAVY sawn_item_state = "sawnshotgun" + has_gun_safety = TRUE /obj/item/gun/ballistic/shotgun/attackby(obj/item/A, mob/user, params) . = ..() diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm index a72a172891..0f8e3e8214 100644 --- a/code/modules/projectiles/guns/ballistic/toy.dm +++ b/code/modules/projectiles/guns/ballistic/toy.dm @@ -13,6 +13,7 @@ clumsy_check = 0 item_flags = NONE casing_ejector = FALSE + has_gun_safety = TRUE /obj/item/gun/ballistic/automatic/toy/unrestricted pin = /obj/item/firing_pin diff --git a/code/modules/projectiles/guns/energy/dueling.dm b/code/modules/projectiles/guns/energy/dueling.dm index b11e5533cc..653017e413 100644 --- a/code/modules/projectiles/guns/energy/dueling.dm +++ b/code/modules/projectiles/guns/energy/dueling.dm @@ -161,6 +161,7 @@ var/datum/duel/duel var/mutable_appearance/setting_overlay var/hugbox = DUEL_HUGBOX_NONE + has_gun_safety = TRUE /obj/item/gun/energy/dueling/hugbox hugbox = DUEL_HUGBOX_LETHAL diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm index a34e0f6f12..a5b47ff463 100644 --- a/code/modules/projectiles/guns/energy/energy_gun.dm +++ b/code/modules/projectiles/guns/energy/energy_gun.dm @@ -9,6 +9,7 @@ ammo_x_offset = 3 flight_x_offset = 15 flight_y_offset = 10 + has_gun_safety = TRUE /obj/item/gun/energy/e_gun/mini name = "miniature energy gun" diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 5fd158c028..68e5faa412 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -34,6 +34,7 @@ lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' ammo_type = list(/obj/item/ammo_casing/energy/kinetic/premium) + has_gun_safety = TRUE /obj/item/ammo_casing/energy/kinetic/premium projectile_type = /obj/item/projectile/kinetic/premium diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index f73ac9cfb8..89aeef8c48 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -8,6 +8,7 @@ ammo_type = list(/obj/item/ammo_casing/energy/lasergun) ammo_x_offset = 1 shaded_charge = 1 + has_gun_safety = TRUE /obj/item/gun/energy/laser/practice name = "practice laser gun" @@ -92,6 +93,7 @@ selfcharge = EGUN_SELFCHARGE_BORG cell_type = /obj/item/stock_parts/cell/secborg charge_delay = 3 + has_gun_safety = FALSE /obj/item/gun/energy/laser/cyborg/emp_act() return @@ -129,6 +131,7 @@ ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) pin = null ammo_x_offset = 3 + has_gun_safety = TRUE /obj/item/ammo_casing/energy/laser/accelerator projectile_type = /obj/item/projectile/beam/laser/accelerator diff --git a/code/modules/projectiles/guns/energy/megabuster.dm b/code/modules/projectiles/guns/energy/megabuster.dm index dddfd749de..a980cab6fa 100644 --- a/code/modules/projectiles/guns/energy/megabuster.dm +++ b/code/modules/projectiles/guns/energy/megabuster.dm @@ -10,6 +10,7 @@ selfcharge = EGUN_SELFCHARGE cell_type = "/obj/item/stock_parts/cell/pulse" icon = 'modular_citadel/icons/obj/guns/VGguns.dmi' + has_gun_safety = TRUE /obj/item/gun/energy/megabuster/proto name = "Proto-buster" diff --git a/code/modules/projectiles/guns/energy/mounted.dm b/code/modules/projectiles/guns/energy/mounted.dm index 25a94895f8..304b7f4dd9 100644 --- a/code/modules/projectiles/guns/energy/mounted.dm +++ b/code/modules/projectiles/guns/energy/mounted.dm @@ -8,6 +8,7 @@ selfcharge = EGUN_SELFCHARGE can_flashlight = 0 trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses neural signals instead + has_gun_safety = TRUE /obj/item/gun/energy/laser/mounted name = "mounted laser" @@ -18,3 +19,4 @@ force = 5 selfcharge = EGUN_SELFCHARGE trigger_guard = TRIGGER_GUARD_ALLOW_ALL + has_gun_safety = TRUE diff --git a/code/modules/projectiles/guns/energy/plasma_cit.dm b/code/modules/projectiles/guns/energy/plasma_cit.dm index 9f9340a725..852baabaa0 100644 --- a/code/modules/projectiles/guns/energy/plasma_cit.dm +++ b/code/modules/projectiles/guns/energy/plasma_cit.dm @@ -9,6 +9,7 @@ shaded_charge = 1 lefthand_file = 'modular_citadel/icons/mob/citadel/guns_lefthand.dmi' righthand_file = 'modular_citadel/icons/mob/citadel/guns_righthand.dmi' + has_gun_safety = TRUE /obj/item/gun/energy/plasma/rifle diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index d5dc39695b..08326794a7 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -10,6 +10,7 @@ slot_flags = ITEM_SLOT_BACK ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) cell_type = "/obj/item/stock_parts/cell/pulse" + has_gun_safety = TRUE /obj/item/gun/energy/pulse/emp_act(severity) return diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 67e7b58bd3..b19873236e 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -11,6 +11,7 @@ ammo_x_offset = 3 flight_x_offset = 17 flight_y_offset = 9 + has_gun_safety = TRUE /obj/item/gun/energy/ionrifle/emp_act(severity) return @@ -49,6 +50,7 @@ modifystate = 1 ammo_x_offset = 1 selfcharge = EGUN_SELFCHARGE + has_gun_safety = FALSE /obj/item/gun/energy/meteorgun name = "meteor gun" @@ -60,6 +62,7 @@ cell_type = "/obj/item/stock_parts/cell/potato" clumsy_check = 0 //Admin spawn only, might as well let clowns use it. selfcharge = EGUN_SELFCHARGE + has_gun_safety = TRUE /obj/item/gun/energy/meteorgun/pen name = "meteor pen" @@ -70,6 +73,7 @@ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' w_class = WEIGHT_CLASS_TINY + has_gun_safety = TRUE /obj/item/gun/energy/mindflayer name = "\improper Mind Flayer" @@ -78,6 +82,7 @@ item_state = null ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) ammo_x_offset = 2 + has_gun_safety = FALSE /obj/item/gun/energy/kinetic_accelerator/crossbow name = "mini energy crossbow" @@ -96,6 +101,7 @@ unique_frequency = TRUE can_flashlight = 0 max_mod_capacity = 0 + has_gun_safety = TRUE /obj/item/gun/energy/kinetic_accelerator/crossbow/halloween name = "candy corn crossbow" @@ -103,6 +109,7 @@ icon_state = "crossbow_halloween" item_state = "crossbow" ammo_type = list(/obj/item/ammo_casing/energy/bolt/halloween) + has_gun_safety = FALSE /obj/item/gun/energy/kinetic_accelerator/crossbow/large name = "energy crossbow" @@ -133,6 +140,7 @@ usesound = list('sound/items/welder.ogg', 'sound/items/welder2.ogg') tool_behaviour = TOOL_WELDER toolspeed = 0.7 //plasmacutters can be used as welders, and are faster than standard welders + has_gun_safety = FALSE /obj/item/gun/energy/plasmacutter/ComponentInitialize() . = ..() @@ -189,6 +197,7 @@ icon_state = "gravity_gun" var/power = 4 var/firing_core = FALSE + has_gun_safety = TRUE /obj/item/gun/energy/gravity_gun/attackby(obj/item/C, mob/user) if(istype(C, /obj/item/assembly/signaler/anomaly)) @@ -216,6 +225,7 @@ var/obj/effect/portal/p_orange var/atmos_link = FALSE var/firing_core = FALSE + has_gun_safety = TRUE /obj/item/gun/energy/wormhole_projector/attackby(obj/item/C, mob/user) if(istype(C, /obj/item/assembly/signaler/anomaly)) @@ -305,6 +315,7 @@ ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) can_charge = 0 use_cyborg_cell = 1 + has_gun_safety = FALSE /obj/item/gun/energy/printer/ComponentInitialize() . = ..() @@ -334,6 +345,7 @@ desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." ammo_type = list(/obj/item/ammo_casing/energy/instakill) force = 60 + has_gun_safety = TRUE /obj/item/gun/energy/laser/instakill/red desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." @@ -362,6 +374,7 @@ cell_type = /obj/item/stock_parts/cell/super ammo_type = list(/obj/item/ammo_casing/energy/emitter) automatic_charge_overlays = FALSE + has_gun_safety = TRUE /obj/item/gun/energy/emitter/update_icon_state() var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index] @@ -377,3 +390,4 @@ icon_state = "decloner" no_pin_required = TRUE ammo_type = list(/obj/item/ammo_casing/energy/pickle) + has_gun_safety = FALSE diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index 6f70998834..e64a77015d 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -31,6 +31,7 @@ canMouseDown = TRUE can_turret = FALSE can_circuit = FALSE + has_gun_safety = TRUE //Cit changes: beam rifle stats. slowdown = 1 item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | NEEDS_PERMIT diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm index a88f395561..c9848cfaab 100644 --- a/code/modules/projectiles/guns/misc/blastcannon.dm +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -10,6 +10,7 @@ item_flags = NONE clumsy_check = FALSE randomspread = FALSE + has_gun_safety = TRUE var/hugbox = TRUE var/max_power = INFINITY diff --git a/code/modules/projectiles/guns/misc/chameleon.dm b/code/modules/projectiles/guns/misc/chameleon.dm index 428de747f7..a097347b09 100644 --- a/code/modules/projectiles/guns/misc/chameleon.dm +++ b/code/modules/projectiles/guns/misc/chameleon.dm @@ -13,6 +13,7 @@ item_flags = NONE pin = /obj/item/firing_pin cell_type = /obj/item/stock_parts/cell/bluespace + var/datum/action/item_action/chameleon/change/gun/chameleon_action var/list/chameleon_projectile_vars diff --git a/code/modules/projectiles/guns/misc/chem_gun.dm b/code/modules/projectiles/guns/misc/chem_gun.dm index 9f0815ebfb..f4604c49e3 100644 --- a/code/modules/projectiles/guns/misc/chem_gun.dm +++ b/code/modules/projectiles/guns/misc/chem_gun.dm @@ -20,6 +20,7 @@ var/obj/item/reagent_containers/glass/bottle/vial/vial var/list/allowed_containers = list(/obj/item/reagent_containers/glass/bottle/vial/small, /obj/item/reagent_containers/glass/bottle/vial/large) var/quickload = TRUE + has_gun_safety = TRUE /obj/item/gun/chem/Initialize(mapload) . = ..() @@ -110,6 +111,7 @@ desc = "An experimental improved version of the smartdart rifle. It synthesizes medicinal smart darts which it fills using an inserted hypovial. It can accommodate both large and small hypovials." icon_state = "chemgunrepeater" item_state = "syringegun" + has_gun_safety = TRUE obj/item/gun/chem/smart/Initialize(mapload) . = ..() diff --git a/code/modules/projectiles/guns/misc/grenade_launcher.dm b/code/modules/projectiles/guns/misc/grenade_launcher.dm index 86dd0c6ce4..20448e26ef 100644 --- a/code/modules/projectiles/guns/misc/grenade_launcher.dm +++ b/code/modules/projectiles/guns/misc/grenade_launcher.dm @@ -11,6 +11,7 @@ var/list/grenades = new/list() var/max_grenades = 3 custom_materials = list(/datum/material/iron=2000) + has_gun_safety = TRUE /obj/item/gun/grenadelauncher/examine(mob/user) . = ..() diff --git a/code/modules/projectiles/guns/misc/medbeam.dm b/code/modules/projectiles/guns/misc/medbeam.dm index 4a64317fd9..1bff8548cc 100644 --- a/code/modules/projectiles/guns/misc/medbeam.dm +++ b/code/modules/projectiles/guns/misc/medbeam.dm @@ -13,6 +13,7 @@ var/active = 0 var/datum/beam/current_beam = null var/mounted = 0 //Denotes if this is a handheld or mounted version + has_gun_safety = FALSE //no safety here. weapon_weight = WEAPON_MEDIUM diff --git a/code/modules/projectiles/guns/misc/syringe_gun.dm b/code/modules/projectiles/guns/misc/syringe_gun.dm index 8de7633a79..581e7cd8d4 100644 --- a/code/modules/projectiles/guns/misc/syringe_gun.dm +++ b/code/modules/projectiles/guns/misc/syringe_gun.dm @@ -14,6 +14,7 @@ fire_sound = 'sound/items/syringeproj.ogg' var/list/syringes = list() var/max_syringes = 1 + has_gun_safety = TRUE /obj/item/gun/syringe/Initialize(mapload) . = ..() @@ -160,6 +161,7 @@ icon_state = "blowgun" item_state = "syringegun" fire_sound = 'sound/items/syringeproj.ogg' + has_gun_safety = FALSE //unga bunga, tube no fit safety /obj/item/gun/syringe/blowgun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, stam_cost = 0) visible_message("[user] starts aiming with a blowgun!") From 64ce47676416657fbb6feeb2114b5e4a91fc482d Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Fri, 16 Sep 2022 19:28:56 -0700 Subject: [PATCH 09/12] removes admin messaging --- code/modules/projectiles/gun.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index e8ab864ef4..50e1744f84 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -116,17 +116,14 @@ /obj/item/gun/ui_action_click(mob/user, action) if(istype(action, /datum/action/item_action/toggle_firemode)) - message_admins("Fireselector button hit") fire_select() else if(istype(action, /datum/action/item_action/toggle_safety)) toggle_safety(user) - message_admins("Safety button hit") else if(istype(action, /datum/action/item_action/toggle_scope_zoom)) zoom(user, user.dir) else if(istype(action, alight)) toggle_gunlight() else - message_admins("something hit wrong with full auto") ..() /obj/item/gun/Initialize(mapload) From 15006a5dfd8331ac7033d19206c8f846f57dbd04 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Sat, 17 Sep 2022 06:47:26 -0700 Subject: [PATCH 10/12] Revert "adds gun safeties, remember to turn them off." This reverts commit 9ca985b769c93c3457c465d3973f36f71a8bd109. --- .../projectiles/guns/ballistic/automatic.dm | 1 - code/modules/projectiles/guns/ballistic/bow.dm | 1 - .../projectiles/guns/ballistic/derringer.dm | 1 - .../projectiles/guns/ballistic/launchers.dm | 3 --- .../projectiles/guns/ballistic/magweapon.dm | 1 - .../modules/projectiles/guns/ballistic/revolver.dm | 1 - code/modules/projectiles/guns/ballistic/shotgun.dm | 1 - code/modules/projectiles/guns/ballistic/toy.dm | 1 - code/modules/projectiles/guns/energy/dueling.dm | 1 - code/modules/projectiles/guns/energy/energy_gun.dm | 1 - .../projectiles/guns/energy/kinetic_accelerator.dm | 1 - code/modules/projectiles/guns/energy/laser.dm | 3 --- code/modules/projectiles/guns/energy/megabuster.dm | 1 - code/modules/projectiles/guns/energy/mounted.dm | 2 -- code/modules/projectiles/guns/energy/plasma_cit.dm | 1 - code/modules/projectiles/guns/energy/pulse.dm | 1 - code/modules/projectiles/guns/energy/special.dm | 14 -------------- code/modules/projectiles/guns/misc/beam_rifle.dm | 1 - code/modules/projectiles/guns/misc/blastcannon.dm | 1 - code/modules/projectiles/guns/misc/chameleon.dm | 1 - code/modules/projectiles/guns/misc/chem_gun.dm | 2 -- .../projectiles/guns/misc/grenade_launcher.dm | 1 - code/modules/projectiles/guns/misc/medbeam.dm | 1 - code/modules/projectiles/guns/misc/syringe_gun.dm | 2 -- 24 files changed, 44 deletions(-) diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 150d07e745..4d08520246 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -7,7 +7,6 @@ burst_size = 3 fire_delay = 2 fire_select_modes = list(SELECT_SEMI_AUTOMATIC, SELECT_BURST_SHOT, SELECT_FULLY_AUTOMATIC) - has_gun_safety = TRUE /obj/item/gun/ballistic/automatic/proto name = "\improper Nanotrasen Saber SMG" diff --git a/code/modules/projectiles/guns/ballistic/bow.dm b/code/modules/projectiles/guns/ballistic/bow.dm index 714a4e10a8..dbf249b3f8 100644 --- a/code/modules/projectiles/guns/ballistic/bow.dm +++ b/code/modules/projectiles/guns/ballistic/bow.dm @@ -15,7 +15,6 @@ pin = null no_pin_required = TRUE trigger_guard = TRIGGER_GUARD_NONE //so ashwalkers can use it - has_gun_safety = FALSE //bows and arrows don't have safeties /obj/item/gun/ballistic/bow/shoot_with_empty_chamber() return diff --git a/code/modules/projectiles/guns/ballistic/derringer.dm b/code/modules/projectiles/guns/ballistic/derringer.dm index 186b2b8edd..47213f17df 100644 --- a/code/modules/projectiles/guns/ballistic/derringer.dm +++ b/code/modules/projectiles/guns/ballistic/derringer.dm @@ -10,7 +10,6 @@ fire_sound = 'sound/weapons/revolvershot.ogg' casing_ejector = FALSE w_class = WEIGHT_CLASS_TINY - has_gun_safety = TRUE /obj/item/gun/ballistic/derringer/get_ammo(countchambered = FALSE, countempties = TRUE) var/boolets = 0 //legacy var name maturity diff --git a/code/modules/projectiles/guns/ballistic/launchers.dm b/code/modules/projectiles/guns/ballistic/launchers.dm index 57f6932bff..10a6eea89d 100644 --- a/code/modules/projectiles/guns/ballistic/launchers.dm +++ b/code/modules/projectiles/guns/ballistic/launchers.dm @@ -11,7 +11,6 @@ fire_sound = 'sound/weapons/grenadelaunch.ogg' w_class = WEIGHT_CLASS_NORMAL pin = /obj/item/firing_pin/implant/pindicate - has_gun_safety = FALSE /obj/item/gun/ballistic/revolver/grenadelauncher/unrestricted pin = /obj/item/firing_pin @@ -28,7 +27,6 @@ icon_state = "mecha_grenadelnchr" mag_type = /obj/item/ammo_box/magazine/internal/cylinder/grenademulti pin = /obj/item/firing_pin - has_gun_safety = FALSE /obj/item/gun/ballistic/revolver/grenadelauncher/cyborg/attack_self() return @@ -94,7 +92,6 @@ casing_ejector = FALSE weapon_weight = WEAPON_HEAVY magazine_wording = "rocket" - has_gun_safety = TRUE /obj/item/gun/ballistic/rocketlauncher/unrestricted pin = /obj/item/firing_pin diff --git a/code/modules/projectiles/guns/ballistic/magweapon.dm b/code/modules/projectiles/guns/ballistic/magweapon.dm index 3150c09126..f3a11eb179 100644 --- a/code/modules/projectiles/guns/ballistic/magweapon.dm +++ b/code/modules/projectiles/guns/ballistic/magweapon.dm @@ -20,7 +20,6 @@ w_class = WEIGHT_CLASS_BULKY var/obj/item/stock_parts/cell/cell var/cell_type = /obj/item/stock_parts/cell/magnetic - has_gun_safety = TRUE /obj/item/gun/ballistic/automatic/magrifle/Initialize(mapload) . = ..() diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index f5dc57afa7..f3b07aa7a4 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -6,7 +6,6 @@ fire_sound = "sound/weapons/revolvershot.ogg" casing_ejector = FALSE recoil = 0.5 - has_gun_safety = TRUE /obj/item/gun/ballistic/revolver/Initialize(mapload) . = ..() diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 1d9ceacff5..b52620e7e0 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -15,7 +15,6 @@ var/clip_delay = CLICK_CD_MELEE weapon_weight = WEAPON_HEAVY sawn_item_state = "sawnshotgun" - has_gun_safety = TRUE /obj/item/gun/ballistic/shotgun/attackby(obj/item/A, mob/user, params) . = ..() diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm index 0f8e3e8214..a72a172891 100644 --- a/code/modules/projectiles/guns/ballistic/toy.dm +++ b/code/modules/projectiles/guns/ballistic/toy.dm @@ -13,7 +13,6 @@ clumsy_check = 0 item_flags = NONE casing_ejector = FALSE - has_gun_safety = TRUE /obj/item/gun/ballistic/automatic/toy/unrestricted pin = /obj/item/firing_pin diff --git a/code/modules/projectiles/guns/energy/dueling.dm b/code/modules/projectiles/guns/energy/dueling.dm index 653017e413..b11e5533cc 100644 --- a/code/modules/projectiles/guns/energy/dueling.dm +++ b/code/modules/projectiles/guns/energy/dueling.dm @@ -161,7 +161,6 @@ var/datum/duel/duel var/mutable_appearance/setting_overlay var/hugbox = DUEL_HUGBOX_NONE - has_gun_safety = TRUE /obj/item/gun/energy/dueling/hugbox hugbox = DUEL_HUGBOX_LETHAL diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm index a5b47ff463..a34e0f6f12 100644 --- a/code/modules/projectiles/guns/energy/energy_gun.dm +++ b/code/modules/projectiles/guns/energy/energy_gun.dm @@ -9,7 +9,6 @@ ammo_x_offset = 3 flight_x_offset = 15 flight_y_offset = 10 - has_gun_safety = TRUE /obj/item/gun/energy/e_gun/mini name = "miniature energy gun" diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 68e5faa412..5fd158c028 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -34,7 +34,6 @@ lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' ammo_type = list(/obj/item/ammo_casing/energy/kinetic/premium) - has_gun_safety = TRUE /obj/item/ammo_casing/energy/kinetic/premium projectile_type = /obj/item/projectile/kinetic/premium diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 89aeef8c48..f73ac9cfb8 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -8,7 +8,6 @@ ammo_type = list(/obj/item/ammo_casing/energy/lasergun) ammo_x_offset = 1 shaded_charge = 1 - has_gun_safety = TRUE /obj/item/gun/energy/laser/practice name = "practice laser gun" @@ -93,7 +92,6 @@ selfcharge = EGUN_SELFCHARGE_BORG cell_type = /obj/item/stock_parts/cell/secborg charge_delay = 3 - has_gun_safety = FALSE /obj/item/gun/energy/laser/cyborg/emp_act() return @@ -131,7 +129,6 @@ ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) pin = null ammo_x_offset = 3 - has_gun_safety = TRUE /obj/item/ammo_casing/energy/laser/accelerator projectile_type = /obj/item/projectile/beam/laser/accelerator diff --git a/code/modules/projectiles/guns/energy/megabuster.dm b/code/modules/projectiles/guns/energy/megabuster.dm index a980cab6fa..dddfd749de 100644 --- a/code/modules/projectiles/guns/energy/megabuster.dm +++ b/code/modules/projectiles/guns/energy/megabuster.dm @@ -10,7 +10,6 @@ selfcharge = EGUN_SELFCHARGE cell_type = "/obj/item/stock_parts/cell/pulse" icon = 'modular_citadel/icons/obj/guns/VGguns.dmi' - has_gun_safety = TRUE /obj/item/gun/energy/megabuster/proto name = "Proto-buster" diff --git a/code/modules/projectiles/guns/energy/mounted.dm b/code/modules/projectiles/guns/energy/mounted.dm index 304b7f4dd9..25a94895f8 100644 --- a/code/modules/projectiles/guns/energy/mounted.dm +++ b/code/modules/projectiles/guns/energy/mounted.dm @@ -8,7 +8,6 @@ selfcharge = EGUN_SELFCHARGE can_flashlight = 0 trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses neural signals instead - has_gun_safety = TRUE /obj/item/gun/energy/laser/mounted name = "mounted laser" @@ -19,4 +18,3 @@ force = 5 selfcharge = EGUN_SELFCHARGE trigger_guard = TRIGGER_GUARD_ALLOW_ALL - has_gun_safety = TRUE diff --git a/code/modules/projectiles/guns/energy/plasma_cit.dm b/code/modules/projectiles/guns/energy/plasma_cit.dm index 852baabaa0..9f9340a725 100644 --- a/code/modules/projectiles/guns/energy/plasma_cit.dm +++ b/code/modules/projectiles/guns/energy/plasma_cit.dm @@ -9,7 +9,6 @@ shaded_charge = 1 lefthand_file = 'modular_citadel/icons/mob/citadel/guns_lefthand.dmi' righthand_file = 'modular_citadel/icons/mob/citadel/guns_righthand.dmi' - has_gun_safety = TRUE /obj/item/gun/energy/plasma/rifle diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index 08326794a7..d5dc39695b 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -10,7 +10,6 @@ slot_flags = ITEM_SLOT_BACK ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) cell_type = "/obj/item/stock_parts/cell/pulse" - has_gun_safety = TRUE /obj/item/gun/energy/pulse/emp_act(severity) return diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index b19873236e..67e7b58bd3 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -11,7 +11,6 @@ ammo_x_offset = 3 flight_x_offset = 17 flight_y_offset = 9 - has_gun_safety = TRUE /obj/item/gun/energy/ionrifle/emp_act(severity) return @@ -50,7 +49,6 @@ modifystate = 1 ammo_x_offset = 1 selfcharge = EGUN_SELFCHARGE - has_gun_safety = FALSE /obj/item/gun/energy/meteorgun name = "meteor gun" @@ -62,7 +60,6 @@ cell_type = "/obj/item/stock_parts/cell/potato" clumsy_check = 0 //Admin spawn only, might as well let clowns use it. selfcharge = EGUN_SELFCHARGE - has_gun_safety = TRUE /obj/item/gun/energy/meteorgun/pen name = "meteor pen" @@ -73,7 +70,6 @@ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' w_class = WEIGHT_CLASS_TINY - has_gun_safety = TRUE /obj/item/gun/energy/mindflayer name = "\improper Mind Flayer" @@ -82,7 +78,6 @@ item_state = null ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) ammo_x_offset = 2 - has_gun_safety = FALSE /obj/item/gun/energy/kinetic_accelerator/crossbow name = "mini energy crossbow" @@ -101,7 +96,6 @@ unique_frequency = TRUE can_flashlight = 0 max_mod_capacity = 0 - has_gun_safety = TRUE /obj/item/gun/energy/kinetic_accelerator/crossbow/halloween name = "candy corn crossbow" @@ -109,7 +103,6 @@ icon_state = "crossbow_halloween" item_state = "crossbow" ammo_type = list(/obj/item/ammo_casing/energy/bolt/halloween) - has_gun_safety = FALSE /obj/item/gun/energy/kinetic_accelerator/crossbow/large name = "energy crossbow" @@ -140,7 +133,6 @@ usesound = list('sound/items/welder.ogg', 'sound/items/welder2.ogg') tool_behaviour = TOOL_WELDER toolspeed = 0.7 //plasmacutters can be used as welders, and are faster than standard welders - has_gun_safety = FALSE /obj/item/gun/energy/plasmacutter/ComponentInitialize() . = ..() @@ -197,7 +189,6 @@ icon_state = "gravity_gun" var/power = 4 var/firing_core = FALSE - has_gun_safety = TRUE /obj/item/gun/energy/gravity_gun/attackby(obj/item/C, mob/user) if(istype(C, /obj/item/assembly/signaler/anomaly)) @@ -225,7 +216,6 @@ var/obj/effect/portal/p_orange var/atmos_link = FALSE var/firing_core = FALSE - has_gun_safety = TRUE /obj/item/gun/energy/wormhole_projector/attackby(obj/item/C, mob/user) if(istype(C, /obj/item/assembly/signaler/anomaly)) @@ -315,7 +305,6 @@ ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) can_charge = 0 use_cyborg_cell = 1 - has_gun_safety = FALSE /obj/item/gun/energy/printer/ComponentInitialize() . = ..() @@ -345,7 +334,6 @@ desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." ammo_type = list(/obj/item/ammo_casing/energy/instakill) force = 60 - has_gun_safety = TRUE /obj/item/gun/energy/laser/instakill/red desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." @@ -374,7 +362,6 @@ cell_type = /obj/item/stock_parts/cell/super ammo_type = list(/obj/item/ammo_casing/energy/emitter) automatic_charge_overlays = FALSE - has_gun_safety = TRUE /obj/item/gun/energy/emitter/update_icon_state() var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index] @@ -390,4 +377,3 @@ icon_state = "decloner" no_pin_required = TRUE ammo_type = list(/obj/item/ammo_casing/energy/pickle) - has_gun_safety = FALSE diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index e64a77015d..6f70998834 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -31,7 +31,6 @@ canMouseDown = TRUE can_turret = FALSE can_circuit = FALSE - has_gun_safety = TRUE //Cit changes: beam rifle stats. slowdown = 1 item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | NEEDS_PERMIT diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm index c9848cfaab..a88f395561 100644 --- a/code/modules/projectiles/guns/misc/blastcannon.dm +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -10,7 +10,6 @@ item_flags = NONE clumsy_check = FALSE randomspread = FALSE - has_gun_safety = TRUE var/hugbox = TRUE var/max_power = INFINITY diff --git a/code/modules/projectiles/guns/misc/chameleon.dm b/code/modules/projectiles/guns/misc/chameleon.dm index a097347b09..428de747f7 100644 --- a/code/modules/projectiles/guns/misc/chameleon.dm +++ b/code/modules/projectiles/guns/misc/chameleon.dm @@ -13,7 +13,6 @@ item_flags = NONE pin = /obj/item/firing_pin cell_type = /obj/item/stock_parts/cell/bluespace - var/datum/action/item_action/chameleon/change/gun/chameleon_action var/list/chameleon_projectile_vars diff --git a/code/modules/projectiles/guns/misc/chem_gun.dm b/code/modules/projectiles/guns/misc/chem_gun.dm index f4604c49e3..9f0815ebfb 100644 --- a/code/modules/projectiles/guns/misc/chem_gun.dm +++ b/code/modules/projectiles/guns/misc/chem_gun.dm @@ -20,7 +20,6 @@ var/obj/item/reagent_containers/glass/bottle/vial/vial var/list/allowed_containers = list(/obj/item/reagent_containers/glass/bottle/vial/small, /obj/item/reagent_containers/glass/bottle/vial/large) var/quickload = TRUE - has_gun_safety = TRUE /obj/item/gun/chem/Initialize(mapload) . = ..() @@ -111,7 +110,6 @@ desc = "An experimental improved version of the smartdart rifle. It synthesizes medicinal smart darts which it fills using an inserted hypovial. It can accommodate both large and small hypovials." icon_state = "chemgunrepeater" item_state = "syringegun" - has_gun_safety = TRUE obj/item/gun/chem/smart/Initialize(mapload) . = ..() diff --git a/code/modules/projectiles/guns/misc/grenade_launcher.dm b/code/modules/projectiles/guns/misc/grenade_launcher.dm index 20448e26ef..86dd0c6ce4 100644 --- a/code/modules/projectiles/guns/misc/grenade_launcher.dm +++ b/code/modules/projectiles/guns/misc/grenade_launcher.dm @@ -11,7 +11,6 @@ var/list/grenades = new/list() var/max_grenades = 3 custom_materials = list(/datum/material/iron=2000) - has_gun_safety = TRUE /obj/item/gun/grenadelauncher/examine(mob/user) . = ..() diff --git a/code/modules/projectiles/guns/misc/medbeam.dm b/code/modules/projectiles/guns/misc/medbeam.dm index 1bff8548cc..4a64317fd9 100644 --- a/code/modules/projectiles/guns/misc/medbeam.dm +++ b/code/modules/projectiles/guns/misc/medbeam.dm @@ -13,7 +13,6 @@ var/active = 0 var/datum/beam/current_beam = null var/mounted = 0 //Denotes if this is a handheld or mounted version - has_gun_safety = FALSE //no safety here. weapon_weight = WEAPON_MEDIUM diff --git a/code/modules/projectiles/guns/misc/syringe_gun.dm b/code/modules/projectiles/guns/misc/syringe_gun.dm index 581e7cd8d4..8de7633a79 100644 --- a/code/modules/projectiles/guns/misc/syringe_gun.dm +++ b/code/modules/projectiles/guns/misc/syringe_gun.dm @@ -14,7 +14,6 @@ fire_sound = 'sound/items/syringeproj.ogg' var/list/syringes = list() var/max_syringes = 1 - has_gun_safety = TRUE /obj/item/gun/syringe/Initialize(mapload) . = ..() @@ -161,7 +160,6 @@ icon_state = "blowgun" item_state = "syringegun" fire_sound = 'sound/items/syringeproj.ogg' - has_gun_safety = FALSE //unga bunga, tube no fit safety /obj/item/gun/syringe/blowgun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, stam_cost = 0) visible_message("[user] starts aiming with a blowgun!") From 1ebc2e584b13e3bbd6db5bab14f3e551cf97dba0 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Sat, 17 Sep 2022 06:53:46 -0700 Subject: [PATCH 11/12] makes weapons unsafe --- code/datums/action.dm | 5 ----- code/modules/projectiles/gun.dm | 33 -------------------------------- icons/hud/actions.dmi | Bin 392 -> 0 bytes 3 files changed, 38 deletions(-) delete mode 100644 icons/hud/actions.dmi diff --git a/code/datums/action.dm b/code/datums/action.dm index bfc51b0411..304aa47baa 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -228,11 +228,6 @@ 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/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 50e1744f84..236143dc43 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -91,11 +91,6 @@ var/zoom_out_amt = 0 var/datum/action/item_action/toggle_scope_zoom/azoom - //gun safeties - var/safety = TRUE /// 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 - //Firemodes var/datum/action/item_action/toggle_firemode/firemode_action /// Current fire selection, can choose between burst, single, and full auto. @@ -117,8 +112,6 @@ /obj/item/gun/ui_action_click(mob/user, action) if(istype(action, /datum/action/item_action/toggle_firemode)) fire_select() - else if(istype(action, /datum/action/item_action/toggle_safety)) - toggle_safety(user) else if(istype(action, /datum/action/item_action/toggle_scope_zoom)) zoom(user, user.dir) else if(istype(action, alight)) @@ -136,9 +129,6 @@ 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) @@ -170,8 +160,6 @@ QDEL_NULL(chambered) if(azoom) QDEL_NULL(azoom) - if(toggle_safety_action) - QDEL_NULL(toggle_safety_action) if(firemode_action) QDEL_NULL(firemode_action) return ..() @@ -198,8 +186,6 @@ . += "[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 @@ -367,22 +353,6 @@ 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(!.) @@ -392,9 +362,6 @@ 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/icons/hud/actions.dmi b/icons/hud/actions.dmi deleted file mode 100644 index 5d718bc9e0b62dbb73800aa62c77aa6eae7abcea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!VDw>HYaZfQW60^A+84w95DXR5ET^_7#Qg9 z@88hS;Ns%)pMgQ-bxko)4P!}=UoeBivm0qZPH|O8M2SmkadJ^+K}lwQ9s@(goZzs6 zqSEhQf(t%=eWK;9t99jJaWn(XeIgchCnG~WB zywbu@ck2=(JA41mZe!KWDjp;1r zydbyC_jGX#vFJ@sSfC{!qO!oDVFD+MsKAz{M#1?ilbIMzopr06dG3djJ3c From 17f513eae4a48001a4e0aa5b6fb94b7ca3f96085 Mon Sep 17 00:00:00 2001 From: shellspeed1 Date: Tue, 20 Sep 2022 23:08:24 -0700 Subject: [PATCH 12/12] Fixes clicking in the object menu but at what cost --- code/modules/client/client_procs.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 4f956b5964..6ca963df73 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -872,8 +872,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( last_activity = world.time last_click = world.time //fullauto stuff + /* if(!control) return + */ if(click_intercept_time) if(click_intercept_time >= world.time) click_intercept_time = 0 //Reset and return. Next click should work, but not this one.