diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 55a83a46ea6..4611e51e993 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -289,6 +289,19 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
/// Proceed with the attack chain, but don't call the normal methods.
#define SECONDARY_ATTACK_CONTINUE_CHAIN 3
+//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)
+
/// Martial arts attack requested but is not available, allow a check for a regular attack.
#define MARTIAL_ATTACK_INVALID -1
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 485501a3638..46369eb5dff 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -534,11 +534,19 @@
///sent from borg recharge stations: (amount, repairs)
#define COMSIG_PROCESS_BORGCHARGER_OCCUPANT "living_charge"
-///sent when a mob/login() finishes: (client)
-#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login"
///sent from borg mobs to itself, for tools to catch an upcoming destroy() due to safe decon (rather than detonation)
#define COMSIG_BORG_SAFE_DECONSTRUCT "borg_safe_decon"
+///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"
+
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
///from base of mob/living/Stun() (amount, ignore_canstun)
diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm
index eb07eafabfe..07137c192fd 100644
--- a/code/__DEFINES/projectiles.dm
+++ b/code/__DEFINES/projectiles.dm
@@ -43,8 +43,6 @@
#define CALIBER_LASER "laser"
/// The caliber used by most energy guns.
#define CALIBER_ENERGY "energy"
-/// The caliber used by the laser minigun.
-#define CALIBER_GATLING "gatling"
/// The acliber used by foam force and donksoft toy guns.
#define CALIBER_FOAM "foam_force"
/// The caliber used by the bow and arrow.
diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm
index 88858abe7fc..41ccfff56f6 100644
--- a/code/_onclick/drag_drop.dm
+++ b/code/_onclick/drag_drop.dm
@@ -24,8 +24,13 @@
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)
@@ -39,7 +44,11 @@
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)
@@ -74,9 +83,6 @@
/obj/item/proc/onMouseUp(object, location, params, mob)
return
-/obj/item/gun/CanItemAutoclick(object, location, params)
- . = automatic
-
/atom/proc/IsAutoclickable()
return TRUE
@@ -103,6 +109,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)
diff --git a/code/datums/components/crafting/guncrafting.dm b/code/datums/components/crafting/guncrafting.dm
index 8ff1fac16f4..5e74737afe3 100644
--- a/code/datums/components/crafting/guncrafting.dm
+++ b/code/datums/components/crafting/guncrafting.dm
@@ -30,9 +30,9 @@
desc = "A suitcase containing the necessary gun parts to tranform a standard energy gun into an advaned energy gun."
/obj/item/weaponcrafting/gunkit/tesla
- name = "tesla revolver parts kit"
- desc = "A suitcase containing the necessary gun parts to construct a tesla revolver around a stabilized flux anomaly. Handle with care."
-
+ name = "tesla cannon parts kit"
+ desc = "A suitcase containing the necessary gun parts to construct a tesla cannon around a stabilized flux anomaly. Handle with care."
+
/obj/item/weaponcrafting/gunkit/xray
name = "x-ray laser gun parts kit"
desc = "A suitcase containing the necessary gun parts to turn a laser gun into a x-ray laser gun. Do not point most parts directly towards face."
@@ -44,7 +44,7 @@
/obj/item/weaponcrafting/gunkit/temperature
name = "temperature gun parts kit"
desc = "A suitcase containing the necessary gun parts to tranform a standard energy gun into a temperature gun. Fantastic at birthday parties and killing indigenious populations of lizardpeople."
-
+
/obj/item/weaponcrafting/gunkit/beam_rifle
name = "particle acceleration rifle part kit"
desc = "The coup de grace of guncrafting. This suitcase contains the highly experimental rig for a particle acceleration rifle. Requires an energy gun, a stabilized flux anomaly and a stabilized gravity anomaly."
diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm
index b65738f79d8..7b3d816c87a 100644
--- a/code/datums/components/crafting/recipes.dm
+++ b/code/datums/components/crafting/recipes.dm
@@ -307,10 +307,10 @@
..()
blacklist += subtypesof(/obj/item/gun/energy/laser)
-/datum/crafting_recipe/teslarevolver
- name = "Tesla Revolver"
+/datum/crafting_recipe/teslacannon
+ name = "Tesla Cannon"
tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
- result = /obj/item/gun/energy/tesla_revolver
+ result = /obj/item/gun/energy/tesla_cannon
reqs = list(/obj/item/assembly/signaler/anomaly/flux = 1,
/obj/item/stack/cable_coil = 5,
/obj/item/weaponcrafting/gunkit/tesla = 1)
diff --git a/code/datums/components/fullauto.dm b/code/datums/components/fullauto.dm
new file mode 100644
index 00000000000..0da742c8a1f
--- /dev/null
+++ b/code/datums/components/fullauto.dm
@@ -0,0 +1,277 @@
+#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(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
+
+ 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))
+ 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
+
+ RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED, COMSIG_ITEM_DROPPED), .proc/autofire_off)
+
+ if(iscarbon(user))
+ var/mob/living/carbon/shooter = user
+ if(shooter.is_holding(parent))
+ 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_ALERT|AUTOFIRE_STAT_FIRING))
+ return
+ autofire_stat = AUTOFIRE_STAT_ALERT
+ clicker = usercli
+ shooter = clicker.mob
+ RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDOWN, .proc/on_mouse_down)
+ RegisterSignal(shooter, COMSIG_MOB_LOGOUT, .proc/autofire_off)
+ if(!QDELETED(shooter))
+ UnregisterSignal(shooter, COMSIG_MOB_LOGIN)
+ 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
+ RegisterSignal(shooter, COMSIG_MOB_LOGIN, .proc/on_client_login)
+ if(!QDELETED(shooter))
+ 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 //Already pew-pewing.
+ 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
+ switch(autofire_stat)
+ if(AUTOFIRE_STAT_IDLE, AUTOFIRE_STAT_ALERT)
+ 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
+ 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(!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))
+ 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_DOES_SLEEP
+ if(!can_shoot())
+ shoot_with_empty_chamber(shooter)
+ return NONE
+ 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)
+ return COMPONENT_AUTOFIRE_SHOT_SUCCESS //All is well, we can continue shooting.
+
+#undef AUTOFIRE_MOUSEUP
+#undef AUTOFIRE_MOUSEDOWN
diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm
index 9e842de748c..9af434081c6 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 58a2e519641..c79aafe2fec 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -13,6 +13,8 @@
var/datum/admins/holder = null
///Needs to implement InterceptClickOn(user,params,atom) proc
var/datum/click_intercept = null
+ ///Time when the click was intercepted
+ var/click_intercept_time = 0
///Used for admin AI interaction
var/AI_Interact = FALSE
@@ -75,6 +77,8 @@
var/mouse_up_icon = null
///used to make a special mouse cursor, this one for mouse up icon
var/mouse_down_icon = null
+ ///used to override the mouse cursor so it doesnt get reset
+ var/mouse_override_icon = null
///Used for ip intel checking to identify evaders, disabled because of issues with traffic
var/ip_intel = "Disabled"
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 2581548022f..19f4a385f55 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -835,6 +835,13 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
ip_intel = res.intel
/client/Click(atom/object, atom/location, control, params)
+ 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/ab = FALSE
var/list/modifiers = params2list(params)
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index 5c1664b5659..b81a3477d58 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -291,7 +291,7 @@
/obj/item/energy_katana,
/obj/item/hierophant_club,
/obj/item/his_grace,
- /obj/item/gun/ballistic/minigun,
+ /obj/item/gun/energy/minigun,
/obj/item/gun/ballistic/automatic/l6_saw,
/obj/item/gun/magic/staff/chaos,
/obj/item/gun/magic/staff/spellblade,
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 7909196e816..ef0a205647b 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1180,19 +1180,17 @@
///Update the mouse pointer of the attached client in this mob
/mob/proc/update_mouse_pointer()
- if (!client)
+ if(!client)
return
client.mouse_pointer_icon = initial(client.mouse_pointer_icon)
if(examine_cursor_icon && client.keys_held["Shift"]) //mouse shit is hardcoded, make this non hard-coded once we make mouse modifiers bindable
client.mouse_pointer_icon = examine_cursor_icon
- else if (ismecha(loc))
- var/obj/vehicle/sealed/mecha/M = loc
- if(M.mouse_pointer)
- client.mouse_pointer_icon = M.mouse_pointer
- else if (istype(loc, /obj/vehicle/sealed))
+ if(istype(loc, /obj/vehicle/sealed))
var/obj/vehicle/sealed/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
///This mob is abile to read books
diff --git a/code/modules/projectiles/ammunition/caseless/misc.dm b/code/modules/projectiles/ammunition/caseless/misc.dm
index 3e0bfbdb320..be500bf6645 100644
--- a/code/modules/projectiles/ammunition/caseless/misc.dm
+++ b/code/modules/projectiles/ammunition/caseless/misc.dm
@@ -8,11 +8,6 @@
fire_sound = 'sound/weapons/laser.ogg'
firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy
-/obj/item/ammo_casing/caseless/laser/gatling
- projectile_type = /obj/projectile/beam/weak/penetrator
- variance = 0.8
- click_cooldown_override = 1
-
// Harpoons (Ballistic Harpoon Gun)
/obj/item/ammo_casing/caseless/harpoon
diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm
index 7ad3f1c0b18..5508457e018 100644
--- a/code/modules/projectiles/ammunition/energy/laser.dm
+++ b/code/modules/projectiles/ammunition/energy/laser.dm
@@ -76,3 +76,8 @@
projectile_type = /obj/projectile/beam/mindflayer
select_name = "MINDFUCK"
fire_sound = 'sound/weapons/laser.ogg'
+
+/obj/item/ammo_casing/energy/laser/minigun
+ select_name = "kill"
+ projectile_type = /obj/projectile/beam/weak/penetrator
+ variance = 0.8
diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm
index 5b8911c229a..965985624c5 100644
--- a/code/modules/projectiles/ammunition/energy/special.dm
+++ b/code/modules/projectiles/ammunition/energy/special.dm
@@ -69,11 +69,11 @@
/obj/item/ammo_casing/energy/instakill/red
projectile_type = /obj/projectile/beam/instakill/red
-/obj/item/ammo_casing/energy/tesla_revolver
- fire_sound = 'sound/magic/lightningbolt.ogg'
- e_cost = 200
- select_name = "stun"
- projectile_type = /obj/projectile/energy/tesla/revolver
+/obj/item/ammo_casing/energy/tesla_cannon
+ fire_sound = 'sound/magic/lightningshock.ogg'
+ e_cost = 50
+ select_name = "shock"
+ projectile_type = /obj/projectile/energy/tesla_cannon
/obj/item/ammo_casing/energy/shrink
projectile_type = /obj/projectile/beam/shrink
diff --git a/code/modules/projectiles/boxes_magazines/internal/misc.dm b/code/modules/projectiles/boxes_magazines/internal/misc.dm
index 63e5582df86..729968ab011 100644
--- a/code/modules/projectiles/boxes_magazines/internal/misc.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/misc.dm
@@ -1,9 +1,3 @@
-/obj/item/ammo_box/magazine/internal/minigun
- name = "gatling gun fusion core"
- ammo_type = /obj/item/ammo_casing/caseless/laser/gatling
- caliber = CALIBER_GATLING
- max_ammo = 5000
-
/obj/item/ammo_box/magazine/internal/hook
name = "hook internal tube"
ammo_type = /obj/item/ammo_casing/magic/hook
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 24efe69b527..b32b81b838c 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -76,8 +76,6 @@
var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect)
var/zoom_out_amt = 0
var/datum/action/toggle_scope_zoom/azoom
-
- var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds
var/pb_knockback = 0
/obj/item/gun/Initialize()
diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm
index f6eac290183..dde5ef6cd69 100644
--- a/code/modules/projectiles/guns/ballistic/automatic.dm
+++ b/code/modules/projectiles/guns/ballistic/automatic.dm
@@ -244,10 +244,9 @@
slot_flags = 0
mag_type = /obj/item/ammo_box/magazine/mm712x82
weapon_weight = WEAPON_HEAVY
- var/cover_open = FALSE
+ burst_size = 1
+ actions_types = list()
can_suppress = FALSE
- burst_size = 3
- fire_delay = 1
spread = 7
pin = /obj/item/firing_pin/implant/pindicate
bolt_type = BOLT_TYPE_OPEN
@@ -258,14 +257,15 @@
fire_sound = 'sound/weapons/gun/l6/shot.ogg'
rack_sound = 'sound/weapons/gun/l6/l6_rack.ogg'
suppressed_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg'
+ 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()
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
+ AddComponent(/datum/component/automatic_fire, 0.2 SECONDS)
/obj/item/gun/ballistic/automatic/l6_saw/examine(mob/user)
. = ..()
diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/energy/laser_gatling.dm
similarity index 71%
rename from code/modules/projectiles/guns/ballistic/laser_gatling.dm
rename to code/modules/projectiles/guns/energy/laser_gatling.dm
index 45f07241567..b82c983c901 100644
--- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm
+++ b/code/modules/projectiles/guns/energy/laser_gatling.dm
@@ -11,7 +11,8 @@
righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi'
slot_flags = ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_HUGE
- var/obj/item/gun/ballistic/minigun/gun
+ var/obj/item/gun/energy/minigun/gun
+ var/obj/item/stock_parts/cell/minigun/battery
var/armed = FALSE //whether the gun is attached, FALSE is attached, TRUE is the gun is wielded.
var/overheat = 0
var/overheat_max = 40
@@ -20,6 +21,7 @@
/obj/item/minigunpack/Initialize()
. = ..()
gun = new(src)
+ battery = new(src)
START_PROCESSING(SSobj, src)
/obj/item/minigunpack/Destroy()
@@ -91,29 +93,24 @@
user.update_inv_back()
-/obj/item/gun/ballistic/minigun
+/obj/item/gun/energy/minigun
name = "laser gatling gun"
desc = "An advanced laser cannon with an incredible rate of fire. Requires a bulky backpack power source to use."
icon = 'icons/obj/guns/minigun.dmi'
icon_state = "minigun_spin"
inhand_icon_state = "minigun"
- flags_1 = CONDUCT_1
slowdown = 1
slot_flags = null
w_class = WEIGHT_CLASS_HUGE
custom_materials = null
- burst_size = 3
- automatic = 0
- fire_delay = 1
weapon_weight = WEAPON_HEAVY
- fire_sound = 'sound/weapons/laser.ogg'
- mag_type = /obj/item/ammo_box/magazine/internal/minigun
- tac_reloads = FALSE
- casing_ejector = FALSE
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/minigun)
+ cell_type = /obj/item/stock_parts/cell/crap
item_flags = NEEDS_PERMIT | SLOWS_WHILE_IN_HAND
+ can_charge = FALSE
var/obj/item/minigunpack/ammo_pack
-/obj/item/gun/ballistic/minigun/Initialize()
+/obj/item/gun/energy/minigun/Initialize()
if(istype(loc, /obj/item/minigunpack)) //We should spawn inside an ammo pack so let's use that one.
ammo_pack = loc
else
@@ -121,25 +118,41 @@
return ..()
-/obj/item/gun/ballistic/minigun/attack_self(mob/living/user)
+/obj/item/gun/energy/minigun/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/update_icon_blocker)
+ AddComponent(/datum/component/automatic_fire, 0.2 SECONDS)
+
+/obj/item/gun/energy/minigun/attack_self(mob/living/user)
return
-/obj/item/gun/ballistic/minigun/dropped(mob/user)
+/obj/item/gun/energy/minigun/dropped(mob/user)
SHOULD_CALL_PARENT(FALSE)
if(ammo_pack)
ammo_pack.attach_gun(user)
else
qdel(src)
-/obj/item/gun/ballistic/minigun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
- if(ammo_pack)
- if(ammo_pack.overheat < ammo_pack.overheat_max)
- ammo_pack.overheat += burst_size
- ..()
- else
- to_chat(user, "The gun's heat sensor locked the trigger to prevent lens damage!")
+/obj/item/gun/energy/minigun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
+ if(ammo_pack && ammo_pack.overheat >= ammo_pack.overheat_max)
+ to_chat(user, "The gun's heat sensor locked the trigger to prevent lens damage!")
+ return
+ ..()
+ ammo_pack.overheat += burst_size
+ if(ammo_pack.battery)
+ var/totransfer = min(100, ammo_pack.battery.charge)
+ var/transferred = cell.give(totransfer)
+ ammo_pack.battery.use(transferred)
-/obj/item/gun/ballistic/minigun/afterattack(atom/target, mob/living/user, flag, params)
+
+/obj/item/gun/energy/minigun/afterattack(atom/target, mob/living/user, flag, params)
if(!ammo_pack || ammo_pack.loc != user)
to_chat(user, "You need the backpack power source to fire the gun!")
. = ..()
+
+/obj/item/stock_parts/cell/minigun
+ name = "gatling gun fusion core"
+ desc = "Where did these come from?"
+ icon_state = "h+cell"
+ maxcharge = 500000
+ chargerate = 5000
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 8dc29120d10..998773f5079 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -59,7 +59,7 @@
inhand_icon_state = "c20r"
w_class = WEIGHT_CLASS_BULKY
ammo_type = list(/obj/item/ammo_casing/energy/meteor)
- cell_type = "/obj/item/stock_parts/cell/potato"
+ cell_type = /obj/item/stock_parts/cell/potato
clumsy_check = 0 //Admin spawn only, might as well let clowns use it.
selfcharge = 1
@@ -310,7 +310,7 @@
desc = "A gun that changes temperatures. Comes with a collapsible stock."
w_class = WEIGHT_CLASS_NORMAL
ammo_type = list(/obj/item/ammo_casing/energy/temp, /obj/item/ammo_casing/energy/temp/hot)
- cell_type = "/obj/item/stock_parts/cell/high"
+ cell_type = /obj/item/stock_parts/cell/high
pin = null
/obj/item/gun/energy/temperature/security
@@ -369,3 +369,15 @@
return FALSE
return ..()
+/obj/item/gun/energy/tesla_cannon
+ name = "tesla cannon"
+ icon_state = "tesla"
+ inhand_icon_state = "tesla"
+ desc = "A gun that shoots balls of \"tesla\", whatever that is."
+ ammo_type = list(/obj/item/ammo_casing/energy/tesla_cannon)
+ shaded_charge = 1
+ weapon_weight = WEAPON_HEAVY
+
+/obj/item/gun/energy/tesla_cannon/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/automatic_fire, 0.1 SECONDS)
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index fea2c04426b..ae5f1a4131a 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -6,15 +6,6 @@
ammo_type = list(/obj/item/ammo_casing/energy/electrode)
ammo_x_offset = 3
-/obj/item/gun/energy/tesla_revolver
- name = "tesla gun"
- desc = "An experimental gun based on an experimental engine, it's about as likely to kill its operator as it is the target."
- icon_state = "tesla"
- inhand_icon_state = "tesla"
- ammo_type = list(/obj/item/ammo_casing/energy/tesla_revolver)
- can_flashlight = FALSE
- shaded_charge = 1
-
/obj/item/gun/energy/e_gun/advtaser
name = "hybrid taser"
desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams."
diff --git a/code/modules/projectiles/projectile/energy/tesla.dm b/code/modules/projectiles/projectile/energy/tesla.dm
index 1aead137ab3..16ceff536ca 100644
--- a/code/modules/projectiles/projectile/energy/tesla.dm
+++ b/code/modules/projectiles/projectile/energy/tesla.dm
@@ -24,3 +24,16 @@
name = "tesla orb"
power = 20000
damage = 15 //Mech man big
+
+/obj/projectile/energy/tesla_cannon
+ name = "tesla orb"
+ icon_state = "ice_1"
+ damage = 0
+ speed = 1.5
+ var/shock_damage = 5
+
+/obj/projectile/energy/tesla_cannon/on_hit(atom/target)
+ . = ..()
+ if(isliving(target))
+ var/mob/living/victim = target
+ victim.electrocute_act(shock_damage, src, siemens_coeff = 1, flags = SHOCK_NOSTUN)
diff --git a/icons/effects/mouse_pointers/weapon_pointer.dmi b/icons/effects/mouse_pointers/weapon_pointer.dmi
new file mode 100644
index 00000000000..b5070062c0b
Binary files /dev/null and b/icons/effects/mouse_pointers/weapon_pointer.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 6e7e396f242..24a14b84061 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -504,6 +504,7 @@
#include "code\datums\components\explodable.dm"
#include "code\datums\components\footstep.dm"
#include "code\datums\components\forensics.dm"
+#include "code\datums\components\fullauto.dm"
#include "code\datums\components\gps.dm"
#include "code\datums\components\grillable.dm"
#include "code\datums\components\gunpoint.dm"
@@ -2961,7 +2962,6 @@
#include "code\modules\projectiles\guns\energy.dm"
#include "code\modules\projectiles\guns\magic.dm"
#include "code\modules\projectiles\guns\ballistic\automatic.dm"
-#include "code\modules\projectiles\guns\ballistic\laser_gatling.dm"
#include "code\modules\projectiles\guns\ballistic\launchers.dm"
#include "code\modules\projectiles\guns\ballistic\pistol.dm"
#include "code\modules\projectiles\guns\ballistic\revolver.dm"
@@ -2972,6 +2972,7 @@
#include "code\modules\projectiles\guns\energy\energy_gun.dm"
#include "code\modules\projectiles\guns\energy\kinetic_accelerator.dm"
#include "code\modules\projectiles\guns\energy\laser.dm"
+#include "code\modules\projectiles\guns\energy\laser_gatling.dm"
#include "code\modules\projectiles\guns\energy\mounted.dm"
#include "code\modules\projectiles\guns\energy\pulse.dm"
#include "code\modules\projectiles\guns\energy\special.dm"