Skip to content

Commit

Permalink
Gun attachments (shiptest-ss13#2917)
Browse files Browse the repository at this point in the history
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request
Finishing shiptest-ss13#145
Requires shiptest-ss13#2877
<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

## Why It's Good For The Game

<!-- Please add a short description of why you think these changes would
benefit the game. If you can't justify it in words, it might not be
worth adding. -->

## Changelog

:cl:
refactor: refactored attachments to be modular
/:cl:

<!-- Both :cl:'s are required for the changelog to work! You can put
your name to the right of the first :cl: if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

---------

Signed-off-by: FalloutFalcon <[email protected]>
Signed-off-by: Theos <[email protected]>
Co-authored-by: Matthew <[email protected]>
Co-authored-by: Zephyr <[email protected]>
Co-authored-by: Theos <[email protected]>
Co-authored-by: thgvr <[email protected]>
  • Loading branch information
5 people committed Aug 30, 2024
1 parent 9f0fdab commit 4cd699c
Show file tree
Hide file tree
Showing 63 changed files with 1,172 additions and 749 deletions.
2 changes: 1 addition & 1 deletion code/__DEFINES/combat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(/obj/item/gun)))
#define EXPLODE_DEVASTATE 1
#define EXPLODE_HEAVY 2
#define EXPLODE_LIGHT 3
#define EXPLODE_GIB_THRESHOLD 50 //ex_act() with EXPLODE_DEVASTATE severity will gib mobs with less than this much bomb armor
#define EXPLODE_GIB_THRESHOLD 50 //ex_act() with EXPLODE_DEVASTATE severity will gib mobs with less than this much bomb armor

#define EMP_HEAVY 1
#define EMP_LIGHT 2
Expand Down
97 changes: 84 additions & 13 deletions code/__DEFINES/guns.dm
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
#define TRIGGER_GUARD_NONE 0
#define TRIGGER_GUARD_NORMAL 1
//Gun bolt types
///The gun has a closed bolt, when resting it's closed, and must be racked to get a bullet from a magazine. see: Every Fucking Videogame Gun Ever
///Gun has a bolt, it stays closed while not cycling. The gun must be racked to have a bullet chambered when a mag is inserted.
/// Example: c20, shotguns, m90
#define BOLT_TYPE_STANDARD 1
///Gun has a bolt, it is open when ready to fire. The gun can never have a chambered bullet with no magazine, but the bolt stays ready when a mag is removed.
/// Example: Some SMGs, the L6
Expand All @@ -29,18 +30,6 @@
///added recoil of sawn off guns
#define SAWN_OFF_RECOIL 1

//ammo box sprite defines
///ammo box will always use provided icon state
#define AMMO_BOX_ONE_SPRITE 0
///ammo box will have a different state for each bullet; <icon_state>-<bullets left>
#define AMMO_BOX_PER_BULLET 1
///ammo box will have a different state for full and empty; <icon_state>-max_ammo and <icon_state>-0
#define AMMO_BOX_FULL_EMPTY 2

#define SUPPRESSED_NONE 0
#define SUPPRESSED_QUIET 1 ///standard suppressed
#define SUPPRESSED_VERY 2 /// no message

//Autofire component
/// Compatible firemode is in the gun. Wait until it's held in the user hands.
#define AUTOFIRE_STAT_IDLE (1<<0)
Expand All @@ -54,6 +43,10 @@
#define COMSIG_AUTOFIRE_SHOT "autofire_shot"
#define COMPONENT_AUTOFIRE_SHOT_SUCCESS (1<<0)

#define SUPPRESSED_NONE 0
#define SUPPRESSED_QUIET 1 ///standard suppressed
#define SUPPRESSED_VERY 2 /// no message

#define DUALWIELD_PENALTY_EXTRA_MULTIPLIER 1.6

#define MANUFACTURER_NONE null
Expand All @@ -72,6 +65,69 @@
#define MANUFACTURER_PGF "the Etherbor Industries emblem"
#define MANUFACTURER_IMPORT "Lanchester Import Co."

