diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 99add07ecc06..65d55014d2f2 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -112,8 +112,23 @@ #define MOVESET_ROLES "moveset_role" #define MOVESET_QUALITY "moveset_quality" + +//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) + //Painkiller effectiveness (for get_painkiller_effect() comparison) #define PAINKILLERS_EFFECT_SLIGHT 0.95 //all painkillers. #define PAINKILLERS_EFFECT_MEDIUM 0.75 //weak painkillers, allow you to ignore minor pain and not see pain() messages. #define PAINKILLERS_EFFECT_HEAVY 0.6 //powerful painkillers, allow you to not see custom_pain() messages. #define PAINKILLERS_EFFECT_VERY_HEAVY 0.5 //very powerful painkillers that does not allow the user to determine the location of the injury. + diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index e6be0a84a9cf..7aeae642e3e5 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -424,3 +424,11 @@ #define COMSIG_REMOVE_GENE_DISABILITY "remove_gene_disability" // send this signal to handle disabilities in life for mob/living/carbon/human #define COMSIG_HANDLE_DISABILITIES "handle_disabilities" + +//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" diff --git a/code/__DEFINES/subsystem.dm b/code/__DEFINES/subsystem.dm index f9b91ad1fdd6..30133b79d53c 100644 --- a/code/__DEFINES/subsystem.dm +++ b/code/__DEFINES/subsystem.dm @@ -81,6 +81,7 @@ #define SS_PRIORITY_LOW 1 +#define SS_WAIT_FULLAUTO 1 #define SS_WAIT_EXPLOSION 1 #define SS_WAIT_INPUT 1 #define SS_WAIT_DEMO 1 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index f5254f82ecef..dbbc836fccaf 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -197,6 +197,7 @@ #define TRAIT_MIMING "miming" #define TRAIT_WILLPOWER_IMPLANT "willpower_implant" #define TRAIT_CAN_LEAP "can_leap" +#define TRAIT_AUTOFIRE_SHOOTS "autofire_shoots" /* * Used for movables that need to be updated, via COMSIG_ENTER_AREA and COMSIG_EXIT_AREA, when transitioning areas. diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 0c3785628578..41209e09c563 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1662,3 +1662,23 @@ var/global/list/WALLITEMS = typecacheof(list( location = location.loc if(location && include_turf) //At this point, only the turf is left, provided it exists. . += location + +/proc/parse_caught_click_modifiers(list/modifiers, turf/origin, client/viewing_client) + 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 diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 79e3368daacf..7d0761e32eaa 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -53,6 +53,11 @@ * mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed */ /mob/proc/ClickOn( atom/A, params ) + if(client.click_intercept_time) + if(client.click_intercept_time >= world.time) + client.click_intercept_time = 0 //Reset and return. Next click should work, but not this one. + return + client.click_intercept_time = 0 //Just reset. Let's not keep re-checking forever. if(world.time <= next_click) return next_click = world.time + 1 @@ -160,6 +165,18 @@ else RangedAttack(A, params) +/client/MouseDown(datum/object, location, control, params) + SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEDOWN, object, location, control, params) + ..() + +/client/MouseUp(object, location, control, params) + if(SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEUP, object, location, control, params) & COMPONENT_CLIENT_MOUSEUP_INTERCEPT) + click_intercept_time = world.time + +/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params) + SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEDRAG, src_object, over_object, src_location, over_location, src_control, over_control, params) + ..() + // Default behavior: ignore double clicks (don't add normal clicks, as it will do three clicks instead of two with double). /mob/proc/DblClickOn(atom/A, params) return diff --git a/code/controllers/subsystem/fullauto.dm b/code/controllers/subsystem/fullauto.dm new file mode 100644 index 000000000000..856bc49f8e9d --- /dev/null +++ b/code/controllers/subsystem/fullauto.dm @@ -0,0 +1,5 @@ +PROCESSING_SUBSYSTEM_DEF(fullauto) + name = "Fullauto" + flags = SS_NO_INIT | SS_BACKGROUND + priority = SS_PRIORITY_LOW + wait = SS_WAIT_FULLAUTO diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 2fe896c8820e..52928242093f 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -151,6 +151,10 @@ if(!length(signal_procs[target])) signal_procs -= target +/datum/proc/RegisterSignals(datum/target, list/signal_types, proctype, override = FALSE) + for (var/signal_type in signal_types) + RegisterSignal(target, signal_type, proctype, override) + /datum/component/proc/InheritComponent(datum/component/C, i_am_original) return diff --git a/code/datums/components/fullauto.dm b/code/datums/components/fullauto.dm new file mode 100644 index 000000000000..3bd03c5351e2 --- /dev/null +++ b/code/datums/components/fullauto.dm @@ -0,0 +1,290 @@ +#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 + /// Time between individual shots. + var/autofire_shot_delay = 0.3 SECONDS + /// 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. + var/mouse_status = AUTOFIRE_MOUSEUP + + ///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) + . = ..() + if(!istype(parent, /obj/item/weapon/gun)) + return COMPONENT_NOT_ATTACHED + var/obj/item/weapon/gun/gun = parent + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(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_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(seconds_per_tick) + if(autofire_stat != AUTOFIRE_STAT_FIRING) + STOP_PROCESSING(SSfullauto, 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(user.get_active_hand() == parent) + autofire_on(user.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_REF(on_mouse_down)) + if(!QDELETED(shooter)) + RegisterSignal(shooter, COMSIG_LOGOUT, PROC_REF(autofire_off)) + UnregisterSignal(shooter, COMSIG_LOGIN) + RegisterSignals(parent, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED), PROC_REF(autofire_off)) + parent.RegisterSignal(src, COMSIG_AUTOFIRE_ONMOUSEDOWN, TYPE_PROC_REF(/obj/item/weapon/gun, autofire_bypass_check)) + parent.RegisterSignal(parent, COMSIG_AUTOFIRE_SHOT, TYPE_PROC_REF(/obj/item/weapon/gun, 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_LOGIN, PROC_REF(on_client_login)) + UnregisterSignal(shooter, COMSIG_LOGOUT) + UnregisterSignal(parent, list(COMSIG_PARENT_QDELETING, 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.get_active_hand() == src) + 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(modifiers[SHIFT_CLICK] || modifiers[MIDDLE_CLICK] || modifiers[RIGHT_CLICK] || modifiers[ALT_CLICK]) + return + 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) || 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 = parse_caught_click_modifiers(modifiers, get_turf(source.eye), source) + params = list2params(modifiers) + 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 + INVOKE_ASYNC(src, PROC_REF(start_autofiring)) + +/datum/component/automatic_fire/proc/start_autofiring() + if(autofire_stat == AUTOFIRE_STAT_FIRING) + return + autofire_stat = AUTOFIRE_STAT_FIRING + + clicker.mouse_override_icon = 'icons/hud/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_REF(on_mouse_up)) + mouse_status = AUTOFIRE_MOUSEDOWN + + RegisterSignal(shooter, COMSIG_MOB_SWAP_HANDS, PROC_REF(stop_autofiring)) + + if(!istype(parent, /obj/item/weapon/gun)) + var/obj/item/weapon/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. + + ADD_TRAIT(shooter, TRAIT_AUTOFIRE_SHOOTS, GENERIC_TRAIT) + START_PROCESSING(SSfullauto, src) + RegisterSignal(clicker, COMSIG_CLIENT_MOUSEDRAG, PROC_REF(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(SSfullauto, 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) + REMOVE_TRAIT(shooter, TRAIT_AUTOFIRE_SHOOTS, GENERIC_TRAIT) + 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(!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) + 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 + 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_REF(windup_reset), FALSE), windup_spindown, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) + 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/weapon/gun/proc/on_autofire_start(mob/living/shooter) + if(!ready_to_fire() || shooter.incapacitated()) + return FALSE + if(!can_fire()) + shoot_with_empty_chamber(shooter) + return FALSE + return TRUE + +/obj/item/weapon/gun/proc/autofire_bypass_check(datum/source, client/clicker, atom/target, turf/location, control, params) + SIGNAL_HANDLER + if(clicker.mob.get_active_hand() != src) + return COMPONENT_AUTOFIRE_ONMOUSEDOWN_BYPASS + + +/obj/item/weapon/gun/proc/do_autofire(datum/source, atom/target, mob/living/shooter, params) + SIGNAL_HANDLER + if(!ready_to_fire() || shooter.incapacitated()) + return NONE + if(!can_fire()) + shoot_with_empty_chamber(shooter) + return NONE + INVOKE_ASYNC(src, PROC_REF(do_autofire_shot), source, target, shooter, params) + return COMPONENT_AUTOFIRE_SHOT_SUCCESS //All is well, we can continue shooting. + +/obj/item/weapon/gun/proc/do_autofire_shot(datum/source, atom/target, mob/living/shooter, params) + afterattack(target, shooter, FALSE) + +#undef AUTOFIRE_MOUSEUP +#undef AUTOFIRE_MOUSEDOWN diff --git a/code/datums/uplinks_items.dm b/code/datums/uplinks_items.dm index a1fe5f67feb1..63c8e898c5d6 100644 --- a/code/datums/uplinks_items.dm +++ b/code/datums/uplinks_items.dm @@ -242,7 +242,7 @@ /datum/uplink_item/dangerous/machinegun name = "L6 Squad Automatic Weapon" - desc = "A traditionally constructed machine gun made by AA-2531. This deadly weapon has a massive 50-round magazine of 7.62x51mm ammunition." + desc = "A traditionally constructed machine gun made by AA-2531. This deadly weapon has a massive 100-round magazine of 7.62x51mm ammunition." item = /obj/item/weapon/gun/projectile/automatic/l6_saw cost = 30 uplink_types = list("nuclear") @@ -522,7 +522,7 @@ /datum/uplink_item/ammo/machinegun name = "Ammo-7.62x51mm" - desc = "A 50-round magazine of 7.62x51mm ammunition for use in the L6 SAW machinegun. By the time you need to use this, you'll already be on a pile of corpses." + desc = "A 100-round magazine of 7.62x51mm ammunition for use in the L6 SAW machinegun. By the time you need to use this, you'll already be on a pile of corpses." item = /obj/item/ammo_box/magazine/saw cost = 10 uplink_types = list("nuclear") diff --git a/code/game/gamemodes/modes_gameplays/families/outfit.dm b/code/game/gamemodes/modes_gameplays/families/outfit.dm index 0ade42619f2a..e3b2ed643949 100644 --- a/code/game/gamemodes/modes_gameplays/families/outfit.dm +++ b/code/game/gamemodes/modes_gameplays/families/outfit.dm @@ -65,7 +65,7 @@ head = /obj/item/clothing/head/helmet/laserproof/police shoes = /obj/item/clothing/shoes/boots/combat gloves = /obj/item/clothing/gloves/combat/police - suit_store = /obj/item/weapon/gun/projectile/automatic + suit_store = /obj/item/weapon/gun/projectile/automatic/saber backpack_contents = list( /obj/item/weapon/storage/box/handcuffs = 1, /obj/item/ammo_box/magazine/smg = 3, diff --git a/code/game/objects/random/random_guns.dm b/code/game/objects/random/random_guns.dm index 7742f2589511..af5342398ecb 100644 --- a/code/game/objects/random/random_guns.dm +++ b/code/game/objects/random/random_guns.dm @@ -92,7 +92,7 @@ return pick(\ prob(15);/obj/item/weapon/gun/projectile/shotgun/bolt_action,\ prob(15);/obj/item/weapon/gun/projectile/shotgun/repeater,\ - prob(15);/obj/item/weapon/gun/projectile/automatic,\ + prob(15);/obj/item/weapon/gun/projectile/automatic/saber,\ prob(14);/obj/item/weapon/gun/projectile/automatic/c20r,\ prob(12);/obj/item/weapon/gun/projectile/automatic/mini_uzi,\ prob(10);/obj/item/weapon/gun/projectile/automatic/bar,\ @@ -188,7 +188,7 @@ /obj/item/weapon/gun/projectile/revolver/doublebarrel/dungeon/sawn_off,\ /obj/item/weapon/gun/projectile/revolver,\ /obj/item/weapon/gun/projectile/revolver/detective,\ - /obj/item/weapon/gun/projectile/automatic,\ + /obj/item/weapon/gun/projectile/automatic/saber,\ /obj/item/weapon/gun/projectile/automatic/mini_uzi,\ /obj/item/weapon/gun/projectile/automatic/c20r,\ /obj/item/weapon/gun/projectile/automatic/l13,\ diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index a737576889b8..ef38f33c5e50 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -174,6 +174,46 @@ var/global/list/all_supply_groups = list("Operations","Security","Hospitality"," access = access_brig group = "Security" +/datum/supply_pack/ballistic/smg + name = ".38 SMG crate" + contains = list(/obj/item/weapon/gun/projectile/automatic/l13, + /obj/item/weapon/gun/projectile/automatic/l13, + /obj/item/weapon/gun/projectile/automatic/l13) + additional_costs = 2300 + crate_type = /obj/structure/closet/crate/secure/weapon + crate_name = ".38 SMG crate" + access = access_brig + group = "Security" + +/datum/supply_pack/ballistic/smg_magazine + name = ".38 magazine" + contains = list(/obj/item/ammo_box/magazine/l13/lethal, + /obj/item/ammo_box/magazine/l13/lethal, + /obj/item/ammo_box/magazine/l13/lethal, + /obj/item/ammo_box/magazine/l13/lethal, + /obj/item/ammo_box/magazine/l13/lethal, + /obj/item/ammo_box/magazine/l13/lethal) + additional_costs = 500 + crate_type = /obj/structure/closet/crate/secure + crate_name = ".38 magazine" + access = access_armory + group = "Security" + +/datum/supply_pack/ballistic/smg_magazine_rubber + name = ".38 magazine (rubber)" + contains = list(/obj/item/ammo_box/magazine/l13, + /obj/item/ammo_box/magazine/l13, + /obj/item/ammo_box/magazine/l13, + /obj/item/ammo_box/magazine/l13, + /obj/item/ammo_box/magazine/l13, + /obj/item/ammo_box/magazine/l13) + additional_costs = 400 + crate_type = /obj/structure/closet/crate/secure + crate_name = ".38 magazine (rubber)" + access = access_brig + group = "Security" + + /datum/supply_pack/ballistic/pistol name = "9mm pistol crate" contains = list(/obj/item/weapon/gun/projectile/automatic/pistol/glock, diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 6b470e732a73..8e35c5318c0c 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -75,6 +75,8 @@ var/last_asset_job = 0 var/last_completed_asset_job = 0 + ///Time when the click was intercepted + var/click_intercept_time = 0 ///Amount of keydowns in the last keysend checking interval var/client_keysend_amount = 0 @@ -118,3 +120,5 @@ COOLDOWN_DECLARE(say_slowmode) var/is_in_spawner = FALSE + ///used to override the mouse cursor so it doesnt get reset + var/mouse_override_icon = null diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index ddd9358ab0c7..d1e52bba2bea 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -667,7 +667,7 @@ var/global/mining_shuttle_location = 0 // 0 = station 13, 1 = mining station damtype = BURN hitsound = list('sound/weapons/sear.ogg') ammo_type = list(/obj/item/ammo_casing/energy/laser/cutter) - fire_delay = 3 + fire_delay = 0 w_class = SIZE_SMALL //it is smaller than the pickaxe origin_tech = "materials=4;phorontech=3;engineering=3" desc = "The latest self-rechargeable low-power cutter using bursts of hot plasma. You could use it to cut limbs off of xenos! Or, you know, mine stuff." @@ -697,16 +697,15 @@ var/global/mining_shuttle_location = 0 // 0 = station 13, 1 = mining station if((iswallturf(target)) && (prob(destruction_chance))) target.ex_act(EXPLODE_HEAVY) - /obj/item/weapon/gun/energy/laser/cutter/atom_init() . = ..() power_supply.AddComponent(/datum/component/cell_selfrecharge, 50) + AddComponent(/datum/component/automatic_fire, 0.4 SECONDS) /obj/item/weapon/gun/energy/laser/cutter/emag_act(mob/user) if(emagged) return FALSE ammo_type += new /obj/item/ammo_casing/energy/laser/cutter/emagged(src) - fire_delay = 5 origin_tech += ";syndicate=1" emagged = TRUE to_chat(user, "Ошибка: Обнаружен несовместимый модуль. Ошибкаошибкаошибка.") diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index a5d8a7bf3f34..a186e529b11d 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -136,6 +136,9 @@ if(get_species() == UNATHI && bodytemperature > species.body_temperature) tally -= min((bodytemperature - species.body_temperature) / 10, 1) //will be on the border of heat_level_1 + if(HAS_TRAIT(src, TRAIT_AUTOFIRE_SHOOTS)) // so that you can’t run at full speed and shoot everyone and everything + tally += 0.75 + return (tally + config.human_delay) /mob/living/carbon/human/Process_Spacemove(movement_dir = 0) diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index dc095251e28c..f62b0ef51e59 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -331,11 +331,11 @@ origin_tech = "combat=2" ammo_type = /obj/item/ammo_casing/a762 caliber = "a762" - max_ammo = 50 + max_ammo = 100 /obj/item/ammo_box/magazine/saw/update_icon() ..() - icon_state = "[initial(icon_state)]-[round(ammo_count(),10)]" + icon_state = "[initial(icon_state)]-[round(ammo_count(),20)]" /obj/item/ammo_box/magazine/chameleon name = "magazine (.45)" diff --git a/code/modules/projectiles/firing.dm b/code/modules/projectiles/firing.dm index c71ca8829f6d..85549bad2870 100644 --- a/code/modules/projectiles/firing.dm +++ b/code/modules/projectiles/firing.dm @@ -50,6 +50,7 @@ qdel(BB) BB = null return 1 + BB.dispersion += weapon.spread BB.loc = get_turf(src) BB.starting = get_turf(src) BB.current = curloc diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index ed31e7d13761..121f87fe482a 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -39,6 +39,11 @@ var/fire_delay = 6 var/last_fired = 0 var/two_hand_weapon = FALSE + var/burst = 1 //burst size + var/burst_delay = 1 //cooldown between burst shots + var/spread_increase = 0 // per shot + var/spread_max = 0 + var/spread = 0 lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' righthand_file = 'icons/mob/inhands/guns_righthand.dmi' @@ -46,6 +51,12 @@ /datum/action/item_action/hands_free/switch_gun name = "Switch Gun" +/obj/item/weapon/gun/process() + if(spread == 0) + STOP_PROCESSING(SSfastprocess, src) + else + spread = clamp(spread - 0.1, 0, spread_max) + /obj/item/weapon/gun/examine(mob/user) ..() if(two_hand_weapon) @@ -80,12 +91,21 @@ var/skill_recoil_duration = max(DEFAULT_DURATION_RECOIL, apply_skill_bonus(user, recoil, list(/datum/skill/firearms = SKILL_LEVEL_TRAINED), multiplier = -0.5)) if(two_hand_weapon != DESIRABLE_TWOHAND) shake_camera(user, skill_recoil_duration, OPTIMAL_POWER_RECOIL) + if(spread_increase) + spread = clamp(spread + spread_increase, 0, spread_max) + START_PROCESSING(SSfastprocess, src) if(two_hand_weapon == DESIRABLE_TWOHAND) //No OPTIMAL_POWER_RECOIL only for increasing user's motivation to drop other hand if(user.get_inactive_hand()) shake_camera(user, recoil + 2, recoil + 1) + if(spread_increase) + spread = clamp(spread + spread_increase + 1, 0, spread_max) + START_PROCESSING(SSfastprocess, src) else shake_camera(user, skill_recoil_duration, OPTIMAL_POWER_RECOIL) + if(spread_increase) + spread = clamp(spread + spread_increase, 0, spread_max) + START_PROCESSING(SSfastprocess, src) if(silenced) playsound(user, fire_sound, VOL_EFFECTS_MASTER, 30, FALSE, null, -4) @@ -174,25 +194,28 @@ return if (!ready_to_fire()) - if (world.time % 3) //to prevent spam - to_chat(user, "[src] is not ready to fire again!") return - if(chambered) - if(point_blank) - if(!chambered.BB.fake) - user.visible_message(" \The [user] fires \the [src] point blank at [target]!") - chambered.BB.damage *= 1.3 - if(!chambered.fire(src, target, user, params, , silenced)) - shoot_with_empty_chamber(user) - else - shoot_live_shot(user) - user.newtonian_move(get_dir(target, user)) - else - shoot_with_empty_chamber(user) - process_chamber() - update_icon() - update_inv_mob() + user.next_click = world.time + (burst - 1) * burst_delay + for(var/i in 1 to burst) + if(chambered) + if(point_blank) + if(!chambered.BB.fake) + user.visible_message(" \The [user] fires \the [src] point blank at [target]!") + chambered.BB.damage *= 1.3 + if(!chambered.fire(src, target, user, params, , silenced)) + shoot_with_empty_chamber(user) + break + else + shoot_live_shot(user) + user.newtonian_move(get_dir(target, user)) + else + shoot_with_empty_chamber(user) + break + sleep(burst_delay) + process_chamber() + update_icon() + update_inv_mob() /obj/item/weapon/gun/proc/can_fire() return diff --git a/code/modules/projectiles/guns/plasma/plasma.dm b/code/modules/projectiles/guns/plasma/plasma.dm index 0a7761add0a0..a7e256a658e8 100644 --- a/code/modules/projectiles/guns/plasma/plasma.dm +++ b/code/modules/projectiles/guns/plasma/plasma.dm @@ -16,11 +16,12 @@ desc = "Стандартный плазменный карабин типа булл-пап обладающий высокой скорострельностью." icon_state = "plasma10_car" item_state = "plasma10_car" - fire_delay = 2 + fire_delay = 0 origin_tech = "combat=3;magnets=2" fire_sound = 'sound/weapons/guns/plasma10_shot.ogg' recoil = FALSE can_be_holstered = FALSE + var/fullauto = TRUE var/overcharge_fire_sound = 'sound/weapons/guns/plasma10_overcharge_shot.ogg' @@ -40,6 +41,7 @@ icon_state = "plasma104_stg" item_state = "plasma104_stg" origin_tech = "combat=4;magnets=3" + fullauto = FALSE overcharge_fire_sound = 'sound/weapons/guns/plasma10_overcharge_massive_shot.ogg' @@ -55,6 +57,8 @@ /obj/item/weapon/gun/plasma/atom_init() . = ..() + if(fullauto) + AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) magazine = new initial_mag(src) for(var/i in ammo_type) var/path = ammo_type[i] @@ -85,7 +89,7 @@ fire_sound = initial(fire_sound) else shot = ammo_type[PLASMAGUN_OVERCHARGE_TYPE] - fire_delay = 1 + fire_delay = 0 fire_sound = overcharge_fire_sound max_projectile_per_fire = 1 diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index be0134592d26..48b983faa04e 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -1,6 +1,6 @@ -/obj/item/weapon/gun/projectile/automatic //Hopefully someone will find a way to make these fire in bursts or something. --Superxpdude - name = "submachine gun" - desc = "Легкий, скорострельный пистолет-пулемёт. Использует патроны калибра 9мм." +/obj/item/weapon/gun/projectile/automatic + name = "generic automatic gun" + desc = "О боже, вы не должны были видеть это!" icon_state = "saber" item_state = null w_class = SIZE_SMALL @@ -46,6 +46,17 @@ return install_silencer(I, user, params) return ..() +/obj/item/weapon/gun/projectile/automatic/saber + name = "submachine gun" + desc = "Легкий, скорострельный пистолет-пулемёт. Использует патроны калибра 9мм." + spread_increase = 0.5 + spread_max = 1.5 + fire_delay = 0 + +/obj/item/weapon/gun/projectile/automatic/saber/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) + /obj/item/weapon/gun/projectile/automatic/mini_uzi name = "Mac-10" desc = "Легкий и скорострельный пистолет-пулемёт для тех случаев, когда нужно кого-то быстро убить. Использует патроны калибра 9мм." @@ -56,6 +67,13 @@ origin_tech = "combat=5;materials=2;syndicate=8" initial_mag = /obj/item/ammo_box/magazine/mac10 can_be_silenced = TRUE + fire_delay = 0 + spread_increase = 0.25 + spread_max = 2 + +/obj/item/weapon/gun/projectile/automatic/mini_uzi/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.1 SECONDS) /obj/item/weapon/gun/projectile/automatic/c20r name = "C-20r SMG" @@ -70,6 +88,13 @@ should_alarm_when_empty = TRUE can_be_silenced = TRUE has_ammo_counter = TRUE + fire_delay = 0 + spread_increase = 0.25 + spread_max = 1.5 + +/obj/item/weapon/gun/projectile/automatic/c20r/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) /obj/item/weapon/gun/projectile/automatic/l6_saw name = "L6 SAW" @@ -83,9 +108,16 @@ has_cover = TRUE two_hand_weapon = ONLY_TWOHAND has_ammo_counter = TRUE + fire_delay = 0 + spread_increase = 0.5 + spread_max = 2 + +/obj/item/weapon/gun/projectile/automatic/l6_saw/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.25 SECONDS) /obj/item/weapon/gun/projectile/automatic/l6_saw/update_icon() - icon_state = "l6[cover_open ? "open" : "closed"][magazine ? CEIL(get_ammo(0) / 12.5) * 25 : "-empty"]" + icon_state = "l6[cover_open ? "open" : "closed"][magazine ? CEIL(get_ammo(0) / 25) * 25 : "-empty"]" item_state = "l6[cover_open ? "open" : "closed"][magazine ? "mag" : "nomag"]" /obj/item/weapon/gun/projectile/automatic/l6_saw/afterattack(atom/target, mob/user, proximity, params) //what I tried to do here is just add a check to see if the cover is open or not and add an icon_state change because I can't figure out how c-20rs do it with overlays @@ -136,6 +168,13 @@ suitable_mags = list(/obj/item/ammo_box/magazine/l13, /obj/item/ammo_box/magazine/l13/lethal) fire_sound = 'sound/weapons/guns/gunshot_l13.ogg' can_be_silenced = TRUE + fire_delay = 0 + spread_increase = 0.25 + spread_max = 1.5 + +/obj/item/weapon/gun/projectile/automatic/l13/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) /obj/item/weapon/gun/projectile/automatic/tommygun name = "tommy gun" @@ -149,6 +188,13 @@ initial_mag = /obj/item/ammo_box/magazine/tommygun fire_sound = 'sound/weapons/guns/gunshot_light.ogg' can_be_silenced = TRUE + fire_delay = 0 + spread_increase = 0.25 + spread_max = 2 + +/obj/item/weapon/gun/projectile/automatic/tommygun/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.15 SECONDS) /obj/item/weapon/gun/projectile/automatic/bar name = "Browning M1918" @@ -160,6 +206,13 @@ origin_tech = "combat=5;materials=2" initial_mag = /obj/item/ammo_box/magazine/bar fire_sound = 'sound/weapons/guns/Gunshot2.ogg' + fire_delay = 0 + spread_increase = 0.5 + spread_max = 1 + +/obj/item/weapon/gun/projectile/automatic/bar/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.4 SECONDS) /obj/item/weapon/gun/projectile/automatic/borg name = "Robot SMG" @@ -167,6 +220,8 @@ initial_mag = /obj/item/ammo_box/magazine/borg45 fire_sound = 'sound/weapons/guns/gunshot_medium.ogg' has_ammo_counter = TRUE + burst = 3 + burst_delay = 2 /obj/item/weapon/gun/projectile/automatic/borg/update_icon() return @@ -205,6 +260,13 @@ initial_mag = /obj/item/ammo_box/magazine/a28 suitable_mags = list(/obj/item/ammo_box/magazine/a28, /obj/item/ammo_box/magazine/a28/nonlethal, /obj/item/ammo_box/magazine/a28/incendiary) fire_sound = 'sound/weapons/guns/gunshot_medium.ogg' + fire_delay = 0 + spread_increase = 0.5 + spread_max = 1.5 + +/obj/item/weapon/gun/projectile/automatic/a28/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.25 SECONDS) /obj/item/weapon/gun/projectile/automatic/a74 name = "A74 assault rifle" @@ -217,6 +279,13 @@ item_state = "a74" origin_tech = "combat=5;materials=4;syndicate=6" fire_sound = 'sound/weapons/guns/gunshot_ak74.ogg' + fire_delay = 0 + spread_increase = 0.5 + spread_max = 1.5 + +/obj/item/weapon/gun/projectile/automatic/a74/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.25 SECONDS) /obj/item/weapon/gun/projectile/automatic/a74/krinkov name = "Krinkov" @@ -299,7 +368,10 @@ initial_mag = /obj/item/ammo_box/magazine/m41a w_class = SIZE_SMALL two_hand_weapon = DESIRABLE_TWOHAND - fire_delay = 1 + fire_delay = 3 + burst = 3 + spread_increase = 0.5 + spread_max = 1.5 /obj/item/weapon/gun/projectile/automatic/m41a/process_chamber() return ..(1, 1, 1) @@ -325,10 +397,13 @@ /obj/item/weapon/gun/projectile/automatic/m41a/launcher/proc/toggle_gl(mob/user) using_gl = !using_gl if(using_gl) + spread = 0 + burst = 1 user.visible_message("[user] presses a button, activating their [launcher]!",\ "You activate your [launcher].",\ "You hear an ominous click.") else + burst = 3 user.visible_message("[user] presses a button, deciding to stop the bombings.",\ "You deactivate your [launcher].",\ "You hear a click.") diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 1525ed64b7ce..da7b49a15de4 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -65,6 +65,13 @@ initial_mag = /obj/item/ammo_box/magazine/stechkin suitable_mags = list(/obj/item/ammo_box/magazine/stechkin, /obj/item/ammo_box/magazine/stechkin/extended) can_be_silenced = TRUE + fire_delay = 0 + spread_increase = 0.5 + spread_max = 1.5 + +/obj/item/weapon/gun/projectile/automatic/pistol/stechkin/atom_init() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.3 SECONDS) /obj/item/weapon/gun/projectile/automatic/pistol/colt1911 desc = "Дешевая марсианская подделка Colt M1911. Использует менее смертоносные патроны 45-го калибра." diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 19f1150e5ca6..ba946827310d 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -238,7 +238,7 @@ proj_act_sound = SOUNDIN_WEAKBULLETACT /obj/item/projectile/bullet/a762 - damage = 50 + damage = 30 embed = 0 /obj/item/projectile/bullet/incendiary diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index ede37b37c08d..d07c41f77cb3 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -2083,7 +2083,7 @@ other types of metals and chemistry for reagents). id = "smg" build_type = PROTOLATHE materials = list(MAT_METAL = 8000, MAT_SILVER = 2000, MAT_DIAMOND = 1000) - build_path = /obj/item/weapon/gun/projectile/automatic + build_path = /obj/item/weapon/gun/projectile/automatic/saber category = list("Weapons") /datum/design/msmg9mm diff --git a/code/modules/research/prototipify.dm b/code/modules/research/prototipify.dm index 1983ceecae52..d712c0f0ed61 100644 --- a/code/modules/research/prototipify.dm +++ b/code/modules/research/prototipify.dm @@ -92,12 +92,17 @@ power_supply.maxcharge /= 2 power_supply.charge = power_supply.maxcharge +/obj/item/weapon/gun/energy/laser/cutter/set_prototype_qualities(rel_val=100, mark=0) + if(mark) + power_supply.maxcharge += (mark - 1) * 200 + if(!prob(reliability)) + power_supply.maxcharge /= 2 + power_supply.charge = power_supply.maxcharge + /obj/item/weapon/gun/projectile/automatic/set_prototype_qualities(rel_val=100, mark=0) if(mark) recoil = max(recoil / mark, 0.5) - fire_delay = max(fire_delay / mark, 2) if(!prob(reliability)) - fire_delay *= 2 recoil += 1 /obj/item/weapon/gun/plasma/set_prototype_qualities(rel_val=100, mark=0) diff --git a/icons/hud/weapon_pointer.dmi b/icons/hud/weapon_pointer.dmi new file mode 100644 index 000000000000..b5070062c0bb Binary files /dev/null and b/icons/hud/weapon_pointer.dmi differ diff --git a/icons/obj/ammo/magazines.dmi b/icons/obj/ammo/magazines.dmi index 7ee6f6754b0e..4582c3c2f36d 100644 Binary files a/icons/obj/ammo/magazines.dmi and b/icons/obj/ammo/magazines.dmi differ diff --git a/maps/centcom/centcom.dmm b/maps/centcom/centcom.dmm index c8f3f5f20100..2c6d0b6e89eb 100644 --- a/maps/centcom/centcom.dmm +++ b/maps/centcom/centcom.dmm @@ -14441,13 +14441,13 @@ /area/custom/wizard_station) "aGr" = ( /obj/structure/rack, -/obj/item/weapon/gun/projectile/automatic{ +/obj/item/weapon/gun/projectile/automatic/saber{ pixel_y = 6 }, -/obj/item/weapon/gun/projectile/automatic{ +/obj/item/weapon/gun/projectile/automatic/saber{ pixel_y = 3 }, -/obj/item/weapon/gun/projectile/automatic, +/obj/item/weapon/gun/projectile/automatic/saber, /obj/machinery/camera{ c_tag = "Spec. Ops. Armory North"; network = list("ERT") diff --git a/maps/templates/space_structures/old_station.dmm b/maps/templates/space_structures/old_station.dmm index 72dc11c50b6f..3abc5fe6f605 100644 --- a/maps/templates/space_structures/old_station.dmm +++ b/maps/templates/space_structures/old_station.dmm @@ -6967,7 +6967,7 @@ /area/space_structures/old_station/central) "OZ" = ( /obj/structure/rack, -/obj/item/weapon/gun/projectile/automatic, +/obj/item/weapon/gun/projectile/automatic/saber, /obj/effect/decal/turf_decal/alpha/yellow{ icon_state = "bot" }, diff --git a/taucetistation.dme b/taucetistation.dme index e3766ada9b0d..4cbf3b8f140b 100644 --- a/taucetistation.dme +++ b/taucetistation.dme @@ -240,6 +240,7 @@ #include "code\controllers\subsystem\environment.dm" #include "code\controllers\subsystem\events.dm" #include "code\controllers\subsystem\explosions.dm" +#include "code\controllers\subsystem\fullauto.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\holiday.dm" #include "code\controllers\subsystem\holomaps.dm" @@ -342,6 +343,7 @@ #include "code\datums\components\fishing.dm" #include "code\datums\components\footstep.dm" #include "code\datums\components\forcefield.dm" +#include "code\datums\components\fullauto.dm" #include "code\datums\components\gnawing.dm" #include "code\datums\components\logout_spawner.dm" #include "code\datums\components\magic_item.dm"