diff --git a/code/__DEFINES/dcs/signals/signals.dm b/code/__DEFINES/dcs/signals/signals.dm
index 28beea516c5f..7e0baa2152c6 100644
--- a/code/__DEFINES/dcs/signals/signals.dm
+++ b/code/__DEFINES/dcs/signals/signals.dm
@@ -265,6 +265,9 @@
#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click"
///from base of atom/CtrlShiftRightClick(/mob)
#define COMSIG_CLICK_CTRL_SHIFT_RIGHT "ctrl_shift_right_click"
+/// from mob/ver/do_unique_action
+#define COMSIG_CLICK_UNIQUE_ACTION "unique_action"
+ #define OVERIDE_UNIQUE_ACTION 1
//from base of atom/MouseDrop(): (/atom/over, /mob/user)
#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto"
#define COMPONENT_NO_MOUSEDROP 1
diff --git a/code/__DEFINES/guns.dm b/code/__DEFINES/guns.dm
index 0dd8018e2477..9581fb506361 100644
--- a/code/__DEFINES/guns.dm
+++ b/code/__DEFINES/guns.dm
@@ -114,8 +114,14 @@
#define COMSIG_ATTACHMENT_EXAMINE "attach-examine"
#define COMSIG_ATTACHMENT_EXAMINE_MORE "attach-examine-more"
#define COMSIG_ATTACHMENT_PRE_ATTACK "attach-pre-attack"
+#define COMSIG_ATTACHMENT_AFTER_ATTACK "attach-after-attack"
#define COMSIG_ATTACHMENT_ATTACK "attach-attacked"
+#define COMSIG_ATTACHMENT_WIELD "attach-wield"
+#define COMSIG_ATTACHMENT_UNWIELD "attach-unwield"
#define COMSIG_ATTACHMENT_UPDATE_OVERLAY "attach-overlay"
+#define COMSIG_ATTACHMENT_UNIQUE_ACTION "attach-unique-action"
+#define COMSIG_ATTACHMENT_CTRL_CLICK "attach-ctrl-click"
+#define COMSIG_ATTACHMENT_ALT_CLICK "attach-alt-click"
#define COMSIG_ATTACHMENT_TOGGLE "attach-toggle"
@@ -199,6 +205,7 @@
#define FIREMODE_FULLAUTO "auto"
#define FIREMODE_OTHER "other"
#define FIREMODE_OTHER_TWO "other2"
+#define FIREMODE_UNDERBARREL "underbarrel"
#define GUN_LEFTHAND_ICON 'icons/mob/inhands/weapons/guns_lefthand.dmi'
#define GUN_RIGHTHAND_ICON 'icons/mob/inhands/weapons/guns_righthand.dmi'
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 3d3777521f26..cfd08960cd55 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -160,7 +160,10 @@
UnarmedAttack(A,1)
else
if(W)
- W.afterattack(A,src,0,params)
+ if(W.pre_attack(A,src,params))
+ return
+ else
+ W.afterattack(A,src,0,params)
else
RangedAttack(A,params)
@@ -342,10 +345,11 @@
A.AltClick(src)
/atom/proc/AltClick(mob/user)
- SEND_SIGNAL(src, COMSIG_CLICK_ALT, user)
+ var/result = SEND_SIGNAL(src, COMSIG_CLICK_ALT, user)
var/turf/T = get_turf(src)
if(T && (isturf(loc) || isturf(src)) && user.TurfAdjacent(T))
user.set_listed_turf(T)
+ return result
/// Use this instead of [/mob/proc/AltClickOn] where you only want turf content listing without additional atom alt-click interaction
/atom/proc/AltClickNoInteract(mob/user, atom/A)
diff --git a/code/datums/components/attachment.dm b/code/datums/components/attachment.dm
index 5c4274efbe3e..265aef622081 100644
--- a/code/datums/components/attachment.dm
+++ b/code/datums/components/attachment.dm
@@ -8,8 +8,17 @@
var/datum/callback/on_attach
var/datum/callback/on_detach
var/datum/callback/on_toggle
+ var/datum/callback/on_attacked
+ var/datum/callback/on_unique_action
+ var/datum/callback/on_ctrl_click
+ var/datum/callback/on_alt_click
+ var/datum/callback/on_examine
///Called on the parents preattack
var/datum/callback/on_preattack
+ ///Called on the parents wield
+ var/datum/callback/on_wield
+ ///Called on the parents unwield
+ var/datum/callback/on_unwield
///Unused...Also a little broken..
var/list/datum/action/actions
///Generated if the attachment can toggle, sends COMSIG_ATTACHMENT_TOGGLE
@@ -23,6 +32,13 @@
datum/callback/on_detach = null,
datum/callback/on_toggle = null,
datum/callback/on_preattack = null,
+ datum/callback/on_attacked = null,
+ datum/callback/on_unique_action = null,
+ datum/callback/on_ctrl_click = null,
+ datum/callback/on_wield = null,
+ datum/callback/on_unwield = null,
+ datum/callback/on_examine = null,
+ datum/callback/on_alt_click = null,
list/signals = null
)
@@ -36,6 +52,13 @@
src.on_detach = on_detach
src.on_toggle = on_toggle
src.on_preattack = on_preattack
+ src.on_attacked = on_attacked
+ src.on_unique_action = on_unique_action
+ src.on_ctrl_click = on_ctrl_click
+ src.on_wield = on_wield
+ src.on_unwield = on_unwield
+ src.on_examine = on_examine
+ src.on_alt_click = on_alt_click
ADD_TRAIT(parent, TRAIT_ATTACHABLE, "attachable")
RegisterSignal(parent, COMSIG_ATTACHMENT_ATTACH, PROC_REF(try_attach))
@@ -48,6 +71,12 @@
RegisterSignal(parent, COMSIG_ATTACHMENT_PRE_ATTACK, PROC_REF(relay_pre_attack))
RegisterSignal(parent, COMSIG_ATTACHMENT_UPDATE_OVERLAY, PROC_REF(update_overlays))
RegisterSignal(parent, COMSIG_ATTACHMENT_GET_SLOT, PROC_REF(send_slot))
+ RegisterSignal(parent, COMSIG_ATTACHMENT_WIELD, PROC_REF(try_wield))
+ RegisterSignal(parent, COMSIG_ATTACHMENT_UNWIELD, PROC_REF(try_unwield))
+ RegisterSignal(parent, COMSIG_ATTACHMENT_ATTACK, PROC_REF(relay_attacked))
+ RegisterSignal(parent, COMSIG_ATTACHMENT_UNIQUE_ACTION, PROC_REF(relay_unique_action))
+ RegisterSignal(parent, COMSIG_ATTACHMENT_CTRL_CLICK, PROC_REF(relay_ctrl_click))
+ RegisterSignal(parent, COMSIG_ATTACHMENT_ALT_CLICK, PROC_REF(relay_alt_click))
for(var/signal in signals)
RegisterSignal(parent, signal, signals[signal])
@@ -129,6 +158,9 @@
/datum/component/attachment/proc/handle_examine(obj/item/parent, mob/user, list/examine_list)
SIGNAL_HANDLER
+ if(on_examine)
+ on_examine.Invoke(parent, user, examine_list)
+
/datum/component/attachment/proc/handle_examine_more(obj/item/parent, mob/user, list/examine_list)
SIGNAL_HANDLER
@@ -138,6 +170,42 @@
if(on_preattack)
return on_preattack.Invoke(gun, target_atom, user, params)
+/datum/component/attachment/proc/relay_attacked(obj/item/parent, obj/item/gun, obj/item, mob/user, params)
+ SIGNAL_HANDLER_DOES_SLEEP
+
+ if(on_attacked)
+ return on_attacked.Invoke(gun, user, item)
+
+/datum/component/attachment/proc/try_wield(obj/item/parent, obj/item/gun, mob/user, params)
+ SIGNAL_HANDLER_DOES_SLEEP
+
+ if(on_wield)
+ return on_wield.Invoke(gun, user, params)
+
+/datum/component/attachment/proc/try_unwield(obj/item/parent, obj/item/gun, mob/user, params)
+ SIGNAL_HANDLER_DOES_SLEEP
+
+ if(on_unwield)
+ return on_unwield.Invoke(gun, user, params)
+
+/datum/component/attachment/proc/relay_unique_action(obj/item/parent, obj/item/gun, mob/user, params)
+ SIGNAL_HANDLER_DOES_SLEEP
+
+ if(on_unique_action)
+ return on_unique_action.Invoke(gun, user, params)
+
+/datum/component/attachment/proc/relay_ctrl_click(obj/item/parent, obj/item/gun, mob/user, params)
+ SIGNAL_HANDLER_DOES_SLEEP
+
+ if(on_ctrl_click)
+ return on_ctrl_click.Invoke(gun, user, params)
+
+/datum/component/attachment/proc/relay_alt_click(obj/item/parent, obj/item/gun, mob/user, params)
+ SIGNAL_HANDLER_DOES_SLEEP
+
+ if(on_alt_click)
+ return on_alt_click.Invoke(gun, user, params)
+
/datum/component/attachment/proc/send_slot(obj/item/parent)
SIGNAL_HANDLER
return attachment_slot_to_bflag(slot)
diff --git a/code/datums/components/attachment_holder.dm b/code/datums/components/attachment_holder.dm
index f83a55eb201a..e1564902c5ae 100644
--- a/code/datums/components/attachment_holder.dm
+++ b/code/datums/components/attachment_holder.dm
@@ -21,7 +21,7 @@
var/obj/item/gun/parent_gun = parent
src.slot_room = slot_room
- src.valid_types = valid_types
+ src.valid_types = typecacheof(valid_types)
src.slot_offsets = slot_offsets
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(handle_attack))
@@ -29,8 +29,12 @@
RegisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE, PROC_REF(handle_examine_more))
RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(handle_qdel))
RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(handle_item_pre_attack))
+ RegisterSignal(parent, COMSIG_TWOHANDED_WIELD, PROC_REF(handle_item_wield))
+ RegisterSignal(parent, COMSIG_TWOHANDED_UNWIELD, PROC_REF(handle_item_unwield))
RegisterSignal(parent, COMSIG_CLICK_CTRL_SHIFT, PROC_REF(handle_ctrl_shift_click))
+ RegisterSignal(parent, COMSIG_CLICK_CTRL, PROC_REF(handle_ctrl_click))
RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(handle_alt_click))
+ RegisterSignal(parent, COMSIG_CLICK_UNIQUE_ACTION, PROC_REF(handle_unique_action))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(handle_overlays))
if(length(default_attachments))
@@ -80,7 +84,20 @@
/datum/component/attachment_holder/proc/handle_alt_click(obj/item/parent, mob/user)
SIGNAL_HANDLER
- INVOKE_ASYNC(src, PROC_REF(handle_detach), parent, user)
+ if(user.a_intent == INTENT_HARM)
+ INVOKE_ASYNC(src, PROC_REF(handle_detach), parent, user)
+ return TRUE
+ else
+ for(var/obj/item/attach as anything in attachments)
+ if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_ALT_CLICK, parent, user))
+ return TRUE
+
+/datum/component/attachment_holder/proc/handle_ctrl_click(obj/item/parent, mob/user)
+ SIGNAL_HANDLER
+
+ for(var/obj/item/attach as anything in attachments)
+ if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_CTRL_CLICK, parent, user))
+ return TRUE
/datum/component/attachment_holder/proc/do_attachment_radial(obj/item/parent, mob/user)
var/list/attachments_as_list = attachments_to_list(TRUE)
@@ -93,6 +110,7 @@
/datum/component/attachment_holder/proc/handle_examine(obj/item/parent, mob/user, list/examine_list)
if(length(attachments))
examine_list += span_notice("It has [length(attachments)] attachment\s.")
+ examine_list += span_notice("You can remove them by pressing alt-click on the [parent] on harm intent.")
for(var/obj/item/attach as anything in attachments)
SEND_SIGNAL(attach, COMSIG_ATTACHMENT_EXAMINE, user, examine_list)
@@ -114,7 +132,7 @@
/datum/component/attachment_holder/proc/do_attach(obj/item/attachment, mob/user, bypass_checks)
var/slot = SEND_SIGNAL(attachment, COMSIG_ATTACHMENT_GET_SLOT)
slot = attachment_slot_from_bflag(slot)
- if(!(attachment.type in valid_types))
+ if(!(is_type_in_typecache(attachment,valid_types)))
to_chat(user, span_notice("[attachment] is not a valid attachment for this [parent]!"))
return
if(!slot_room[slot])
@@ -152,13 +170,15 @@
var/selected = tgui_input_list(user, "Select Attachment", "Detach", tool_list)
if(!parent.Adjacent(user) || !selected || !tool || !tool.use_tool(parent, user, 2 SECONDS * tool.toolspeed))
return
- do_detach(tool_list[selected], user)
+ if(selected)
+ do_detach(tool_list[selected], user)
else
if(!length(hand_list))
return
var/selected = tgui_input_list(user, "Select Attachment", "Detach", hand_list)
- if(do_after(user, 2 SECONDS, parent))
- do_detach(hand_list[selected], user)
+ if(selected)
+ if(do_after(user, 2 SECONDS, parent))
+ do_detach(hand_list[selected], user)
/datum/component/attachment_holder/proc/handle_attack(obj/item/parent, obj/item/item, mob/user)
@@ -186,3 +206,24 @@
for(var/obj/item/attach as anything in attachments)
if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_PRE_ATTACK, parent, target_atom, user, params))
return TRUE
+
+/datum/component/attachment_holder/proc/handle_item_wield(obj/item/parent, mob/user, params)
+ SIGNAL_HANDLER
+
+ for(var/obj/item/attach as anything in attachments)
+ if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_WIELD , parent, user, params))
+ return TRUE
+
+/datum/component/attachment_holder/proc/handle_item_unwield(obj/item/parent, mob/user, params)
+ SIGNAL_HANDLER
+
+ for(var/obj/item/attach as anything in attachments)
+ if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_UNWIELD, parent, user, params))
+ return TRUE
+
+/datum/component/attachment_holder/proc/handle_unique_action(obj/item/parent, mob/user, params)
+ SIGNAL_HANDLER
+
+ for(var/obj/item/attach as anything in attachments)
+ if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_UNIQUE_ACTION, parent, user, params))
+ return TRUE
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index a008822e8f08..4e1b85164638 100644
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -22,6 +22,7 @@
/obj/item/modular_computer,
/obj/item/gun/ballistic/automatic/powered,
/obj/item/gun/ballistic/automatic/assault/e40,
+ /obj/item/attachment/gun/energy,
/obj/item/stock_parts/cell/gun
))
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 58cbaabc5e86..65ff09df9c6f 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1194,8 +1194,12 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
///Intended for interactions with guns, like racking
/obj/item/proc/unique_action(mob/living/user)
- return
+///Called before unique action, if any other associated items should do a unique action or override it.
+/obj/item/proc/pre_unique_action(mob/living/user)
+ if(SEND_SIGNAL(src,COMSIG_CLICK_UNIQUE_ACTION,user) & OVERIDE_UNIQUE_ACTION)
+ return TRUE
+ return FALSE //return true if the proc should end here
/**
* Returns null if this object cannot be used to interact with physical writing mediums such as paper.
* Returns a list of key attributes for this object interacting with paper otherwise.
diff --git a/code/game/objects/items/attachments/_attachment.dm b/code/game/objects/items/attachments/_attachment.dm
index 51e6603eb40b..1b46b59f8db2 100644
--- a/code/game/objects/items/attachments/_attachment.dm
+++ b/code/game/objects/items/attachments/_attachment.dm
@@ -50,6 +50,13 @@
CALLBACK(src, PROC_REF(remove_attachment)), \
CALLBACK(src, PROC_REF(toggle_attachment)), \
CALLBACK(src, PROC_REF(on_preattack)), \
+ CALLBACK(src, PROC_REF(on_attacked)), \
+ CALLBACK(src, PROC_REF(on_unique_action)), \
+ CALLBACK(src, PROC_REF(on_ctrl_click)), \
+ CALLBACK(src, PROC_REF(on_wield)), \
+ CALLBACK(src, PROC_REF(on_unwield)), \
+ CALLBACK(src, PROC_REF(on_examine)), \
+ CALLBACK(src, PROC_REF(on_alt_click)), \
signals)
/obj/item/attachment/Destroy()
@@ -89,6 +96,33 @@
/obj/item/attachment/proc/on_preattack(obj/item/gun/gun, atom/target, mob/user, list/params)
return FALSE
+/obj/item/attachment/proc/on_wield(obj/item/gun/gun, mob/user, list/params)
+ return FALSE
+
+/obj/item/attachment/proc/on_unwield(obj/item/gun/gun, mob/user, list/params)
+ return FALSE
+
+/obj/item/attachment/proc/on_attacked(obj/item/gun/gun, mob/user, obj/item)
+ return FALSE
+
+/obj/item/attachment/proc/on_unique_action(obj/item/gun/gun, mob/user, obj/item)
+ return FALSE
+
+/obj/item/attachment/proc/on_ctrl_click(obj/item/gun/gun, mob/user, params)
+ return FALSE
+
+/obj/item/attachment/proc/on_examine(obj/item/gun/gun, mob/user, list/examine_list)
+ return
+
+/obj/item/attachment/proc/on_alt_click(obj/item/gun/gun, mob/user, list/examine_list)
+ AltClick(user)
+ return TRUE
+
+/obj/item/attachment/examine(mob/user)
+ . = ..()
+ var/list/examine_info = list()
+ . += on_examine(examine_list = examine_info)
+
///Handles the modifiers to the parent gun
/obj/item/attachment/proc/apply_modifiers(obj/item/gun/gun, mob/user, attaching)
if(attaching)
diff --git a/code/game/objects/items/attachments/_gun_attachment.dm b/code/game/objects/items/attachments/_gun_attachment.dm
new file mode 100644
index 000000000000..d7b858922d80
--- /dev/null
+++ b/code/game/objects/items/attachments/_gun_attachment.dm
@@ -0,0 +1,78 @@
+/obj/item/attachment/gun
+ name = "underbarrel gun"
+ desc = "A gun that goes on the underbarrel of another gun. You probably shouldn't be seeing this."
+ icon_state = "gun"
+
+ attach_features_flags = ATTACH_REMOVABLE_HAND
+ pixel_shift_x = 1
+ pixel_shift_y = 4
+ wield_delay = 0.1 SECONDS
+ var/weapon_type = /obj/item/gun/ballistic/shotgun/automatic
+ var/obj/item/gun/attached_gun
+ //basically so the fire select shows the right icon
+ var/underbarrel_prefix = ""
+
+/obj/item/attachment/gun/Initialize()
+ . = ..()
+ if(weapon_type)
+ attached_gun = new weapon_type(src)
+
+/obj/item/attachment/gun/Destroy()
+ . = ..()
+ QDEL_NULL(attached_gun)
+
+/obj/item/attachment/gun/apply_attachment(obj/item/gun/gun, mob/user)
+ . = ..()
+ if(FIREMODE_UNDERBARREL in gun.gun_firemodes)
+ to_chat(user,span_warning("The [gun] already has an underbarrel gun and can't take the [src]!"))
+ return FALSE
+ else
+ gun.gun_firemodes += FIREMODE_UNDERBARREL
+ gun.underbarrel_prefix = underbarrel_prefix
+ if(attached_gun)
+ attached_gun.safety = gun.safety
+ gun.build_firemodes()
+ if(user)
+ gun.equipped(user)
+
+/obj/item/attachment/gun/remove_attachment(obj/item/gun/gun, mob/user)
+ . = ..()
+ var/firemode_to_remove = gun.gun_firemodes.Find(FIREMODE_UNDERBARREL)
+ if(firemode_to_remove)
+ gun.gun_firemodes -= gun.gun_firemodes[firemode_to_remove]
+ gun.underbarrel_prefix = ""
+ gun.build_firemodes()
+ gun.equipped(user)
+
+/obj/item/attachment/gun/on_wield(obj/item/gun/gun, mob/user, list/params)
+ if(attached_gun)
+ attached_gun.wielded_fully = TRUE
+
+/obj/item/attachment/gun/on_unwield(obj/item/gun/gun, mob/user, list/params)
+ if(attached_gun)
+ attached_gun.on_unwield(src, user)
+
+/obj/item/attachment/gun/on_attacked(obj/item/gun/gun, mob/user, obj/item/attack_item)
+ if(gun.gun_firemodes[gun.firemode_index] == FIREMODE_UNDERBARREL)
+ attackby(attack_item,user)
+
+/obj/item/attachment/gun/on_preattack(obj/item/gun/gun, atom/target, mob/living/user, list/params)
+ if(gun.gun_firemodes[gun.firemode_index] == FIREMODE_UNDERBARREL)
+ attached_gun.process_fire(target,user,TRUE)
+ return COMPONENT_NO_ATTACK
+
+/obj/item/attachment/gun/unique_action(mob/living/user)
+ attached_gun.unique_action(user)
+
+/obj/item/attachment/gun/on_unique_action(obj/item/gun/gun, mob/user)
+ if(gun.gun_firemodes[gun.firemode_index] == FIREMODE_UNDERBARREL)
+ attached_gun.unique_action(user)
+ return OVERIDE_UNIQUE_ACTION
+
+/obj/item/attachment/gun/on_ctrl_click(obj/item/gun/gun, mob/user)
+ attached_gun.toggle_safety(user,TRUE)
+
+/obj/item/attachment/gun/on_alt_click(obj/item/gun/gun, mob/user, list/examine_list)
+ if(gun.gun_firemodes[gun.firemode_index] == FIREMODE_UNDERBARREL)
+ return ..()
+
diff --git a/code/game/objects/items/attachments/gun_attachments/ballistic.dm b/code/game/objects/items/attachments/gun_attachments/ballistic.dm
new file mode 100644
index 000000000000..6697895e48ff
--- /dev/null
+++ b/code/game/objects/items/attachments/gun_attachments/ballistic.dm
@@ -0,0 +1,57 @@
+/obj/item/attachment/gun/ballistic
+ name = "ballistic underbarrel gun"
+ desc = "A ballistic underbarrel gun. It shoots bullets. Or something."
+ underbarrel_prefix = "bullet_"
+
+/obj/item/attachment/gun/ballistic/attackby(obj/item/I, mob/living/user, params)
+ if(istype(I,/obj/item/ammo_casing) || istype(I, /obj/item/ammo_box))
+ attached_gun.attackby(I, user)
+ else
+ return ..()
+
+/obj/item/attachment/gun/ballistic/on_examine(obj/item/gun/gun, mob/user, list/examine_list)
+ var/obj/item/gun/ballistic/ballistic_gun = attached_gun
+ var/gun_bolt = ballistic_gun.bolt_type
+ var/count_chambered = !(gun_bolt == BOLT_TYPE_NO_BOLT || gun_bolt == BOLT_TYPE_OPEN)
+ examine_list += span_notice("-The [name] has [ballistic_gun.get_ammo(count_chambered)] round\s remaining.")
+ if (!attached_gun.chambered)
+ examine_list += span_notice("-The [name] does not seem to have a round chambered.")
+ if (attached_gun.bolt_locked)
+ examine_list += span_notice("-The [name]'s [ballistic_gun.bolt_wording] is locked back and needs to be released before firing.")
+ examine_list += span_notice("-You can [ballistic_gun.bolt_wording] [src] by pressing the unique action key. By default, this is space")
+ return examine_list
+
+/obj/item/gun/ballistic/shotgun/underbarrel
+ name = "underbarrel ballistic gun"
+ desc = "You shouldnt be seeing this."
+ semi_auto = TRUE
+ always_chambers = TRUE
+ casing_ejector = TRUE
+ gunslinger_recoil_bonus = 0
+ default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/underbarrel
+ allowed_ammo_types = list(
+ /obj/item/ammo_box/magazine/internal/shot/underbarrel,
+ )
+
+/obj/item/attachment/gun/ballistic/shotgun
+ name = "underbarrel shotgun"
+ desc = "A single shot underbarrel shotgun for warding off anyone who gets too close for comfort."
+ underbarrel_prefix = "sg_"
+ weapon_type = /obj/item/gun/ballistic/shotgun/underbarrel
+
+/obj/item/attachment/gun/ballistic/launcher
+ name = "underbarrel 40mm grenade launcher"
+ desc = "A single shot 40mm underbarel grenade launcher. A compact way to deliver a big boom."
+ underbarrel_prefix = "launcher_"
+ icon_state = "glauncher"
+ weapon_type = /obj/item/gun/ballistic/shotgun/underbarrel/grenadelauncher
+
+/obj/item/gun/ballistic/shotgun/underbarrel/grenadelauncher
+ name = "underbarrel grenade launcher"
+ fire_sound = 'sound/weapons/gun/general/grenade_launch.ogg'
+ default_ammo_type = /obj/item/ammo_box/magazine/internal/grenadelauncher
+ allowed_ammo_types = list(
+ /obj/item/ammo_box/magazine/internal/grenadelauncher
+ )
+
+
diff --git a/code/game/objects/items/attachments/gun_attachments/energy.dm b/code/game/objects/items/attachments/gun_attachments/energy.dm
new file mode 100644
index 000000000000..04962bb54d19
--- /dev/null
+++ b/code/game/objects/items/attachments/gun_attachments/energy.dm
@@ -0,0 +1,47 @@
+/obj/item/attachment/gun/energy
+ name = "underbarrel e-gun"
+ desc = "Pew pew laser beam. You probably shouldnt be seeing this."
+ underbarrel_prefix = "laser_"
+ icon_state = "energy"
+ weapon_type = /obj/item/gun/energy/e_gun
+ var/automatic_charge_overlays = TRUE
+
+/obj/item/attachment/gun/energy/attackby(obj/item/I, mob/living/user, params)
+ if(istype(I, /obj/item/stock_parts/cell/gun))
+ attached_gun.attackby(I, user)
+ update_appearance()
+ else
+ return ..()
+
+/obj/item/attachment/gun/energy/on_examine(obj/item/gun/gun, mob/user, list/examine_list)
+ var/obj/item/gun/energy/e_gun = attached_gun
+ var/obj/item/ammo_casing/energy/shot = e_gun.ammo_type[e_gun.select]
+ var/obj/item/stock_parts/cell/gun/gun_cell = get_cell()
+ if(e_gun.ammo_type.len > 1)
+ examine_list += span_notice("- You can switch firemodes on the [name] by pressing the unique action key. By default, this is space")
+ if(e_gun.cell)
+ examine_list += span_notice("- \The [name]'s cell has [gun_cell.percent()]% charge remaining.")
+ examine_list += span_notice("- \The [name] has [round(gun_cell.charge/shot.e_cost)] shots remaining on [shot.select_name] mode.")
+ else
+ . += span_notice("- \The [name] doesn't seem to have a cell!")
+ if(!e_gun.internal_magazine)
+ examine_list += span_notice("- The cell retainment latch is [e_gun.latch_closed ? "CLOSED" : "OPEN"]. Alt-Click to toggle the latch.")
+ return examine_list
+
+/obj/item/attachment/gun/energy/AltClick(mob/user)
+ . = ..()
+ attached_gun.AltClick(user)
+
+/obj/item/attachment/gun/energy/get_cell()
+ return attached_gun.cell
+
+/obj/item/attachment/gun/energy/e_gun
+ name = "underbarrel energy gun"
+ desc = "A compact underbarrel energy gun. The reduction in size makes it less power effiecent per shot than the standard model."
+ weapon_type = /obj/item/gun/energy/e_gun/underbarrel
+
+/obj/item/gun/energy/e_gun/underbarrel
+ name = "underbarrel energy gun"
+ ammo_type = list(/obj/item/ammo_casing/energy/disabler/underbarrel, /obj/item/ammo_casing/energy/laser/underbarrel)
+ spawn_no_ammo = TRUE
+
diff --git a/code/game/objects/items/attachments/gun_attachments/flamethrower.dm b/code/game/objects/items/attachments/gun_attachments/flamethrower.dm
new file mode 100644
index 000000000000..22c08bdab3bc
--- /dev/null
+++ b/code/game/objects/items/attachments/gun_attachments/flamethrower.dm
@@ -0,0 +1,93 @@
+/obj/item/attachment/gun/flamethrower
+ name = "underbarrel flamethrower"
+ desc = "A compact underbarrel flamethrower holding up to 20 units of fuel, enough for two sprays."
+ icon_state = "flamethrower"
+ weapon_type = null
+ var/obj/item/flamethrower/underbarrel/attached_flamethrower
+
+/obj/item/attachment/gun/flamethrower/Initialize()
+ . = ..()
+ attached_flamethrower = new /obj/item/flamethrower/underbarrel(src)
+
+/obj/item/attachment/gun/flamethrower/Destroy()
+ . = ..()
+ QDEL_NULL(attached_flamethrower)
+
+/obj/item/attachment/gun/flamethrower/apply_attachment(obj/item/gun/gun, mob/user)
+ . = ..()
+ if(gun.safety == TRUE && attached_flamethrower.lit == TRUE)
+ attached_flamethrower.toggle_igniter(user)
+ else if(gun.safety == FALSE && attached_flamethrower.lit == FALSE)
+ attached_flamethrower.toggle_igniter(user)
+
+/obj/item/attachment/gun/flamethrower/attackby(obj/item/I, mob/living/user, params)
+ if(istype(I,/obj/item/reagent_containers/glass) || istype(I,/obj/item/reagent_containers/food/drinks))
+ attached_flamethrower.attackby(I,user)
+ else
+ return ..()
+
+/obj/item/attachment/gun/flamethrower/on_preattack(obj/item/gun/gun, atom/target, mob/living/user, list/params)
+ if(gun.gun_firemodes[gun.firemode_index] == FIREMODE_UNDERBARREL)
+ log_combat(user, target, "flamethrowered", src)
+ attached_flamethrower.flame_turf(get_turf(target))
+ return COMPONENT_NO_ATTACK
+
+/obj/item/attachment/gun/flamethrower/on_unique_action(obj/item/gun/gun, mob/user)
+ if(gun.gun_firemodes[gun.firemode_index] == FIREMODE_UNDERBARREL)
+ attached_flamethrower.unique_action(user)
+ return OVERIDE_UNIQUE_ACTION
+
+/obj/item/attachment/gun/flamethrower/on_examine(obj/item/gun/gun, mob/user, list/examine_list)
+ var/total_volume = 0
+ for(var/datum/reagent/R in attached_flamethrower.beaker.reagents.reagent_list)
+ total_volume += R.volume
+ examine_list += span_notice("-\The [src] has [total_volume] units of fuel left.")
+ examine_list += span_notice("-You can empty the [attached_flamethrower.beaker] by pressing the unique action key. By default, this is space")
+ return examine_list
+
+/obj/item/attachment/gun/flamethrower/on_ctrl_click(obj/item/gun/gun, mob/user)
+ . = ..()
+ attached_flamethrower.toggle_igniter(user)
+
+/obj/item/flamethrower/underbarrel
+ name = "underbarrel flamethrower"
+ desc = "Something is wrong if you're seeing this."
+ create_full = TRUE
+
+/obj/item/flamethrower/underbarrel/Initialize(mapload)
+ . = ..()
+ beaker = new /obj/item/reagent_containers/glass/beaker/flamethrower_underbarrel(src)
+
+// you cant pull out the fuel beaker
+/obj/item/flamethrower/underbarrel/AltClick(mob/user)
+ return
+
+/obj/item/flamethrower/underbarrel/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/reagent_containers/glass) || istype(W,/obj/item/reagent_containers/food/drinks))
+ var/obj/item/reagent_containers/glass/source = W
+ if(!source.is_refillable())
+ to_chat(user, span_danger("\The [source]'s cap is on! Take it off first."))
+ return
+ if(beaker.reagents.total_volume >= beaker.volume)
+ to_chat(user, span_danger("\The [beaker] is full."))
+ return
+ source.reagents.trans_to(beaker, source.amount_per_transfer_from_this, transfered_by = user)
+ playsound(user,'sound/items/glass_transfer.ogg',100)
+ to_chat(user, span_notice("You transfer [source.amount_per_transfer_from_this] units to \the [beaker]"))
+ else
+ return ..()
+
+/obj/item/flamethrower/underbarrel/unique_action(mob/living/user)
+ . = ..()
+ beaker.reagents.clear_reagents()
+ playsound(user,'sound/items/glass_splash.ogg',100)
+
+
+/obj/item/reagent_containers/glass/beaker/flamethrower_underbarrel
+ name = "internal fuel tank"
+ desc = "An internal fuel tank for a flamethrower. You shouldn't have been able to pull this out."
+ icon = 'icons/obj/chemical/hypovial.dmi'
+ icon_state = "hypovial"
+ volume = 20
+
+
diff --git a/code/game/objects/items/attachments/gun_attachments/flaregun.dm b/code/game/objects/items/attachments/gun_attachments/flaregun.dm
new file mode 100644
index 000000000000..0517c2e31f8d
--- /dev/null
+++ b/code/game/objects/items/attachments/gun_attachments/flaregun.dm
@@ -0,0 +1,62 @@
+/obj/item/attachment/gun/flare
+ name = "underbarrel flaregun"
+ desc = "An underbarrel flaregun for lighting the path ahead."
+ icon_state = "riotlauncher"
+ weapon_type = null
+ var/obj/item/flashlight/flare/loaded_flare
+
+/obj/item/attachment/gun/flare/Destroy()
+ . = ..()
+ QDEL_NULL(loaded_flare)
+
+/obj/item/attachment/gun/flare/attackby(obj/item/I, mob/living/user, params)
+ . = ..()
+ if(istype(I,/obj/item/flashlight/flare))
+ if(!loaded_flare)
+ var/obj/item/flashlight/flare/flare_to_load = I
+ if(flare_to_load.on)
+ to_chat(user, span_warning("You can't load a lit flare into the [name]!"))
+ return FALSE
+ if(!flare_to_load.fuel)
+ to_chat(user, span_warning("You can't load a used flare into the [name]!"))
+ return FALSE
+ loaded_flare = flare_to_load
+ if(!user.transferItemToLoc(flare_to_load, src))
+ return FALSE
+ playsound(src,'sound/weapons/gun/shotgun/insert_shell.ogg',100)
+ to_chat(user, span_notice("You load a flare into \the [name]."))
+ else
+ to_chat(user, span_warning("\The [name] already has a flare loaded!"))
+ return FALSE
+
+/obj/item/attachment/gun/flare/on_preattack(obj/item/gun/gun, atom/target, mob/living/user, list/params)
+ if(gun.gun_firemodes[gun.firemode_index] == FIREMODE_UNDERBARREL && !gun.safety)
+ if(loaded_flare)
+ user.visible_message(span_warning("[user] fires a flare!"), span_warning("You fire the [name] at \the [target]!"))
+ var/obj/item/flashlight/flare/flare_to_fire = loaded_flare
+ loaded_flare = null
+ flare_to_fire.attack_self(user)
+ flare_to_fire.forceMove(user.loc)
+ flare_to_fire.throw_at(target,30,2,user)
+ playsound(src,'sound/weapons/gun/general/rocket_launch.ogg',100)
+ else
+ to_chat(user,span_warning("\The [name] doesn't have a flare loaded!"))
+ playsound(src,'sound/weapons/gun/pistol/dry_fire.ogg')
+ return COMPONENT_NO_ATTACK
+
+/obj/item/attachment/gun/flare/on_unique_action(obj/item/gun/gun, mob/user)
+ . = ..()
+ if(loaded_flare)
+ user.put_in_hands(loaded_flare)
+ to_chat(user, span_notice("You unload the flare from \the [name]."))
+ loaded_flare = null
+ playsound(src,'sound/weapons/gun/shotgun/rack.ogg',100)
+ return OVERIDE_UNIQUE_ACTION
+
+/obj/item/attachment/gun/flare/on_examine(obj/item/gun/gun, mob/user, list/examine_list)
+ . = ..()
+ examine_list += span_notice("-\The [name] [loaded_flare ? "has a flare loaded." : "is empty."]")
+ examine_list += span_notice("-You can unload \the [name] by pressing the unique action key. By default, this is space")
+ return examine_list
+
+
diff --git a/code/game/objects/items/attachments/gun_attachments/riot_launcher.dm b/code/game/objects/items/attachments/gun_attachments/riot_launcher.dm
new file mode 100644
index 000000000000..ffc1899407c9
--- /dev/null
+++ b/code/game/objects/items/attachments/gun_attachments/riot_launcher.dm
@@ -0,0 +1,24 @@
+/obj/item/attachment/gun/riot
+ name = "underbarrel riot grenade launcher"
+ desc = "A multipurpose underbarrel riot grenade launcher, typically issued to law enforcement. Loads any tradionally handthrown grenade. Warranty is voided should a lethal grenade be loaded."
+ underbarrel_prefix = "launcher_"
+ icon_state = "riotlauncher"
+ weapon_type = /obj/item/gun/grenadelauncher/underbarrel
+
+/obj/item/attachment/gun/riot/attackby(obj/item/I, mob/living/user, params)
+ if(istype(I, /obj/item/grenade))
+ attached_gun.attackby(I, user)
+ else
+ return ..()
+
+/obj/item/attachment/gun/riot/on_examine(obj/item/gun/gun, mob/user, list/examine_list)
+ var/obj/item/gun/grenadelauncher/launcher = attached_gun
+ if(launcher.grenades.len)
+ examine_list += "The [name] is loaded with a grenade."
+ examine_list += span_notice("-You can eject a grenade from the [src] by pressing the unique action key. By default, this is space")
+ return examine_list
+
+/obj/item/gun/grenadelauncher/underbarrel
+ name = "underbarrel riot grenade launcher"
+ desc = "An even more terrible thing. Just despicable, really. You shouldn't be seeing this."
+ max_grenades = 1
diff --git a/code/modules/cargo/packs/gun.dm b/code/modules/cargo/packs/gun.dm
index 3d88b0e07e23..e9b7fad8585a 100644
--- a/code/modules/cargo/packs/gun.dm
+++ b/code/modules/cargo/packs/gun.dm
@@ -705,7 +705,7 @@
/datum/supply_pack/gun/attachment/laser_sight
name = "Laser Sight Crate"
- desc = "Contains a single rail light to be mounted on a firearm."
+ desc = "Contains a single laser sight to be mounted on a firearm."
cost = 250
contains = list(/obj/item/attachment/laser_sight)
crate_name = "laser sight crate"
@@ -734,9 +734,46 @@
contains = list(/obj/item/attachment/silencer)
crate_name = "suppressor crate"
+/datum/supply_pack/gun/attachment/shotgun
+ name = "Underbarrel Shotgun Crate"
+ desc = "Contains a single shot underbarrel shotgun to be mounted on a firearm."
+ cost = 750
+ contains = list(/obj/item/attachment/gun/ballistic/shotgun)
+ crate_name = "underbarrel shotgun crate"
+
+/datum/supply_pack/gun/attachment/flamethrower
+ name = "Underbarrel Flamethrower Crate"
+ desc = "Contains a compact underbarrel flamethrower to be mounted on a firearm."
+ cost = 750
+ contains = list(/obj/item/attachment/gun/flamethrower)
+ crate_name = "underbarrel flamethrower crate"
+
+/datum/supply_pack/gun/attachment/e_gun
+ name = "Underbarrel Energy Gun Crate"
+ desc = "Contains an underbarrel energy gun to be mounted on a firearm."
+ cost = 750
+ contains = list(/obj/item/attachment/gun/energy/e_gun)
+ crate_name = "underbarrel energy gun crate"
+
/datum/supply_pack/gun/attachment/sling
name = "Shoulder Sling Crate"
desc = "Contains a single shoulder sling to be mounted on a firearm for easy carrying without armor holsters. Only compatible with longarms."
cost = 250
contains = list(/obj/item/attachment/sling)
crate_name = "shoulder sling crate"
+
+/datum/supply_pack/gun/attachment/riot_launcher
+ name = "Underbarrel Riot Grenade Launcher Crate"
+ desc = "Contains a single shot underbarrel riot grenade launcher to be mounted on a firearm."
+ cost = 750
+ contains = list(/obj/item/attachment/gun/riot)
+ crate_name = "underbarrel riot grenade launcher crate"
+
+/datum/supply_pack/gun/attachment/flare
+ name = "Underbarrel Flare Gun Crate"
+ desc = "Contains a single shot underbarrel flare gun to be mounted on a firearm. One box of flares included."
+ cost = 200
+ contains = list(/obj/item/attachment/gun/flare)
+ crate_name = "underbarrel flare gun crate"
+
+
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d56560a5acb5..eb3ee07a322b 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -675,6 +675,9 @@
var/obj/item/I = get_active_held_item()
if(I)
+ if(I.pre_unique_action(src))
+ update_inv_hands()
+ return
I.unique_action(src)
update_inv_hands()
diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm
index a256a34270c9..eb08fe0f8d23 100644
--- a/code/modules/projectiles/ammunition/energy/laser.dm
+++ b/code/modules/projectiles/ammunition/energy/laser.dm
@@ -2,6 +2,10 @@
projectile_type = /obj/projectile/beam/laser
select_name = "kill"
+/obj/item/ammo_casing/energy/laser/underbarrel
+ projectile_type = /obj/projectile/beam/laser
+ e_cost = 1250
+
/obj/item/ammo_casing/energy/laser/slug
projectile_type = /obj/projectile/beam/laser/slug
select_name = "slug"
diff --git a/code/modules/projectiles/ammunition/energy/stun.dm b/code/modules/projectiles/ammunition/energy/stun.dm
index 1d9eda158668..658a1b41affe 100644
--- a/code/modules/projectiles/ammunition/energy/stun.dm
+++ b/code/modules/projectiles/ammunition/energy/stun.dm
@@ -25,6 +25,9 @@
fire_sound = 'sound/weapons/taser2.ogg'
harmful = FALSE
+/obj/item/ammo_casing/energy/disabler/underbarrel
+ e_cost = 625
+
/obj/item/ammo_casing/energy/disabler/hos
e_cost = 600
diff --git a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
index f797d5b4ee55..d0365b09223a 100644
--- a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
@@ -61,6 +61,12 @@
max_ammo = 1
multiload = FALSE
+/obj/item/ammo_box/magazine/internal/shot/underbarrel
+ name = "underbarrel shotgun internal magazine"
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+ max_ammo = 1
+ start_empty = TRUE
+
/obj/item/ammo_box/magazine/internal/shot/sex
name = "six-barrel shotgun internal magazine"
max_ammo = 6
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 8cb1db68d754..353630dfea74 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -269,6 +269,7 @@
var/list/slot_available = ATTACHMENT_DEFAULT_SLOT_AVAILABLE
///Offsets for the slots on this gun. should be indexed by SLOT and then by X/Y
var/list/slot_offsets = list()
+ var/underbarrel_prefix = "" // so the action has the right icon for underbarrel gun
/*
* Zooming
@@ -315,7 +316,7 @@
/// Our firemodes, subtract and add to this list as needed. NOTE that the autofire component is given on init when FIREMODE_FULLAUTO is here.
var/list/gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST, FIREMODE_FULLAUTO, FIREMODE_OTHER, FIREMODE_OTHER_TWO)
/// A acoc list that determines the names of firemodes. Use if you wanna be weird and set the name of say, FIREMODE_OTHER to "Underbarrel grenade launcher" for example.
- var/list/gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst fire", FIREMODE_FULLAUTO = "full auto", FIREMODE_OTHER = "misc. fire", FIREMODE_OTHER_TWO = "very misc. fire")
+ var/list/gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst fire", FIREMODE_FULLAUTO = "full auto", FIREMODE_OTHER = "misc. fire", FIREMODE_OTHER_TWO = "very misc. fire", FIREMODE_UNDERBARREL = "underbarrel weapon")
///BASICALLY: the little button you select firing modes from? this is jsut the prefix of the icon state of that. For example, if we set it as "laser", the fire select will use "laser_single" and so on.
var/fire_select_icon_state_prefix = ""
///If true, we put "safety_" before fire_select_icon_state_prefix's prefix. ex. "safety_laser_single"
@@ -401,6 +402,11 @@
if(manufacturer)
. += "It has [manufacturer] engraved on it."
+/obj/item/gun/attackby(obj/item/I, mob/living/user, params)
+ . = ..()
+ if(gun_firemodes[firemode_index] == FIREMODE_UNDERBARREL)
+ return TRUE
+
/obj/item/gun/equipped(mob/living/user, slot)
. = ..()
if(zoomed && user.get_active_held_item() != src)
@@ -1058,8 +1064,11 @@
/obj/item/gun/proc/build_firemodes()
if(FIREMODE_FULLAUTO in gun_firemodes)
- AddComponent(/datum/component/automatic_fire, fire_delay)
+ if(!GetComponent(/datum/component/automatic_fire))
+ AddComponent(/datum/component/automatic_fire, fire_delay)
SEND_SIGNAL(src, COMSIG_GUN_DISABLE_AUTOFIRE)
+ for(var/datum/action/item_action/toggle_firemode/old_firemode in actions)
+ old_firemode.Destroy()
var/datum/action/item_action/our_action
if(gun_firemodes.len > 1)
@@ -1109,7 +1118,10 @@
var/current_firemode = our_gun.gun_firemodes[our_gun.firemode_index]
//tldr; if we have adjust_fire_select_icon_state_on_safety as true, we append "safety_" to the prefix, otherwise nothing.
var/safety_prefix = "[our_gun.adjust_fire_select_icon_state_on_safety ? "[our_gun.safety ? "safety_" : ""]" : ""]"
- button_icon_state = "[safety_prefix][our_gun.fire_select_icon_state_prefix][current_firemode]"
+ if(current_firemode == FIREMODE_UNDERBARREL)
+ button_icon_state = "[safety_prefix][our_gun.underbarrel_prefix][current_firemode]"
+ else
+ button_icon_state = "[safety_prefix][our_gun.fire_select_icon_state_prefix][current_firemode]"
return ..()
GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list(
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index a16f35612692..ac9567a72e50 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -16,6 +16,8 @@
w_class = WEIGHT_CLASS_NORMAL
has_safety = TRUE
safety = TRUE
+ // when we load the gun, should it instantly chamber the next round?
+ var/always_chambers = FALSE
min_recoil = 0.1
@@ -24,6 +26,7 @@
/obj/item/attachment/laser_sight,
/obj/item/attachment/rail_light,
/obj/item/attachment/bayonet,
+ /obj/item/attachment/gun,
/obj/item/attachment/sling
)
slot_available = list(
@@ -218,10 +221,8 @@
return chambered
/obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params)
- . = ..()
-
- if(.)
- return
+ if(..())
+ return FALSE
if(sealed_magazine)
to_chat(user, span_warning("The [magazine_wording] on [src] is sealed and cannot be reloaded!"))
@@ -239,14 +240,14 @@
if(istype(A, /obj/item/ammo_casing) || istype(A, /obj/item/ammo_box))
if (bolt_type == BOLT_TYPE_NO_BOLT || internal_magazine)
- if (chambered && !chambered.BB)
+ if ((chambered && !chambered.BB) || (chambered && always_chambers))
chambered.on_eject(shooter = user)
chambered = null
var/num_loaded = magazine.attackby(A, user, params)
if (num_loaded)
to_chat(user, "You load [num_loaded] [cartridge_wording]\s into \the [src].")
playsound(src, load_sound, load_sound_volume, load_sound_vary)
- if (chambered == null && bolt_type == BOLT_TYPE_NO_BOLT)
+ if ((chambered == null && bolt_type == BOLT_TYPE_NO_BOLT) || always_chambers)
chamber_round()
A.update_appearance()
update_appearance()
diff --git a/code/modules/projectiles/guns/ballistic/assault.dm b/code/modules/projectiles/guns/ballistic/assault.dm
index 41f5d4f9056d..70125488ccbe 100644
--- a/code/modules/projectiles/guns/ballistic/assault.dm
+++ b/code/modules/projectiles/guns/ballistic/assault.dm
@@ -327,5 +327,6 @@
gun_firemodes = list(FIREMODE_FULLAUTO)
default_firemode = FIREMODE_FULLAUTO
latch_toggle_delay = 1.2 SECONDS
+ valid_attachments = list()
spread_unwielded = 20
diff --git a/code/modules/projectiles/guns/ballistic/smg.dm b/code/modules/projectiles/guns/ballistic/smg.dm
index 620c53583205..74d0a3f50860 100644
--- a/code/modules/projectiles/guns/ballistic/smg.dm
+++ b/code/modules/projectiles/guns/ballistic/smg.dm
@@ -103,6 +103,7 @@
/obj/item/attachment/laser_sight,
/obj/item/attachment/rail_light,
/obj/item/attachment/bayonet,
+ /obj/item/attachment/gun,
/obj/item/attachment/foldable_stock
)
@@ -164,6 +165,7 @@
/obj/item/attachment/laser_sight,
/obj/item/attachment/rail_light,
/obj/item/attachment/bayonet,
+ /obj/item/attachment/gun,
/obj/item/attachment/foldable_stock/inteq
)
default_attachments = list(/obj/item/attachment/foldable_stock/inteq)
@@ -200,6 +202,7 @@
/obj/item/attachment/laser_sight,
/obj/item/attachment/rail_light,
/obj/item/attachment/bayonet,
+ /obj/item/attachment/gun,
/obj/item/attachment/foldable_stock
)
default_attachments = list(/obj/item/attachment/foldable_stock)
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index d9bb74da59de..5ade10ca6c9a 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -37,6 +37,7 @@
/obj/item/attachment/laser_sight,
/obj/item/attachment/rail_light,
/obj/item/attachment/bayonet,
+ /obj/item/attachment/gun,
/obj/item/attachment/sling
)
slot_available = list(
@@ -129,6 +130,9 @@
update_appearance()
/obj/item/gun/energy/attackby(obj/item/A, mob/user, params)
+ if(..())
+ return FALSE
+
if (!internal_magazine && (A.type in (allowed_ammo_types - blacklisted_ammo_types)))
var/obj/item/stock_parts/cell/gun/C = A
if (!cell)
@@ -137,8 +141,6 @@
if (tac_reloads)
eject_cell(user, C)
- return ..()
-
/obj/item/gun/energy/proc/insert_cell(mob/user, obj/item/stock_parts/cell/gun/C)
if(!latch_closed)
if(user.transferItemToLoc(C, src))
@@ -160,19 +162,20 @@
var/obj/item/stock_parts/cell/gun/old_cell = cell
old_cell.update_appearance()
cell = null
- to_chat(user, span_notice("You pull the cell out of \the [src]."))
update_appearance()
- if(tac_load && tac_reloads)
- if(do_after(user, tactical_reload_delay, src, hidden = TRUE))
- if(insert_cell(user, tac_load))
- to_chat(user, span_notice("You perform a tactical reload on \the [src]."))
+ if(user)
+ to_chat(user, span_notice("You pull the cell out of \the [src]."))
+ if(tac_load && tac_reloads)
+ if(do_after(user, tactical_reload_delay, src, hidden = TRUE))
+ if(insert_cell(user, tac_load))
+ to_chat(user, span_notice("You perform a tactical reload on \the [src]."))
+ else
+ to_chat(user, span_warning("You dropped the old cell, but the new one doesn't fit. How embarassing."))
else
- to_chat(user, span_warning("You dropped the old cell, but the new one doesn't fit. How embarassing."))
- else
- to_chat(user, span_warning("Your reload was interupted!"))
- return
+ to_chat(user, span_warning("Your reload was interupted!"))
+ return
- user.put_in_hands(old_cell)
+ user.put_in_hands(old_cell)
update_appearance()
//special is_type_in_list method to counteract problem with current method
@@ -183,6 +186,8 @@
return FALSE
/obj/item/gun/energy/AltClick(mob/living/user)
+ if(..())
+ return
if(!internal_magazine && latch_closed)
to_chat(user, span_notice("You start to unlatch the [src]'s power cell retainment clip..."))
if(do_after(user, latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE))
@@ -192,8 +197,8 @@
latch_closed = FALSE
update_appearance()
else if(!internal_magazine && !latch_closed)
- if(!cell && is_attachment_in_contents_list())
- return ..() //should bring up the attachment menu if attachments are added. If none are added, it just does leaves the latch open
+ // if(!cell && is_attachment_in_contents_list())
+ // return ..() //should bring up the attachment menu if attachments are added. If none are added, it just does leaves the latch open
to_chat(user, span_warning("You start to latch the [src]'s power cell retainment clip..."))
if (do_after(user, latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE))
to_chat(user, span_notice("You latch the [src]'s power cell retainment clip " + "CLOSED" + "."))
diff --git a/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm b/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm
index c8ec146ebccf..c3143930d7d5 100644
--- a/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm
+++ b/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm
@@ -1,4 +1,4 @@
-#define CLIP_ATTACHMENTS list(list(/obj/item/attachment/silencer, /obj/item/attachment/laser_sight, /obj/item/attachment/rail_light, /obj/item/attachment/bayonet))
+#define CLIP_ATTACHMENTS list(list(/obj/item/attachment/silencer, /obj/item/attachment/laser_sight, /obj/item/attachment/rail_light, /obj/item/attachment/bayonet, /obj/item/attachment/sling, /obj/item/attachment/gun))
#define CLIP_ATTACHMNENT_POINTS list()
diff --git a/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm b/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm
index d96e3f544364..dd73d3ce61cc 100644
--- a/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm
+++ b/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm
@@ -1,4 +1,4 @@
-#define SCARBOROUGH_ATTACHMENTS list(/obj/item/attachment/silencer, /obj/item/attachment/laser_sight, /obj/item/attachment/rail_light, /obj/item/attachment/bayonet, /obj/item/attachment/energy_bayonet)
+#define SCARBOROUGH_ATTACHMENTS list(/obj/item/attachment/silencer, /obj/item/attachment/laser_sight, /obj/item/attachment/rail_light, /obj/item/attachment/bayonet, /obj/item/attachment/energy_bayonet, /obj/item/attachment/gun)
#define SCARBOROUGH_ATTACH_SLOTS list(ATTACHMENT_SLOT_MUZZLE = 1, ATTACHMENT_SLOT_SCOPE = 1, ATTACHMENT_SLOT_RAIL = 1)
//########### PISTOLS ###########//
@@ -467,6 +467,8 @@ NO_MAG_GUN_HELPER(automatic/smg/cobra/indie)
/obj/item/attachment/silencer,
/obj/item/attachment/laser_sight,
/obj/item/attachment/rail_light,
+ /obj/item/attachment/bayonet,
+ /obj/item/attachment/gun,
/obj/item/attachment/foldable_stock/sidewinder
)
slot_available = list(
@@ -666,7 +668,7 @@ NO_MAG_GUN_HELPER(automatic/marksman/taipan)
allowed_ammo_types = list(
/obj/item/ammo_box/magazine/m556_42_hydra,
)
- gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst fire", FIREMODE_FULLAUTO = "full auto", FIREMODE_OTHER = "underbarrel grenade launcher")
+ gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst fire", FIREMODE_FULLAUTO = "full auto")
gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO)
//gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST, FIREMODE_OTHER)
default_firemode = FIREMODE_SEMIAUTO
@@ -795,72 +797,7 @@ NO_MAG_GUN_HELPER(automatic/assault/hydra/dmr)
name = "SMR-80 \"Hydra\""
desc = "Scarborough Arms' premier modular assault rifle platform. This is the basic configuration, optimized for light weight and handiness. A very well-regarded, if expensive and rare, assault rifle. This one has an underslung grenade launcher attached. Chambered in 5.56x42mm CLIP."
- icon_state = "hydra_gl"
- item_state = "hydra_gl"
-
- gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO, FIREMODE_OTHER)
-
- var/obj/item/gun/ballistic/revolver/grenadelauncher/secondary
-
- slot_available = list( //no rail slot
- ATTACHMENT_SLOT_MUZZLE = 1,
- ATTACHMENT_SLOT_SCOPE = 1,
- )
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/Initialize()
- . = ..()
- secondary = new /obj/item/gun/ballistic/revolver/grenadelauncher(src)
- RegisterSignal(secondary, COMSIG_ATOM_UPDATE_ICON, PROC_REF(secondary_update_icon))
- update_appearance()
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/process_other(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0)
- return secondary.pre_fire(target, user, message, params, zone_override, bonus_spread)
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/can_shoot()
- var/current_firemode = gun_firemodes[firemode_index]
- if(current_firemode != FIREMODE_OTHER)
- return ..()
- return secondary.can_shoot()
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/afterattack(atom/target, mob/living/user, flag, params)
- var/current_firemode = gun_firemodes[firemode_index]
- if(current_firemode != FIREMODE_OTHER)
- return ..()
- return secondary.afterattack(target, user, flag, params)
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/attackby(obj/item/attack_obj, mob/user, params)
- if(istype(attack_obj, secondary.magazine.ammo_type))
- secondary.unique_action()
- return secondary.attackby(attack_obj, user, params)
- return ..()
-
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/can_shoot()
- var/current_firemode = gun_firemodes[firemode_index]
- if(current_firemode != FIREMODE_OTHER)
- return ..()
- return secondary.can_shoot()
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/on_wield(obj/item/source, mob/user)
- wielded = TRUE
- secondary.wielded = TRUE
- INVOKE_ASYNC(src, PROC_REF(do_wield), user)
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/do_wield(mob/user)
- . = ..()
- secondary.wielded_fully = wielded_fully
-
-/// triggered on unwield of two handed item
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/on_unwield(obj/item/source, mob/user)
- . = ..()
- secondary.wielded_fully = FALSE
- secondary.wielded = FALSE
-
-
-/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl/proc/secondary_update_icon()
- update_appearance()
- SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD)
-
+ default_attachments = list(/obj/item/attachment/gun/ballistic/launcher)
/obj/item/ammo_box/magazine/m556_42_hydra
name = "Hydra assault rifle magazine (5.56x42mm CLIP)"
diff --git a/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm b/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm
index 9cce455663ca..04a0bb7f7b22 100644
--- a/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm
+++ b/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm
@@ -1,4 +1,4 @@
-#define SERENE_ATTACHMENTS list(/obj/item/attachment/rail_light, /obj/item/attachment/bayonet)
+#define SERENE_ATTACHMENTS list(/obj/item/attachment/rail_light, /obj/item/attachment/bayonet, /obj/item/attachment/sling, /obj/item/attachment/gun)
#define SERENE_ATTACH_SLOTS list(ATTACHMENT_SLOT_MUZZLE = 1, ATTACHMENT_SLOT_RAIL = 1)
/* Micro Target */
diff --git a/code/modules/projectiles/guns/misc/grenade_launcher.dm b/code/modules/projectiles/guns/misc/grenade_launcher.dm
index b381c8267738..fb9274ba2d0f 100644
--- a/code/modules/projectiles/guns/misc/grenade_launcher.dm
+++ b/code/modules/projectiles/guns/misc/grenade_launcher.dm
@@ -15,9 +15,20 @@
/obj/item/gun/grenadelauncher/examine(mob/user)
. = ..()
. += "[grenades.len] / [max_grenades] grenades loaded."
+ . += span_notice("You can eject a grenade from the [src] by pressing the unique action key. By default, this is space")
-/obj/item/gun/grenadelauncher/attackby(obj/item/I, mob/user, params)
+/obj/item/gun/grenadelauncher/unique_action(mob/living/user)
+ // there are no grenades
+ if(!can_shoot())
+ return
+ var/obj/item/grenade/F = grenades[1]
+ grenades -= F
+ user.put_in_hands(F)
+ playsound(src,'sound/weapons/gun/shotgun/rack.ogg',100)
+ to_chat(user, span_notice("You unload the [F] from the [src]."))
+
+/obj/item/gun/grenadelauncher/attackby(obj/item/I, mob/user, params)
if((istype(I, /obj/item/grenade)))
if(grenades.len < max_grenades)
if(!user.transferItemToLoc(I, src))
@@ -25,6 +36,7 @@
grenades += I
to_chat(user, "You put the grenade in the grenade launcher.")
to_chat(user, "[grenades.len] / [max_grenades] Grenades.")
+ playsound(src,'sound/weapons/gun/shotgun/insert_shell.ogg',100)
else
to_chat(usr, "The grenade launcher cannot hold more grenades!")
@@ -32,6 +44,8 @@
return grenades.len
/obj/item/gun/grenadelauncher/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
+ if(!can_shoot())
+ return
user.visible_message("[user] fired a grenade!", \
"You fire the grenade launcher!")
var/obj/item/grenade/F = grenades[1] //Now with less copypasta!
diff --git a/code/modules/projectiles/guns/powered.dm b/code/modules/projectiles/guns/powered.dm
index 44419c099564..b2066ed41173 100644
--- a/code/modules/projectiles/guns/powered.dm
+++ b/code/modules/projectiles/guns/powered.dm
@@ -47,11 +47,12 @@
//the things below were taken from energy gun code. blame whoever coded this, not me
/obj/item/gun/ballistic/automatic/powered/attackby(obj/item/A, mob/user, params)
+ if(..())
+ return FALSE
if (!internal_cell && (A.type in allowed_cell_types))
var/obj/item/stock_parts/cell/gun/C = A
if (!cell)
insert_cell(user, C)
- return ..()
/obj/item/gun/ballistic/automatic/powered/proc/insert_cell(mob/user, obj/item/stock_parts/cell/gun/C)
if(user.transferItemToLoc(C, src))
diff --git a/icons/mob/actions/actions_items.dmi b/icons/mob/actions/actions_items.dmi
index 9baf5cdde9b3..0f399ff1a9e3 100644
Binary files a/icons/mob/actions/actions_items.dmi and b/icons/mob/actions/actions_items.dmi differ
diff --git a/icons/obj/guns/attachments.dmi b/icons/obj/guns/attachments.dmi
index 83232d3427e7..30c7f7e0391b 100644
Binary files a/icons/obj/guns/attachments.dmi and b/icons/obj/guns/attachments.dmi differ
diff --git a/shiptest.dme b/shiptest.dme
index b6fb8021c85a..5d6f640c94f6 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -1251,6 +1251,7 @@
#include "code\game\objects\items\vending_items.dm"
#include "code\game\objects\items\wayfinding.dm"
#include "code\game\objects\items\attachments\_attachment.dm"
+#include "code\game\objects\items\attachments\_gun_attachment.dm"
#include "code\game\objects\items\attachments\bayonet.dm"
#include "code\game\objects\items\attachments\energy_bayonet.dm"
#include "code\game\objects\items\attachments\laser_sight.dm"
@@ -1259,6 +1260,11 @@
#include "code\game\objects\items\attachments\shoulder_sling.dm"
#include "code\game\objects\items\attachments\silencer.dm"
#include "code\game\objects\items\attachments\stock.dm"
+#include "code\game\objects\items\attachments\gun_attachments\ballistic.dm"
+#include "code\game\objects\items\attachments\gun_attachments\energy.dm"
+#include "code\game\objects\items\attachments\gun_attachments\flamethrower.dm"
+#include "code\game\objects\items\attachments\gun_attachments\flaregun.dm"
+#include "code\game\objects\items\attachments\gun_attachments\riot_launcher.dm"
#include "code\game\objects\items\circuitboards\circuitboard.dm"
#include "code\game\objects\items\circuitboards\computer_circuitboards.dm"
#include "code\game\objects\items\circuitboards\machine_circuitboards.dm"