/////////////////
// ATTACHMENTS //
/////////////////
#define TRAIT_ATTACHABLE "attachable"

#define COMSIG_ATTACHMENT_ATTACH "attach-attach"
#define COMSIG_ATTACHMENT_DETACH "attach-detach"
#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_ATTACK "attach-attacked"
#define COMSIG_ATTACHMENT_UPDATE_OVERLAY "attach-overlay"

#define COMSIG_ATTACHMENT_TOGGLE "attach-toggle"

#define COMSIG_ATTACHMENT_GET_SLOT "attach-slot-who"
#define ATTACHMENT_SLOT_MUZZLE "muzzle"
#define ATTACHMENT_SLOT_SCOPE "scope"
#define ATTACHMENT_SLOT_GRIP "grip"
#define ATTACHMENT_SLOT_RAIL "rail"
#define ATTACHMENT_SLOT_STOCK "stock"

/proc/attachment_slot_to_bflag(slot)
switch(slot)
if(ATTACHMENT_SLOT_MUZZLE)
return (1<<0)
if(ATTACHMENT_SLOT_SCOPE)
return (1<<1)
if(ATTACHMENT_SLOT_GRIP)
return (1<<2)
if(ATTACHMENT_SLOT_RAIL)
return (1<<3)
if(ATTACHMENT_SLOT_STOCK)
return (1<<4)

/proc/attachment_slot_from_bflag(slot)
switch(slot)
if(1<<0)
return ATTACHMENT_SLOT_MUZZLE
if(1<<1)
return ATTACHMENT_SLOT_SCOPE
if(1<<2)
return ATTACHMENT_SLOT_GRIP
if(1<<3)
return ATTACHMENT_SLOT_RAIL
if(1<<4)
return ATTACHMENT_SLOT_STOCK

#define ATTACHMENT_DEFAULT_SLOT_AVAILABLE list( \
ATTACHMENT_SLOT_MUZZLE = 1, \
ATTACHMENT_SLOT_SCOPE = 1, \
ATTACHMENT_SLOT_GRIP = 1, \
ATTACHMENT_SLOT_RAIL = 1, \
ATTACHMENT_SLOT_STOCK = 1, \
)

//attach_features_flags
/// Removable by hand
#define ATTACH_REMOVABLE_HAND (1<<0)
/// Removable via crowbar
#define ATTACH_REMOVABLE_TOOL (1<<1)
#define ATTACH_TOGGLE (1<<2)
#define ATTACH_NO_SPRITE (1<<3)

/////////////////
// PROJECTILES //
Expand All @@ -84,6 +140,18 @@

#define NICE_SHOT_RICOCHET_BONUS 10 //if the shooter has the NICE_SHOT trait and they fire a ricocheting projectile, add this to the ricochet chance and auto aim angle

//ammo box sprite defines
///ammo box will always use provided icon state
#define AMMO_BOX_ONE_SPRITE 0
///ammo box will have a different state for each bullet; <icon_state>-<bullets left>
#define AMMO_BOX_PER_BULLET 1
///ammo box will have a different state for full and empty; <icon_state>-max_ammo and <icon_state>-0
#define AMMO_BOX_FULL_EMPTY 2

#define MAG_SIZE_SMALL 1
#define MAG_SIZE_MEDIUM 2
#define MAG_SIZE_LARGE 3

//Projectile Reflect
#define REFLECT_NORMAL (1<<0)
#define REFLECT_FAKEPROJECTILE (1<<1)
Expand All @@ -96,3 +164,6 @@
#define FIREMODE_FULLAUTO "auto"
#define FIREMODE_OTHER "other"
#define FIREMODE_OTHER_TWO "other2"

#define GUN_LEFTHAND_ICON 'icons/mob/inhands/weapons/guns_lefthand.dmi'
#define GUN_RIGHTHAND_ICON 'icons/mob/inhands/weapons/guns_righthand.dmi'
2 changes: 2 additions & 0 deletions code/__DEFINES/sound.dm
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,5 @@
#define SOUND_AREA_LAVALAND SOUND_ENVIRONMENT_MOUNTAINS
#define SOUND_AREA_ICEMOON SOUND_ENVIRONMENT_CAVE
#define SOUND_AREA_WOODFLOOR SOUND_ENVIRONMENT_CITY

