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"