From 8c6189473226813090dd54dea14a850166ca52e0 Mon Sep 17 00:00:00 2001 From: Skies-Of-Blue <86762641+Skies-Of-Blue@users.noreply.github.com> Date: Sun, 14 Jul 2024 01:29:45 -0700 Subject: [PATCH 01/24] SSD Indicators Will Be Real In 2020 (#3067) ## About The Pull Request Loosely ports https://github.com/Skyrat-SS13/Skyrat13/pull/420, splicing Novasector's SSD mechanics with our own. When a player goes SSD, they initially remain awake and gain a Zz icon above the character indicating they are away.
look here! ![nyaru goes afk](https://github.com/shiptest-ss13/Shiptest/assets/86762641/98c61f04-7e65-4b24-a434-2769cc8a25f6)
After the player has been disconnected for three minutes, their character will resume our current implementation of SSD behavior - losing the Zz icon and falling asleep.
see? ![nyaru has been gone a while, huh](https://github.com/shiptest-ss13/Shiptest/assets/86762641/8c51e05f-a41d-40af-a259-3a2dc7e7cca2)
This also adds the time spent SSD to examine text.
it's helpful! ![taking a bit](https://github.com/shiptest-ss13/Shiptest/assets/86762641/15f91ab4-01b8-4d2f-8d8f-f5cf65f99213)
## Why It's Good For The Game Our current implementation of being SSD leaves some things to be desired. Whenever you disconnect, your character instantly passes out. This can lead to: - dropping critical items in dangerous situations, which is just sort of the worst - aghost causing your character to faint on the spot, something I've heard more than a few admins complain about over the last few years - players *reacting* to your character falling asleep as if it was a planned thing within your control - players moving your character to a bed, disrupting a scene you may have been intent on continuing if it weren't for that pesky internet cut - SSD players flooding the nearby chat with *snore out of nowhere Of course, there's merit to our current implementation as well. Having characters fall asleep keeps immersion clean, and having an SSD Indicator can be immersion breaking. This is why I've opted to remove the indicator and have characters fall asleep after a grace period of three minutes. It allows players a time to reconnect, or admins time to view what they needed to, without all of the disruptions listed above. ## Changelog :cl: add: an SSD Indicator for when you have been disconnected for less than three minutes balance: players will now remain awake for three minutes after disconnecting, with a new SSD icon to boot! balance: players SSD longer than three minutes will lose their icon and fall asleep, much like the previous behavior /:cl: --- .../mob/living/carbon/human/examine.dm | 2 +- code/modules/mob/living/carbon/life.dm | 9 ++++-- code/modules/mob/living/living.dm | 26 ++++++++++++++++++ icons/mob/ssd_indicator.dmi | Bin 0 -> 311 bytes 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 icons/mob/ssd_indicator.dmi diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index f92e8d762f2d..7bbe9fb1de72 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -326,7 +326,7 @@ if(!key) msg += "[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely.\n" else if(!client) - msg += "[t_He] appears to be suffering from SSD - Space Sleep Disorder. [t_He] may snap out of it at any time! Or maybe never. It's best to leave [t_him] be.\n" + msg += "[t_He] [t_has] been suffering from SSD - Space Sleep Disorder - for [trunc(((world.time - lastclienttime) / (1 MINUTES)))] minutes. [t_He] may snap out of it at any time! Or maybe never. It's best to leave [t_him] be.\n" if (length(msg)) . += "[msg.Join("")]" diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 0fc21db37d8c..c5145600bbf8 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -25,9 +25,12 @@ if(.) //not dead handle_blood() - if(isLivingSSD())//if you're disconnected, you're going to sleep - if(AmountSleeping() < 20) - AdjustSleeping(20)//adjust every 10 seconds + if(isLivingSSD()) // If you're disconnected, you're going to sleep + if(trunc((world.time - lastclienttime) / (3 MINUTES)) > 0) // After a three minute grace period, your character will fall asleep + if(AmountSleeping() < 20) + AdjustSleeping(20) // Adjust every 10 seconds + if(ssd_indicator) + cut_overlay(GLOB.ssd_indicator_overlay) // Prevents chronically SSD players from breaking immersion if(stat != DEAD) var/bprv = handle_bodyparts() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 5c499fe21029..269c74a837bd 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1410,6 +1410,32 @@ if(player_logged && stat != DEAD) return TRUE +// The above code is kept to prevent old SSD behavior from breaking, while the code below is dedicated to the SSD Indicator + +GLOBAL_VAR_INIT(ssd_indicator_overlay, mutable_appearance('icons/mob/ssd_indicator.dmi', "default0", RUNECHAT_PLANE)) + +/mob/living + var/ssd_indicator = FALSE + var/lastclienttime = 0 + +/mob/living/proc/set_ssd_indicator(state) + if(state == ssd_indicator) + return + ssd_indicator = state + if(ssd_indicator && stat != DEAD) + add_overlay(GLOB.ssd_indicator_overlay) + else + cut_overlay(GLOB.ssd_indicator_overlay) + +/mob/living/Login() + . = ..() + set_ssd_indicator(FALSE) + +/mob/living/Logout() + . = ..() + lastclienttime = world.time + set_ssd_indicator(TRUE) + /mob/living/vv_get_header() . = ..() var/refid = REF(src) diff --git a/icons/mob/ssd_indicator.dmi b/icons/mob/ssd_indicator.dmi new file mode 100644 index 0000000000000000000000000000000000000000..3f7d100b6c67e725055ef21a964cbcbb113dca6c GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ{HliLl8Vm$j4_H-qwSRlG-d9=Bb7470iYcT0gf&&Q+`DHA9k#g1nzrpr!Ksf{3onM7 zt~9UWyzn9_sPVDb6Mr#vp58;BU%iWc)ikAF(|NJtR<-gE?}Z*GC8T-FPfC$i=+qJG z0$Sbd>Eaj?(fam^A>RQ74%UGE9|QLOf3o&PfT5_*@}|@3hKi<|{#~sdQzl#Xud3cD(Xnp937P3qET zuudpu*edNf;oHvH1)_z&{9m+BU|`} Date: Sun, 14 Jul 2024 03:40:46 -0500 Subject: [PATCH 02/24] Automatic changelog generation for PR #3067 [ci skip] --- html/changelogs/AutoChangeLog-pr-3067.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3067.yml diff --git a/html/changelogs/AutoChangeLog-pr-3067.yml b/html/changelogs/AutoChangeLog-pr-3067.yml new file mode 100644 index 000000000000..072045a2f02d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3067.yml @@ -0,0 +1,9 @@ +author: Skies-Of-Blue +changes: + - {rscadd: an SSD Indicator for when you have been disconnected for less than three + minutes} + - {balance: 'players will now remain awake for three minutes after disconnecting, + with a new SSD icon to boot!'} + - {balance: 'players SSD longer than three minutes will lose their icon and fall + asleep, much like the previous behavior'} +delete-after: true From d42c0e6160e20e527c4f90d0cd1296a1e8ef4724 Mon Sep 17 00:00:00 2001 From: Skies-Of-Blue <86762641+Skies-Of-Blue@users.noreply.github.com> Date: Sun, 14 Jul 2024 01:29:59 -0700 Subject: [PATCH 03/24] Ports 'Command bar typing indicators (client side html version)' (#3080) ## About The Pull Request Directly ports https://github.com/tgstation/tgstation/pull/83081, allowing typing indicators to trigger off of the command bar. ## Why It's Good For The Game Typing indicators are there for a reason! It's helpful to know if another player is cooking a message for thirty seconds or just staring at you. However, a good chunk of our players exclusively use the command bar to send messages, leaving us in the dark! This helps level the playing field. ## Changelog :cl: add: typing indicators now trigger off of the command bar /:cl: --- check_regex.yaml | 2 +- code/modules/client/client_procs.dm | 5 ++++ code/modules/client/verbs/typing.dm | 28 ++++++++++++++++++ html/typing_indicator.html | 46 +++++++++++++++++++++++++++++ interface/skin.dmf | 9 ++++++ shiptest.dme | 1 + 6 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 code/modules/client/verbs/typing.dm create mode 100644 html/typing_indicator.html diff --git a/check_regex.yaml b/check_regex.yaml index d03e15391975..3b5e13a650e5 100644 --- a/check_regex.yaml +++ b/check_regex.yaml @@ -38,7 +38,7 @@ standards: - exactly: [ - 265, + 266, "non-bitwise << uses", '(? + + + + + + + + diff --git a/interface/skin.dmf b/interface/skin.dmf index 9d933e057c46..18122c5e3e89 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -90,6 +90,15 @@ window "mainwindow" background-color = #272727 is-visible = false saved-params = "" + elem "commandbar_spy" + type = BROWSER + is-default = false + pos = 0,0 + size = 200x200 + anchor1 = -1,-1 + anchor2 = -1,-1 + is-visible = false + saved-params = "" window "mapwindow" elem "mapwindow" diff --git a/shiptest.dme b/shiptest.dme index 096d653c7158..2b65bc6d6d58 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -1956,6 +1956,7 @@ #include "code\modules\client\verbs\ooc.dm" #include "code\modules\client\verbs\ping.dm" #include "code\modules\client\verbs\reset_held_keys.dm" +#include "code\modules\client\verbs\typing.dm" #include "code\modules\client\verbs\who.dm" #include "code\modules\clothing\chameleon.dm" #include "code\modules\clothing\clothing.dm" From 50acdada1721135c75b4ffec6a36293ffcc287ef Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sun, 14 Jul 2024 03:54:11 -0500 Subject: [PATCH 04/24] Automatic changelog generation for PR #3080 [ci skip] --- html/changelogs/AutoChangeLog-pr-3080.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3080.yml diff --git a/html/changelogs/AutoChangeLog-pr-3080.yml b/html/changelogs/AutoChangeLog-pr-3080.yml new file mode 100644 index 000000000000..5c4a3abdf25d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3080.yml @@ -0,0 +1,4 @@ +author: Skies-Of-Blue +changes: + - {rscadd: typing indicators now trigger off of the command bar} +delete-after: true From 8ef798383a6168fd1e4339b973ae79fb35822886 Mon Sep 17 00:00:00 2001 From: Theos Date: Sun, 14 Jul 2024 06:19:08 -0400 Subject: [PATCH 05/24] Guncrate fixes (#3158) ## About The Pull Request The beacon now has a proper subtype for not being loaded with ammo rather than having that be its default state The WT-550 no longer starts full in its guncase SKM magazines no longer start full in their guncases either Energy weapons which are meant to start empty now actually start empty The brimstone, hellfire, and scout now come with their respective guncases when ordered via cargo ## Why It's Good For The Game jjjjjjjjjjjjjjjjjjjjjj ## Changelog :cl: fix: gun cargo packs now act more as they would be expected to /:cl: --- code/game/objects/items/storage/guncases.dm | 4 ++-- code/modules/cargo/packs/gun.dm | 6 +++--- code/modules/projectiles/boxes_magazines/external/rifle.dm | 2 +- code/modules/projectiles/guns/ballistic/shotgun.dm | 2 +- code/modules/projectiles/guns/energy.dm | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/code/game/objects/items/storage/guncases.dm b/code/game/objects/items/storage/guncases.dm index b70de7a01444..9cda5ac27a39 100644 --- a/code/game/objects/items/storage/guncases.dm +++ b/code/game/objects/items/storage/guncases.dm @@ -44,7 +44,7 @@ /obj/item/storage/guncase/beacon /obj/item/storage/guncase/beacon/PopulateContents() - new /obj/item/gun/ballistic/shotgun/doublebarrel/beacon(src) + new /obj/item/gun/ballistic/shotgun/doublebarrel/beacon/no_mag(src) /obj/item/storage/guncase/scout /obj/item/storage/guncase/scout/PopulateContents() @@ -79,7 +79,7 @@ /obj/item/storage/guncase/wt550 /obj/item/storage/guncase/wt550/PopulateContents() - new /obj/item/gun/ballistic/automatic/smg/wt550(src) + new /obj/item/gun/ballistic/automatic/smg/wt550/no_mag(src) new /obj/item/ammo_box/magazine/wt550m9/empty(src) new /obj/item/ammo_box/magazine/wt550m9/empty(src) diff --git a/code/modules/cargo/packs/gun.dm b/code/modules/cargo/packs/gun.dm index 132e62bb0ada..1d71b2799026 100644 --- a/code/modules/cargo/packs/gun.dm +++ b/code/modules/cargo/packs/gun.dm @@ -120,14 +120,14 @@ name = "Hellfire Shotgun Crate" desc = "For when you need to deal with 8 hooligans. Contains a pump shotgun, with a 8-round capacity." cost = 2000 - contains = list(/obj/item/gun/ballistic/shotgun/hellfire) + contains = list(/obj/item/storage/guncase/hellfire) crate_name = "shotgun crate" /datum/supply_pack/gun/brimstone_shotgun name = "Brimstone Shotgun Crate" desc = "For when you need to deal with 5 hooligans, and QUICKLY. Contains a slamfire shotgun, with a 5-round capacity. Warranty voided if sawed off." cost = 2000 - contains = list(/obj/item/gun/ballistic/shotgun/brimstone) + contains = list(/obj/item/storage/guncase/brimstone) crate_name = "shotgun crate" /* @@ -159,7 +159,7 @@ name = "Scout Sniper Rifle Crate" desc = "Contains a traditional scoped rifle to hunt wildlife and big game from a respectful distance. Chambered in powerful .300 Magnum." cost = 5500 - contains = list(/obj/item/gun/ballistic/rifle/scout) + contains = list(/obj/item/storage/guncase/scout) crate_name = "rifle crate" /datum/supply_pack/gun/cobra20 diff --git a/code/modules/projectiles/boxes_magazines/external/rifle.dm b/code/modules/projectiles/boxes_magazines/external/rifle.dm index 1066e5b7ef2d..9224c0db84af 100644 --- a/code/modules/projectiles/boxes_magazines/external/rifle.dm +++ b/code/modules/projectiles/boxes_magazines/external/rifle.dm @@ -56,7 +56,7 @@ multiple_sprites = AMMO_BOX_FULL_EMPTY /obj/item/ammo_box/magazine/skm_762_40/empty - start_empty = FALSE + start_empty = TRUE /obj/item/ammo_box/magazine/skm_762_40/extended name = "extended assault rifle magazine (7.62x40mm CLIP)" diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 9dba3399fcbd..692c09a45f42 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -797,7 +797,7 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) recoil = 2 recoil_unwielded = 4 -/obj/item/gun/ballistic/shotgun/doublebarrel/beacon +/obj/item/gun/ballistic/shotgun/doublebarrel/beacon/no_mag spawnwithmagazine = FALSE /obj/item/gun/ballistic/shotgun/doublebarrel/beacon/factory diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 07eb9a159897..f3edb62d8942 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -63,8 +63,8 @@ cell = new cell_type(src) else cell = new(src) - if(!dead_cell) - cell.give(cell.maxcharge) + if(dead_cell) + cell.use(cell.maxcharge) update_ammo_types() recharge_newshot(TRUE) if(selfcharge) From ec8c76bb7819cc31cee79ddfa24ea56f1462be81 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sun, 14 Jul 2024 05:30:03 -0500 Subject: [PATCH 06/24] Automatic changelog generation for PR #3158 [ci skip] --- html/changelogs/AutoChangeLog-pr-3158.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3158.yml diff --git a/html/changelogs/AutoChangeLog-pr-3158.yml b/html/changelogs/AutoChangeLog-pr-3158.yml new file mode 100644 index 000000000000..1de2c30deddf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3158.yml @@ -0,0 +1,4 @@ +author: SomeguyManperson +changes: + - {bugfix: gun cargo packs now act more as they would be expected to} +delete-after: true From 0b583941f01a782fbef1027a7a79c5fafa18a051 Mon Sep 17 00:00:00 2001 From: Sadhorizon <108196626+Sadhorizon@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:33:17 +0200 Subject: [PATCH 07/24] Two minor sunskipper tweaks. (#3204) ## About The Pull Request - Changed it's prefix to SV - adds a missing pipe ## Why It's Good For The Game it is ## Changelog :cl: tweak: Changed sunskipper's prefix to SV. fix: Added a missing pipe to the sunskipper. /:cl: --- _maps/configs/independent_sunskipper.json | 2 +- _maps/shuttles/independent/independent_sunskipper.dmm | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/_maps/configs/independent_sunskipper.json b/_maps/configs/independent_sunskipper.json index 08a4a56f9fde..ee17e126cc34 100644 --- a/_maps/configs/independent_sunskipper.json +++ b/_maps/configs/independent_sunskipper.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/shiptest-ss13/Shiptest/master/_maps/ship_config_schema.json", "map_name": "Sunskipper-class Culinary Vessel", - "prefix": "ISV", + "prefix": "SV", "namelists": ["CRUISE", "NATURAL"], "map_short_name": "Sunskipper-class", "map_path": "_maps/shuttles/independent/independent_sunskipper.dmm", diff --git a/_maps/shuttles/independent/independent_sunskipper.dmm b/_maps/shuttles/independent/independent_sunskipper.dmm index 61c49a25295e..113230223069 100644 --- a/_maps/shuttles/independent/independent_sunskipper.dmm +++ b/_maps/shuttles/independent/independent_sunskipper.dmm @@ -1560,6 +1560,9 @@ /obj/structure/cable/green{ icon_state = "4-9" }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{ + dir = 8 + }, /turf/open/floor/carpet/nanoweave, /area/ship/crew/cryo) "tP" = ( From 3f6da00237a10842d38b743c241bfa327c28880b Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sun, 14 Jul 2024 06:43:51 -0500 Subject: [PATCH 08/24] Automatic changelog generation for PR #3204 [ci skip] --- html/changelogs/AutoChangeLog-pr-3204.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3204.yml diff --git a/html/changelogs/AutoChangeLog-pr-3204.yml b/html/changelogs/AutoChangeLog-pr-3204.yml new file mode 100644 index 000000000000..2b6963e69836 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3204.yml @@ -0,0 +1,5 @@ +author: Sadhorizon +changes: + - {tweak: Changed sunskipper's prefix to SV.} + - {bugfix: Added a missing pipe to the sunskipper.} +delete-after: true From 690818d46806f961e2f87a5b696a23ec508040ad Mon Sep 17 00:00:00 2001 From: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Date: Sun, 14 Jul 2024 19:22:32 -0500 Subject: [PATCH 09/24] Gun attachments (#2917) ## About The Pull Request Finishing #145 Requires #2877 ## Why It's Good For The Game ## Changelog :cl: refactor: refactored attachments to be modular /:cl: --------- Signed-off-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Signed-off-by: Theos Co-authored-by: Matthew Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Co-authored-by: Theos Co-authored-by: thgvr <81882910+thgvr@users.noreply.github.com> --- code/__DEFINES/combat.dm | 2 +- code/__DEFINES/guns.dm | 97 ++- code/__DEFINES/sound.dm | 2 + code/_onclick/click.dm | 2 +- code/datums/action.dm | 7 +- code/datums/components/attachment.dm | 182 ++++++ code/datums/components/attachment_holder.dm | 188 ++++++ code/datums/wires/mines.dm | 4 +- code/game/mecha/equipment/weapons/weapons.dm | 8 +- .../objects/items/attachments/_attachment.dm | 99 ++++ .../game/objects/items/attachments/bayonet.dm | 22 + .../objects/items/attachments/laser_sight.dm | 23 + .../objects/items/attachments/rail_light.dm | 33 ++ .../objects/items/attachments/silencer.dm | 19 + code/game/objects/items/attachments/stock.dm | 40 ++ code/game/objects/items/devices/multitool.dm | 2 +- code/game/objects/items/pneumaticCannon.dm | 4 +- code/game/objects/items/storage/backpack.dm | 2 +- code/game/objects/items/storage/briefcase.dm | 2 +- .../game/objects/items/storage/uplink_kits.dm | 2 +- code/game/objects/items/toys.dm | 4 +- .../blackmarket/blackmarket_items/tools.dm | 2 +- code/modules/cargo/packs/gun.dm | 28 + code/modules/flufftext/Hallucination.dm | 8 +- .../mining/equipment/kinetic_crusher.dm | 2 +- .../mining/lavaland/necropolis_chests.dm | 4 +- .../mob/living/carbon/human/human_defense.dm | 4 + .../hostile/megafauna/codename_claw.dm | 1 - .../projectiles/ammunition/_ammunition.dm | 2 +- code/modules/projectiles/gun.dm | 559 +++++++++--------- code/modules/projectiles/guns/ballistic.dm | 173 +----- .../projectiles/guns/ballistic/assault.dm | 1 - .../projectiles/guns/ballistic/automatic.dm | 6 - .../projectiles/guns/ballistic/gauss.dm | 1 - .../modules/projectiles/guns/ballistic/hmg.dm | 3 - .../projectiles/guns/ballistic/launchers.dm | 1 - .../projectiles/guns/ballistic/pistol.dm | 10 +- .../projectiles/guns/ballistic/rifle.dm | 1 - .../projectiles/guns/ballistic/shotgun.dm | 24 +- .../modules/projectiles/guns/ballistic/smg.dm | 131 ++-- .../modules/projectiles/guns/ballistic/toy.dm | 4 - code/modules/projectiles/guns/energy.dm | 74 +-- .../projectiles/guns/energy/energy_gun.dm | 29 +- .../guns/energy/kinetic_accelerator.dm | 16 +- code/modules/projectiles/guns/energy/laser.dm | 8 +- .../projectiles/guns/energy/mounted.dm | 1 - code/modules/projectiles/guns/energy/pulse.dm | 5 +- .../projectiles/guns/energy/special.dm | 13 +- code/modules/projectiles/guns/energy/stun.dm | 6 - .../projectiles/guns/misc/beam_rifle.dm | 2 +- .../projectiles/guns/misc/syringe_gun.dm | 1 - code/modules/projectiles/guns/powered.dm | 35 +- .../reagents/reagent_containers/hypospray.dm | 2 +- .../reagents/reagent_containers/spray.dm | 8 +- .../research/designs/weapon_designs.dm | 2 +- code/modules/uplink/uplink_items.dm | 2 +- icons/obj/guns/48x32guns.dmi | Bin 2116 -> 4457 bytes icons/obj/guns/attachments.dmi | Bin 0 -> 4332 bytes icons/obj/guns/bayonets.dmi | Bin 263 -> 0 bytes icons/obj/guns/energy.dmi | Bin 26588 -> 24323 bytes icons/obj/guns/flashlights.dmi | Bin 660 -> 0 bytes icons/obj/guns/projectile.dmi | Bin 18205 -> 17907 bytes shiptest.dme | 8 + 63 files changed, 1172 insertions(+), 749 deletions(-) create mode 100644 code/datums/components/attachment.dm create mode 100644 code/datums/components/attachment_holder.dm create mode 100644 code/game/objects/items/attachments/_attachment.dm create mode 100644 code/game/objects/items/attachments/bayonet.dm create mode 100644 code/game/objects/items/attachments/laser_sight.dm create mode 100644 code/game/objects/items/attachments/rail_light.dm create mode 100644 code/game/objects/items/attachments/silencer.dm create mode 100644 code/game/objects/items/attachments/stock.dm create mode 100644 icons/obj/guns/attachments.dmi delete mode 100644 icons/obj/guns/bayonets.dmi delete mode 100644 icons/obj/guns/flashlights.dmi diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index ad0754c85b21..69886107d61c 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -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 diff --git a/code/__DEFINES/guns.dm b/code/__DEFINES/guns.dm index dba7ce93afaf..17cbab5683d8 100644 --- a/code/__DEFINES/guns.dm +++ b/code/__DEFINES/guns.dm @@ -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 @@ -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; - -#define AMMO_BOX_PER_BULLET 1 -///ammo box will have a different state for full and empty; -max_ammo and -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) @@ -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 @@ -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 // @@ -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; - +#define AMMO_BOX_PER_BULLET 1 +///ammo box will have a different state for full and empty; -max_ammo and -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) @@ -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' diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index d4d9807ec0af..1bd23038c600 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -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' diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 993026c0d5e0..a6f81d8ca4f1 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -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 /* diff --git a/code/datums/action.dm b/code/datums/action.dm index de13fc002dde..cdca8729984f 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -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()) diff --git a/code/datums/components/attachment.dm b/code/datums/components/attachment.dm new file mode 100644 index 000000000000..01e3abedd80b --- /dev/null +++ b/code/datums/components/attachment.dm @@ -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 diff --git a/code/datums/components/attachment_holder.dm b/code/datums/components/attachment_holder.dm new file mode 100644 index 000000000000..82968a17604b --- /dev/null +++ b/code/datums/components/attachment_holder.dm @@ -0,0 +1,188 @@ +/datum/component/attachment_holder + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + + ///List of things you can attach to the parent + var/list/valid_types = null + ///How many slots a parent can hold of any one slot + var/list/slot_room = null + ///Icon offsets, should match the sprite itself so just find the position where it should attach + var/list/slot_offsets = null + var/list/obj/item/attachments = list() + +/datum/component/attachment_holder/Initialize( + list/slot_room = null, + list/valid_types = null, + list/slot_offsets = null, + list/default_attachments = null + ) + + if(!isgun(parent)) + return COMPONENT_INCOMPATIBLE + var/obj/item/gun/parent_gun = parent + + src.slot_room = slot_room + src.valid_types = valid_types + src.slot_offsets = slot_offsets + + RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(handle_attack)) + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(handle_examine)) + 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_CLICK_CTRL_SHIFT, PROC_REF(handle_ctrl_shift_click)) + RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(handle_alt_click)) + RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(handle_overlays)) + + if(length(default_attachments)) + for(var/attachment in default_attachments) + var/obj/item/attachment/new_attachment = new attachment(parent_gun.loc) + INVOKE_ASYNC(src, PROC_REF(do_attach), new_attachment, null, TRUE) + +/datum/component/attachment_holder/proc/handle_overlays(obj/item/parent, list/overlays) + SIGNAL_HANDLER + + for(var/obj/item/attachment/attach as anything in attachments) + var/slot = SEND_SIGNAL(attach, COMSIG_ATTACHMENT_GET_SLOT) + slot = attachment_slot_from_bflag(slot) + var/list/attach_overlays = list() + SEND_SIGNAL(attach, COMSIG_ATTACHMENT_UPDATE_OVERLAY, attach_overlays) + for(var/mutable_appearance/overlay as anything in attach_overlays) + if(slot_offsets && slot_offsets[slot]) + var/matrix/overlay_matrix = new + overlay_matrix.Translate(slot_offsets[slot]["x"] - attach.pixel_shift_x, slot_offsets[slot]["y"] - attach.pixel_shift_y) + overlay.transform = overlay_matrix + overlays += overlay + +/datum/component/attachment_holder/proc/handle_qdel() + SIGNAL_HANDLER + qdel(src) + +/datum/component/attachment_holder/Destroy(force, silent) + QDEL_LIST(attachments) + attachments = null + return ..() + +/datum/component/attachment_holder/proc/attachments_to_list(only_toggles = FALSE) + . = list() + for(var/obj/item/attachment/attach as anything in attachments) + if(attach.name in .) + stack_trace("two attachments with same name; this shouldn't happen and will cause failures") + continue + if(only_toggles && !(attach.attach_features_flags & ATTACH_TOGGLE)) + continue + .[attach.name] = attach + +/datum/component/attachment_holder/proc/handle_ctrl_shift_click(obj/item/parent, mob/user) + SIGNAL_HANDLER + + INVOKE_ASYNC(src, PROC_REF(do_attachment_radial), parent, user) + +/datum/component/attachment_holder/proc/handle_alt_click(obj/item/parent, mob/user) + SIGNAL_HANDLER + + INVOKE_ASYNC(src, PROC_REF(handle_detach), parent, user) + +/datum/component/attachment_holder/proc/do_attachment_radial(obj/item/parent, mob/user) + var/list/attachments_as_list = attachments_to_list(TRUE) + var/selection = show_radial_menu(user, parent, attachments_as_list) + var/obj/item/attach = attachments_as_list[selection] + if(!attach) + return + SEND_SIGNAL(attach, COMSIG_ATTACHMENT_TOGGLE, parent, user) + +/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.") + for(var/obj/item/attach as anything in attachments) + SEND_SIGNAL(attach, COMSIG_ATTACHMENT_EXAMINE, user, examine_list) + +/datum/component/attachment_holder/proc/handle_examine_more(obj/item/parent, mob/user, list/examine_list) + for(var/key in slot_room) + if(slot_room[key]) + examine_list += span_notice("It has [slot_room[key]] slot\s free for [key] attachments.") + if(length(attachments)) + examine_list += span_notice("It has the following attachments:") + for(var/obj/item/attach as anything in attachments) + examine_list += span_notice("\t- [attach.name]") + if(length(valid_types)) + examine_list += span_notice("It can accept:") + for(var/obj/attach_type as anything in valid_types) + examine_list += span_notice("\t- [initial(attach_type.name)]") + for(var/obj/item/attach as anything in attachments) + SEND_SIGNAL(attach, COMSIG_ATTACHMENT_EXAMINE_MORE, user, examine_list) + +/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)) + to_chat(user, span_notice("[attachment] is not a valid attachment for this [parent]!")) + return + if(!slot_room[slot]) + to_chat(user, span_notice("[parent] does not contain room for [attachment]!")) + return + slot_room[slot]-- + . = SEND_SIGNAL(attachment, COMSIG_ATTACHMENT_ATTACH, parent, user, bypass_checks) + if(.) + attachments += attachment + var/atom/parent = src.parent + parent.update_icon() + +/datum/component/attachment_holder/proc/do_detach(obj/item/attachment, mob/user) + var/slot = SEND_SIGNAL(attachment, COMSIG_ATTACHMENT_GET_SLOT) + slot = attachment_slot_from_bflag(slot) + if(slot in slot_room) + slot_room[slot]++ + . = SEND_SIGNAL(attachment, COMSIG_ATTACHMENT_DETACH, parent, user) + if(.) + attachments -= attachment + var/atom/parent = src.parent + parent.update_icon() + +/datum/component/attachment_holder/proc/handle_detach(obj/item/parent, mob/user, obj/item/tool) + var/list/tool_list = list() + var/list/hand_list = list() + for(var/obj/item/attachment/attach as anything in attachments) + if(attach.attach_features_flags & ATTACH_REMOVABLE_TOOL) + tool_list[attach.name] = attach + if(attach.attach_features_flags & ATTACH_REMOVABLE_HAND) + hand_list[attach.name] = attach + if(tool) + if(!length(tool_list)) + return + 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) + 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) + + +/datum/component/attachment_holder/proc/handle_attack(obj/item/parent, obj/item/item, mob/user) + SIGNAL_HANDLER + + if(!user.Adjacent(parent)) + return + + if(item.tool_behaviour == TOOL_CROWBAR && length(attachments)) + INVOKE_ASYNC(src, PROC_REF(handle_detach), parent, user, item) + return TRUE + + if(HAS_TRAIT(item, TRAIT_ATTACHABLE)) + INVOKE_ASYNC(src, PROC_REF(do_attach), item, user) + return TRUE + + for(var/obj/item/attach as anything in attachments) + if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_ATTACK, parent, item, user)) + parent.update_icon() + return TRUE + +/datum/component/attachment_holder/proc/handle_item_pre_attack(obj/item/parent, atom/target_atom, mob/user, params) + SIGNAL_HANDLER + + for(var/obj/item/attach as anything in attachments) + if(SEND_SIGNAL(attach, COMSIG_ATTACHMENT_PRE_ATTACK, parent, target_atom, user, params)) + return TRUE diff --git a/code/datums/wires/mines.dm b/code/datums/wires/mines.dm index 4c856cf99d3f..91e0ac816189 100644 --- a/code/datums/wires/mines.dm +++ b/code/datums/wires/mines.dm @@ -28,7 +28,7 @@ if(WIRE_PIN) if(ourmine.clicked == TRUE) holder.visible_message(span_notice("[icon2html(ourmine, viewers(holder))] You hear something inside \the [ourmine] click softly.")) - playsound(ourmine, 'sound/weapons/empty.ogg', 30, TRUE) + playsound(ourmine, SOUND_EMPTY_MAG, 30, TRUE) ourmine.clicked = FALSE else holder.visible_message(span_notice("[icon2html(ourmine, viewers(holder))] \The [ourmine]'s detonation pad shifts slightly. Nothing happens.")) @@ -61,7 +61,7 @@ ourmine.dud = TRUE if(ourmine.clicked == TRUE) holder.visible_message(span_notice("[icon2html(ourmine, viewers(holder))] You hear something inside \the [ourmine] shift out of place.")) - playsound(ourmine, 'sound/weapons/empty.ogg', 30, TRUE) + playsound(ourmine, SOUND_EMPTY_MAG, 30, TRUE) ourmine.clicked = FALSE else holder.visible_message(span_notice("[icon2html(ourmine, viewers(holder))] \The [ourmine]'s detonation pad goes loose.")) diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 4a16a6f9b249..b0b40ea0877e 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -6,7 +6,7 @@ var/fire_sound var/projectiles_per_shot = 1 var/variance = 0 - var/randomspread = 0 //use random spread for machineguns, instead of shotgun scatter + var/randomspread = FALSE //use random spread for machineguns, instead of shotgun scatter var/projectile_delay = 0 var/firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect //the visual effect appearing when the weapon is fired. var/kickback = TRUE //Will using this weapon in no grav push mecha back. @@ -137,8 +137,8 @@ desc = "A device that shoots resonant plasma bursts at extreme velocity. The blasts are capable of crushing rock and demolishing solid obstacles." icon_state = "mecha_plasmacutter" item_state = "plasmacutter" - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON energy_drain = 30 projectile = /obj/projectile/plasma/adv/mech fire_sound = 'sound/weapons/plasma_cutter.ogg' @@ -347,7 +347,7 @@ projectiles_cache_max = 1200 projectiles_per_shot = 3 variance = 6 - randomspread = 1 + randomspread = TRUE projectile_delay = 2 harmful = TRUE ammo_type = "lmg" diff --git a/code/game/objects/items/attachments/_attachment.dm b/code/game/objects/items/attachments/_attachment.dm new file mode 100644 index 000000000000..a6c25ec8cdf3 --- /dev/null +++ b/code/game/objects/items/attachments/_attachment.dm @@ -0,0 +1,99 @@ +///Most of the logic of attachments is held within the component which allows you to add other items as attachments in theory +/obj/item/attachment + name = "broken attachment" + desc = "alert coders" + icon = 'icons/obj/guns/attachments.dmi' + + //Slot the attachment goes on, also used in descriptions so should be player readable + var/slot = ATTACHMENT_SLOT_RAIL + ///various yes no flags associated with attachments. See defines for these: [_DEFINES/guns.dm] + var/attach_features_flags = ATTACH_REMOVABLE_HAND + ///See attachment component + var/list/valid_parents = list() + ///Unused.. but could hold extra callbacks I assume? + var/list/signals = list() + ///Component that handles most of the logic of attachments + var/datum/component/attachment/attachment_comp + + ///If the attachment is on or off + var/toggled = FALSE + var/toggle_on_sound = 'sound/items/flashlight_on.ogg' + var/toggle_off_sound = 'sound/items/flashlight_off.ogg' + + ///Determines the amount of pixels to move the icon state for the overlay. in the x direction + var/pixel_shift_x = 16 + ///Determines the amount of pixels to move the icon state for the overlay. in the y direction + var/pixel_shift_y = 16 + + //Toggle modifers are handled seperatly + ///Modifier applied to the parent + var/spread_mod = 0 + ///Modifier applied to the parent + var/spread_unwielded_mod = 0 + ///Modifier applied to the parent, deciseconds + var/wield_delay = 0 + ///Modifier applied to the parent + var/size_mod = 0 + +/obj/item/attachment/Initialize() + . = ..() + attachment_comp = AddComponent( \ + /datum/component/attachment, \ + slot, \ + attach_features_flags, \ + valid_parents, \ + CALLBACK(src, PROC_REF(apply_attachment)), \ + CALLBACK(src, PROC_REF(remove_attachment)), \ + CALLBACK(src, PROC_REF(toggle_attachment)), \ + CALLBACK(src, PROC_REF(on_preattack)), \ + signals) + +/obj/item/attachment/Destroy() + qdel(attachment_comp) + attachment_comp = null + . = ..() + +/obj/item/attachment/proc/toggle_attachment(obj/item/gun/gun, mob/user) + SHOULD_CALL_PARENT(TRUE) + + playsound(user, toggled ? toggle_on_sound : toggle_off_sound, 40, TRUE) + toggled = !toggled + icon_state = "[initial(icon_state)][toggled ? "-on" : ""]" + +/// Checks if a user should be allowed to attach this attachment to the given parent +/obj/item/attachment/proc/apply_attachment(obj/item/gun/gun, mob/user) + SHOULD_CALL_PARENT(TRUE) + + if(toggled) + to_chat(user, span_warning("You cannot attach [src] while it is active!")) + return FALSE + + apply_modifiers(gun, user, TRUE) + playsound(src.loc, 'sound/weapons/gun/pistol/mag_insert_alt.ogg', 75, 1) + return TRUE + +/obj/item/attachment/proc/remove_attachment(obj/item/gun/gun, mob/user) + SHOULD_CALL_PARENT(TRUE) + + if(toggled) + toggle_attachment(gun, user) + + apply_modifiers(gun, user, FALSE) + playsound(src.loc, 'sound/weapons/gun/pistol/mag_release_alt.ogg', 75, 1) + return TRUE + +/obj/item/attachment/proc/on_preattack(obj/item/gun/gun, atom/target, mob/user, list/params) + return FALSE + +///Handles the modifiers to the parent gun +/obj/item/attachment/proc/apply_modifiers(obj/item/gun/gun, mob/user, attaching) + if(attaching) + gun.spread += spread_mod + gun.spread_unwielded += spread_unwielded_mod + gun.wield_delay += wield_delay + gun.w_class += size_mod + else + gun.spread -= spread_mod + gun.spread_unwielded -= spread_unwielded_mod + gun.wield_delay -= wield_delay + gun.w_class -= size_mod diff --git a/code/game/objects/items/attachments/bayonet.dm b/code/game/objects/items/attachments/bayonet.dm new file mode 100644 index 000000000000..6b1961f4b693 --- /dev/null +++ b/code/game/objects/items/attachments/bayonet.dm @@ -0,0 +1,22 @@ +/obj/item/attachment/bayonet + name = "bayonet" + desc = "Stabby-Stabby" + icon_state = "bayonet" + force = 15 + throwforce = 10 + pickup_sound = 'sound/items/handling/knife1_pickup.ogg' + drop_sound = 'sound/items/handling/knife3_drop.ogg' + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + sharpness = IS_SHARP_ACCURATE + + pixel_shift_x = 1 + pixel_shift_y = 4 + spread_mod = 1 + wield_delay = 0.1 SECONDS + +/obj/item/attachment/bayonet/on_preattack(obj/item/gun/gun, atom/target, mob/living/user, list/params) + if(user.a_intent == INTENT_HARM && user.CanReach(target, src, TRUE)) + melee_attack_chain(user, target, params) + return COMPONENT_NO_ATTACK + diff --git a/code/game/objects/items/attachments/laser_sight.dm b/code/game/objects/items/attachments/laser_sight.dm new file mode 100644 index 000000000000..082da1153de5 --- /dev/null +++ b/code/game/objects/items/attachments/laser_sight.dm @@ -0,0 +1,23 @@ +/obj/item/attachment/laser_sight + name = "laser sight" + desc = "Designed to be rail-mounted on a compatible firearm to provide increased accuracy and decreased spread." + icon_state = "laserpointer" + + attach_features_flags = ATTACH_REMOVABLE_HAND|ATTACH_TOGGLE + pixel_shift_x = 1 + pixel_shift_y = 4 + wield_delay = 0.1 SECONDS + +/obj/item/attachment/laser_sight/toggle_attachment(obj/item/gun/gun, mob/user) + . = ..() + + if(toggled) + gun.spread -= 3 + gun.spread_unwielded -= 3 + gun.wield_delay -= 0.3 SECONDS + else + gun.spread += 3 + gun.spread_unwielded += 3 + gun.wield_delay += 0.3 SECONDS + + playsound(user, toggled ? 'sound/weapons/magin.ogg' : 'sound/weapons/magout.ogg', 40, TRUE) diff --git a/code/game/objects/items/attachments/rail_light.dm b/code/game/objects/items/attachments/rail_light.dm new file mode 100644 index 000000000000..0cfbe9661e9e --- /dev/null +++ b/code/game/objects/items/attachments/rail_light.dm @@ -0,0 +1,33 @@ +/obj/item/attachment/rail_light + name = "rail light" + desc = "A flashlight made to be mounted on a firearm." + icon_state = "raillight" + light_color = COLOR_LIGHT_ORANGE + light_system = MOVABLE_LIGHT_DIRECTIONAL + light_range = 4 + light_power = 0.8 + light_on = FALSE + + attach_features_flags = ATTACH_REMOVABLE_HAND|ATTACH_TOGGLE + pixel_shift_x = 1 + pixel_shift_y = 4 + wield_delay = 0.1 SECONDS + +/obj/item/attachment/rail_light/toggle_attachment(obj/item/gun/gun, mob/user) + . = ..() + set_light_on(toggled) + update_icon() + +/obj/item/attachment/rail_light/apply_attachment(obj/item/gun/gun, mob/user) + . = ..() + if(!.) + return + + set_light_flags(light_flags | LIGHT_ATTACHED) + +/obj/item/attachment/rail_light/remove_attachment(obj/item/gun/gun, mob/user) + . = ..() + if(!.) + return + + set_light_flags(light_flags & ~LIGHT_ATTACHED) diff --git a/code/game/objects/items/attachments/silencer.dm b/code/game/objects/items/attachments/silencer.dm new file mode 100644 index 000000000000..31cf3fc15b36 --- /dev/null +++ b/code/game/objects/items/attachments/silencer.dm @@ -0,0 +1,19 @@ +/obj/item/attachment/silencer + name = "suppressor" + desc = "An attachment for the barrel of a firearm. Muffles the gunshot and muzzle flash." + icon_state = "silencer" + + slot = ATTACHMENT_SLOT_MUZZLE + pixel_shift_x = 1 + pixel_shift_y = 2 + spread_mod = -1 + size_mod = 1 + +/obj/item/attachment/silencer/apply_attachment(obj/item/gun/gun, mob/user) + . = ..() + gun.suppressed = TRUE + +/obj/item/attachment/silencer/remove_attachment(obj/item/gun/gun, mob/user) + . = ..() + gun.suppressed = FALSE + return TRUE diff --git a/code/game/objects/items/attachments/stock.dm b/code/game/objects/items/attachments/stock.dm new file mode 100644 index 000000000000..1fe286c14296 --- /dev/null +++ b/code/game/objects/items/attachments/stock.dm @@ -0,0 +1,40 @@ +/obj/item/attachment/foldable_stock + name = "folding stock" + desc = "A folding stock that can be attached to certain weapons to improve stability and decreases recoil." + icon_state = "skm-carbine-stock" + slot = ATTACHMENT_SLOT_STOCK + attach_features_flags = ATTACH_TOGGLE + + pixel_shift_x = 17 + pixel_shift_y = 18 + + var/toggled_slowdown = 0.10 + var/toggled_wield_delay = -0.4 SECONDS + var/toggled_recoil_bonus = -2 + var/toggled_spread_bonus = -5 + +/obj/item/attachment/foldable_stock/toggle_attachment(obj/item/gun/gun, mob/user) + . = ..() + + if(toggled) + to_chat(user, span_notice("You unfold the stock on the [src].")) + gun.w_class += 1 + gun.wield_delay += toggled_wield_delay + gun.wield_slowdown += toggled_slowdown + gun.recoil += toggled_recoil_bonus + gun.spread += toggled_spread_bonus + else + to_chat(user, span_notice("You fold the stock on the [src].")) + gun.w_class -= 1 + gun.wield_delay -= toggled_wield_delay + gun.wield_slowdown -= toggled_slowdown + gun.recoil -= toggled_recoil_bonus + gun.spread -= toggled_spread_bonus + + if(gun.wielded) + user.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gun, multiplicative_slowdown = gun.wield_slowdown) + + playsound(src, SOUND_EMPTY_MAG, 100, 1) + +/obj/item/attachment/foldable_stock/inteq + icon_state = "skm-inteqsmg-stock" diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index dca088c1be2e..204371ca0e51 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -28,7 +28,7 @@ custom_materials = list(/datum/material/iron=50, /datum/material/glass=20) custom_premium_price = 450 toolspeed = 1 - usesound = 'sound/weapons/empty.ogg' + usesound = SOUND_EMPTY_MAG var/obj/machinery/buffer // simple machine buffer for device linkage var/mode = 0 diff --git a/code/game/objects/items/pneumaticCannon.dm b/code/game/objects/items/pneumaticCannon.dm index 060e59f0f5de..5108a8713633 100644 --- a/code/game/objects/items/pneumaticCannon.dm +++ b/code/game/objects/items/pneumaticCannon.dm @@ -11,8 +11,8 @@ icon = 'icons/obj/pneumaticCannon.dmi' icon_state = "pneumaticCannon" item_state = "bulldog" - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 50) var/maxWeightClass = 20 //The max weight of items that can fit into the cannon var/loadedWeightClass = 0 //The weight of items currently in the cannon diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm index 5d87b42b78e5..92123969a4cd 100644 --- a/code/game/objects/items/storage/backpack.dm +++ b/code/game/objects/items/storage/backpack.dm @@ -624,7 +624,7 @@ new /obj/item/ammo_box/magazine/smgm45(src) new /obj/item/ammo_box/magazine/smgm45(src) new /obj/item/gun/ballistic/automatic/smg/c20r(src) - new /obj/item/suppressor/specialoffer(src) + new /obj/item/attachment/silencer(src) /obj/item/storage/backpack/duffelbag/syndie/bulldogbundle desc = "A large duffel bag containing a Bulldog, some drums, and a pair of thermal imaging glasses." diff --git a/code/game/objects/items/storage/briefcase.dm b/code/game/objects/items/storage/briefcase.dm index f55613dd4d21..64a3a11cf327 100644 --- a/code/game/objects/items/storage/briefcase.dm +++ b/code/game/objects/items/storage/briefcase.dm @@ -47,5 +47,5 @@ new /obj/item/clothing/under/syndicate/sniper(src) new /obj/item/ammo_box/magazine/sniper_rounds/soporific(src) new /obj/item/ammo_box/magazine/sniper_rounds/soporific(src) - new /obj/item/suppressor/specialoffer(src) + new /obj/item/attachment/silencer(src) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index bfbc4679af88..f34aae9e6f82 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -120,7 +120,7 @@ switch (pickweight(list( "bond" = 2, "ninja" = 1, "darklord" = 1, "white_whale_holy_grail" = 2, "mad_scientist" = 2, "bee" = 1, "mr_freeze" = 2, "made_man"= 1))) if("bond") new /obj/item/gun/ballistic/automatic/pistol/syndicate(src) - new /obj/item/suppressor(src) + new /obj/item/attachment/silencer(src) new /obj/item/ammo_box/magazine/m10mm(src) new /obj/item/ammo_box/magazine/m10mm(src) new /obj/item/clothing/under/chameleon(src) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index b1dfb479b66c..5ce082d79dc9 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -187,8 +187,8 @@ icon = 'icons/obj/guns/projectile.dmi' icon_state = "revolver" item_state = "gun" - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON flags_1 = CONDUCT_1 slot_flags = ITEM_SLOT_BELT w_class = WEIGHT_CLASS_NORMAL diff --git a/code/modules/cargo/blackmarket/blackmarket_items/tools.dm b/code/modules/cargo/blackmarket/blackmarket_items/tools.dm index 384886ac30d3..d24cbf68171d 100644 --- a/code/modules/cargo/blackmarket/blackmarket_items/tools.dm +++ b/code/modules/cargo/blackmarket/blackmarket_items/tools.dm @@ -125,7 +125,7 @@ /datum/blackmarket_item/tool/suppressor name = "Suppressor" desc = "A suppressor, for when you to keep your murder on the down low." - item = /obj/item/suppressor + item = /obj/item/attachment/silencer price_min = 100 price_max = 700 diff --git a/code/modules/cargo/packs/gun.dm b/code/modules/cargo/packs/gun.dm index 1d71b2799026..7ac296f09398 100644 --- a/code/modules/cargo/packs/gun.dm +++ b/code/modules/cargo/packs/gun.dm @@ -189,3 +189,31 @@ cost = 5000 contains = list(/obj/item/storage/guncase/skm) crate_name = "auto rifle crate" + +/datum/supply_pack/gun/attachment/rail_light + name = "Tactical Rail Light Crate" + desc = "Contains a single rail light to be mounted on a firearm." + cost = 250 + contains = list(/obj/item/attachment/rail_light) + crate_name = "rail light crate" + +/datum/supply_pack/gun/attachment/laser_sight + name = "Laser Sight Crate" + desc = "Contains a single rail light to be mounted on a firearm." + cost = 250 + contains = list(/obj/item/attachment/laser_sight) + crate_name = "laser sight crate" + +/datum/supply_pack/gun/attachment/bayonet + name = "Bayonet Crate" + desc = "Contains a single bayonet to be mounted on a firearm." + cost = 250 + contains = list(/obj/item/attachment/bayonet) + crate_name = "bayonet crate" + +/datum/supply_pack/gun/attachment/silencer + name = "Suppressor Crate" + desc = "Contains a single suppressor to be mounted on a firearm." + cost = 250 + contains = list(/obj/item/attachment/silencer) + crate_name = "silencer crate" diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index ced086e1dfc6..4774ea852424 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -476,15 +476,15 @@ GLOBAL_LIST_INIT(hallucination_list, list( A = image(image_file,H,"dualsaberred1", layer=ABOVE_MOB_LAYER) if("taser") if(side == "right") - image_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + image_file = GUN_RIGHTHAND_ICON else - image_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' + image_file = GUN_LEFTHAND_ICON A = image(image_file,H,"advtaserstun4", layer=ABOVE_MOB_LAYER) if("ebow") if(side == "right") - image_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + image_file = GUN_RIGHTHAND_ICON else - image_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' + image_file = GUN_LEFTHAND_ICON A = image(image_file,H,"crossbow", layer=ABOVE_MOB_LAYER) if("baton") if(side == "right") diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 030e592d80cd..b6073d4c86a2 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -113,7 +113,7 @@ /obj/item/kinetic_crusher/ui_action_click(mob/user, actiontype) set_light_on(!light_on) - playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) + playsound(user, SOUND_EMPTY_MAG, 100, TRUE) update_appearance() diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 268413ef203c..e48f4d5af5aa 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -869,8 +869,8 @@ name = "Slowpoke" desc = "The work of a truly genius gunsmith, altered and \"improved\" by a truly deranged Nanotrasen scientist, using components from a kinetic accelerator and beam rifle. Draw, partner!" icon = 'icons/obj/guns/energy.dmi' - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON icon_state = "spur" item_state = "spur" selfcharge = 1 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 1cb061ff9fd9..64dfdfde91ce 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -501,6 +501,10 @@ else if(!(flags & SHOCK_NOGLOVES)) //This gets the siemens_coeff for all non tesla shocks if(gloves) siemens_coeff *= gloves.siemens_coefficient + //If it doesnt have physiology its prob still initializing. + if(!physiology) + . = ..() + return siemens_coeff *= physiology.siemens_coeff siemens_coeff *= dna.species.siemens_coeff . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/codename_claw.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/codename_claw.dm index 3a4506b62562..a762a9298279 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/codename_claw.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/codename_claw.dm @@ -232,7 +232,6 @@ projectiletype = /obj/projectile/tentacle projectilesound = 'sound/effects/splat.ogg' Shoot(target) - /////TENTACLE END /////STING ATTACK diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm index 26597a64f33b..b216296cfe8d 100644 --- a/code/modules/projectiles/ammunition/_ammunition.dm +++ b/code/modules/projectiles/ammunition/_ammunition.dm @@ -32,7 +32,7 @@ var/pellets = 1 //Pellets for spreadshot var/variance = 0 //Variance for inaccuracy fundamental to the casing - var/randomspread = 0 //Randomspread for automatics + var/randomspread = FALSE //Randomspread for automatics var/delay = 0 //Delay for energy weapons var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index b92b79e0009f..707f4805afe3 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -4,6 +4,8 @@ icon = 'icons/obj/guns/projectile.dmi' icon_state = "flatgun" item_state = "gun" + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON flags_1 = CONDUCT_1 slot_flags = ITEM_SLOT_BELT custom_materials = list(/datum/material/iron=2000) @@ -16,101 +18,185 @@ attack_verb = list("struck", "hit", "bashed") pickup_sound = 'sound/items/handling/gun_pickup.ogg' drop_sound = 'sound/items/handling/gun_drop.ogg' + //trigger guard on the weapon, hulks can't fire them with their big meaty fingers + trigger_guard = TRIGGER_GUARD_NORMAL - /// The manufacturer of this weapon. For flavor mostly. If none, this will not show. + ///The manufacturer of this weapon. For flavor mostly. If none, this will not show. var/manufacturer = MANUFACTURER_NONE +/* + * Muzzle +*/ + ///Effect for the muzzle flash of the gun. + var/obj/effect/muzzle_flash/muzzle_flash + ///Icon state of the muzzle flash effect. + var/muzzleflash_iconstate + ///Brightness of the muzzle flash effect. + var/muzzle_flash_lum = 3 + ///Color of the muzzle flash effect. + var/muzzle_flash_color = COLOR_VERY_SOFT_YELLOW + +/* + * Firing +*/ var/fire_sound = 'sound/weapons/gun/pistol/shot.ogg' var/vary_fire_sound = TRUE var/fire_sound_volume = 50 var/dry_fire_sound = 'sound/weapons/gun/general/dry_fire.ogg' - ///Text showed when attempting to fire with no round or empty round. var/dry_fire_text = "click" - ///whether or not a message is displayed when fired - var/suppressed = null - var/can_suppress = FALSE + +/* + * Reloading +*/ + var/obj/item/ammo_casing/chambered = null + ///Whether the gun can be tacloaded by slapping a fresh magazine directly on it + var/tac_reloads = TRUE + ///If we have the 'snowflake mechanic,' how long should it take to reload? + var/tactical_reload_delay = 1 SECONDS + +//BALLISTIC + ///Compatible magazines with the gun + var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info + ///Whether the gun alarms when empty or not. + var/empty_alarm = FALSE + ///Do we eject the magazine upon runing out of ammo? + var/empty_autoeject = FALSE + ///Whether the gun supports multiple special mag types + var/special_mags = FALSE + + ///Actual magazine currently contained within the gun + var/obj/item/ammo_box/magazine/magazine + ///whether the gun ejects the chambered casing + var/casing_ejector = TRUE + ///Whether the gun has an internal magazine or a detatchable one. Overridden by BOLT_TYPE_NO_BOLT. + var/internal_magazine = FALSE + + ///Phrasing of the magazine in examine and notification messages; ex: magazine, box, etx + var/magazine_wording = "magazine" + ///Phrasing of the cartridge in examine and notification messages; ex: bullet, shell, dart, etc. + var/cartridge_wording = "bullet" + + ///sound when inserting magazine + var/load_sound = 'sound/weapons/gun/general/magazine_insert_full.ogg' + ///sound when inserting an empty magazine + var/load_empty_sound = 'sound/weapons/gun/general/magazine_insert_empty.ogg' + ///volume of loading sound + var/load_sound_volume = 40 + ///whether loading sound should vary + var/load_sound_vary = TRUE + ///Sound of ejecting a magazine + var/eject_sound = 'sound/weapons/gun/general/magazine_remove_full.ogg' + ///sound of ejecting an empty magazine + var/eject_empty_sound = 'sound/weapons/gun/general/magazine_remove_empty.ogg' + ///volume of ejecting a magazine + var/eject_sound_volume = 40 + ///whether eject sound should vary + var/eject_sound_vary = TRUE + +//ENERGY + //What type of power cell this uses + var/obj/item/stock_parts/cell/gun/cell + var/cell_type = /obj/item/stock_parts/cell/gun + //Can it be charged in a recharger? + var/can_charge = TRUE + var/selfcharge = FALSE + var/charge_tick = 0 + var/charge_delay = 4 + //whether the gun's cell drains the cyborg user's cell to recharge + var/use_cyborg_cell = FALSE + ///Used for large and small cells + var/mag_size = MAG_SIZE_MEDIUM + //Time it takes to unscrew the cell + var/unscrewing_time = 2 SECONDS + + ///if the gun's cell cannot be replaced + var/internal_cell = FALSE + + var/list/ammo_type = list(/obj/item/ammo_casing/energy) + //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. + var/select = 1 + +/* + * Operation +*/ + //whether or not a message is displayed when fired + var/suppressed = FALSE var/suppressed_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg' var/suppressed_volume = 60 - var/can_unsuppress = TRUE - var/obj/item/ammo_casing/chambered = null - ///trigger guard on the weapon. Used for hulk mutations and ashies. I honestly dont know how usefult his is, id avoid touching it - trigger_guard = TRIGGER_GUARD_NORMAL - ///Set the description of the gun to this when sawed off - var/sawn_desc = null - ///This triggers some sprite behavior in shotguns and prevents further sawoff, note that can_be_sawn_off is on gun/ballistic and not here, wtf. - var/sawn_off = FALSE - /// how many shots per burst, Ex: most machine pistols, M90, some ARs are 3rnd burst, while others like the GAR and laser minigun are 2 round burst. - var/burst_size = 3 - ///The rate of fire when firing in a burst. Not the delay between bursts - var/burst_delay = 0.15 SECONDS - ///The rate of fire when firing full auto and semi auto, and between bursts; for bursts its fire delay + burst_delay after every burst - var/fire_delay = 0.2 SECONDS + //true if the gun is wielded via twohanded component, shouldnt affect anything else + var/wielded = FALSE + //true if the gun is wielded after delay, should affects accuracy + var/wielded_fully = FALSE + ///Slowdown for wielding + var/wield_slowdown = 0.1 + ///How long between wielding and firing in tenths of seconds + var/wield_delay = 0.4 SECONDS + ///Storing value for above + var/wield_time = 0 - /// after initializing, we set the firemode to this - var/default_firemode = FIREMODE_SEMIAUTO - ///Firemode index, due to code shit this is the currently selected firemode - var/firemode_index - /// 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") - ///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" - var/adjust_fire_select_icon_state_on_safety = FALSE +// BALLISTIC + ///Whether the gun has to be racked each shot or not. + var/semi_auto = TRUE + ///The bolt type of the gun, affects quite a bit of functionality, see gun.dm in defines for bolt types: BOLT_TYPE_STANDARD; BOLT_TYPE_LOCKING; BOLT_TYPE_OPEN; BOLT_TYPE_NO_BOLT + var/bolt_type = BOLT_TYPE_STANDARD + ///Used for locking bolt and open bolt guns. Set a bit differently for the two but prevents firing when true for both. + var/bolt_locked = FALSE + ///Phrasing of the bolt in examine and notification messages; ex: bolt, slide, etc. + var/bolt_wording = "bolt" + ///length between individual racks + var/rack_delay = 5 + ///time of the most recent rack, used for cooldown purposes + var/recent_rack = 0 + + ///Whether the gun can be sawn off by sawing tools + var/can_be_sawn_off = FALSE + //description change if weapon is sawn-off + var/sawn_desc = null + var/sawn_off = FALSE - ///Are we firing a burst? If so, dont fire again until burst is done - var/currently_firing_burst = FALSE - ///This prevents gun from firing until the coodown is done, affected by lag - var/current_cooldown = 0 - ///affects if you can fire it unwielded or even dual wield it. LIGHT means dual wield allowed, HEAVY and higher means you have to wield to fire + ///sound of racking + var/rack_sound = 'sound/weapons/gun/general/bolt_rack.ogg' + ///volume of racking + var/rack_sound_volume = 60 + ///whether racking sound should vary + var/rack_sound_vary = TRUE + ///sound of when the bolt is locked back manually + var/lock_back_sound = 'sound/weapons/gun/general/slide_lock_1.ogg' + ///volume of lock back + var/lock_back_sound_volume = 60 + ///whether lock back varies + var/lock_back_sound_vary = TRUE + + ///sound of dropping the bolt or releasing a slide + var/bolt_drop_sound = 'sound/weapons/gun/general/bolt_drop.ogg' + ///volume of bolt drop/slide release + var/bolt_drop_sound_volume = 60 + ///empty alarm sound (if enabled) + var/empty_alarm_sound = 'sound/weapons/gun/general/empty_alarm.ogg' + ///empty alarm volume sound + var/empty_alarm_volume = 70 + ///whether empty alarm sound varies + var/empty_alarm_vary = TRUE + +/* + * Stats +*/ var/weapon_weight = WEAPON_LIGHT - ///If dual wielding, add this to the spread - var/dual_wield_spread = 24 - /// ???, no clue what this is. Original desc: //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once. - var/randomspread = 1 - - ///Alters projectile damage multiplicatively based on this value. Use it for "better" or "worse" weapons that use the same ammo. + //Alters projectile damage multiplicatively based on this value. Use it for "better" or "worse" weapons that use the same ammo. var/projectile_damage_multiplier = 1 - - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' - - var/list/attachment_options = list() //This.. works for now.. gun refactor soon - - var/can_flashlight = FALSE //if a flashlight can be added or removed if it already has one. - var/obj/item/flashlight/seclite/gun_light - var/datum/action/item_action/toggle_gunlight/alight - var/gunlight_state = "flight" - - var/can_bayonet = FALSE //if a bayonet can be added or removed if it already has one. - var/obj/item/kitchen/knife/bayonet - var/knife_x_offset = 0 - var/knife_y_offset = 0 - - var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite - var/ammo_y_offset = 0 - var/flight_x_offset = 0 - var/flight_y_offset = 0 - - //Zooming - var/zoomable = FALSE //whether the gun generates a Zoom action on creation - var/zoomed = FALSE //Zoom toggle - var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect) - var/zoom_out_amt = 0 - var/datum/action/toggle_scope_zoom/azoom - + //Speed someone can be flung if its point blank var/pb_knockback = 0 - var/wielded = FALSE // true if the gun is wielded via twohanded component, shouldnt affect anything else - - var/wielded_fully = FALSE // true if the gun is wielded after delay, should affects accuracy - + //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once. + var/randomspread = TRUE ///How much the bullet scatters when fired while wielded. var/spread = 4 ///How much the bullet scatters when fired while unwielded. var/spread_unwielded = 12 + //additional spread when dual wielding + var/dual_wield_spread = 24 + ///Screen shake when the weapon is fired while wielded. var/recoil = 0 @@ -121,43 +207,126 @@ ///this is how much deviation the gun recoil can have, recoil pushes the screen towards the reverse angle you shot + some deviation which this is the max. var/recoil_deviation = 22.5 - ///Slowdown for wielding - var/wield_slowdown = 0.1 - ///How long between wielding and firing in tenths of seconds - var/wield_delay = 0.4 SECONDS - ///Storing value for above - var/wield_time = 0 + /// how many shots per burst, Ex: most machine pistols, M90, some ARs are 3rnd burst, while others like the GAR and laser minigun are 2 round burst. + var/burst_size = 3 + ///The rate of fire when firing in a burst. Not the delay between bursts + var/burst_delay = 0.15 SECONDS + ///The rate of fire when firing full auto and semi auto, and between bursts; for bursts its fire delay + burst_delay after every burst + var/fire_delay = 0.2 SECONDS + //Prevent the weapon from firing again while already firing + var/firing_burst = 0 - ///Effect for the muzzle flash of the gun. - var/obj/effect/muzzle_flash/muzzle_flash - ///Icon state of the muzzle flash effect. - var/muzzleflash_iconstate - ///Brightness of the muzzle flash effect. - var/muzzle_flash_lum = 3 - ///Color of the muzzle flash effect. - var/muzzle_flash_color = COLOR_VERY_SOFT_YELLOW +/* + * Overlay +*/ + ///Used for positioning ammo count overlay on sprite + var/ammo_x_offset = 0 + var/ammo_y_offset = 0 - //gun saftey +//BALLISTIC + ///Whether the sprite has a visible magazine or not + var/mag_display = FALSE + ///Whether the sprite has a visible ammo display or not + var/mag_display_ammo = FALSE + ///Whether the sprite has a visible indicator for being empty or not. + var/empty_indicator = FALSE + ///Whether the sprite has a visible magazine or not + var/show_magazine_on_sprite = FALSE + ///Whether the sprite has a visible ammo display or not + var/show_magazine_on_sprite_ammo = FALSE + ///Whether the gun supports multiple special mag types + var/unique_mag_sprites_for_variants = FALSE + +//ENERGY + //Do we handle overlays with base update_appearance()? + var/automatic_charge_overlays = TRUE + var/charge_sections = 4 + //if this gun uses a stateful charge bar for more detail + var/shaded_charge = FALSE + //Modifies WHOS state //im SOMEWHAT this is wether or not the overlay changes based on the ammo type selected + var/modifystate = TRUE + +/* + * Attachment +*/ + ///The types of attachments allowed, a list of types. SUBTYPES OF AN ALLOWED TYPE ARE ALSO ALLOWED + var/list/valid_attachments = list() + ///Reference to our attachment holder to prevent subtypes having to call GetComponent + var/datum/component/attachment_holder/attachment_holder + ///Number of attachments that can fit on a given slot + 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() + +/* + * Zooming +*/ + ///Whether the gun generates a Zoom action on creation + var/zoomable = FALSE + //Zoom toggle + var/zoomed = FALSE + ///Distance in TURFs to move the user's screen forward (the "zoom" effect) + var/zoom_amt = 3 + var/zoom_out_amt = 0 + var/datum/action/toggle_scope_zoom/azoom + +/* + * Safety +*/ ///Does this gun have a saftey and thus can toggle it? var/has_safety = FALSE ///If the saftey on? If so, we can't fire the weapon var/safety = FALSE - ///The wording of safety. Useful for guns that have a non-standard safety system, like a revolver var/safety_wording = "safety" +/* + * Spawn Info (Stuff that becomes useless onces the gun is spawned, mostly here for mappers) +*/ + ///Attachments spawned on initialization. Should also be in valid attachments or it SHOULD(once i add that) fail + var/list/default_attachments = list() + +//BALLISTIC + ///Whether the gun will spawn loaded with a magazine + var/spawnwithmagazine = TRUE + +//ENERGY + //set to true so the gun is given an empty cell + var/dead_cell = FALSE + +// Need to sort + ///trigger guard on the weapon. Used for hulk mutations and ashies. I honestly dont know how usefult his is, id avoid touching it + trigger_guard = TRIGGER_GUARD_NORMAL + + /// after initializing, we set the firemode to this + var/default_firemode = FIREMODE_SEMIAUTO + ///Firemode index, due to code shit this is the currently selected firemode + var/firemode_index + /// 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") + ///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" + var/adjust_fire_select_icon_state_on_safety = FALSE + + ///Are we firing a burst? If so, dont fire again until burst is done + var/currently_firing_burst = FALSE + ///This prevents gun from firing until the coodown is done, affected by lag + var/current_cooldown = 0 + /obj/item/gun/Initialize() . = ..() RegisterSignal(src, COMSIG_TWOHANDED_WIELD, PROC_REF(on_wield)) RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, PROC_REF(on_unwield)) - if(gun_light) - alight = new(src) muzzle_flash = new(src, muzzleflash_iconstate) build_zooming() build_firemodes() /obj/item/gun/ComponentInitialize() . = ..() + attachment_holder = AddComponent(/datum/component/attachment_holder, slot_available, valid_attachments, slot_offsets, default_attachments) AddComponent(/datum/component/two_handed) /// triggered on wield of two handed item @@ -196,16 +365,10 @@ user.remove_movespeed_modifier(/datum/movespeed_modifier/gun) /obj/item/gun/Destroy() - if(gun_light) - QDEL_NULL(gun_light) - if(bayonet) - QDEL_NULL(bayonet) if(chambered) //Not all guns are chambered (EMP'ed energy guns etc) QDEL_NULL(chambered) if(azoom) QDEL_NULL(azoom) - if(isatom(suppressed)) //SUPPRESSED IS USED AS BOTH A TRUE/FALSE AND AS A REF, WHAT THE FUCKKKKKKKKKKKKKKKKK - QDEL_NULL(suppressed) if(muzzle_flash) QDEL_NULL(muzzle_flash) return ..() @@ -213,36 +376,17 @@ /obj/item/gun/handle_atom_del(atom/A) if(A == chambered) chambered = null - update_appearance() - if(A == bayonet) - clear_bayonet() - if(A == gun_light) - clear_gunlight() + update_icon() return ..() /obj/item/gun/examine(mob/user) . = ..() - if(gun_light) - . += "It has \a [gun_light] [can_flashlight ? "" : "permanently "]mounted on it." - if(can_flashlight) //if it has a light and this is false, the light is permanent. - . += "[gun_light] looks like it can be unscrewed from [src]." - else if(can_flashlight) - . += "It has a mounting point for a seclite." - - if(bayonet) - . += "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it." - if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent. - . += "[bayonet] looks like it can be unscrewed from [src]." - else if(can_bayonet) - . += "It has a bayonet lug on it." - if(has_safety) . += "The safety is [safety ? "ON" : "OFF"]. Ctrl-Click to toggle the safety." if(manufacturer) . += "It has [manufacturer] engraved on it." - /obj/item/gun/equipped(mob/living/user, slot) . = ..() if(zoomed && user.get_active_held_item() != src) @@ -250,48 +394,8 @@ /obj/item/gun/attack(mob/M as mob, mob/user) if(user.a_intent == INTENT_HARM) //Flogging - if(bayonet) - M.attackby(bayonet, user) - return - else - return ..() - return - -/obj/item/gun/attack_obj(obj/O, mob/user) - if(user.a_intent == INTENT_HARM) - if(bayonet) - O.attackby(bayonet, user) - return - return ..() - -/obj/item/gun/attackby(obj/item/I, mob/user, params) - if(user.a_intent == INTENT_HARM) - return ..() - else if(istype(I, /obj/item/flashlight/seclite)) - if(!can_flashlight) - return ..() - var/obj/item/flashlight/seclite/S = I - if(!gun_light) - if(!user.transferItemToLoc(I, src)) - return - to_chat(user, "You click [S] into place on [src].") - set_gun_light(S) - update_gunlight() - alight = new(src) - if(loc == user) - alight.Grant(user) - else if(istype(I, /obj/item/kitchen/knife)) - var/obj/item/kitchen/knife/K = I - if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it. - return ..() - if(!user.transferItemToLoc(I, src)) - return - to_chat(user, "You attach [K] to [src]'s bayonet lug.") - bayonet = K - update_appearance() - - else return ..() + return //called after the gun has successfully fired its chambered ammo. /obj/item/gun/proc/process_chamber(atom/shooter) @@ -513,6 +617,7 @@ /obj/item/gun/proc/reset_current_cooldown() current_cooldown = FALSE + /obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj) if(!safety) to_chat(user, "*[dry_fire_text]*") @@ -600,116 +705,6 @@ update_appearance() - -/obj/item/gun/screwdriver_act(mob/living/user, obj/item/I) - . = ..() - if(.) - return - if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - return - attachment_options = list() - get_gun_attachments() - if(LAZYLEN(attachment_options) == 1) - remove_gun_attachments(user, I, attachment_options[1]) - else if (LAZYLEN(attachment_options)) - var/picked_option = show_radial_menu(user, src, attachment_options, radius = 38, require_near = TRUE) - remove_gun_attachments(user, I, picked_option) - -/obj/item/gun/proc/get_gun_attachments() - if(can_flashlight && gun_light) - attachment_options += list("Light" = image(icon = gun_light.icon, icon_state = gun_light.icon_state)) - if(can_bayonet && bayonet) - attachment_options += list("Knife" = image(icon = bayonet.icon, icon_state = bayonet.icon_state)) - -/obj/item/gun/proc/remove_gun_attachments(mob/living/user, obj/item/I, picked_option) - if(picked_option == "Light") - return remove_gun_attachment(user, I, gun_light, "unscrewed") - else if(picked_option == "Knife") - return remove_gun_attachment(user, I, bayonet, "unfix") - -/obj/item/gun/proc/remove_gun_attachment(mob/living/user, obj/item/tool_item, obj/item/item_to_remove, removal_verb) - if(tool_item) - tool_item.play_tool_sound(src) - to_chat(user, "You [removal_verb ? removal_verb : "remove"] [item_to_remove] from [src].") - item_to_remove.forceMove(drop_location()) - - if(Adjacent(user) && !issilicon(user)) - user.put_in_hands(item_to_remove) - - if(item_to_remove == bayonet) - return clear_bayonet() - else if(item_to_remove == gun_light) - return clear_gunlight() - -/obj/item/gun/proc/clear_bayonet() - if(!bayonet) - return - bayonet = null - update_appearance() - return TRUE - -/obj/item/gun/proc/clear_gunlight() - if(!gun_light) - return - var/obj/item/flashlight/seclite/removed_light = gun_light - set_gun_light(null) - update_gunlight() - removed_light.update_brightness() - QDEL_NULL(alight) - return TRUE - -/** - * Swaps the gun's seclight, dropping the old seclight if it has not been qdel'd. - * - * Returns the former gun_light that has now been replaced by this proc. - * Arguments: - * * new_light - The new light to attach to the weapon. Can be null, which will mean the old light is removed with no replacement. - */ -/obj/item/gun/proc/set_gun_light(obj/item/flashlight/seclite/new_light) - // Doesn't look like this should ever happen? We're replacing our old light with our old light? - if(gun_light == new_light) - CRASH("Tried to set a new gun light when the old gun light was also the new gun light.") - - . = gun_light - - // If there's an old gun light that isn't being QDELETED, detatch and drop it to the floor. - if(!QDELETED(gun_light)) - gun_light.set_light_flags(gun_light.light_flags & ~LIGHT_ATTACHED) - if(gun_light.loc != get_turf(src)) - gun_light.forceMove(get_turf(src)) - - // If there's a new gun light to be added, attach and move it to the gun. - if(new_light) - new_light.set_light_flags(new_light.light_flags | LIGHT_ATTACHED) - if(new_light.loc != src) - new_light.forceMove(src) - - gun_light = new_light - -/obj/item/gun/ui_action_click(mob/user, actiontype) - if(istype(actiontype, alight)) - toggle_gunlight() - else - ..() - -/obj/item/gun/proc/toggle_gunlight() - if(!gun_light) - return - - var/mob/living/carbon/human/user = usr - gun_light.on = !gun_light.on - gun_light.update_brightness() - to_chat(user, "You toggle the gunlight [gun_light.on ? "on":"off"].") - - playsound(user, gun_light.on ? gun_light.toggle_on_sound : gun_light.toggle_off_sound, 40, TRUE) - update_gunlight() - -/obj/item/gun/proc/update_gunlight() - update_appearance() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - /obj/item/gun/attack_hand(mob/user) . = ..() update_appearance() @@ -730,27 +725,6 @@ /obj/item/gun/update_overlays() . = ..() - if(gun_light) - var/mutable_appearance/flashlight_overlay - var/state = "[gunlight_state][gun_light.on? "_on":""]" //Generic state. - if(gun_light.icon_state in icon_states('icons/obj/guns/flashlights.dmi')) //Snowflake state? - state = gun_light.icon_state - flashlight_overlay = mutable_appearance('icons/obj/guns/flashlights.dmi', state) - flashlight_overlay.pixel_x = flight_x_offset - flashlight_overlay.pixel_y = flight_y_offset - . += flashlight_overlay - - if(bayonet) - var/mutable_appearance/knife_overlay - var/state = "bayonet" //Generic state. - if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state? - state = bayonet.icon_state - var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi' - knife_overlay = mutable_appearance(bayonet_icons, state) - knife_overlay.pixel_x = knife_x_offset - knife_overlay.pixel_y = knife_y_offset - . += knife_overlay - if(ismob(loc) && has_safety) var/mutable_appearance/safety_overlay safety_overlay = mutable_appearance('icons/obj/guns/safety.dmi') @@ -987,10 +961,7 @@ flash_loc.vis_contents -= muzzle_flash muzzle_flash.applied = FALSE -///////////// -// ZOOMING // -///////////// - +//I need to refactor this into an attachment /datum/action/toggle_scope_zoom name = "Toggle Scope" check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED|AB_CHECK_IMMOBILE|AB_CHECK_LYING diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index a4247898d59d..7c75d8fe9f56 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -13,90 +13,26 @@ has_safety = TRUE safety = TRUE - ///sound when inserting magazine - var/load_sound = 'sound/weapons/gun/general/magazine_insert_full.ogg' - ///sound when inserting an empty magazine - var/load_empty_sound = 'sound/weapons/gun/general/magazine_insert_empty.ogg' - ///volume of loading sound - var/load_sound_volume = 40 - ///whether loading sound should vary - var/load_sound_vary = TRUE - ///sound of racking - var/rack_sound = 'sound/weapons/gun/general/bolt_rack.ogg' - ///volume of racking - var/rack_sound_volume = 60 - ///whether racking sound should vary - var/rack_sound_vary = TRUE - ///sound of when the bolt is locked back manually - var/lock_back_sound = 'sound/weapons/gun/general/slide_lock_1.ogg' - ///volume of lock back - var/lock_back_sound_volume = 60 - ///whether lock back varies - var/lock_back_sound_vary = TRUE - ///Sound of ejecting a magazine - var/eject_sound = 'sound/weapons/gun/general/magazine_remove_full.ogg' - ///sound of ejecting an empty magazine - var/eject_empty_sound = 'sound/weapons/gun/general/magazine_remove_empty.ogg' - ///volume of ejecting a magazine - var/eject_sound_volume = 40 - ///whether eject sound should vary - var/eject_sound_vary = TRUE - ///sound of dropping the bolt or releasing a slide - var/bolt_drop_sound = 'sound/weapons/gun/general/bolt_drop.ogg' - ///volume of bolt drop/slide release - var/bolt_drop_sound_volume = 60 - ///empty alarm sound (if enabled) - var/empty_alarm_sound = 'sound/weapons/gun/general/empty_alarm.ogg' - ///empty alarm volume sound - var/empty_alarm_volume = 70 - ///whether empty alarm sound varies - var/empty_alarm_vary = TRUE - - ///Whether the gun will spawn loaded with a magazine - var/spawnwithmagazine = TRUE - ///Compatible magazines with the gun - var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info - ///Whether the sprite has a visible magazine or not - var/show_magazine_on_sprite = FALSE - ///Whether the sprite has a visible ammo display or not - var/show_magazine_on_sprite_ammo = FALSE - ///Whether the sprite has a visible indicator for being empty or not. - var/empty_indicator = FALSE - ///Whether the gun alarms when empty or not. - var/empty_alarm = FALSE - ///Do we eject the magazine upon runing out of ammo? - var/empty_autoeject = FALSE - ///Whether the gun supports multiple special mag types - var/unique_mag_sprites_for_variants = FALSE - ///The bolt type of the gun, affects quite a bit of functionality, see combat.dm defines for bolt types: BOLT_TYPE_STANDARD; BOLT_TYPE_LOCKING; BOLT_TYPE_OPEN; BOLT_TYPE_NO_BOLT - var/bolt_type = BOLT_TYPE_STANDARD - ///Used for locking bolt and open bolt guns. Set a bit differently for the two but prevents firing when true for both. - var/bolt_locked = FALSE - ///Whether the gun has to be racked each shot or not. - var/semi_auto = TRUE - ///Actual magazine currently contained within the gun - var/obj/item/ammo_box/magazine/magazine - ///whether the gun ejects the chambered casing - var/casing_ejector = TRUE - ///Whether the gun has an internal magazine or a detatchable one. Overridden by BOLT_TYPE_NO_BOLT. - var/internal_magazine = FALSE - ///Phrasing of the bolt in examine and notification messages; ex: bolt, slide, etc. - var/bolt_wording = "bolt" - ///Phrasing of the magazine in examine and notification messages; ex: magazine, box, etx - var/magazine_wording = "magazine" - ///Phrasing of the cartridge in examine and notification messages; ex: bullet, shell, dart, etc. - var/cartridge_wording = "bullet" - ///length between individual racks - var/rack_delay = 5 - ///time of the most recent rack, used for cooldown purposes - var/recent_rack = 0 - ///Whether the gun can be sawn off by sawing tools - var/can_be_sawn_off = FALSE - - ///Whether the gun can be tacloaded by slapping a fresh magazine directly on it - var/tac_reloads = TRUE //Snowflake mechanic no more. - ///If we have the 'snowflake mechanic,' how long should it take to reload? - var/tactical_reload_delay = 1 SECONDS + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 26, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 18, + ) + ) /obj/item/gun/ballistic/Initialize() . = ..() @@ -123,8 +59,6 @@ . += "[icon_state]_bolt[bolt_locked ? "_locked" : ""]" if (bolt_type == BOLT_TYPE_OPEN && bolt_locked) . += "[icon_state]_bolt" - if (suppressed) - . += "[icon_state]_suppressor" if (magazine) if (unique_mag_sprites_for_variants) . += "[icon_state]_mag_[magazine.base_icon_state]" @@ -203,12 +137,12 @@ update_appearance() ///Handles all the logic needed for magazine insertion -/obj/item/gun/ballistic/proc/insert_magazine(mob/user, obj/item/ammo_box/magazine/AM, display_message = TRUE) - if(!istype(AM, mag_type)) - to_chat(user, "\The [AM] doesn't seem to fit into \the [src]...") +/obj/item/gun/ballistic/proc/insert_magazine(mob/user, obj/item/ammo_box/magazine/inserted_mag, display_message = TRUE) + if(!istype(inserted_mag, mag_type)) + to_chat(user, "\The [inserted_mag] doesn't seem to fit into \the [src]...") return FALSE - if(user.transferItemToLoc(AM, src)) - magazine = AM + if(user.transferItemToLoc(inserted_mag, src)) + magazine = inserted_mag if (display_message) to_chat(user, "You load a new [magazine_wording] into \the [src].") if (magazine.ammo_count()) @@ -287,48 +221,11 @@ A.update_appearance() update_appearance() return - if(istype(A, /obj/item/suppressor)) - var/obj/item/suppressor/S = A - if(!can_suppress) - to_chat(user, "You can't seem to figure out how to fit [S] on [src]!") - return - if(!user.is_holding(src)) - to_chat(user, "You need be holding [src] to fit [S] to it!") - return - if(suppressed) - to_chat(user, "[src] already has a suppressor!") - return - if(user.transferItemToLoc(A, src)) - to_chat(user, "You screw \the [S] onto \the [src].") - install_suppressor(A) - return if (can_be_sawn_off) if (sawoff(user, A)) return return FALSE -///Installs a new suppressor, assumes that the suppressor is already in the contents of src -/obj/item/gun/ballistic/proc/install_suppressor(obj/item/suppressor/S) - suppressed = S - w_class += S.w_class //so pistols do not fit in pockets when suppressed - update_appearance() - -/obj/item/gun/ballistic/AltClick(mob/user) - if (unique_reskin && !current_skin && user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY)) - reskin_obj(user) - return - if(loc == user) - if(suppressed && can_unsuppress) - var/obj/item/suppressor/S = suppressed - if(!user.is_holding(src)) - return ..() - to_chat(user, "You unscrew \the [suppressed] from \the [src].") - user.put_in_hands(suppressed) - w_class -= S.w_class - suppressed = null - update_appearance() - return - ///Prefire empty checks for the bolt drop /obj/item/gun/ballistic/proc/prefire_empty_checks() if (!chambered && !get_ammo()) @@ -402,9 +299,7 @@ . += "It does not seem to have a round chambered." if (bolt_locked) . += "The [bolt_wording] is locked back and needs to be released before firing." - if (suppressed) - . += "It has a suppressor attached that can be removed with alt+click." - . += "You can [bolt_wording] [src] by pressing the unique action key. By default, this is space" + . += "You can [bolt_wording] [src] by pressing the unqiue action key. By default, this is space" ///Gets the number of bullets in the gun /obj/item/gun/ballistic/proc/get_ammo(countchambered = TRUE) @@ -437,9 +332,6 @@ GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list( if(sawn_off) to_chat(user, "\The [src] is already shortened!") return - if(bayonet) - to_chat(user, "You cannot saw-off \the [src] with \the [bayonet] attached!") - return user.changeNext_move(CLICK_CD_MELEE) user.visible_message("[user] begins to shorten \the [src].", "You begin to shorten \the [src]...") @@ -470,16 +362,3 @@ GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list( if(AC.BB) process_fire(user, user, FALSE) . = TRUE - - -/obj/item/suppressor - name = "suppressor" - desc = "A syndicate small-arms suppressor for maximum espionage." - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "suppressor" - w_class = WEIGHT_CLASS_TINY - - -/obj/item/suppressor/specialoffer - name = "cheap suppressor" - desc = "A foreign knock-off suppressor, it feels flimsy, cheap, and brittle. Still fits most weapons." diff --git a/code/modules/projectiles/guns/ballistic/assault.dm b/code/modules/projectiles/guns/ballistic/assault.dm index 3f649994827e..ce500cda930b 100644 --- a/code/modules/projectiles/guns/ballistic/assault.dm +++ b/code/modules/projectiles/guns/ballistic/assault.dm @@ -164,7 +164,6 @@ icon_state = "e40" item_state = "e40" mag_type = /obj/item/ammo_box/magazine/e40 - can_suppress = FALSE var/obj/item/gun/energy/laser/e40_laser_secondary/secondary fire_select_icon_state_prefix = "e40_" diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 2fea717c28a0..769ea9e7f57e 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -1,7 +1,6 @@ /obj/item/gun/ballistic/automatic w_class = WEIGHT_CLASS_NORMAL - can_suppress = TRUE gun_firemodes = list(FIREMODE_SEMIAUTO) default_firemode = FIREMODE_SEMIAUTO @@ -33,8 +32,6 @@ mag_type = /obj/item/ammo_box/magazine/m10mm/rifle fire_delay = 0.5 SECONDS burst_size = 1 - can_unsuppress = TRUE - can_suppress = TRUE w_class = WEIGHT_CLASS_HUGE slot_flags = ITEM_SLOT_BACK show_magazine_on_sprite = TRUE @@ -48,7 +45,6 @@ item_state = "arg" mag_type = /obj/item/ammo_box/magazine/recharge fire_delay = 0.2 SECONDS - can_suppress = FALSE burst_size = 0 fire_sound = 'sound/weapons/laser.ogg' casing_ejector = FALSE @@ -59,8 +55,6 @@ icon_state = "ZipPistol" item_state = "ZipPistol" mag_type = /obj/item/ammo_box/magazine/zip_ammo_9mm - can_suppress = FALSE actions_types = list() - can_bayonet = FALSE show_magazine_on_sprite = TRUE weapon_weight = WEAPON_LIGHT diff --git a/code/modules/projectiles/guns/ballistic/gauss.dm b/code/modules/projectiles/guns/ballistic/gauss.dm index 12264a3ccd8f..3fdc2e55dadd 100644 --- a/code/modules/projectiles/guns/ballistic/gauss.dm +++ b/code/modules/projectiles/guns/ballistic/gauss.dm @@ -11,7 +11,6 @@ mag_type = /obj/item/ammo_box/magazine/gauss fire_sound = 'sound/weapons/gun/gauss/magrifle.ogg' load_sound = 'sound/weapons/gun/gauss/rifle_reload.ogg' - can_suppress = FALSE burst_size = 1 fire_delay = 0.3 SECONDS spread = 0 diff --git a/code/modules/projectiles/guns/ballistic/hmg.dm b/code/modules/projectiles/guns/ballistic/hmg.dm index 79977249dd31..8a3e68b9a6ec 100644 --- a/code/modules/projectiles/guns/ballistic/hmg.dm +++ b/code/modules/projectiles/guns/ballistic/hmg.dm @@ -163,7 +163,6 @@ base_icon_state = "l6" mag_type = /obj/item/ammo_box/magazine/mm712x82 - can_suppress = FALSE spread = 7 fire_delay = 0.1 SECONDS @@ -236,8 +235,6 @@ fire_select_icon_state_prefix = "caseless_" - can_suppress = FALSE - can_bayonet = FALSE show_magazine_on_sprite = TRUE w_class = WEIGHT_CLASS_BULKY manufacturer = MANUFACTURER_SOLARARMORIES diff --git a/code/modules/projectiles/guns/ballistic/launchers.dm b/code/modules/projectiles/guns/ballistic/launchers.dm index d82f2dbd1250..f18f97cd0f4f 100644 --- a/code/modules/projectiles/guns/ballistic/launchers.dm +++ b/code/modules/projectiles/guns/ballistic/launchers.dm @@ -55,7 +55,6 @@ fire_sound = 'sound/weapons/gun/general/rocket_launch.ogg' load_sound = 'sound/weapons/gun/general/rocket_load.ogg' w_class = WEIGHT_CLASS_BULKY - can_suppress = FALSE burst_size = 1 fire_delay = 0.4 SECONDS casing_ejector = FALSE diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index 347f4950382e..b2416462da39 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -1,5 +1,4 @@ /obj/item/gun/ballistic/automatic/pistol - can_suppress = TRUE bolt_type = BOLT_TYPE_LOCKING vary_fire_sound = FALSE @@ -67,7 +66,6 @@ EMPTY_GUN_HELPER(automatic/pistol/syndicate) mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' mag_type = /obj/item/ammo_box/magazine/m45 - can_suppress = FALSE fire_sound = 'sound/weapons/gun/pistol/candor.ogg' rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' @@ -106,7 +104,7 @@ EMPTY_GUN_HELPER(automatic/pistol/candor/factory) icon_state = "deagle" force = 14 mag_type = /obj/item/ammo_box/magazine/m50 - can_suppress = FALSE + mag_display = TRUE show_magazine_on_sprite = TRUE fire_sound = 'sound/weapons/gun/pistol/deagle.ogg' rack_sound = 'sound/weapons/gun/pistol/rack.ogg' @@ -166,7 +164,6 @@ EMPTY_GUN_HELPER(automatic/pistol/candor/factory) gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) default_firemode = FIREMODE_SEMIAUTO - /obj/item/gun/ballistic/automatic/pistol/commander name = "\improper Commander" desc = "A classic handgun in a tasteful black and stainless steel color scheme. An enamel Nanotrasen logo is set into the grips. Chambered in 9mm." @@ -178,7 +175,6 @@ EMPTY_GUN_HELPER(automatic/pistol/candor/factory) w_class = WEIGHT_CLASS_NORMAL mag_type = /obj/item/ammo_box/magazine/co9mm - can_suppress = FALSE manufacturer = MANUFACTURER_NANOTRASEN fire_sound = 'sound/weapons/gun/pistol/commander.ogg' load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' @@ -216,7 +212,6 @@ EMPTY_GUN_HELPER(automatic/pistol/commander/inteq) w_class = WEIGHT_CLASS_NORMAL mag_type = /obj/item/ammo_box/magazine/co9mm - can_suppress = FALSE var/funnysounds = TRUE var/cooldown = 0 load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' @@ -256,6 +251,7 @@ EMPTY_GUN_HELPER(automatic/pistol/commander/inteq) to_chat(user, "You toggle [src]'s vox audio functions.") /obj/item/gun/ballistic/automatic/pistol/commissar/AltClick(mob/user) + . = ..() if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user))) return if((cooldown < world.time - 200) && funnysounds) @@ -327,7 +323,6 @@ EMPTY_GUN_HELPER(automatic/pistol/commander/inteq) w_class = WEIGHT_CLASS_NORMAL mag_type = /obj/item/ammo_box/magazine/disposable custom_materials = list(/datum/material/plastic=2000) - can_suppress = FALSE manufacturer = MANUFACTURER_NONE has_safety = FALSE //thing barely costs anything, why would it have a safety? safety = FALSE @@ -410,7 +405,6 @@ EMPTY_GUN_HELPER(automatic/pistol/commander/inteq) w_class = WEIGHT_CLASS_TINY mag_type = /obj/item/ammo_box/magazine/m22lr - can_suppress = FALSE fire_sound = 'sound/weapons/gun/pistol/himehabu.ogg' load_sound = 'sound/weapons/gun/pistol/mag_insert_alt.ogg' diff --git a/code/modules/projectiles/guns/ballistic/rifle.dm b/code/modules/projectiles/guns/ballistic/rifle.dm index 4cf82a445fb3..1e7724c5c9fa 100644 --- a/code/modules/projectiles/guns/ballistic/rifle.dm +++ b/code/modules/projectiles/guns/ballistic/rifle.dm @@ -99,7 +99,6 @@ if(.) spread = 24 spread_unwielded = 30 - can_bayonet = FALSE item_state = "illestren_sawn" mob_overlay_state = item_state weapon_weight = WEAPON_MEDIUM //you can fire it onehanded, makes it worse than worse than useless onehanded, but you can diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 692c09a45f42..1f15f8d01689 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -78,7 +78,7 @@ fire_delay = 0.05 SECONDS //slamfire rack_delay = 0.2 SECONDS - can_be_sawn_off = TRUE + can_be_sawn_off = TRUE /obj/item/gun/ballistic/shotgun/brimstone/sawoff(mob/user) @@ -111,7 +111,7 @@ mag_type = /obj/item/ammo_box/magazine/internal/shot/riot sawn_desc = "Come with me if you want to live." - can_be_sawn_off = TRUE + can_be_sawn_off = TRUE rack_sound = 'sound/weapons/gun/shotgun/rack_alt.ogg' fire_delay = 0.1 SECONDS @@ -141,8 +141,6 @@ recoil = 1 recoil_unwielded = 4 wield_delay = 0.65 SECONDS - -/obj/item/gun/ballistic/shotgun/automatic manufacturer = MANUFACTURER_NANOTRASEN /obj/item/gun/ballistic/shotgun/automatic/shoot_live_shot(mob/living/user) @@ -232,7 +230,6 @@ weapon_weight = WEAPON_MEDIUM mag_type = /obj/item/ammo_box/magazine/m12g - can_suppress = FALSE burst_size = 1 fire_delay = 0.4 SECONDS // this NEEDS the old delay. fire_sound = 'sound/weapons/gun/shotgun/bulldog.ogg' @@ -332,7 +329,7 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) "Stained Green" = "dshotgun_green" ) semi_auto = TRUE - can_be_sawn_off = TRUE + can_be_sawn_off = TRUE bolt_type = BOLT_TYPE_NO_BOLT pb_knockback = 3 // it's a super shotgun! manufacturer = MANUFACTURER_HUNTERSPRIDE @@ -452,8 +449,8 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) name = "improvised shotgun" desc = "A length of pipe and miscellaneous bits of scrap fashioned into a rudimentary single-shot shotgun." icon = 'icons/obj/guns/projectile.dmi' - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON mob_overlay_icon = null base_icon_state = "ishotgun" @@ -519,7 +516,7 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) mag_type = /obj/item/ammo_box/magazine/internal/shot/com/compact/compact w_class = WEIGHT_CLASS_SMALL sawn_desc = "You know, this isn't funny anymore." - can_be_sawn_off = TRUE + can_be_sawn_off = TRUE /obj/item/gun/ballistic/shotgun/automatic/combat/compact/compact/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) if(prob(0 + (magazine.ammo_count() * 20))) //minimum probability of 20, maximum of 60 @@ -542,7 +539,7 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) mag_type = /obj/item/ammo_box/magazine/internal/shot/com/compact/compact/compact w_class = WEIGHT_CLASS_TINY sawn_desc = "Sigh. This is a trigger attached to a bullet." - can_be_sawn_off = TRUE + can_be_sawn_off = TRUE /obj/item/gun/ballistic/shotgun/automatic/combat/compact/compact/compact/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) if(prob(50)) //It's going to blow up. @@ -636,7 +633,7 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) rack_sound = 'sound/weapons/gun/rifle/skm_cocked.ogg' bolt_wording = "lever" cartridge_wording = "bullet" - can_be_sawn_off = TRUE + can_be_sawn_off = TRUE wield_slowdown = 0.5 wield_delay = 0.65 SECONDS @@ -765,15 +762,12 @@ EMPTY_GUN_HELPER(shotgun/bulldog/inteq) inhand_y_dimension = 32 mag_type = /obj/item/ammo_box/magazine/internal/shot/beacon fire_sound = 'sound/weapons/gun/revolver/shot_hunting.ogg' - can_be_sawn_off=TRUE w_class = WEIGHT_CLASS_BULKY weapon_weight = WEAPON_MEDIUM force = 10 - flags_1 = CONDUCT_1 - slot_flags = ITEM_SLOT_BACK obj_flags = UNIQUE_RENAME semi_auto = TRUE - can_be_sawn_off = TRUE + can_be_sawn_off = TRUE pb_knockback = 3 wield_slowdown = 0.7 spread_unwielded = 15 diff --git a/code/modules/projectiles/guns/ballistic/smg.dm b/code/modules/projectiles/guns/ballistic/smg.dm index 96d4e7d922db..2cb68fc09176 100644 --- a/code/modules/projectiles/guns/ballistic/smg.dm +++ b/code/modules/projectiles/guns/ballistic/smg.dm @@ -50,10 +50,6 @@ item_state = "c20r" mag_type = /obj/item/ammo_box/magazine/smgm45 - can_bayonet = TRUE - can_suppress = FALSE - knife_x_offset = 26 - knife_y_offset = 12 show_magazine_on_sprite = TRUE show_magazine_on_sprite_ammo = TRUE empty_indicator = TRUE @@ -68,7 +64,6 @@ EMPTY_GUN_HELPER(automatic/smg/c20r) /obj/item/gun/ballistic/automatic/smg/c20r/cobra name = "\improper Cobra 20" desc = "An older model of SMG manufactured by Scarborough Arms, a predecessor to the military C-20 series. Chambered in .45. " - can_bayonet = FALSE icon_state = "cobra20" item_state = "cobra20" @@ -90,11 +85,7 @@ EMPTY_GUN_HELPER(automatic/smg/c20r) icon_state = "wt550" item_state = "arg" mag_type = /obj/item/ammo_box/magazine/wt550m9 - can_suppress = FALSE actions_types = list() - can_bayonet = TRUE - knife_x_offset = 25 - knife_y_offset = 12 show_magazine_on_sprite = TRUE show_magazine_on_sprite_ammo = TRUE empty_indicator = TRUE @@ -158,7 +149,6 @@ EMPTY_GUN_HELPER(automatic/smg/c20r) item_state = "m90" mag_type = /obj/item/ammo_box/magazine/m556 - can_suppress = FALSE gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst fire", FIREMODE_FULLAUTO = "full auto", FIREMODE_OTHER = "underbarrel grenade launcher") gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST, FIREMODE_OTHER) default_firemode = FIREMODE_SEMIAUTO @@ -241,7 +231,6 @@ EMPTY_GUN_HELPER(automatic/smg/c20r) icon_state = "firestorm" item_state = "firestorm" mag_type = /obj/item/ammo_box/magazine/c45_firestorm_mag - can_suppress = FALSE unique_mag_sprites_for_variants = TRUE burst_size = 1 actions_types = list() @@ -315,11 +304,9 @@ EMPTY_GUN_HELPER(automatic/smg/cm5) eject_empty_sound = 'sound/weapons/gun/rifle/skm_unload.ogg' weapon_weight = WEAPON_MEDIUM - w_class = WEIGHT_CLASS_BULKY + w_class = WEIGHT_CLASS_NORMAL mag_type = /obj/item/ammo_box/magazine/skm_545_39 - actions_types = list(/datum/action/item_action/fold_stock) //once again, ideally an attatchment in the future - recoil = 2 recoil_unwielded = 6 @@ -329,76 +316,35 @@ EMPTY_GUN_HELPER(automatic/smg/cm5) wield_delay = 0.6 SECONDS wield_slowdown = 0.35 - ///is the bipod deployed? - var/stock_folded = FALSE - - ///we add these two values to recoi/spread when we have the bipod deployed - var/stock_recoil_bonus = -2 - var/stock_spread_bonus = -5 - - var/folded_slowdown = 0.6 - var/folded_wield_delay = 0.6 SECONDS - - var/unfolded_slowdown = 0.35 - var/unfolded_wield_delay = 0.2 SECONDS - -/obj/item/gun/ballistic/automatic/smg/skm_carbine/ComponentInitialize() - . = ..() - AddElement(/datum/element/update_icon_updates_onmob) - -/datum/action/item_action/fold_stock - name = "Fold/Unfold stock" - desc = "Fold or unfold the stock for easier storage." - -/obj/item/gun/ballistic/automatic/smg/skm_carbine/ui_action_click(mob/user, action) - if(!istype(action, /datum/action/item_action/fold_stock)) - return ..() - fold(user) - - -/obj/item/gun/ballistic/automatic/smg/skm_carbine/proc/fold(mob/user) - if(stock_folded) - to_chat(user, "You unfold the stock on the [src].") - w_class = WEIGHT_CLASS_BULKY - wield_delay = folded_wield_delay - wield_slowdown = folded_slowdown - else - to_chat(user, "You fold the stock on the [src].") - w_class = WEIGHT_CLASS_NORMAL - wield_delay = unfolded_wield_delay - wield_slowdown = unfolded_slowdown - - if(wielded) - user.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gun, multiplicative_slowdown = wield_slowdown) - - stock_folded = !stock_folded - playsound(src, 'sound/weapons/empty.ogg', 100, 1) - update_appearance() - - -/obj/item/gun/ballistic/automatic/smg/skm_carbine/calculate_recoil(mob/user, recoil_bonus = 0) - var/total_recoil = recoil_bonus - if(!stock_folded) - total_recoil += stock_recoil_bonus - - return ..(user, total_recoil) - -/obj/item/gun/ballistic/automatic/smg/skm_carbine/calculate_spread(mob/user, bonus_spread) - var/total_spread = bonus_spread - - if(!stock_folded) - total_spread += stock_spread_bonus - - return ..(user, total_spread) - -/obj/item/gun/ballistic/automatic/smg/skm_carbine/update_icon_state() - . = ..() - item_state = "[initial(item_state)][stock_folded ? "_nostock" : ""]" - mob_overlay_state = "[initial(item_state)][stock_folded ? "_nostock" : ""]" - -/obj/item/gun/ballistic/automatic/smg/skm_carbine/update_overlays() - . = ..() - . += "[base_icon_state || initial(icon_state)][stock_folded ? "_nostock" : "_stock"]" + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet, + /obj/item/attachment/foldable_stock + ) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_STOCK = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 26, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 18, + ), + ATTACHMENT_SLOT_STOCK = list( + "x" = 11, + "y" = 20, + ) + ) + + default_attachments = list(/obj/item/attachment/foldable_stock) /obj/item/gun/ballistic/automatic/smg/skm_carbine/inteq name = "\improper SKM-44v Mongrel" @@ -426,16 +372,16 @@ EMPTY_GUN_HELPER(automatic/smg/cm5) recoil = 0 recoil_unwielded = 4 - stock_spread_bonus = -4 - stock_recoil_bonus = -1 - wield_delay = 0.4 SECONDS - folded_slowdown = 0.15 - folded_wield_delay = 0.2 SECONDS - - unfolded_slowdown = 0.35 - unfolded_wield_delay = 0.4 SECONDS + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet, + /obj/item/attachment/foldable_stock/inteq + ) + default_attachments = list(/obj/item/attachment/foldable_stock/inteq) /obj/item/gun/ballistic/automatic/smg/skm_carbine/inteq/proto name = "\improper Nanotrasen Saber SMG" @@ -447,4 +393,3 @@ EMPTY_GUN_HELPER(automatic/smg/cm5) bolt_type = BOLT_TYPE_LOCKING show_magazine_on_sprite = TRUE manufacturer = MANUFACTURER_NANOTRASEN_OLD - diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm index 160569d3f86c..135a1b2d06b0 100644 --- a/code/modules/projectiles/guns/ballistic/toy.dm +++ b/code/modules/projectiles/guns/ballistic/toy.dm @@ -8,7 +8,6 @@ force = 0 throwforce = 0 burst_size = 3 - can_suppress = TRUE item_flags = NONE casing_ejector = FALSE manufacturer = MANUFACTURER_NANOTRASEN @@ -54,7 +53,6 @@ fire_sound = 'sound/items/syringeproj.ogg' item_flags = NONE casing_ejector = FALSE - can_suppress = FALSE pb_knockback = 0 recoil = -10 //its a toy... recoil_unwielded = -10 @@ -85,7 +83,6 @@ /obj/item/gun/ballistic/automatic/smg/c20r/toy name = "donksoft SMG" desc = "A bullpup two-round burst toy SMG, designated 'C-20r'. Ages 8 and up." - can_suppress = FALSE item_flags = NONE mag_type = /obj/item/ammo_box/magazine/toy/smgm45 fire_sound = 'sound/items/syringeproj.ogg' @@ -105,7 +102,6 @@ name = "donksoft LMG" desc = "A heavily modified toy light machine gun, designated 'L6 SAW'. Ages 8 and up." fire_sound = 'sound/items/syringeproj.ogg' - can_suppress = FALSE item_flags = NONE mag_type = /obj/item/ammo_box/magazine/toy/m762 casing_ejector = FALSE diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index f3edb62d8942..abe3e3fd7b43 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -10,41 +10,31 @@ has_safety = TRUE safety = TRUE + modifystate = FALSE + ammo_x_offset = 2 + gun_firemodes = list(FIREMODE_SEMIAUTO) default_firemode = FIREMODE_SEMIAUTO fire_select_icon_state_prefix = "laser_" - var/obj/item/stock_parts/cell/gun/cell //What type of power cell this uses - var/cell_type = /obj/item/stock_parts/cell/gun - var/modifystate = 0 - var/list/ammo_type = list(/obj/item/ammo_casing/energy) - var/select = 1 //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. - var/can_charge = TRUE //Can it be charged in a recharger? - var/automatic_charge_overlays = TRUE //Do we handle overlays with base update_appearance()? - var/charge_sections = 4 - ammo_x_offset = 2 - var/shaded_charge = FALSE //if this gun uses a stateful charge bar for more detail - var/selfcharge = 0 - var/charge_tick = 0 - var/charge_delay = 4 - var/use_cyborg_cell = FALSE //whether the gun's cell drains the cyborg user's cell to recharge - var/dead_cell = FALSE //set to true so the gun is given an empty cell - - var/internal_cell = FALSE ///if the gun's cell cannot be replaced - var/small_gun = FALSE ///if the gun is small and can only fit the small gun cell - var/big_gun = FALSE ///if the gun is big and can fit the comically large gun cell - var/unscrewing_time = 20 ///Time it takes to unscrew the cell - - ///Whether the gun can be tacloaded by slapping a fresh magazine directly on it - var/tac_reloads = FALSE - ///If we allow tacitcal reloads, how long should it take to reload? - var/tactical_reload_delay = 1.2 SECONDS - - var/load_sound = 'sound/weapons/gun/general/magazine_insert_full.ogg' //Sound when inserting magazine. UPDATE PLEASE - var/eject_sound = 'sound/weapons/gun/general/magazine_remove_full.ogg' //Sound of ejecting a cell. UPDATE PLEASE - var/sound_volume = 40 //Volume of loading/unloading sounds - var/load_sound_vary = TRUE //Should the load/unload sounds vary? + tac_reloads = FALSE + tactical_reload_delay = 1.2 SECONDS + + valid_attachments = list( + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet + ) + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 18, + ) + ) /obj/item/gun/energy/emp_act(severity) . = ..() @@ -133,16 +123,16 @@ return ..() /obj/item/gun/energy/proc/insert_cell(mob/user, obj/item/stock_parts/cell/gun/C) - if(small_gun && !istype(C, /obj/item/stock_parts/cell/gun/mini)) + if(mag_size == MAG_SIZE_SMALL && !istype(C, /obj/item/stock_parts/cell/gun/mini)) to_chat(user, span_warning("\The [C] doesn't seem to fit into \the [src]...")) return FALSE - if(!big_gun && istype(C, /obj/item/stock_parts/cell/gun/large)) + if(mag_size == MAG_SIZE_LARGE && !istype(C, /obj/item/stock_parts/cell/gun/large)) to_chat(user, span_warning("\The [C] doesn't seem to fit into \the [src]...")) return FALSE if(user.transferItemToLoc(C, src)) cell = C to_chat(user, span_notice("You load the [C] into \the [src].")) - playsound(src, load_sound, sound_volume, load_sound_vary) + playsound(src, load_sound, load_sound_volume, load_sound_vary) update_appearance() return TRUE else @@ -150,7 +140,7 @@ return FALSE /obj/item/gun/energy/proc/eject_cell(mob/user, obj/item/stock_parts/cell/gun/tac_load = null) - playsound(src, load_sound, sound_volume, load_sound_vary) + playsound(src, load_sound, load_sound_volume, load_sound_vary) cell.forceMove(drop_location()) var/obj/item/stock_parts/cell/gun/old_cell = cell old_cell.update_appearance() @@ -170,17 +160,13 @@ user.put_in_hands(old_cell) update_appearance() -/obj/item/gun/energy/get_gun_attachments() +/obj/item/gun/energy/screwdriver_act(mob/living/user, obj/item/I) if(cell && !internal_cell) - attachment_options += list("Cell" = image(icon = cell.icon, icon_state = cell.icon_state)) - ..() - -/obj/item/gun/energy/remove_gun_attachments(mob/living/user, obj/item/I, picked_option) - if(picked_option == "Cell") - if(I.use_tool(src, user, unscrewing_time, volume=100)) - eject_cell(user, I) - return TRUE - ..() + to_chat(user, span_notice("You begin unscrewing and pulling out the cell...")) + if(I.use_tool(src, user, unscrewing_time, volume = 100)) + to_chat(user, span_notice("You remove the power cell.")) + eject_cell(user) + return ..() /obj/item/gun/energy/can_shoot(visuals) if(safety && !visuals) diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm index 6988f1f6cb72..90bfa9a8ead1 100644 --- a/code/modules/projectiles/guns/energy/energy_gun.dm +++ b/code/modules/projectiles/guns/energy/energy_gun.dm @@ -8,11 +8,8 @@ icon_state = "energy" item_state = null //so the human update icon uses the icon_state instead. ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser) - modifystate = 1 - can_flashlight = TRUE + modifystate = TRUE ammo_x_offset = 2 - flight_x_offset = 15 - flight_y_offset = 10 dual_wield_spread = 60 manufacturer = MANUFACTURER_SHARPLITE_NEW @@ -26,25 +23,16 @@ item_state = "gun" w_class = WEIGHT_CLASS_SMALL cell_type = /obj/item/stock_parts/cell/gun/mini - small_gun = TRUE + mag_size = MAG_SIZE_SMALL throwforce = 11 //This is funny, trust me. ammo_x_offset = 2 charge_sections = 3 - can_flashlight = FALSE // Can't attach or detach the flashlight, and override it's icon update - gunlight_state = "mini-light" - flight_x_offset = 19 - flight_y_offset = 13 - wield_delay = 0.2 SECONDS wield_slowdown = 0.15 spread = 2 spread_unwielded = 5 -/obj/item/gun/energy/e_gun/mini/Initialize() - set_gun_light(new /obj/item/flashlight/seclite(src)) - return ..() - /obj/item/gun/energy/e_gun/mini/empty_cell dead_cell = TRUE @@ -108,10 +96,9 @@ desc = "The \"Dynamic Rapid-Apprehension of the Guilty\" net is a revolution in law enforcement technology." icon_state = "dragnet" item_state = "dragnet" - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON ammo_type = list(/obj/item/ammo_casing/energy/net, /obj/item/ammo_casing/energy/trap) - can_flashlight = FALSE ammo_x_offset = 1 /obj/item/gun/energy/e_gun/dragnet/snare @@ -128,7 +115,6 @@ w_class = WEIGHT_CLASS_HUGE ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) weapon_weight = WEAPON_HEAVY - can_flashlight = FALSE trigger_guard = TRIGGER_GUARD_NONE ammo_x_offset = 2 @@ -198,7 +184,7 @@ item_state = "gun" ammo_x_offset = 2 charge_sections = 6 - small_gun = TRUE + mag_size = MAG_SIZE_SMALL wield_delay = 0.2 SECONDS wield_slowdown = 0.15 @@ -242,7 +228,7 @@ inhand_y_dimension = 64 icon_state = "iotshotgun" item_state = "shotgun_combat" - shaded_charge = 1 + shaded_charge = TRUE w_class = WEIGHT_CLASS_BULKY ammo_type = list(/obj/item/ammo_casing/energy/disabler/scatter/ultima, /obj/item/ammo_casing/energy/laser/ultima) var/obj/item/modular_computer/integratedNTOS @@ -257,10 +243,7 @@ mob_overlay_icon = 'icons/obj/guns/manufacturer/eoehoma/onmob.dmi' icon_state = "e11" ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/eoehoma) - can_flashlight = TRUE ammo_x_offset = 0 - flight_x_offset = 20 - flight_y_offset = 9 spread = 80 spread_unwielded = 140 dual_wield_spread = 140 diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index ac3aa9e3d21a..2ca71649a6f7 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -8,14 +8,8 @@ item_flags = NONE obj_flags = UNIQUE_RENAME weapon_weight = WEAPON_LIGHT - can_flashlight = TRUE - flight_x_offset = 15 - flight_y_offset = 9 automatic_charge_overlays = FALSE - can_bayonet = TRUE - knife_x_offset = 20 - knife_y_offset = 12 - internal_cell = TRUE + internal_cell = TRUE //prevents you from giving it an OP cell - WS Edit custom_price = 750 w_class = WEIGHT_CLASS_BULKY @@ -28,12 +22,18 @@ var/overheat = FALSE var/mob/holder - var/max_mod_capacity = 100 var/list/modkits = list() var/recharge_timerid + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 24, + "y" = 13, + ) + ) + /obj/item/gun/energy/kinetic_accelerator/shoot_with_empty_chamber(mob/living/user) playsound(src, dry_fire_sound, 30, TRUE) //click sound but no to_chat message to cut on spam return diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index c564ae32b4da..eff3d67b2df1 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -84,7 +84,7 @@ icon_state = "cshotgun" item_state = "shotgun" desc = "A combat shotgun gutted and refitted with an internal laser system. Can switch between taser and scattered disabler shots." - shaded_charge = 0 + shaded_charge = FALSE ammo_type = list(/obj/item/ammo_casing/energy/disabler/scatter, /obj/item/ammo_casing/energy/electrode) manufacturer = MANUFACTURER_NONE @@ -96,7 +96,7 @@ icon_state = "lasercannon" item_state = "laser" w_class = WEIGHT_CLASS_BULKY - big_gun = TRUE + mag_size = MAG_SIZE_LARGE cell_type = "/obj/item/stock_parts/cell/gun/large" force = 10 flags_1 = CONDUCT_1 @@ -203,7 +203,7 @@ item_state = "gun" ammo_x_offset = 2 charge_sections = 4 - small_gun = TRUE + mag_size = MAG_SIZE_SMALL w_class = WEIGHT_CLASS_NORMAL cell_type = /obj/item/stock_parts/cell/gun/mini ammo_type = list(/obj/item/ammo_casing/energy/lasergun/hitscan) @@ -245,7 +245,7 @@ icon_state = "e50" item_state = "e50" - big_gun = TRUE + mag_size = MAG_SIZE_LARGE cell_type = /obj/item/stock_parts/cell/gun/large ammo_type = list(/obj/item/ammo_casing/energy/laser/eoehoma/e50) weapon_weight = WEAPON_HEAVY diff --git a/code/modules/projectiles/guns/energy/mounted.dm b/code/modules/projectiles/guns/energy/mounted.dm index 4dc5cae77558..cf67db9fd5ae 100644 --- a/code/modules/projectiles/guns/energy/mounted.dm +++ b/code/modules/projectiles/guns/energy/mounted.dm @@ -6,7 +6,6 @@ item_state = "armcannonstun4" force = 5 selfcharge = 1 - can_flashlight = FALSE trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses neural signals instead /obj/item/gun/energy/e_gun/advtaser/mounted/dropped()//if somebody manages to drop this somehow... diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index dfa23e26b468..c2e5b4cb2933 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -39,11 +39,8 @@ icon_state = "pulse_carbine" item_state = null internal_cell = FALSE - big_gun = TRUE //haha gun go brr + mag_size = MAG_SIZE_LARGE //haha gun go brr cell_type = /obj/item/stock_parts/cell/gun/large - can_flashlight = TRUE - flight_x_offset = 18 - flight_y_offset = 12 ammo_x_offset = 2 charge_sections = 4 diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 07f0aebaa9a0..09de7690b5ea 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -6,9 +6,8 @@ shaded_charge = FALSE ammo_x_offset = 2 ammo_y_offset = 2 - can_flashlight = FALSE w_class = WEIGHT_CLASS_HUGE - big_gun = TRUE //yes, you can put the comically large cell in it. No, you aren't getting it roundstart. You slut. + mag_size = MAG_SIZE_LARGE //yes, you can put the comically large cell in it. No, you aren't getting it roundstart. You slut. flags_1 = CONDUCT_1 slot_flags = ITEM_SLOT_BACK ammo_type = list(/obj/item/ammo_casing/energy/ion) @@ -28,9 +27,6 @@ slot_flags = ITEM_SLOT_BELT ammo_x_offset = 2 ammo_y_offset = 0 - can_flashlight = TRUE - flight_x_offset = 18 - flight_y_offset = 11 /obj/item/gun/energy/decloner name = "biological demolecularisor" @@ -51,10 +47,10 @@ icon_state = "flora" item_state = "gun" ammo_type = list(/obj/item/ammo_casing/energy/flora/yield, /obj/item/ammo_casing/energy/flora/mut, /obj/item/ammo_casing/energy/flora/revolution) - modifystate = 1 + modifystate = TRUE ammo_x_offset = 1 selfcharge = 1 - shaded_charge = 1 + shaded_charge = TRUE /obj/item/gun/energy/meteorgun name = "meteor gun" @@ -98,7 +94,6 @@ overheat_time = 20 holds_charge = TRUE unique_frequency = TRUE - can_flashlight = FALSE max_mod_capacity = 0 manufacturer = MANUFACTURER_SCARBOROUGH @@ -108,7 +103,7 @@ icon_state = "crossbowlarge" w_class = WEIGHT_CLASS_NORMAL custom_materials = list(/datum/material/iron=4000) - suppressed = null + suppressed = FALSE ammo_type = list(/obj/item/ammo_casing/energy/bolt/large) manufacturer = MANUFACTURER_NONE diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 168bbdc099dc..e70c46cc76f6 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -5,7 +5,6 @@ item_state = null //so the human update icon uses the icon_state instead. ammo_type = list(/obj/item/ammo_casing/energy/electrode) ammo_x_offset = 3 - spread = 2 spread_unwielded = 4 @@ -22,7 +21,6 @@ /obj/item/gun/energy/e_gun/advtaser/cyborg name = "cyborg taser" desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The weapon contains a limiter to prevent the cyborg's power cell from overheating." - can_flashlight = FALSE can_charge = FALSE use_cyborg_cell = TRUE @@ -37,11 +35,7 @@ item_state = null ammo_type = list(/obj/item/ammo_casing/energy/disabler) ammo_x_offset = 2 - can_flashlight = TRUE - flight_x_offset = 15 - flight_y_offset = 10 manufacturer = MANUFACTURER_SHARPLITE_NEW - spread = 2 spread_unwielded = 4 diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index 8b6626bc5e97..b131b03ec148 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -29,7 +29,7 @@ w_class = WEIGHT_CLASS_BULKY ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan) internal_cell = FALSE //prevents you from giving it an OP cell - WS Edit //shut up dumb nerd - big_gun = TRUE + mag_size = MAG_SIZE_LARGE cell_type = "/obj/item/stock_parts/cell/gun/large" canMouseDown = TRUE var/aiming = FALSE diff --git a/code/modules/projectiles/guns/misc/syringe_gun.dm b/code/modules/projectiles/guns/misc/syringe_gun.dm index 84d00b226371..809c15682cd4 100644 --- a/code/modules/projectiles/guns/misc/syringe_gun.dm +++ b/code/modules/projectiles/guns/misc/syringe_gun.dm @@ -80,7 +80,6 @@ w_class = WEIGHT_CLASS_SMALL force = 2 //Also very weak because it's smaller suppressed = TRUE //Softer fire sound - can_unsuppress = FALSE //Permanently silenced /obj/item/gun/syringe/dna name = "modified syringe gun" diff --git a/code/modules/projectiles/guns/powered.dm b/code/modules/projectiles/guns/powered.dm index ac7418748d9b..a9ab2f6365f4 100644 --- a/code/modules/projectiles/guns/powered.dm +++ b/code/modules/projectiles/guns/powered.dm @@ -1,21 +1,6 @@ /obj/item/gun/ballistic/automatic/powered mag_type = /obj/item/ammo_box/magazine/gauss - can_suppress = FALSE - - var/obj/item/stock_parts/cell/cell - var/cell_type = /obj/item/stock_parts/cell/gun - var/charge_sections = 3 - var/empty_battery_sound = FALSE // play empty alarm if no battery - - var/shaded_charge = FALSE //if this gun uses a stateful charge bar for more detail - var/automatic_charge_overlays = TRUE //Do we handle overlays with base update_appearance()? - - var/internal_cell = FALSE ///if the gun's cell cannot be replaced - var/small_gun = FALSE ///if the gun is small and can only fit the small gun cell - var/big_gun = FALSE ///if the gun is big and can fit the comically large gun cell - var/unscrewing_time = 2 SECONDS ///Time it takes to unscrew the cell - var/sound_volume = 40 //Volume of loading/unloading cell sounds - + charge_sections = 3 /obj/item/gun/ballistic/automatic/powered/Initialize() . = ..() @@ -61,24 +46,24 @@ return ..() /obj/item/gun/ballistic/automatic/powered/proc/insert_cell(mob/user, obj/item/stock_parts/cell/gun/C) - if(small_gun && !istype(C, /obj/item/stock_parts/cell/gun/mini)) - to_chat(user, "[C] doesn't seem to fit into [src]...") + if(mag_size == MAG_SIZE_SMALL && !istype(C, /obj/item/stock_parts/cell/gun/mini)) + to_chat(user, "\The [C] doesn't seem to fit into \the [src]...") return FALSE - if(!big_gun && istype(C, /obj/item/stock_parts/cell/gun/large)) - to_chat(user, "[C] doesn't seem to fit into [src]...") + if(mag_size == MAG_SIZE_LARGE && !istype(C, /obj/item/stock_parts/cell/gun/large)) + to_chat(user, "\The [C] doesn't seem to fit into \the [src]...") return FALSE if(user.transferItemToLoc(C, src)) cell = C - to_chat(user, "You load [C] into [src].") - playsound(src, load_sound, sound_volume, load_sound_vary) + to_chat(user, "You load the [C] into \the [src].") + playsound(src, load_sound, load_sound_volume, load_sound_vary) update_appearance() return TRUE else - to_chat(user, "You cannot seem to get [src] out of your hands!") + to_chat(user, "You cannot seem to get \the [src] out of your hands!") return FALSE /obj/item/gun/ballistic/automatic/powered/proc/eject_cell(mob/user, obj/item/stock_parts/cell/gun/tac_load = null) - playsound(src, load_sound, sound_volume, load_sound_vary) + playsound(src, load_sound, load_sound_volume, load_sound_vary) cell.forceMove(drop_location()) var/obj/item/stock_parts/cell/gun/old_cell = cell cell = null @@ -88,7 +73,7 @@ update_appearance() /obj/item/gun/ballistic/automatic/powered/screwdriver_act(mob/living/user, obj/item/I) - if(cell && !internal_cell && !bayonet && (!gun_light || !can_flashlight)) + if(cell && !internal_cell) to_chat(user, "You begin unscrewing and pulling out the cell...") if(I.use_tool(src, user, unscrewing_time, volume=100)) to_chat(user, "You remove the power cell.") diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 11cbd03c9787..78326279b6f9 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -395,7 +395,7 @@ to_chat(user, "You remove [vial] from [src].") vial = null update_appearance() - playsound(loc, 'sound/weapons/empty.ogg', 50, 1) + playsound(loc, SOUND_EMPTY_MAG, 50, 1) else to_chat(user, "This hypo isn't loaded!") return diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 05f68682083c..b4f46e6e4c19 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -260,8 +260,8 @@ icon = 'icons/obj/guns/projectile.dmi' icon_state = "chemsprayer" item_state = "chemsprayer" - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON throwforce = 0 w_class = WEIGHT_CLASS_NORMAL stream_mode = 1 @@ -298,8 +298,8 @@ desc = "A utility used to spray large amounts of cleaning reagents in a given area. It regenerates space cleaner by itself but it's unable to be fueled by normal means." icon_state = "chemsprayer_janitor" item_state = "chemsprayer_janitor" - lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON reagent_flags = NONE list_reagents = list(/datum/reagent/space_cleaner = 1000) volume = 1000 diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index 3353dcfb3289..c444336adde5 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -558,7 +558,7 @@ id = "suppressor" build_type = PROTOLATHE materials = list(/datum/material/iron = 2000, /datum/material/silver = 500) - build_path = /obj/item/suppressor + build_path = /obj/item/attachment/silencer category = list("Weapons") departmental_flags = DEPARTMENTAL_FLAG_SECURITY diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 11eb6a4b49e6..9e935611e15f 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -683,7 +683,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/stealthy_weapons/suppressor name = "Suppressor" desc = "This suppressor will silence the shots of the weapon it is attached to for increased stealth and superior ambushing capability. It is compatible with many small ballistic guns including the Stechkin and C-20r, but not revolvers or energy guns." - item = /obj/item/suppressor + item = /obj/item/attachment/silencer cost = 3 surplus = 10 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) diff --git a/icons/obj/guns/48x32guns.dmi b/icons/obj/guns/48x32guns.dmi index cbc495275adddddeace4e704d9e2211958b755e0..960b9ec448af3d5219cec73a774ceac1b17eaab6 100644 GIT binary patch literal 4457 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 literal 2116 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 FCIE^xUJC#K diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi index e02a7e288cc8ae7f1a93df0a1e7f3fa9b423eaeb..6a32de5b08fbc1589b237d0337b0be2ca1d4f298 100644 GIT binary patch literal 24323 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

$lUEy3zB0vNylj{yQpEs)ZAv zSM>MPxn&6hYbXt*AX%;FYnY!{HRVHQM)F_Tl)nfXkUjHlCQK9WDcJ+`JtWTAP)hL! zdHrW8K!X_uqBch)uOZzB=g7=qY$V59cy8|&19+bKE9D2$DZY%a=qsge*0mE!R!40^ zAgR(xxtB*>rs3tLE?7xYfG4isnbPSl4N9M7)bBj<+S1udHttrKmCX_8)MbM6Xe_OT zPfZubJaEi!v%Lp@YXr?WK95dLuZaAn$k;KSMlJ3Y=y5Y30{Iwf)J(K){^y$ajRdvl~i@ZQQr(Qw+w=)m~X_Of)%^$ATKX06{I zryE<2YLjpODX0ILLUFb)9#(gfRMKB>OM7LVpmi`Q%8T#{aoYR#ZS|2um_x!(RQbwZYrST>Fw%gHwbMl-hQlXberH_U5|4nmaNLYbQZs?7 zEAcHBNwEgj(Y^gL3<4I_c5<=P%Slw0$QH^+Gw+V$Lr+~g*LMm+=R2Buc%*bnPxpl5mbZ@}67$-%|&szdB0 z**&_n?-CkSEXv3Zy#_z{aglG6dhnh;ZV=QE&(rEUwx`>*QlCED13magOd}7@s z6e@|cJO8%-MYCii?6c5d8N-G}dDp541a#QkDs>D65n^{+WJl-H!+Y;*eNQ~%wUVyc z-U(hBoiEi6{4@_-Xk;$^Y!!b-$@mT6Anea>yP5s#`NxVX11291(2-vH04NWksEjps zGh)>r!4dWQJx?RXghsJ(A@!I#BR1U&mW4vxAsM2?^c!@UkV=*G#>Uy7J;C}I`Po-Q zYy$1hyU;hqZMR;45LK40ZQxb1gI*#-diG}BEkB!N$RXB-r~zYvJo`V8o9`u8JmF|& zAsxnOxpcj}+A`kHWH~s6Q#6=OtmIs`6nZ`GqfXK@ld)hSO+@}P%P_(92Ls8htxY~1 zMd5@<>V3UOM~B(0Ww)PpC#Bnh2;tow)08(*B>-@u$#m|fxY zBRc92&dD7IT4HzEu8vPbdw%pq;zKG9;OjlEpMz(9rhqy$EZR?;F%P3YKcDj{T6yKO z>~_8om+r#_W`cz3%ZRg&PoZZcU_E6HVnXZ0KW;gm&aHaK(N%s2MkZ+~?R&FcTq0pZ zJf$rxcxe8ex*Q?G@DEB2(4pZtx~E!`iu7Y=$pxpNdMEbggLTN4-WAWQmq%wKs}jZ{ zrFT=1KXrfWRfBedg$kv6Rp{#Aj&)CxZp2K6{s{Wi86qCe8O40TeoO0GrG8`6~k(X1wX`T3;y-<0KXHD6=p3c5QWLdVYm5Oo`|z{sSOtTXB=rKK%<# z4_i#9yvk;^ms``<+9<#5z6ire0A>NsOX4Tl$uy zFqM1sZS%~wid3)`W|fx>EsF|%1MP8;v)(%M!)zR|9QOVc<7vxHg7@Ns$$Y&nx6JTI6WLv_#w_GjtcMBY ztqZ$kf1A&VK`?8WhEI$!rhym$>vl|5R#p+rf|1yjSoy!$Yj}A8QFqg-e$HP0`WPT( o01W>@dKo&Kxc`sQkMBrNKYe~eVC)KhwFjc`Qdy?tg-PK51DoG;IRF3v diff --git a/icons/obj/guns/flashlights.dmi b/icons/obj/guns/flashlights.dmi deleted file mode 100644 index eef6d953f94a53b63fc3346fd27240336082d8b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=%~c^0B`&GO$wiq3C7Jno3=9=> zg2M`mO22;zF8KKMiI%sn*10q1gExd4Tr__0Nawtd=E(pc$L!IgGahGs&TBLlba@MT z2Z`p0PPtTMblKQz$_|TQqwX%C0MAZ~%Uwdn!HPL1sULqeZuZJ-$7~qox=+cdRCK+I zdvPsq5AWRsh2*90bY^y4Ipg@p=cRB;O0l}TgQIWuB>ySZDlAMBtBX3O9$aD3S9sdw z22+j-Lv%wgO9Ji&Lo`Sng~spoCkYZ)0Lr^P0ni>!HGt>388b!BDzZQe4a z(2v)DPLE<*_2%1-=>LHtu}?1j);%V%`|cTr2S1eq_22yk+E_cy`6a{usjK(%{!+s3 zW`Z6wmtVliu=wJMXO;in9)4L8b=vO1%Aco8m}K5EhdCx&$cWuO&A(QRL4kv5$;(Bj zcCs#irhl1J$BMzAtiEGKSF`GeZMQ=$U%N|u?q+Zl5b3u$fAQ_NY|)$Z>+jdL9O7D$ o$O#N}em&;}`?kJMIQO3^xO$(nR_wXyz+}eY>FVdQ&MBb@00lJ}N&o-= diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi index 986c2f99692f6c9e31e560f9efe3202b1a398189..ab051d8a21bbe1185930e6a6e4d3663f5ac93166 100644 GIT binary patch literal 17907 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 literal 18205 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 Date: Sun, 14 Jul 2024 19:33:40 -0500 Subject: [PATCH 10/24] Automatic changelog generation for PR #2917 [ci skip] --- html/changelogs/AutoChangeLog-pr-2917.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-2917.yml diff --git a/html/changelogs/AutoChangeLog-pr-2917.yml b/html/changelogs/AutoChangeLog-pr-2917.yml new file mode 100644 index 000000000000..0835401bda75 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2917.yml @@ -0,0 +1,4 @@ +author: FalloutFalcon +changes: + - {refactor: refactored attachments to be modular} +delete-after: true From 7935bf7e90320441cd26c12dc5c5eb3875fe9d39 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Mon, 15 Jul 2024 00:56:44 +0000 Subject: [PATCH 11/24] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-2917.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3067.yml | 9 --------- html/changelogs/AutoChangeLog-pr-3080.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3158.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3204.yml | 5 ----- html/changelogs/archive/2024-07.yml | 16 ++++++++++++++++ 6 files changed, 16 insertions(+), 26 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-2917.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3067.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3080.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3158.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3204.yml diff --git a/html/changelogs/AutoChangeLog-pr-2917.yml b/html/changelogs/AutoChangeLog-pr-2917.yml deleted file mode 100644 index 0835401bda75..000000000000 --- a/html/changelogs/AutoChangeLog-pr-2917.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: FalloutFalcon -changes: - - {refactor: refactored attachments to be modular} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3067.yml b/html/changelogs/AutoChangeLog-pr-3067.yml deleted file mode 100644 index 072045a2f02d..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3067.yml +++ /dev/null @@ -1,9 +0,0 @@ -author: Skies-Of-Blue -changes: - - {rscadd: an SSD Indicator for when you have been disconnected for less than three - minutes} - - {balance: 'players will now remain awake for three minutes after disconnecting, - with a new SSD icon to boot!'} - - {balance: 'players SSD longer than three minutes will lose their icon and fall - asleep, much like the previous behavior'} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3080.yml b/html/changelogs/AutoChangeLog-pr-3080.yml deleted file mode 100644 index 5c4a3abdf25d..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3080.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Skies-Of-Blue -changes: - - {rscadd: typing indicators now trigger off of the command bar} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3158.yml b/html/changelogs/AutoChangeLog-pr-3158.yml deleted file mode 100644 index 1de2c30deddf..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3158.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: SomeguyManperson -changes: - - {bugfix: gun cargo packs now act more as they would be expected to} -delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-3204.yml b/html/changelogs/AutoChangeLog-pr-3204.yml deleted file mode 100644 index 2b6963e69836..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3204.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: Sadhorizon -changes: - - {tweak: Changed sunskipper's prefix to SV.} - - {bugfix: Added a missing pipe to the sunskipper.} -delete-after: true diff --git a/html/changelogs/archive/2024-07.yml b/html/changelogs/archive/2024-07.yml index d16159c1679a..fd9a9542550c 100644 --- a/html/changelogs/archive/2024-07.yml +++ b/html/changelogs/archive/2024-07.yml @@ -92,3 +92,19 @@ - balance: Decreased the amount of landmines required for its mission and increased the value per mine slightly - rscdel: Capsaicin no longer Boils you +2024-07-15: + FalloutFalcon: + - refactor: refactored attachments to be modular + Sadhorizon: + - tweak: Changed sunskipper's prefix to SV. + - bugfix: Added a missing pipe to the sunskipper. + Skies-Of-Blue: + - rscadd: typing indicators now trigger off of the command bar + - rscadd: an SSD Indicator for when you have been disconnected for less than three + minutes + - balance: players will now remain awake for three minutes after disconnecting, + with a new SSD icon to boot! + - balance: players SSD longer than three minutes will lose their icon and fall asleep, + much like the previous behavior + SomeguyManperson: + - bugfix: gun cargo packs now act more as they would be expected to From c600289da2aafabc9cc7762c863e530948b54284 Mon Sep 17 00:00:00 2001 From: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:38:29 -0500 Subject: [PATCH 12/24] Express Console Supremacy (#2994) ## About The Pull Request Cuts out the old cargo console the express was a subtype, combining behaviors so there is only one. I would like to add the shopping list feature back so we can pool shipments together since most of our packs are now 1 item. ## Why It's Good For The Game Easier code managment ## Changelog :cl: refactor: refactored express console to not be a subtype of the cargo console and replacing it. /:cl: --- .../JungleRuins/jungle_cavecrew.dmm | 2 +- .../SpaceRuins/singularity_lab.dmm | 2 +- _maps/outpost/hangar/nt_asteroid_20x20.dmm | 2 +- _maps/outpost/hangar/nt_asteroid_40x20.dmm | 2 +- _maps/outpost/hangar/nt_asteroid_40x40.dmm | 2 +- _maps/outpost/hangar/nt_asteroid_56x20.dmm | 2 +- _maps/outpost/hangar/nt_asteroid_56x40.dmm | 2 +- _maps/outpost/nanotrasen_asteroid.dmm | 2 +- .../independent/independent_beluga.dmm | 2 +- .../shuttles/independent/independent_box.dmm | 2 +- .../independent/independent_bubble.dmm | 2 +- .../independent/independent_dwayne.dmm | 2 +- .../independent/independent_junker.dmm | 2 +- .../shuttles/independent/independent_kilo.dmm | 2 +- .../independent/independent_lagoon.dmm | 2 +- .../independent/independent_mudskipper.dmm | 2 +- .../independent/independent_rigger.dmm | 2 +- .../independent/independent_schmiedeberg.dmm | 2 +- .../independent/independent_shetland.dmm | 2 +- .../independent/independent_sunskipper.dmm | 2 +- _maps/shuttles/inteq/inteq_colossus.dmm | 2 +- _maps/shuttles/inteq/inteq_hound.dmm | 2 +- _maps/shuttles/inteq/inteq_talos.dmm | 2 +- _maps/shuttles/inteq/inteq_valor.dmm | 2 +- _maps/shuttles/inteq/inteq_vaquero.dmm | 2 +- .../shuttles/nanotrasen/nanotrasen_delta.dmm | 2 +- .../shuttles/nanotrasen/nanotrasen_gecko.dmm | 2 +- .../shuttles/nanotrasen/nanotrasen_heron.dmm | 2 +- _maps/shuttles/nanotrasen/nanotrasen_meta.dmm | 4 +- .../shuttles/nanotrasen/nanotrasen_mimir.dmm | 2 +- .../shuttles/nanotrasen/nanotrasen_osprey.dmm | 2 +- .../shuttles/nanotrasen/nanotrasen_ranger.dmm | 2 +- .../nanotrasen/nanotrasen_skipper.dmm | 2 +- _maps/shuttles/pgf/pgf_crying_sun.dmm | 2 +- _maps/shuttles/roumain/srm_elder.dmm | 2 +- _maps/shuttles/solgov/solgov_chronicle.dmm | 2 +- _maps/shuttles/solgov/solgov_inkwell.dmm | 2 +- _maps/shuttles/solgov/solgov_paracelsus.dmm | 2 +- .../syndicate/syndicate_cybersun_kansatsu.dmm | 2 +- .../syndicate/syndicate_gorlex_hyena.dmm | 2 +- .../syndicate/syndicate_gorlex_komodo.dmm | 2 +- .../syndicate/syndicate_litieguai.dmm | 2 +- .../shuttles/syndicate/syndicate_panacea.dmm | 2 +- .../syndicate/syndicate_twinkleshine.dmm | 2 +- check_regex.yaml | 2 +- .../circuitboards/computer_circuitboards.dm | 12 +- code/game/objects/structures/salvaging.dm | 2 +- code/modules/cargo/console.dm | 370 +++++++----- code/modules/cargo/expressconsole.dm | 262 --------- code/modules/cargo/supplypod_beacon.dm | 28 +- .../research/designs/mining_designs.dm | 2 +- shiptest.dme | 2 +- tgui/packages/tgui/interfaces/Cargo.js | 533 ------------------ .../OutpostCommunications/Catalog.js | 190 +++++++ .../index.tsx} | 39 +- .../interfaces/OutpostCommunications/types.ts | 28 + 56 files changed, 509 insertions(+), 1051 deletions(-) delete mode 100644 code/modules/cargo/expressconsole.dm delete mode 100644 tgui/packages/tgui/interfaces/Cargo.js create mode 100644 tgui/packages/tgui/interfaces/OutpostCommunications/Catalog.js rename tgui/packages/tgui/interfaces/{OutpostCommunications.tsx => OutpostCommunications/index.tsx} (88%) create mode 100644 tgui/packages/tgui/interfaces/OutpostCommunications/types.ts diff --git a/_maps/RandomRuins/JungleRuins/jungle_cavecrew.dmm b/_maps/RandomRuins/JungleRuins/jungle_cavecrew.dmm index 781ae59a84f4..dec4c65755f9 100644 --- a/_maps/RandomRuins/JungleRuins/jungle_cavecrew.dmm +++ b/_maps/RandomRuins/JungleRuins/jungle_cavecrew.dmm @@ -1762,7 +1762,7 @@ /turf/open/floor/plasteel/tech, /area/ruin/jungle/cavecrew/security) "vr" = ( -/obj/machinery/computer/cargo/express, +/obj/machinery/computer/cargo, /turf/open/floor/plasteel/patterned, /area/ruin/jungle/cavecrew/cargo) "vH" = ( diff --git a/_maps/RandomRuins/SpaceRuins/singularity_lab.dmm b/_maps/RandomRuins/SpaceRuins/singularity_lab.dmm index 45591d25dde5..99ccda138d00 100644 --- a/_maps/RandomRuins/SpaceRuins/singularity_lab.dmm +++ b/_maps/RandomRuins/SpaceRuins/singularity_lab.dmm @@ -3490,7 +3490,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer2{ dir = 9 }, -/obj/machinery/computer/cargo/express, +/obj/machinery/computer/cargo, /turf/open/floor/carpet/nanoweave/beige, /area/ruin/space/has_grav/singularitylab/cargo) "nT" = ( diff --git a/_maps/outpost/hangar/nt_asteroid_20x20.dmm b/_maps/outpost/hangar/nt_asteroid_20x20.dmm index 118f810e93f8..858d984f4603 100644 --- a/_maps/outpost/hangar/nt_asteroid_20x20.dmm +++ b/_maps/outpost/hangar/nt_asteroid_20x20.dmm @@ -370,7 +370,7 @@ }, /area/hangar) "jw" = ( -/obj/machinery/computer/cargo/express, +/obj/machinery/computer/cargo, /obj/structure/railing{ dir = 8; layer = 4.1 diff --git a/_maps/outpost/hangar/nt_asteroid_40x20.dmm b/_maps/outpost/hangar/nt_asteroid_40x20.dmm index b57c4972362c..312e0443aeea 100644 --- a/_maps/outpost/hangar/nt_asteroid_40x20.dmm +++ b/_maps/outpost/hangar/nt_asteroid_40x20.dmm @@ -839,7 +839,7 @@ }, /area/hangar) "tH" = ( -/obj/machinery/computer/cargo/express, +/obj/machinery/computer/cargo, /obj/item/toy/plush/knight{ pixel_y = 25; pixel_x = 9 diff --git a/_maps/outpost/hangar/nt_asteroid_40x40.dmm b/_maps/outpost/hangar/nt_asteroid_40x40.dmm index 48649aedf4d8..005b657e38ee 100644 --- a/_maps/outpost/hangar/nt_asteroid_40x40.dmm +++ b/_maps/outpost/hangar/nt_asteroid_40x40.dmm @@ -60,7 +60,7 @@ /obj/effect/turf_decal/techfloor{ dir = 4 }, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/machinery/light/directional/east, diff --git a/_maps/outpost/hangar/nt_asteroid_56x20.dmm b/_maps/outpost/hangar/nt_asteroid_56x20.dmm index 9dac115ca5e7..11ba5baac070 100644 --- a/_maps/outpost/hangar/nt_asteroid_56x20.dmm +++ b/_maps/outpost/hangar/nt_asteroid_56x20.dmm @@ -284,7 +284,7 @@ }, /area/hangar) "kx" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8; pixel_x = 7 }, diff --git a/_maps/outpost/hangar/nt_asteroid_56x40.dmm b/_maps/outpost/hangar/nt_asteroid_56x40.dmm index a3018e28aa32..5d66d8966d0b 100644 --- a/_maps/outpost/hangar/nt_asteroid_56x40.dmm +++ b/_maps/outpost/hangar/nt_asteroid_56x40.dmm @@ -723,7 +723,7 @@ }, /area/hangar) "Ed" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8; pixel_x = 7 }, diff --git a/_maps/outpost/nanotrasen_asteroid.dmm b/_maps/outpost/nanotrasen_asteroid.dmm index d1a184d736fc..816befea0002 100644 --- a/_maps/outpost/nanotrasen_asteroid.dmm +++ b/_maps/outpost/nanotrasen_asteroid.dmm @@ -5585,7 +5585,7 @@ /turf/open/floor/concrete/reinforced, /area/outpost/maintenance/aft) "tW" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/effect/turf_decal/techfloor{ diff --git a/_maps/shuttles/independent/independent_beluga.dmm b/_maps/shuttles/independent/independent_beluga.dmm index 1eae63fcac48..c8ef49a3b35e 100644 --- a/_maps/shuttles/independent/independent_beluga.dmm +++ b/_maps/shuttles/independent/independent_beluga.dmm @@ -4742,7 +4742,7 @@ /area/ship/crew) "UO" = ( /obj/effect/turf_decal/industrial/traffic/corner, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /obj/machinery/light/directional/south, diff --git a/_maps/shuttles/independent/independent_box.dmm b/_maps/shuttles/independent/independent_box.dmm index 0e1e4e9439b5..577290c6581c 100644 --- a/_maps/shuttles/independent/independent_box.dmm +++ b/_maps/shuttles/independent/independent_box.dmm @@ -1802,7 +1802,7 @@ /obj/effect/turf_decal/corner/opaque/blue{ dir = 4 }, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /obj/machinery/firealarm/directional/south, diff --git a/_maps/shuttles/independent/independent_bubble.dmm b/_maps/shuttles/independent/independent_bubble.dmm index c34ad8349987..08fd116f2536 100644 --- a/_maps/shuttles/independent/independent_bubble.dmm +++ b/_maps/shuttles/independent/independent_bubble.dmm @@ -1164,7 +1164,7 @@ }, /obj/item/circuitboard/computer/selling_pad_control, /obj/item/circuitboard/machine/selling_pad, -/obj/item/circuitboard/computer/cargo/express, +/obj/item/circuitboard/computer/cargo, /obj/structure/closet/crate/engineering, /turf/open/floor/plasteel, /area/ship/cargo) diff --git a/_maps/shuttles/independent/independent_dwayne.dmm b/_maps/shuttles/independent/independent_dwayne.dmm index ef1435cb1558..668e1e164530 100644 --- a/_maps/shuttles/independent/independent_dwayne.dmm +++ b/_maps/shuttles/independent/independent_dwayne.dmm @@ -1974,7 +1974,7 @@ /obj/effect/turf_decal/corner/opaque/blue/half{ dir = 4 }, -/obj/machinery/computer/cargo/express/retro{ +/obj/machinery/computer/cargo/retro{ dir = 8 }, /turf/open/floor/plasteel/dark, diff --git a/_maps/shuttles/independent/independent_junker.dmm b/_maps/shuttles/independent/independent_junker.dmm index a5b4354e7456..136e6e6e17fb 100644 --- a/_maps/shuttles/independent/independent_junker.dmm +++ b/_maps/shuttles/independent/independent_junker.dmm @@ -2277,7 +2277,7 @@ }, /obj/effect/decal/cleanable/glass, /obj/structure/safe/floor, -/obj/item/circuitboard/computer/cargo/express, +/obj/item/circuitboard/computer/cargo, /turf/open/floor/pod/dark, /area/ship/crew/office) "Rj" = ( diff --git a/_maps/shuttles/independent/independent_kilo.dmm b/_maps/shuttles/independent/independent_kilo.dmm index a4c390afde8d..0f1f92a475c8 100644 --- a/_maps/shuttles/independent/independent_kilo.dmm +++ b/_maps/shuttles/independent/independent_kilo.dmm @@ -1252,7 +1252,7 @@ /area/ship/crew) "sW" = ( /obj/effect/turf_decal/industrial/outline/yellow, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/machinery/airalarm/directional/east, diff --git a/_maps/shuttles/independent/independent_lagoon.dmm b/_maps/shuttles/independent/independent_lagoon.dmm index 14823f92db63..77b0d605e91b 100644 --- a/_maps/shuttles/independent/independent_lagoon.dmm +++ b/_maps/shuttles/independent/independent_lagoon.dmm @@ -256,7 +256,7 @@ /turf/open/floor/plasteel, /area/ship/external) "bt" = ( -/obj/machinery/computer/cargo/express/retro{ +/obj/machinery/computer/cargo/retro{ dir = 4 }, /turf/open/floor/plasteel/patterned/cargo_one, diff --git a/_maps/shuttles/independent/independent_mudskipper.dmm b/_maps/shuttles/independent/independent_mudskipper.dmm index f82cdc7ba748..d7a3341b8927 100644 --- a/_maps/shuttles/independent/independent_mudskipper.dmm +++ b/_maps/shuttles/independent/independent_mudskipper.dmm @@ -913,7 +913,7 @@ /obj/structure/sign/warning/incident{ pixel_x = -32 }, -/obj/machinery/computer/cargo/express/retro{ +/obj/machinery/computer/cargo/retro{ dir = 4 }, /turf/open/floor/plasteel/tech/grid, diff --git a/_maps/shuttles/independent/independent_rigger.dmm b/_maps/shuttles/independent/independent_rigger.dmm index daf3cf9ecdb3..42a9f999f3f7 100644 --- a/_maps/shuttles/independent/independent_rigger.dmm +++ b/_maps/shuttles/independent/independent_rigger.dmm @@ -2231,7 +2231,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer2{ dir = 4 }, -/obj/machinery/computer/cargo/express, +/obj/machinery/computer/cargo, /obj/item/radio/intercom/directional/north, /turf/open/floor/carpet/blue, /area/ship/bridge) diff --git a/_maps/shuttles/independent/independent_schmiedeberg.dmm b/_maps/shuttles/independent/independent_schmiedeberg.dmm index 454e7503cb52..ce7b407a4731 100644 --- a/_maps/shuttles/independent/independent_schmiedeberg.dmm +++ b/_maps/shuttles/independent/independent_schmiedeberg.dmm @@ -756,7 +756,7 @@ /turf/open/floor/wood/walnut, /area/ship/crew/canteen) "ka" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /obj/item/radio/intercom/directional/south, diff --git a/_maps/shuttles/independent/independent_shetland.dmm b/_maps/shuttles/independent/independent_shetland.dmm index e269c70cfefa..f147aeb82276 100644 --- a/_maps/shuttles/independent/independent_shetland.dmm +++ b/_maps/shuttles/independent/independent_shetland.dmm @@ -5464,7 +5464,7 @@ /obj/effect/turf_decal/corner/opaque/neutral/three_quarters{ dir = 1 }, -/obj/machinery/computer/cargo/express/retro, +/obj/machinery/computer/cargo/retro, /turf/open/floor/plasteel/telecomms_floor, /area/ship/bridge) "VD" = ( diff --git a/_maps/shuttles/independent/independent_sunskipper.dmm b/_maps/shuttles/independent/independent_sunskipper.dmm index 113230223069..de92179a11f7 100644 --- a/_maps/shuttles/independent/independent_sunskipper.dmm +++ b/_maps/shuttles/independent/independent_sunskipper.dmm @@ -2050,7 +2050,7 @@ /turf/open/floor/plasteel/tech, /area/ship/cargo) "AP" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ icon_state = "computer-right"; dir = 8 }, diff --git a/_maps/shuttles/inteq/inteq_colossus.dmm b/_maps/shuttles/inteq/inteq_colossus.dmm index 870a12949973..5925b9c0acdd 100644 --- a/_maps/shuttles/inteq/inteq_colossus.dmm +++ b/_maps/shuttles/inteq/inteq_colossus.dmm @@ -852,7 +852,7 @@ /area/ship/hallway/fore) "iX" = ( /obj/item/radio/intercom/wideband/directional/south, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /obj/effect/turf_decal/borderfloor, diff --git a/_maps/shuttles/inteq/inteq_hound.dmm b/_maps/shuttles/inteq/inteq_hound.dmm index 5616d1d2d4b0..da82ccbf26b7 100644 --- a/_maps/shuttles/inteq/inteq_hound.dmm +++ b/_maps/shuttles/inteq/inteq_hound.dmm @@ -1580,7 +1580,7 @@ dir = 1 }, /obj/effect/turf_decal/steeldecal/steel_decals_central4, -/obj/machinery/computer/cargo/express, +/obj/machinery/computer/cargo, /obj/item/radio/intercom/wideband/directional/north{ pixel_y = 25; pixel_x = 6 diff --git a/_maps/shuttles/inteq/inteq_talos.dmm b/_maps/shuttles/inteq/inteq_talos.dmm index 7a4a3bec6618..491853b5266c 100644 --- a/_maps/shuttles/inteq/inteq_talos.dmm +++ b/_maps/shuttles/inteq/inteq_talos.dmm @@ -2329,7 +2329,7 @@ /turf/open/floor/plasteel/tech, /area/ship/security/armory) "os" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/effect/turf_decal/corner/opaque/brown{ diff --git a/_maps/shuttles/inteq/inteq_valor.dmm b/_maps/shuttles/inteq/inteq_valor.dmm index 1d0c4910e49e..0b41578dd31b 100644 --- a/_maps/shuttles/inteq/inteq_valor.dmm +++ b/_maps/shuttles/inteq/inteq_valor.dmm @@ -3855,7 +3855,7 @@ /turf/open/floor/plasteel/patterned, /area/ship/cargo) "Ko" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /obj/effect/turf_decal/corner/opaque/brown{ diff --git a/_maps/shuttles/inteq/inteq_vaquero.dmm b/_maps/shuttles/inteq/inteq_vaquero.dmm index 23a80717b42f..9e8f7d6c81f5 100644 --- a/_maps/shuttles/inteq/inteq_vaquero.dmm +++ b/_maps/shuttles/inteq/inteq_vaquero.dmm @@ -2897,7 +2897,7 @@ /turf/open/floor/plasteel/tech, /area/ship/cargo) "TK" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/effect/turf_decal/corner/opaque/yellow, diff --git a/_maps/shuttles/nanotrasen/nanotrasen_delta.dmm b/_maps/shuttles/nanotrasen/nanotrasen_delta.dmm index b723b020aa36..e5890b401cfa 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_delta.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_delta.dmm @@ -2530,7 +2530,7 @@ /turf/open/floor/plasteel/dark, /area/ship/bridge) "Lv" = ( -/obj/machinery/computer/cargo/express/retro{ +/obj/machinery/computer/cargo/retro{ dir = 8 }, /obj/effect/turf_decal/corner/opaque/nsorange, diff --git a/_maps/shuttles/nanotrasen/nanotrasen_gecko.dmm b/_maps/shuttles/nanotrasen/nanotrasen_gecko.dmm index b05fd520c158..351f188e3369 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_gecko.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_gecko.dmm @@ -4283,7 +4283,7 @@ /turf/open/floor/plasteel/grimy, /area/ship/crew) "RK" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 4 }, /obj/item/radio/intercom/directional/west, diff --git a/_maps/shuttles/nanotrasen/nanotrasen_heron.dmm b/_maps/shuttles/nanotrasen/nanotrasen_heron.dmm index b92747f3a3e2..0a0c9dead99f 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_heron.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_heron.dmm @@ -10099,7 +10099,7 @@ /turf/open/floor/plasteel/tech, /area/ship/engineering) "LS" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/structure/railing{ diff --git a/_maps/shuttles/nanotrasen/nanotrasen_meta.dmm b/_maps/shuttles/nanotrasen/nanotrasen_meta.dmm index 9abfe4c6c6cb..663a567b8bbc 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_meta.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_meta.dmm @@ -1107,7 +1107,7 @@ /turf/open/floor/plasteel/dark, /area/ship/cargo) "dg" = ( -/obj/machinery/computer/cargo/express/retro{ +/obj/machinery/computer/cargo/retro{ dir = 8 }, /obj/effect/turf_decal/corner/opaque/blue/three_quarters{ @@ -2647,7 +2647,7 @@ /area/ship/cargo) "Du" = ( /obj/effect/turf_decal/corner/transparent/neutral/full, -/obj/machinery/computer/cargo/express/retro{ +/obj/machinery/computer/cargo/retro{ dir = 8 }, /obj/effect/decal/cleanable/dirt/dust, diff --git a/_maps/shuttles/nanotrasen/nanotrasen_mimir.dmm b/_maps/shuttles/nanotrasen/nanotrasen_mimir.dmm index f5ffea852281..928fc6bd90ee 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_mimir.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_mimir.dmm @@ -4024,7 +4024,7 @@ /turf/open/floor/plasteel, /area/ship/security/prison) "xT" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /turf/open/floor/plasteel/telecomms_floor, diff --git a/_maps/shuttles/nanotrasen/nanotrasen_osprey.dmm b/_maps/shuttles/nanotrasen/nanotrasen_osprey.dmm index 774f1d57cc40..0c860abb9e20 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_osprey.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_osprey.dmm @@ -95,7 +95,7 @@ /obj/structure/disposalpipe/segment{ dir = 8 }, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/effect/turf_decal/corner/opaque/brown{ diff --git a/_maps/shuttles/nanotrasen/nanotrasen_ranger.dmm b/_maps/shuttles/nanotrasen/nanotrasen_ranger.dmm index a106a307bb18..342ffb8fa928 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_ranger.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_ranger.dmm @@ -464,7 +464,7 @@ /turf/open/floor/plasteel/white, /area/ship/hallway/port) "eE" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /turf/open/floor/plasteel/tech, diff --git a/_maps/shuttles/nanotrasen/nanotrasen_skipper.dmm b/_maps/shuttles/nanotrasen/nanotrasen_skipper.dmm index f910af5043c8..f9aeea662f05 100644 --- a/_maps/shuttles/nanotrasen/nanotrasen_skipper.dmm +++ b/_maps/shuttles/nanotrasen/nanotrasen_skipper.dmm @@ -4235,7 +4235,7 @@ "JA" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /turf/open/floor/plasteel/dark, diff --git a/_maps/shuttles/pgf/pgf_crying_sun.dmm b/_maps/shuttles/pgf/pgf_crying_sun.dmm index 919770509bec..e016ef725525 100644 --- a/_maps/shuttles/pgf/pgf_crying_sun.dmm +++ b/_maps/shuttles/pgf/pgf_crying_sun.dmm @@ -464,7 +464,7 @@ /turf/open/floor/plasteel/telecomms_floor, /area/ship/hallway/port) "dK" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/structure/catwalk/over/plated_catwalk/dark, diff --git a/_maps/shuttles/roumain/srm_elder.dmm b/_maps/shuttles/roumain/srm_elder.dmm index e4a85560cb67..5ef138ad19db 100644 --- a/_maps/shuttles/roumain/srm_elder.dmm +++ b/_maps/shuttles/roumain/srm_elder.dmm @@ -3609,7 +3609,7 @@ /turf/open/floor/wood/maple, /area/ship/crew/cryo) "Sg" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/effect/turf_decal/spline/fancy/wood{ diff --git a/_maps/shuttles/solgov/solgov_chronicle.dmm b/_maps/shuttles/solgov/solgov_chronicle.dmm index 13e78929b4dc..012261bda246 100644 --- a/_maps/shuttles/solgov/solgov_chronicle.dmm +++ b/_maps/shuttles/solgov/solgov_chronicle.dmm @@ -350,7 +350,7 @@ /turf/open/floor/wood, /area/ship/crew) "da" = ( -/obj/machinery/computer/cargo/express/solgov{ +/obj/machinery/computer/cargo/solgov{ dir = 4 }, /obj/item/radio/intercom/directional/west, diff --git a/_maps/shuttles/solgov/solgov_inkwell.dmm b/_maps/shuttles/solgov/solgov_inkwell.dmm index da2a6b032eaa..86781cc2e251 100644 --- a/_maps/shuttles/solgov/solgov_inkwell.dmm +++ b/_maps/shuttles/solgov/solgov_inkwell.dmm @@ -2139,7 +2139,7 @@ /turf/open/floor/plasteel/mono, /area/ship/cargo) "nO" = ( -/obj/machinery/computer/cargo/express/solgov, +/obj/machinery/computer/cargo/solgov, /turf/open/floor/wood/maple, /area/ship/bridge) "nR" = ( diff --git a/_maps/shuttles/solgov/solgov_paracelsus.dmm b/_maps/shuttles/solgov/solgov_paracelsus.dmm index b099e500aee9..1235af6da913 100644 --- a/_maps/shuttles/solgov/solgov_paracelsus.dmm +++ b/_maps/shuttles/solgov/solgov_paracelsus.dmm @@ -2725,7 +2725,7 @@ /obj/structure/railing/wood{ dir = 8 }, -/obj/machinery/computer/cargo/express/solgov{ +/obj/machinery/computer/cargo/solgov{ dir = 4 }, /obj/effect/turf_decal/siding/wood{ diff --git a/_maps/shuttles/syndicate/syndicate_cybersun_kansatsu.dmm b/_maps/shuttles/syndicate/syndicate_cybersun_kansatsu.dmm index 41faf816d827..ffc2472b5dec 100644 --- a/_maps/shuttles/syndicate/syndicate_cybersun_kansatsu.dmm +++ b/_maps/shuttles/syndicate/syndicate_cybersun_kansatsu.dmm @@ -258,7 +258,7 @@ /turf/open/floor/plasteel/stairs, /area/ship/cargo) "fk" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /obj/machinery/button/door{ diff --git a/_maps/shuttles/syndicate/syndicate_gorlex_hyena.dmm b/_maps/shuttles/syndicate/syndicate_gorlex_hyena.dmm index 7c7a34fd2690..ae46dc0286f3 100644 --- a/_maps/shuttles/syndicate/syndicate_gorlex_hyena.dmm +++ b/_maps/shuttles/syndicate/syndicate_gorlex_hyena.dmm @@ -952,7 +952,7 @@ "pI" = ( /obj/item/radio/intercom/wideband/directional/east, /obj/effect/decal/cleanable/dirt/dust, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 1 }, /obj/effect/turf_decal/borderfloorblack, diff --git a/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm b/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm index 7c323e43dbe0..bfbdc4e43e04 100644 --- a/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm +++ b/_maps/shuttles/syndicate/syndicate_gorlex_komodo.dmm @@ -3507,7 +3507,7 @@ /turf/open/floor/mineral/plastitanium/red, /area/ship/hallway/central) "HI" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ layer = 3 }, /obj/effect/turf_decal/techfloor{ diff --git a/_maps/shuttles/syndicate/syndicate_litieguai.dmm b/_maps/shuttles/syndicate/syndicate_litieguai.dmm index c2e92f15f167..09ad85bc606f 100644 --- a/_maps/shuttles/syndicate/syndicate_litieguai.dmm +++ b/_maps/shuttles/syndicate/syndicate_litieguai.dmm @@ -2608,7 +2608,7 @@ /turf/open/floor/plasteel/white, /area/ship/hallway/central) "TA" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /turf/open/floor/plasteel/dark, diff --git a/_maps/shuttles/syndicate/syndicate_panacea.dmm b/_maps/shuttles/syndicate/syndicate_panacea.dmm index a55e39dd9e27..702798048e53 100644 --- a/_maps/shuttles/syndicate/syndicate_panacea.dmm +++ b/_maps/shuttles/syndicate/syndicate_panacea.dmm @@ -887,7 +887,7 @@ /turf/open/floor/suns/dark/plain, /area/ship/crew/office/lobby) "fG" = ( -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8 }, /turf/open/floor/suns/dark, diff --git a/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm b/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm index 9967d69f6d07..1adef58948fe 100644 --- a/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm +++ b/_maps/shuttles/syndicate/syndicate_twinkleshine.dmm @@ -1580,7 +1580,7 @@ /obj/effect/turf_decal/corner/opaque/syndiered/half{ dir = 4 }, -/obj/machinery/computer/cargo/express{ +/obj/machinery/computer/cargo{ dir = 8; icon_state = "computer-left" }, diff --git a/check_regex.yaml b/check_regex.yaml index 3b5e13a650e5..441f1e44d743 100644 --- a/check_regex.yaml +++ b/check_regex.yaml @@ -31,7 +31,7 @@ standards: - exactly: [4, "/mob text paths", '"/mob'] - exactly: [43, "/obj text paths", '"/obj'] - exactly: [0, "/turf text paths", '"/turf'] - - exactly: [117, "text2path uses", "text2path"] + - exactly: [116, "text2path uses", "text2path"] - exactly: [18, "world<< uses", 'world[ \t]*<<'] - exactly: [0, "world.log<< uses", 'world.log[ \t]*<<'] diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm index 54dcfb36c131..47739ded1551 100644 --- a/code/game/objects/items/circuitboards/computer_circuitboards.dm +++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm @@ -362,7 +362,7 @@ build_path = /obj/machinery/computer/bounty /obj/item/circuitboard/computer/cargo - name = "Supply Console (Computer Board)" + name = "Outpost Comms Console (Computer Board)" icon_state = "supply" build_path = /obj/machinery/computer/cargo var/contraband = FALSE @@ -381,16 +381,6 @@ obj_flags |= EMAGGED to_chat(user, "You adjust [src]'s routing and receiver spectrum, unlocking special supplies and contraband.") -/obj/item/circuitboard/computer/cargo/express - name = "Outpost Comms Console (Computer Board)" - build_path = /obj/machinery/computer/cargo/express - -/obj/item/circuitboard/computer/cargo/express/multitool_act(mob/living/user) - return - -/obj/item/circuitboard/computer/cargo/express/emag_act(mob/living/user) - return - /obj/item/circuitboard/computer/mining name = "Outpost Status Display (Computer Board)" icon_state = "supply" diff --git a/code/game/objects/structures/salvaging.dm b/code/game/objects/structures/salvaging.dm index ebefc58e5c01..d373f8eb94a9 100644 --- a/code/game/objects/structures/salvaging.dm +++ b/code/game/objects/structures/salvaging.dm @@ -673,7 +673,7 @@ /obj/effect/spawner/lootdrop/random_computer_circuit_rare loot = list( - /obj/item/circuitboard/computer/cargo/express = 5, + /obj/item/circuitboard/computer/cargo = 5, /obj/item/circuitboard/computer/communications = 5, /obj/item/circuitboard/computer/shuttle/helm = 5, /obj/item/circuitboard/computer/med_data = 5, diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm index 143480b2bc71..82fd2c11d01a 100644 --- a/code/modules/cargo/console.dm +++ b/code/modules/cargo/console.dm @@ -1,42 +1,57 @@ +#define BEACON_COST 500 +#define SP_LINKED 1 +#define SP_READY 2 +#define SP_LAUNCH 3 +#define SP_UNLINK 4 +#define SP_UNREADY 5 + /obj/machinery/computer/cargo - name = "supply console" - desc = "Used to order supplies, approve requests, and control the shuttle." - icon_screen = "supply" + name = "outpost communications console" + desc = "This console allows the user to communicate with a nearby outpost to \ + purchase supplies and manage missions. Purchases are delivered near-instantly." + icon_screen = "supply_express" circuit = /obj/item/circuitboard/computer/cargo light_color = COLOR_BRIGHT_ORANGE - var/requestonly = FALSE var/contraband = FALSE var/self_paid = FALSE var/safety_warning = "For safety reasons, the automated supply shuttle \ cannot transport live organisms, human remains, classified nuclear weaponry, \ homing beacons or machinery housing any form of artificial intelligence." - var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible." - /// radio used by the console to send messages on supply channel - var/obj/item/radio/headset/radio /// var that tracks message cooldown var/message_cooldown - -/obj/machinery/computer/cargo/request - name = "supply request console" - desc = "Used to request supplies from cargo." - icon_screen = "request" - circuit = /obj/item/circuitboard/computer/cargo/request - requestonly = TRUE + var/blockade_warning = "Bluespace instability detected. Delivery impossible." + var/message + /// Number of beacons printed. Used to determine beacon names. + var/printed_beacons = 0 + var/list/supply_pack_data + /// The currently linked supplypod beacon + var/obj/item/supplypod_beacon/beacon + /// Area instance that cargo pods are sent to + var/area/landingzone + /// The pod type used to deliver orders + var/podType = /obj/structure/closet/supplypod/centcompod + /// Cooldown to prevent printing supplypod beacon spam + var/cooldown = 0 + /// Is the console in beacon mode? exists to let beacon know when a pod may come in + var/use_beacon = FALSE + /// The account to charge purchases to, defaults to the cargo budget + var/datum/bank_account/charge_account /obj/machinery/computer/cargo/Initialize() . = ..() - radio = new /obj/item/radio/headset/headset_cargo(src) var/obj/item/circuitboard/computer/cargo/board = circuit contraband = board.contraband if (board.obj_flags & EMAGGED) obj_flags |= EMAGGED else obj_flags &= ~EMAGGED + generate_pack_data() /obj/machinery/computer/cargo/Destroy() - QDEL_NULL(radio) + if(beacon) + beacon.unlink_console() return ..() /obj/machinery/computer/cargo/proc/get_export_categories() @@ -65,49 +80,68 @@ /obj/machinery/computer/cargo/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "Cargo", name) + ui = new(user, src, "OutpostCommunications", name) ui.open() + if(!charge_account) + reconnect() -/obj/machinery/computer/cargo/ui_data() +/obj/machinery/computer/cargo/ui_data(mob/user) + var/canBeacon = beacon && (isturf(beacon.loc) || ismob(beacon.loc))//is the beacon in a valid location? var/list/data = list() - data["location"] = SSshuttle.supply.getStatusText() - var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) + + // not a big fan of get_containing_shuttle + var/obj/docking_port/mobile/D = SSshuttle.get_containing_shuttle(src) + var/datum/overmap/ship/controlled/ship + var/outpost_docked = FALSE if(D) - data["points"] = D.account_balance - data["away"] = SSshuttle.supply.get_docked() == SSshuttle.supply_away_port - data["self_paid"] = self_paid - data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE - var/message = "Remember to stamp and send back the supply manifests." - if(SSshuttle.centcom_message) - message = SSshuttle.centcom_message + ship = D.current_ship + outpost_docked = istype(ship.docked_to, /datum/overmap/outpost) + + data["onShip"] = !isnull(ship) + data["numMissions"] = ship ? LAZYLEN(ship.missions) : 0 + data["maxMissions"] = ship ? ship.max_missions : 0 + data["outpostDocked"] = outpost_docked + data["points"] = charge_account ? charge_account.account_balance : 0 + data["siliconUser"] = user.has_unlimited_silicon_privilege && check_ship_ai_access(user) + data["beaconZone"] = beacon ? get_area(beacon) : ""//where is the beacon located? outputs in the tgui + data["usingBeacon"] = use_beacon //is the mode set to deliver to the beacon or the cargobay? + data["canBeacon"] = !use_beacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location? + data["canBuyBeacon"] = charge_account ? (cooldown <= 0 && charge_account.account_balance >= BEACON_COST) : FALSE + data["beaconError"] = use_beacon && !canBeacon ? "(BEACON ERROR)" : ""//changes button text to include an error alert if necessary + data["hasBeacon"] = beacon != null//is there a linked beacon? + data["beaconName"] = beacon ? beacon.name : "No Beacon Found" + data["printMsg"] = cooldown > 0 ? "Print Beacon for [BEACON_COST] credits ([cooldown])" : "Print Beacon for [BEACON_COST] credits"//buttontext for printing beacons + data["supplies"] = list() + message = "Sales are near-instantaneous - please choose carefully." if(SSshuttle.supplyBlocked) message = blockade_warning + if(use_beacon && !beacon) + message = "BEACON ERROR: BEACON MISSING"//beacon was destroyed + else if (use_beacon && !canBeacon) + message = "BEACON ERROR: MUST BE EXPOSED"//beacon's loc/user's loc must be a turf data["message"] = message - data["cart"] = list() - for(var/datum/supply_order/SO in SSshuttle.shoppinglist) - data["cart"] += list(list( - "object" = SO.pack.name, - "cost" = SO.pack.cost, - "id" = SO.id, - "orderer" = SO.orderer, - "paid" = !isnull(SO.paying_account) //paid by requester - )) + if(!supply_pack_data) + generate_pack_data() + stack_trace("You didn't give the cargo tech good advice, and he ripped the manifest. As a result, there was no pack data for [src]") + data["supplies"] = supply_pack_data + if (cooldown > 0)//cooldown used for printing beacons + cooldown-- - data["requests"] = list() - for(var/datum/supply_order/SO in SSshuttle.requestlist) - data["requests"] += list(list( - "object" = SO.pack.name, - "cost" = SO.pack.cost, - "orderer" = SO.orderer, - "reason" = SO.reason, - "id" = SO.id - )) + data["shipMissions"] = list() + data["outpostMissions"] = list() + + if(ship) + for(var/datum/mission/M as anything in ship.missions) + data["shipMissions"] += list(M.get_tgui_info()) + if(outpost_docked) + var/datum/overmap/outpost/out = ship.docked_to + for(var/datum/mission/M as anything in out.missions) + data["outpostMissions"] += list(M.get_tgui_info()) return data /obj/machinery/computer/cargo/ui_static_data(mob/user) var/list/data = list() - data["requestonly"] = requestonly data["supplies"] = list() for(var/pack in SSshuttle.supply_packs) var/datum/supply_pack/P = SSshuttle.supply_packs[pack] @@ -116,7 +150,7 @@ "name" = P.group, "packs" = list() ) - if((P.hidden && !(obj_flags & EMAGGED)) || (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly) + if(P.hidden && !(obj_flags & EMAGGED)) continue data["supplies"][P.group]["packs"] += list(list( "name" = P.name, @@ -124,7 +158,6 @@ "id" = pack, "desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name. "small_item" = P.small_item, - "access" = P.access )) return data @@ -133,115 +166,154 @@ if(.) return switch(action) - if("send") - if(!SSshuttle.supply.canMove()) - say(safety_warning) + if("withdrawCash") + var/val = text2num(params["value"]) + // no giving yourself money + if(!charge_account || !val || val <= 0) return - if(SSshuttle.supplyBlocked) - say(blockade_warning) - return - if(SSshuttle.supply.get_docked() == SSshuttle.supply_home_port) - SSshuttle.supply.export_categories = get_export_categories() - SSshuttle.moveShuttle(SSshuttle.supply, SSshuttle.supply_away_port, TRUE) - say("The supply shuttle is departing.") - investigate_log("[key_name(usr)] sent the supply shuttle away.", INVESTIGATE_CARGO) - else - investigate_log("[key_name(usr)] called the supply shuttle.", INVESTIGATE_CARGO) - say("The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minutes.") - SSshuttle.moveShuttle(SSshuttle.supply, SSshuttle.supply_home_port, TRUE) - . = TRUE + if(charge_account.adjust_money(-val)) + var/obj/item/holochip/cash_chip = new /obj/item/holochip(drop_location(), val) + if(ishuman(usr)) + var/mob/living/carbon/human/user = usr + user.put_in_hands(cash_chip) + playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE) + src.visible_message("[src] dispenses a holochip.") + return TRUE + + if("LZCargo") + use_beacon = FALSE + if (beacon) + beacon.update_status(SP_UNREADY) //ready light on beacon will turn off + if("LZBeacon") + use_beacon = TRUE + if (beacon) + beacon.update_status(SP_READY) //turns on the beacon's ready light + if("printBeacon") + if(charge_account?.adjust_money(-BEACON_COST)) + cooldown = 10//a ~ten second cooldown for printing beacons to prevent spam + var/obj/item/supplypod_beacon/C = new /obj/item/supplypod_beacon(drop_location()) + C.link_console(src, usr)//rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc) + printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1 + beacon.name = "Supply Pod Beacon #[printed_beacons]" if("add") - if(istype(src, /obj/machinery/computer/cargo/express)) - return - var/id = text2path(params["id"]) - var/datum/supply_pack/pack = SSshuttle.supply_packs[id] - if(!istype(pack)) - return - if((pack.hidden && !(obj_flags & EMAGGED)) || (pack.contraband && !contraband) || pack.DropPodOnly) + var/area/ship/current_area = get_area(src) + var/datum/supply_pack/pack = SSshuttle.supply_packs[text2path(params["id"])] + if( \ + !pack || !charge_account?.has_money(pack.cost) || !istype(current_area) || \ + !istype(current_area.mobile_port.current_ship.docked_to, /datum/overmap/outpost) \ + ) return - var/name = "*None Provided*" - var/rank = "*None Provided*" - var/ckey = usr.ckey - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - name = H.get_authentification_name() - rank = H.get_assignment(hand_first = TRUE) - else if(issilicon(usr)) - name = usr.real_name - rank = "Silicon" - - var/datum/bank_account/account - if(self_paid && ishuman(usr)) - var/mob/living/carbon/human/H = usr - var/obj/item/card/id/id_card = H.get_idcard(TRUE) - if(!istype(id_card)) - say("No ID card detected.") - return - account = id_card.registered_account - if(!istype(account)) - say("Invalid bank account.") - return + var/turf/landing_turf + if(!isnull(beacon) && use_beacon) // prioritize beacons over landing in cargobay + landing_turf = get_turf(beacon) + beacon.update_status(SP_LAUNCH) + else if(!use_beacon)// find a suitable supplypod landing zone in cargobay + var/list/empty_turfs = list() + if(!landingzone) + reconnect() + if(!landingzone) + WARNING("[src] couldnt find a Ship/Cargo (aka cargobay) area on a ship, and as such it has set the supplypod landingzone to the area it resides in.") + landingzone = get_area(src) + for(var/turf/open/floor/T in landingzone.contents)//uses default landing zone + if(T.is_blocked_turf()) + continue + empty_turfs += T + CHECK_TICK + landing_turf = pick(empty_turfs) + + // note that, because of CHECK_TICK above, we aren't sure if we can + // afford the pack, even though we checked earlier. luckily adjust_money + // returns false if the account can't afford the price + if(landing_turf && charge_account.adjust_money(-pack.cost)) + var/name = "*None Provided*" + var/rank = "*None Provided*" + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + name = H.get_authentification_name() + rank = H.get_assignment(hand_first = TRUE) + else if(issilicon(usr)) + name = usr.real_name + rank = "Silicon" + var/datum/supply_order/SO = new(pack, name, rank, usr.ckey, "") + new /obj/effect/pod_landingzone(landing_turf, podType, SO) + update_appearance() // ?????????????????? + return TRUE - var/reason = "" - if(requestonly && !self_paid) - reason = stripped_input("Reason:", name, "") - if(isnull(reason) || ..()) + if("mission-act") + var/datum/mission/mission = locate(params["ref"]) + var/obj/docking_port/mobile/D = SSshuttle.get_containing_shuttle(src) + var/datum/overmap/ship/controlled/ship = D.current_ship + var/datum/overmap/outpost/outpost = ship.docked_to + if(!istype(outpost) || mission.source_outpost != outpost) // important to check these to prevent href fuckery + return + if(!mission.accepted) + if(LAZYLEN(ship.missions) >= ship.max_missions) return + mission.accept(ship, loc) + return TRUE + else if(mission.servant == ship) + if(mission.can_complete()) + mission.turn_in() + else + mission.give_up() + return TRUE - var/turf/T = get_turf(src) - var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account) - SO.generateRequisition(T) - if(requestonly && !self_paid) - SSshuttle.requestlist += SO - else - SSshuttle.shoppinglist += SO - if(self_paid) - say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.") - if(requestonly && message_cooldown < world.time) - radio.talk_into(src, "A new order has been requested.", RADIO_CHANNEL_COMMAND) - message_cooldown = world.time + 30 SECONDS - . = TRUE - if("remove") - var/id = text2num(params["id"]) - for(var/datum/supply_order/SO in SSshuttle.shoppinglist) - if(SO.id == id) - SSshuttle.shoppinglist -= SO - . = TRUE - break - if("clear") - SSshuttle.shoppinglist.Cut() - . = TRUE - if("approve") - var/id = text2num(params["id"]) - for(var/datum/supply_order/SO in SSshuttle.requestlist) - if(SO.id == id) - SSshuttle.requestlist -= SO - SSshuttle.shoppinglist += SO - . = TRUE - break - if("deny") - var/id = text2num(params["id"]) - for(var/datum/supply_order/SO in SSshuttle.requestlist) - if(SO.id == id) - SSshuttle.requestlist -= SO - . = TRUE - break - if("denyall") - SSshuttle.requestlist.Cut() - . = TRUE - if("toggleprivate") - self_paid = !self_paid - . = TRUE - if(.) - post_signal("supply") +/obj/machinery/computer/cargo/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock) + . = ..() + reconnect(port) -/obj/machinery/computer/cargo/proc/post_signal(command) +/obj/machinery/computer/cargo/proc/reconnect(obj/docking_port/mobile/port) + if(!port) + var/area/ship/current_area = get_area(src) + if(!istype(current_area)) + return + port = current_area.mobile_port + if(!port) + return + charge_account = port.current_ship.ship_account + landingzone = locate(/area/ship/cargo) in port.shuttle_areas - var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) +/obj/machinery/computer/cargo/attackby(obj/item/W, mob/living/user, params) + var/value = W.get_item_credit_value() + if(value && charge_account) + charge_account.adjust_money(value) + to_chat(user, "You deposit [W]. The Vessel Budget is now [charge_account.account_balance] cr.") + qdel(W) + return TRUE + else if(istype(W, /obj/item/supplypod_beacon)) + var/obj/item/supplypod_beacon/sb = W + if (sb.cargo_console != src) + sb.link_console(src, user) + return TRUE + else + to_chat(user, "[src] is already linked to [sb].") + ..() - if(!frequency) - return +/obj/machinery/computer/cargo/proc/generate_pack_data() + supply_pack_data = list() + for(var/pack in SSshuttle.supply_packs) + var/datum/supply_pack/P = SSshuttle.supply_packs[pack] + if(!supply_pack_data[P.group]) + supply_pack_data[P.group] = list( + "name" = P.group, + "packs" = list() + ) + if((P.hidden)) + continue + supply_pack_data[P.group]["packs"] += list(list( + "name" = P.name, + "cost" = P.cost, + "id" = pack, + "desc" = P.desc || P.name // If there is a description, use it. Otherwise use the pack's name. + )) + +/obj/machinery/computer/cargo/retro + icon = 'icons/obj/machines/retro_computer.dmi' + icon_state = "computer-retro" + deconpath = /obj/structure/frame/computer/retro - var/datum/signal/status_signal = new(list("command" = command)) - frequency.post_signal(src, status_signal) +/obj/machinery/computer/cargo/solgov + icon = 'icons/obj/machines/retro_computer.dmi' + icon_state = "computer-solgov" + deconpath = /obj/structure/frame/computer/solgov diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm deleted file mode 100644 index 81409d63d031..000000000000 --- a/code/modules/cargo/expressconsole.dm +++ /dev/null @@ -1,262 +0,0 @@ -#define BEACON_COST 500 -#define SP_LINKED 1 -#define SP_READY 2 -#define SP_LAUNCH 3 -#define SP_UNLINK 4 -#define SP_UNREADY 5 - -/obj/machinery/computer/cargo/express - name = "outpost communications console" - desc = "This console allows the user to communicate with a nearby outpost to \ - purchase supplies and manage missions. Purchases are delivered near-instantly." - icon_screen = "supply_express" - circuit = /obj/item/circuitboard/computer/cargo/express - var/blockade_warning = "Bluespace instability detected. Delivery impossible." - - var/message - /// Number of beacons printed. Used to determine beacon names. - var/printed_beacons = 0 - var/list/meme_pack_data - /// The currently linked supplypod beacon - var/obj/item/supplypod_beacon/beacon - /// Area instance that cargo pods are sent to - var/area/landingzone - /// The pod type used to deliver orders - var/podType = /obj/structure/closet/supplypod/centcompod - /// Cooldown to prevent printing supplypod beacon spam - var/cooldown = 0 - /// Is the console in beacon mode? exists to let beacon know when a pod may come in - var/use_beacon = FALSE - /// The account to charge purchases to, defaults to the cargo budget - var/datum/bank_account/charge_account - -/obj/machinery/computer/cargo/express/retro - icon = 'icons/obj/machines/retro_computer.dmi' - icon_state = "computer-retro" - deconpath = /obj/structure/frame/computer/retro - -/obj/machinery/computer/cargo/express/solgov - icon = 'icons/obj/machines/retro_computer.dmi' - icon_state = "computer-solgov" - deconpath = /obj/structure/frame/computer/solgov - -/obj/machinery/computer/cargo/express/Initialize() - . = ..() - packin_up() - -/obj/machinery/computer/cargo/express/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock) - . = ..() - reconnect(port) - -/obj/machinery/computer/cargo/express/proc/reconnect(obj/docking_port/mobile/port) - if(!port) - var/area/ship/current_area = get_area(src) - if(!istype(current_area)) - return - port = current_area.mobile_port - if(!port) - return - charge_account = port.current_ship.ship_account - landingzone = locate(/area/ship/cargo) in port.shuttle_areas - -/obj/machinery/computer/cargo/express/Destroy() - if(beacon) - beacon.unlink_console() - return ..() - -/obj/machinery/computer/cargo/express/attackby(obj/item/W, mob/living/user, params) - var/value = W.get_item_credit_value() - if(value && charge_account) - charge_account.adjust_money(value) - to_chat(user, "You deposit [W]. The Vessel Budget is now [charge_account.account_balance] cr.") - qdel(W) - return TRUE - else if(istype(W, /obj/item/supplypod_beacon)) - var/obj/item/supplypod_beacon/sb = W - if (sb.express_console != src) - sb.link_console(src, user) - return TRUE - else - to_chat(user, "[src] is already linked to [sb].") - ..() - -/obj/machinery/computer/cargo/express/proc/packin_up() // oh shit, I'm sorry - meme_pack_data = list() // sorry for what? - for(var/pack in SSshuttle.supply_packs) // our quartermaster taught us not to be ashamed of our supply packs - var/datum/supply_pack/P = SSshuttle.supply_packs[pack] // specially since they're such a good price and all - if(!meme_pack_data[P.group]) // yeah, I see that, your quartermaster gave you good advice - meme_pack_data[P.group] = list( // it gets cheaper when I return it - "name" = P.group, // mmhm - "packs" = list() // sometimes, I return it so much, I rip the manifest - ) // see, my quartermaster taught me a few things too - if((P.hidden)) // like, how not to rip the manifest - continue// by using someone else's crate - meme_pack_data[P.group]["packs"] += list(list( - "name" = P.name, - "cost" = P.cost, - "id" = pack, - "desc" = P.desc || P.name // If there is a description, use it. Otherwise use the pack's name. - )) - -/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "OutpostCommunications", name) - ui.open() - if(!charge_account) - reconnect() - -/obj/machinery/computer/cargo/express/ui_data(mob/user) - var/canBeacon = beacon && (isturf(beacon.loc) || ismob(beacon.loc))//is the beacon in a valid location? - var/list/data = list() - - // not a big fan of get_containing_shuttle - var/obj/docking_port/mobile/D = SSshuttle.get_containing_shuttle(src) - var/datum/overmap/ship/controlled/ship - var/outpost_docked = FALSE - if(D) - ship = D.current_ship - outpost_docked = istype(ship.docked_to, /datum/overmap/outpost) - - data["onShip"] = !isnull(ship) - data["numMissions"] = ship ? LAZYLEN(ship.missions) : 0 - data["maxMissions"] = ship ? ship.max_missions : 0 - data["outpostDocked"] = outpost_docked - data["points"] = charge_account ? charge_account.account_balance : 0 - data["siliconUser"] = user.has_unlimited_silicon_privilege && check_ship_ai_access(user) - data["beaconZone"] = beacon ? get_area(beacon) : ""//where is the beacon located? outputs in the tgui - data["usingBeacon"] = use_beacon //is the mode set to deliver to the beacon or the cargobay? - data["canBeacon"] = !use_beacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location? - data["canBuyBeacon"] = charge_account ? (cooldown <= 0 && charge_account.account_balance >= BEACON_COST) : FALSE - data["beaconError"] = use_beacon && !canBeacon ? "(BEACON ERROR)" : ""//changes button text to include an error alert if necessary - data["hasBeacon"] = beacon != null//is there a linked beacon? - data["beaconName"] = beacon ? beacon.name : "No Beacon Found" - data["printMsg"] = cooldown > 0 ? "Print Beacon for [BEACON_COST] credits ([cooldown])" : "Print Beacon for [BEACON_COST] credits"//buttontext for printing beacons - data["supplies"] = list() - message = "Sales are near-instantaneous - please choose carefully." - if(SSshuttle.supplyBlocked) - message = blockade_warning - if(use_beacon && !beacon) - message = "BEACON ERROR: BEACON MISSING"//beacon was destroyed - else if (use_beacon && !canBeacon) - message = "BEACON ERROR: MUST BE EXPOSED"//beacon's loc/user's loc must be a turf - data["message"] = message - if(!meme_pack_data) - packin_up() - stack_trace("You didn't give the cargo tech good advice, and he ripped the manifest. As a result, there was no pack data for [src]") - data["supplies"] = meme_pack_data - if (cooldown > 0)//cooldown used for printing beacons - cooldown-- - - data["shipMissions"] = list() - data["outpostMissions"] = list() - - if(ship) - for(var/datum/mission/M as anything in ship.missions) - data["shipMissions"] += list(M.get_tgui_info()) - if(outpost_docked) - var/datum/overmap/outpost/out = ship.docked_to - for(var/datum/mission/M as anything in out.missions) - data["outpostMissions"] += list(M.get_tgui_info()) - - return data - -/obj/machinery/computer/cargo/express/ui_act(action, params, datum/tgui/ui) - . = ..() - if(.) - return - - switch(action) - if("withdrawCash") - var/val = text2num(params["value"]) - // no giving yourself money - if(!charge_account || !val || val <= 0) - return - if(charge_account.adjust_money(-val)) - var/obj/item/holochip/cash_chip = new /obj/item/holochip(drop_location(), val) - if(ishuman(usr)) - var/mob/living/carbon/human/user = usr - user.put_in_hands(cash_chip) - playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE) - src.visible_message("[src] dispenses a holochip.") - return TRUE - - if("LZCargo") - use_beacon = FALSE - if (beacon) - beacon.update_status(SP_UNREADY) //ready light on beacon will turn off - if("LZBeacon") - use_beacon = TRUE - if (beacon) - beacon.update_status(SP_READY) //turns on the beacon's ready light - if("printBeacon") - if(charge_account?.adjust_money(-BEACON_COST)) - cooldown = 10//a ~ten second cooldown for printing beacons to prevent spam - var/obj/item/supplypod_beacon/C = new /obj/item/supplypod_beacon(drop_location()) - C.link_console(src, usr)//rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc) - printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1 - beacon.name = "Supply Pod Beacon #[printed_beacons]" - - if("add") - var/area/ship/current_area = get_area(src) - var/datum/supply_pack/pack = SSshuttle.supply_packs[text2path(params["id"])] - if( \ - !pack || !charge_account?.has_money(pack.cost) || !istype(current_area) || \ - !istype(current_area.mobile_port.current_ship.docked_to, /datum/overmap/outpost) \ - ) - return - - var/turf/landing_turf - if(!isnull(beacon) && use_beacon) // prioritize beacons over landing in cargobay - landing_turf = get_turf(beacon) - beacon.update_status(SP_LAUNCH) - else if(!use_beacon)// find a suitable supplypod landing zone in cargobay - var/list/empty_turfs = list() - if(!landingzone) - reconnect() - if(!landingzone) - WARNING("[src] couldnt find a Ship/Cargo (aka cargobay) area on a ship, and as such it has set the supplypod landingzone to the area it resides in.") - landingzone = get_area(src) - for(var/turf/open/floor/T in landingzone.contents)//uses default landing zone - if(T.is_blocked_turf()) - continue - empty_turfs += T - CHECK_TICK - landing_turf = pick(empty_turfs) - - // note that, because of CHECK_TICK above, we aren't sure if we can - // afford the pack, even though we checked earlier. luckily adjust_money - // returns false if the account can't afford the price - if(landing_turf && charge_account.adjust_money(-pack.cost)) - var/name = "*None Provided*" - var/rank = "*None Provided*" - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - name = H.get_authentification_name() - rank = H.get_assignment(hand_first = TRUE) - else if(issilicon(usr)) - name = usr.real_name - rank = "Silicon" - var/datum/supply_order/SO = new(pack, name, rank, usr.ckey, "") - new /obj/effect/pod_landingzone(landing_turf, podType, SO) - update_appearance() // ?????????????????? - return TRUE - - if("mission-act") - var/datum/mission/mission = locate(params["ref"]) - var/obj/docking_port/mobile/D = SSshuttle.get_containing_shuttle(src) - var/datum/overmap/ship/controlled/ship = D.current_ship - var/datum/overmap/outpost/outpost = ship.docked_to - if(!istype(outpost) || mission.source_outpost != outpost) // important to check these to prevent href fuckery - return - if(!mission.accepted) - if(LAZYLEN(ship.missions) >= ship.max_missions) - return - mission.accept(ship, loc) - return TRUE - else if(mission.servant == ship) - if(mission.can_complete()) - mission.turn_in() - else - mission.give_up() - return TRUE diff --git a/code/modules/cargo/supplypod_beacon.dm b/code/modules/cargo/supplypod_beacon.dm index 11fd10229e5e..b9c41a29e11f 100644 --- a/code/modules/cargo/supplypod_beacon.dm +++ b/code/modules/cargo/supplypod_beacon.dm @@ -7,7 +7,7 @@ lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' w_class = WEIGHT_CLASS_SMALL - var/obj/machinery/computer/cargo/express/express_console + var/obj/machinery/computer/cargo/cargo_console var/linked = FALSE var/ready = FALSE var/launched = FALSE @@ -49,39 +49,39 @@ /obj/item/supplypod_beacon/examine(user) . = ..() - if(!express_console) + if(!cargo_console) . += "[src] is not currently linked to an Express Supply console." else . += "Alt-click to unlink it from the Express Supply console." /obj/item/supplypod_beacon/Destroy() - if(express_console) - express_console.beacon = null + if(cargo_console) + cargo_console.beacon = null return ..() /obj/item/supplypod_beacon/proc/unlink_console() - if(express_console) - express_console.beacon = null - express_console = null + if(cargo_console) + cargo_console.beacon = null + cargo_console = null update_status(SP_UNLINK) update_status(SP_UNREADY) -/obj/item/supplypod_beacon/proc/link_console(obj/machinery/computer/cargo/express/C, mob/living/user) +/obj/item/supplypod_beacon/proc/link_console(obj/machinery/computer/cargo/C, mob/living/user) if (C.beacon)//if new console has a beacon, then... C.beacon.unlink_console()//unlink the old beacon from new console - if (express_console)//if this beacon has an express console - express_console.beacon = null//remove the connection the expressconsole has from beacons - express_console = C//set the linked console var to the console - express_console.beacon = src//out with the old in with the news + if (cargo_console)//if this beacon has an express console + cargo_console.beacon = null//remove the connection the expressconsole has from beacons + cargo_console = C//set the linked console var to the console + cargo_console.beacon = src//out with the old in with the news update_status(SP_LINKED) - if (express_console.use_beacon) + if (cargo_console.use_beacon) update_status(SP_READY) to_chat(user, "[src] linked to [C].") /obj/item/supplypod_beacon/AltClick(mob/user) if (!user.canUseTopic(src, !issilicon(user))) return - if (express_console) + if (cargo_console) unlink_console() else to_chat(user, "There is no linked console.") diff --git a/code/modules/research/designs/mining_designs.dm b/code/modules/research/designs/mining_designs.dm index 2cddc5043c3f..aa221c2b21a7 100644 --- a/code/modules/research/designs/mining_designs.dm +++ b/code/modules/research/designs/mining_designs.dm @@ -8,7 +8,7 @@ id = "cargoexpress"//the coder reading this build_type = IMPRINTER materials = list(/datum/material/glass = 1000) - build_path = /obj/item/circuitboard/computer/cargo/express + build_path = /obj/item/circuitboard/computer/cargo category = list("Mining Designs") departmental_flags = DEPARTMENTAL_FLAG_CARGO diff --git a/shiptest.dme b/shiptest.dme index 4b8478ffbf1b..c0d9a7d4df66 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -1882,9 +1882,9 @@ #include "code\modules\cargo\bounty.dm" #include "code\modules\cargo\bounty_console.dm" #include "code\modules\cargo\centcom_podlauncher.dm" +#include "code\modules\cargo\console.dm" #include "code\modules\cargo\export_scanner.dm" #include "code\modules\cargo\exports.dm" -#include "code\modules\cargo\expressconsole.dm" #include "code\modules\cargo\gondolapod.dm" #include "code\modules\cargo\order.dm" #include "code\modules\cargo\packs.dm" diff --git a/tgui/packages/tgui/interfaces/Cargo.js b/tgui/packages/tgui/interfaces/Cargo.js deleted file mode 100644 index 9dfcd417f593..000000000000 --- a/tgui/packages/tgui/interfaces/Cargo.js +++ /dev/null @@ -1,533 +0,0 @@ -import { flow } from 'common/fp'; -import { filter, sortBy } from 'common/collections'; -import { useBackend, useSharedState } from '../backend'; -import { - AnimatedNumber, - Box, - Button, - Flex, - Icon, - Input, - LabeledList, - NoticeBox, - Section, - Stack, - Table, - Tabs, -} from '../components'; -import { formatMoney } from '../format'; -import { Window } from '../layouts'; - -export const Cargo = (props, context) => { - return ( - - - - - - ); -}; - -export const CargoContent = (props, context) => { - const { act, data } = useBackend(context); - const [tab, setTab] = useSharedState(context, 'tab', 'catalog'); - const { requestonly } = data; - const cart = data.cart || []; - const requests = data.requests || []; - return ( - - -
- - setTab('catalog')} - > - Catalog - - 0 && 'yellow'} - selected={tab === 'requests'} - onClick={() => setTab('requests')} - > - Requests ({requests.length}) - - {!requestonly && ( - <> - 0 && 'yellow'} - selected={tab === 'cart'} - onClick={() => setTab('cart')} - > - Checkout ({cart.length}) - - setTab('help')} - > - Help - - - )} - -
- {tab === 'catalog' && } - {tab === 'requests' && } - {tab === 'cart' && } - {tab === 'help' && } -
- ); -}; - -const CargoStatus = (props, context) => { - const { act, data } = useBackend(context); - const { - department, - grocery, - away, - docked, - loan, - loan_dispatched, - location, - message, - points, - requestonly, - can_send, - } = data; - return ( -
- formatMoney(value)} - /> - {' credits'} - - } - > - - - {(docked && !requestonly && can_send && ( -
- ); -}; - -/** - * Take entire supplies tree - * and return a flat supply pack list that matches search, - * sorted by name and only the first page. - * @param {any[]} supplies Supplies list. - * @param {string} search The search term - * @returns {any[]} The flat list of supply packs. - */ -const searchForSupplies = (supplies, search) => { - search = search.toLowerCase(); - - return flow([ - (categories) => categories.flatMap((category) => category.packs), - filter( - (pack) => - pack.name?.toLowerCase().includes(search.toLowerCase()) || - pack.desc?.toLowerCase().includes(search.toLowerCase()) - ), - sortBy((pack) => pack.name), - (packs) => packs.slice(0, 25), - ])(supplies); -}; - -export const CargoCatalog = (props, context) => { - const { express } = props; - const { act, data } = useBackend(context); - - const { self_paid, app_cost } = data; - - const supplies = Object.values(data.supplies); - - const [activeSupplyName, setActiveSupplyName] = useSharedState( - context, - 'supply', - supplies[0]?.name - ); - - const [searchText, setSearchText] = useSharedState( - context, - 'search_text', - '' - ); - - const activeSupply = - activeSupplyName === 'search_results' - ? { packs: searchForSupplies(supplies, searchText) } - : supplies.find((supply) => supply.name === activeSupplyName); - - return ( -
- - act('toggleprivate')} - /> - - ) - } - > - - - - - - - - - - { - if (value === searchText) { - return; - } - - if (value.length) { - // Start showing results - setActiveSupplyName('search_results'); - } else if (activeSupplyName === 'search_results') { - // return to normal category - setActiveSupplyName(supplies[0]?.name); - } - setSearchText(value); - }} - onChange={(e, value) => { - // Allow edge cases like the X button to work - const onInput = e.target?.props?.onInput; - if (onInput) { - onInput(e, value); - } - }} - /> - - - - {supplies.map((supply) => ( - { - setActiveSupplyName(supply.name); - setSearchText(''); - }} - > - {supply.name} ({supply.packs.length}) - - ))} - - - - - {activeSupply?.packs.map((pack) => { - const tags = []; - if (pack.small_item) { - tags.push('Small'); - } - if (pack.access) { - tags.push('Restricted'); - } - return ( - - {pack.name} - - {tags.join(', ')} - - - - - - ); - })} -
-
-
-
- ); -}; - -const CargoRequests = (props, context) => { - const { act, data } = useBackend(context); - const { requestonly, can_send, can_approve_requests } = data; - const requests = data.requests || []; - // Labeled list reimplementation to squeeze extra columns out of it - return ( -
act('denyall')} - /> - ) - } - > - {requests.length === 0 && No Requests} - {requests.length > 0 && ( - - {requests.map((request) => ( - - - #{request.id} - - {request.object} - - {request.orderer} - - - {request.reason} - - - {formatMoney(request.cost)} cr - - {(!requestonly || can_send) && can_approve_requests && ( - -
- )} -
- ); -}; - -const CargoCartButtons = (props, context) => { - const { act, data } = useBackend(context); - const { requestonly, can_send, can_approve_requests } = data; - const cart = data.cart || []; - const total = cart.reduce((total, entry) => total + entry.cost, 0); - if (requestonly || !can_send || !can_approve_requests) { - return null; - } - return ( - <> - - {cart.length === 0 && 'Cart is empty'} - {cart.length === 1 && '1 item'} - {cart.length >= 2 && cart.length + ' items'}{' '} - {total > 0 && `(${formatMoney(total)} cr)`} - -