#define SOUND_EMPTY_MAG 'sound/weapons/empty.ogg'
2 changes: 1 addition & 1 deletion code/_onclick/click.dm
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@
return

/atom/proc/CtrlShiftClick(mob/user)
SEND_SIGNAL(src, COMSIG_CLICK_CTRL_SHIFT)
SEND_SIGNAL(src, COMSIG_CLICK_CTRL_SHIFT, user)
return

/*
Expand Down
7 changes: 4 additions & 3 deletions code/datums/action.dm
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@
if(owner)
UnregisterSignal(owner, COMSIG_PARENT_QDELETING)
owner = null
button.moved = FALSE //so the button appears in its normal position when given to another owner.
button.locked = FALSE
button.id = null
if(button)
button.moved = FALSE //so the button appears in its normal position when given to another owner.
button.locked = FALSE
button.id = null

/datum/action/proc/Trigger()
if(!IsAvailable())
Expand Down
182 changes: 182 additions & 0 deletions code/datums/components/attachment.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/datum/component/attachment
///Slot the attachment goes on, also used in descriptions so should be player readable
var/slot
///various yes no flags associated with attachments. See defines for these: [_DEFINES/guns.dm]
var/attach_features_flags
///Unused so far, should probally handle it in the parent unless you have a specific reason
var/list/valid_parent_types
var/datum/callback/on_attach
var/datum/callback/on_detach
var/datum/callback/on_toggle
///Called on the parents preattack
var/datum/callback/on_preattack
///Unused...Also a little broken..
var/list/datum/action/actions
///Generated if the attachment can toggle, sends COMSIG_ATTACHMENT_TOGGLE
var/datum/action/attachment/attachment_toggle_action

/datum/component/attachment/Initialize(
slot = ATTACHMENT_SLOT_RAIL,
attach_features_flags = ATTACH_REMOVABLE_HAND,
valid_parent_types = list(/obj/item/gun),
datum/callback/on_attach = null,
datum/callback/on_detach = null,
datum/callback/on_toggle = null,
datum/callback/on_preattack = null,
list/signals = null
)

if(!isitem(parent))
return COMPONENT_INCOMPATIBLE

src.slot = slot
src.attach_features_flags = attach_features_flags
src.valid_parent_types = valid_parent_types
src.on_attach = on_attach
src.on_detach = on_detach
src.on_toggle = on_toggle
src.on_preattack = on_preattack

ADD_TRAIT(parent, TRAIT_ATTACHABLE, "attachable")
RegisterSignal(parent, COMSIG_ATTACHMENT_ATTACH, PROC_REF(try_attach))
RegisterSignal(parent, COMSIG_ATTACHMENT_DETACH, PROC_REF(try_detach))
RegisterSignal(parent, COMSIG_ATTACHMENT_EXAMINE, PROC_REF(handle_examine))
RegisterSignal(parent, COMSIG_ATTACHMENT_EXAMINE_MORE, PROC_REF(handle_examine_more))
if(attach_features_flags & ATTACH_TOGGLE)
RegisterSignal(parent, COMSIG_ATTACHMENT_TOGGLE, PROC_REF(try_toggle))
attachment_toggle_action = new /datum/action/attachment(parent)
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))

for(var/signal in signals)
RegisterSignal(parent, signal, signals[signal])

/datum/component/attachment/Destroy(force, silent)
REMOVE_TRAIT(parent, TRAIT_ATTACHABLE, "attachable")
if(actions && length(actions))
var/obj/item/gun/parent = src.parent
parent.actions -= actions
QDEL_LIST(actions)
qdel(attachment_toggle_action)
return ..()

/datum/component/attachment/proc/try_toggle(obj/item/parent, obj/item/holder, mob/user)
SIGNAL_HANDLER
if(attach_features_flags & ATTACH_TOGGLE)
INVOKE_ASYNC(src, PROC_REF(do_toggle), parent, holder, user)
holder.update_icon()
attachment_toggle_action.UpdateButtonIcon()

/datum/component/attachment/proc/do_toggle(obj/item/parent, obj/item/holder, mob/user)
if(on_toggle)
on_toggle.Invoke(holder, user)
return TRUE

parent.attack_self(user)
return TRUE

/datum/component/attachment/proc/update_overlays(obj/item/parent, list/overlays, list/offset)
if(!(attach_features_flags & ATTACH_NO_SPRITE))
overlays += mutable_appearance(parent.icon, "[parent.icon_state]-attached")

/datum/component/attachment/proc/try_attach(obj/item/parent, obj/item/holder, mob/user, bypass_checks)
SIGNAL_HANDLER

if(!bypass_checks)
if(!parent.Adjacent(user) || (length(valid_parent_types) && (holder.type in valid_parent_types)))
return FALSE

if(on_attach && !on_attach.Invoke(holder, user))
return FALSE

parent.forceMove(holder)

if(attach_features_flags & ATTACH_TOGGLE)
holder.actions += list(attachment_toggle_action)
attachment_toggle_action.gun = holder
attachment_toggle_action.Grant(user)

return TRUE

/datum/component/attachment/proc/try_detach(obj/item/parent, obj/item/holder, mob/user)
SIGNAL_HANDLER

if(!parent.Adjacent(user) || (valid_parent_types && (holder.type in valid_parent_types)))
return FALSE

if(on_attach && !on_detach.Invoke(holder, user))
return FALSE

if(attach_features_flags & ATTACH_TOGGLE)
holder.actions -= list(attachment_toggle_action)
attachment_toggle_action.gun = null
attachment_toggle_action.Remove(user)

if(user.can_put_in_hand(parent))
user.put_in_hand(parent)
return TRUE

parent.forceMove(holder.drop_location())
return TRUE

/datum/component/attachment/proc/handle_examine(obj/item/parent, mob/user, list/examine_list)
SIGNAL_HANDLER

/datum/component/attachment/proc/handle_examine_more(obj/item/parent, mob/user, list/examine_list)
SIGNAL_HANDLER

/datum/component/attachment/proc/relay_pre_attack(obj/item/parent, obj/item/gun, atom/target_atom, mob/user, params)
SIGNAL_HANDLER_DOES_SLEEP

if(on_preattack)
return on_preattack.Invoke(gun, target_atom, user, params)

/datum/component/attachment/proc/send_slot(obj/item/parent)
SIGNAL_HANDLER
return attachment_slot_to_bflag(slot)

/datum/action/attachment
name = "Toggle Attachment"
check_flags = AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
button_icon_state = null
///Decides where we send our toggle signal for when pressed
var/obj/item/gun/gun = null

/datum/action/attachment/New(Target)
..()
name = "Toggle [target.name]"
button.name = name
icon_icon = target.icon
button_icon_state = target.icon_state

/datum/action/attachment/Destroy()
. = ..()
gun = null

/datum/action/attachment/Trigger()
..()
SEND_SIGNAL(target, COMSIG_ATTACHMENT_TOGGLE, gun, owner)

/datum/action/attachment/UpdateButtonIcon()
icon_icon = target.icon
button_icon_state = target.icon_state
..()

//Copied from item action..
/datum/action/attachment/ApplyIcon(atom/movable/screen/movable/action_button/current_button, force)
if(button_icon && button_icon_state)
// If set, use the custom icon that we set instead
// of the item appearence
..()
else if((target && current_button.appearance_cache != target.appearance) || force) //replace with /ref comparison if this is not valid.
var/obj/item/I = target
var/old_layer = I.layer
var/old_plane = I.plane
I.layer = FLOAT_LAYER //AAAH
I.plane = FLOAT_PLANE //^ what that guy said
current_button.cut_overlays()
current_button.add_overlay(I)
I.layer = old_layer
I.plane = old_plane
current_button.appearance_cache = I.appearance
Loading

0 comments on commit 4cd699c

Please sign in to comment.