From e9061e8b19b36c0fc0b9ffbf5eb7d5402e35e7be Mon Sep 17 00:00:00 2001 From: ArrisFairburne Date: Fri, 3 Jul 2026 15:05:07 -0400 Subject: [PATCH] Blacksmith Revamp (#5360) ## About The Pull Request Makes a number of changes to blacksmithing to make the role more appealing, convenient, and special:
New Additions - [ ] Added a new blacksmith recipe that requires legendary skill and the skillchip: parts for a classical single-action revolver. High damage, but finnicky to use; no safety mechanism, can't accept speed loaders, and the hammer has to be cocked before each shot. Cocking the hammer is faster if both hands are free, allowing for fanning the hammer. - [ ] Smiths can now make new storage options: a cowboy holster that stores a handgun and multiple ammo boxes, a charging belt that recharges one energy gun, a botany bag with expanded storage, and multiple types of scabbards. - [ ] Added a new skillchip specific to blacksmithing; without the skillchip, your options on what you can produce via smithing are reduced. This is to make it so that the smithing job role (or at least, whoever has been given the skillchip from the QM's locker) is more special. Currently smithing is just sort of something anyone can do, the only things unique about spawning in as the blacksmith is that you have cargo access and smithing-specific bounties. - [ ] Replaced the water barrel with a new structure: the reagent quenching trough. This spawns in filled with a new reagent, quenching oil. Quenching a smithing item in the trough with any reagent will imbue it for zero reagent use, and heat the trough's reagent.
General direction changes - [ ] Perfect hits are no longer probability-dependant; they're based on the player's timing. Except if the player is max level at blacksmithing, in which case all hits are perfects. - [ ] Right-clicking with a hammer on the anvil will allow the player to automatically forge the item they're working on, with a slower pace compared to manual forging. This is mostly meant to allow talking to someone while working on forges. - [ ] Removed multi-anvil smithing; blacksmithing lacks a dedicated space on most maps and so must occupy a hallway or something else instead. "Playing optimally" with multi-anvil smithing means taking up significantly more space, in addition to looking extremely silly and bypassing part of the loop with blacksmithing (levelling up the smithing skills to produce equipment faster); the smithing chip will reduce the swings needed to finish a job to compensate. - [ ] Reworked reagent imbued equipment; removed the bonus from leaving a weapon unimbued. This bonus can now be regained specifically by imbuing smithing oil (smithing oil is special; imbuing it doesn't inflict it as a reagent for effects, but makes the armor/weapon/etc. better by numbers). Also, reagent imbued equipment no longer has a big damage multiplier but will merely stop applying reagents after a certain integrity threshold is passed. - [ ] Significantly reduced the income from smithing cargo bounties. It honestly kind of sucks that blacksmiths' value to the station is that they print money and not for the actual quality of their items. - [ ] Blacksmiths now have weapon access by default. It's honestly kind of weird that the guy whose whole schtick is making weapons, didn't have weapon access to start. - [ ] Moved the crusader belt from the general crafting menu to the smithing crafting menu; it now requires lv7 smithing and the smithing chip. This change is because it's highly contingent on blacksmithing equipment for its main storage so it seems most sensible for a smith to work it. - [ ] The leather apron that blacksmiths roundstart with now has dedicated storage for various smithing and glassblowing tools.
Interactivity changes - [ ] Trying to pick up a heated blacksmith item will now burn your hand. - [ ] Various quenched smithing items now have associated force values for dealing damage, but will also (usually) cut the user's hand for doing so. - [ ] You can now use water tiles or certain reagent containers (needs at least 300 units) to quench smithing equipment. - [ ] The forge now heats up it's surroundings and burns carbon dioxide while doing so. The wall-mounted heater now auto charges it's cell to compensate for this. - [ ] You can now weapon-bash your blacksmithing shields, similar to how you can slap your baton against your riot shields to intimidate.
Convenience and misc. - [ ] Changed the Rapier sprites to match the other smithing weapon sprites. - [ ] Added more tutorial tooltips when inspecting blacksmith equipment. - [ ] The blacksmithing noises are quieter, thank god. - [ ] Using tongs on a stack now pulls only one item from the stack, instead of pulling all items in the stack. You never actually want to have more than one sheet in the tongs so there's no reason to include it really. - [ ] Completed blacksmith items can now be picked up with tongs and placed into the crafting bench with them. This is meant to reduce the tool-juggling for moving the item from the anvil to the basin, then moving the item from the basin to the bench. - [ ] The blacksmith ID trim now has an actual icon - [ ] Commented out the cortical borer cage recipe. We don't really need this since we don't run cortical borers. - [ ] Halved the number of necessary chains and plates for all recipes, but doubled the time needed to hammer them; this is to reduce how much item juggling is needed. - [ ] Cargo guards and cargo secoffs now have blacksmithing access. - [ ] Ensure that Ashwalkers, Primal Podpeople, and feral nekos have all the same mechanics as a skillchipped crewie, except cannot make the revolver
Code refactoring - [ ] The reagent clothing and reagent weapon components now come from a general reagent imbued parent component. - [ ] Refactored how anvil smithing works; it now relies on an item component that handles item heat, hammer strikes, and quenching, instead of all the code being handled within the anvil itself. - [ ] Removed the skyrat_obj_flags variable that only functioned for the anvil_repair flag and nothing else. We have different ways to check for the stuff it relied on so it's not really needed. - [ ] Removed the in_use code from various forging objects. It's a really cumbersome system and annoying to manage all the time. Doafters with interaction_key stand as a replacement.
Overall, in the hands of blacksmiths, smithing-chipped characters, and the tribal factions, this should feel like a general buff. For everyone else, this should feel like a nerf, and they should go to the blacksmith or get skillchipped so they can make smithing stuff. It should also feel less 'shallow' and a bit more interactive with the rest of the game. ## Proof Of Testing
Screenshots/Videos image image https://youtu.be/hcPZXzCOSIU https://youtu.be/V7GeLetcRtk https://youtu.be/YEyW9pKidTU
## Changelog :cl: add: Way, way too many blacksmith job features & smithing system changes to list. But here's the highlights: balance: Forged blacksmith weapons can now be used nonlethally via striking with pommels, spear shafts, etc. which knocks down victims and stamina damages them like a weaker baton. qol: Blacksmith tongs now only pull one item at a time, if used on a stackable item. qol: Blacksmiths can now auto-smith at an anvil by right clicking with a hammer. qol: Blacksmithing tools now have more tooltips to tutorialize their usage. qol: Blacksmithing crafting bench can now store item stacks, forging plates, and forging chains. Right-click to view what's in it. balance: Removed multi-anvil smithing. Individual smithing pieces can now be made faster with the smithing chip and other means to compensate. add: Blacksmiths can now build a single action revolver if they are fully levelled. add: Blacksmiths can build a variety of new equipment at the crafting bench -- gun-charging holsters, knife belts, multiple types of scabbards, and even a new expanded-storage botany bag. add: Replaced the water barrel for smithing with a barrel of smithing oil; smithing oil is now used to grant the bonuses that was previously given by leaving the item unimbued. The new barrel can have its contents changed for reagent imbuing. add: A new reagent "smithing oil" which gives innate bonuses to any blacksmith equipment imbued using it. balance: Material types now affect the quality of resulting items and how quickly they can be forged. balance: The reagent smithing hammer now has increased toolspeed if forged well. balance: Reagent imbuing's durability penalty has been altered; it reduces durability for each time an imbue effect procs, and then stops functioning for such when below a certain durability percentage. /:cl: --------- Co-authored-by: Mathilde <146444134+ibexgoetia@users.noreply.github.com> Co-authored-by: The Sharkening <95130227+StrangeWeirdKitten@users.noreply.github.com> Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com> Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com> --- .../lavaland_surface_seed_vault_bubber.dmm | 2 +- .../lavaland_surface_ash_walker1_skyrat.dmm | 2 +- _maps/RandomZLevels/maintsroom.dmm | 54 +- .../iceboxstation_icecats_lower.dmm | 232 ++--- .../lavaland/lavaland_ashwalker_fortress.dmm | 9 +- .../lavaland/lavaland_ashwalker_nest.dmm | 2 +- .../templates/lavaland/lavaland_smithy.dmm | 2 +- _maps/map_files/biodome/biodome.dmm | 132 +-- _maps/map_files/generic/CentCom_skyrat_z2.dmm | 194 ++-- _maps/map_files/moonstation/moonstation.dmm | 974 +++++++++--------- .../~skyrat_defines/_globalvars/bitfields.dm | 4 - code/__DEFINES/~skyrat_defines/atom_hud.dm | 2 + code/__DEFINES/~skyrat_defines/obj_flags.dm | 3 - .../~~bubber_defines/traits/declarations.dm | 5 + code/_globalvars/traits/_traits.dm | 3 + code/datums/id_trim/jobs.dm | 1 + .../carbon/human/species_types/felinid.dm | 1 + .../human/species_types/lizardpeople.dm | 1 + .../carbon/human/species_types/podpeople.dm | 1 + .../items/stacks/sheets/sheet_types.dm | 13 +- .../master_files/code/game/objects/objs.dm | 3 - .../master_files/icons/mob/huds/hud.dmi | Bin 14474 -> 13584 bytes .../master_files/icons/obj/card.dmi | Bin 6094 -> 6731 bytes .../master_files/icons/obj/clothing/belts.dmi | Bin 9888 -> 8694 bytes .../ashwalkers/code/effects/ash_rituals.dm | 4 +- .../modules/borgs/code/robot_items.dm | 2 +- .../code/appliances/space_heater.dm | 27 +- .../modules/clothing/storage/belts.dm | 136 --- .../modules/goofsec/code/department_guards.dm | 1 + .../modules/modular_items/code/tailoring.dm | 9 - .../primitive_catgirls/code/clothing.dm | 4 +- .../modules/reagent_forging/code/_DEFINES.dm | 210 ++++ .../modules/reagent_forging/code/anvil.dm | 200 ++-- .../reagent_forging/code/cargo_orders.dm | 28 + .../reagent_forging/code/container_boxes.dm | 35 + .../reagent_forging/code/crafting_bench.dm | 475 ++++++--- .../code/crafting_bench_recipes.dm | 545 ++++++++-- .../modules/reagent_forging/code/forge.dm | 526 +++++----- .../reagent_forging/code/forge_belts.dm | 542 ++++++++++ .../reagent_forging/code/forge_clothing.dm | 193 ++-- .../reagent_forging/code/forge_equipment.dm | 60 ++ .../reagent_forging/code/forge_items.dm | 299 ++++-- .../reagent_forging/code/forge_revolver.dm | 225 ++++ .../code/forge_smithable_component.dm | 376 +++++++ .../reagent_forging/code/forge_weapons.dm | 391 +++++-- .../modules/reagent_forging/code/memories.dm | 125 +++ .../reagent_forging/code/mindless_killer.dm | 2 + .../reagent_forging/code/reagent_component.dm | 241 +++-- .../code/reagent_smithing_oil.dm | 16 + .../reagent_forging/code/smith_skill.dm | 2 +- .../code/smithing_trait_and_chip.dm | 21 + .../reagent_forging/code/water_basin.dm | 159 +-- .../icons/obj/forge_clothing.dmi | Bin 1863 -> 10407 bytes .../reagent_forging/icons/obj/forge_items.dmi | Bin 7282 -> 8770 bytes .../icons/obj/forge_structures.dmi | Bin 3464 -> 4395 bytes .../modules/reagent_forging/readme.md | 6 + .../lists/maintenance_loot_common.dm | 2 +- .../code/modules/cargo/bounties/blacksmith.dm | 37 +- .../goodies/company_imports/blacksteel.dm | 2 +- .../code/modules/clothing/outfits/outlast.dm | 4 +- .../code/modules/jobs/job_types/blacksmith.dm | 72 ++ .../code/modules/jobs/trims/jobs.dm | 9 +- modular_zubbers/icons/mob/huds/hud.dmi | Bin 15116 -> 14358 bytes tgstation.dme | 11 +- .../tgui/interfaces/Orbit/JobIcon.tsx | 1 + .../Scripts_Bubber/5360_smithing_repath.txt | 2 + 66 files changed, 4691 insertions(+), 1949 deletions(-) delete mode 100644 modular_skyrat/master_files/code/game/objects/objs.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/_DEFINES.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/cargo_orders.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/container_boxes.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/forge_belts.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/forge_equipment.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/forge_revolver.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/forge_smithable_component.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/memories.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/reagent_smithing_oil.dm create mode 100644 modular_skyrat/modules/reagent_forging/code/smithing_trait_and_chip.dm create mode 100644 tools/UpdatePaths/Scripts_Bubber/5360_smithing_repath.txt diff --git a/_maps/RandomRuins/LavaRuins/bubberstation/lavaland_surface_seed_vault_bubber.dmm b/_maps/RandomRuins/LavaRuins/bubberstation/lavaland_surface_seed_vault_bubber.dmm index 1f0ba31e595..1495c9a30dd 100644 --- a/_maps/RandomRuins/LavaRuins/bubberstation/lavaland_surface_seed_vault_bubber.dmm +++ b/_maps/RandomRuins/LavaRuins/bubberstation/lavaland_surface_seed_vault_bubber.dmm @@ -2068,7 +2068,7 @@ name = "Server Vent" }, /obj/effect/decal/cleanable/dirt, -/obj/structure/reagent_water_basin, +/obj/structure/reagent_dispensers/reagent_smithing_basin, /obj/effect/turf_decal/siding/wideplating_new/dark{ dir = 8 }, diff --git a/_maps/RandomRuins/LavaRuins/skyrat/lavaland_surface_ash_walker1_skyrat.dmm b/_maps/RandomRuins/LavaRuins/skyrat/lavaland_surface_ash_walker1_skyrat.dmm index 6d8c3d0ed0a..c538c60fba2 100644 --- a/_maps/RandomRuins/LavaRuins/skyrat/lavaland_surface_ash_walker1_skyrat.dmm +++ b/_maps/RandomRuins/LavaRuins/skyrat/lavaland_surface_ash_walker1_skyrat.dmm @@ -372,7 +372,7 @@ /turf/open/lava/smooth/lava_land_surface, /area/ruin/unpowered/ash_walkers) "nY" = ( -/obj/structure/reagent_water_basin, +/obj/structure/reagent_dispensers/reagent_smithing_basin, /obj/structure/stone_tile/slab, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/ruin/unpowered/ash_walkers) diff --git a/_maps/RandomZLevels/maintsroom.dmm b/_maps/RandomZLevels/maintsroom.dmm index 832600635d8..c1a3339127d 100644 --- a/_maps/RandomZLevels/maintsroom.dmm +++ b/_maps/RandomZLevels/maintsroom.dmm @@ -6234,22 +6234,6 @@ }, /turf/open/floor/plating/rust, /area/awaymission/caves/maintsroom) -"tX" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/structure/safe/floor, -/obj/item/forging/reagent_weapon/axe/fake_copper{ - desc = "Ea-nasir's own copper axe" - }, -/obj/item/claymore/weak/weaker, -/turf/open/floor/engine{ - slowdown = 2 - }, -/area/awaymission/caves/maintsroom) "tY" = ( /obj/effect/light_emitter/fake_outdoors, /obj/structure/spacevine, @@ -9845,6 +9829,22 @@ /obj/effect/spawner/random/structure/chair_flipped, /turf/open/floor/circuit/red/anim, /area/awaymission/caves/maintsroom) +"Gb" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/structure/safe/floor, +/obj/item/melee/forged_reagent_weapon/axe/fake_copper{ + desc = "Ea-nasir's own copper axe" + }, +/obj/item/claymore/weak/weaker, +/turf/open/floor/engine{ + slowdown = 2 + }, +/area/awaymission/caves/maintsroom) "Gc" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -12447,6 +12447,15 @@ /obj/structure/mecha_wreckage/odysseus, /turf/open/floor/circuit/green, /area/awaymission/caves/maintsroom) +"OB" = ( +/obj/structure/lattice/catwalk/mining, +/obj/item/melee/forged_reagent_weapon/sword, +/obj/structure/closet/crate/goldcrate{ + name = "Bait" + }, +/obj/item/disk/design_disk/cleric_mace, +/turf/open/floor/plating, +/area/awaymission/caves/maintsroom) "OC" = ( /obj/structure/disposalpipe/segment{ dir = 8 @@ -12907,15 +12916,6 @@ /obj/structure/grille/broken, /turf/open/floor/iron/smooth, /area/awaymission/caves/maintsroom) -"Qi" = ( -/obj/structure/lattice/catwalk/mining, -/obj/item/forging/reagent_weapon/sword, -/obj/structure/closet/crate/goldcrate{ - name = "Bait" - }, -/obj/item/disk/design_disk/cleric_mace, -/turf/open/floor/plating, -/area/awaymission/caves/maintsroom) "Qj" = ( /obj/structure/table/optable/abductor, /turf/open/floor/plating/abductor, @@ -17188,7 +17188,7 @@ ov ov ov TM -Qi +OB TA ov ov @@ -40731,7 +40731,7 @@ PR PR PR PR -tX +Gb MD rW gW diff --git a/_maps/bubber/automapper/templates/IceBoxStation/iceboxstation_icecats_lower.dmm b/_maps/bubber/automapper/templates/IceBoxStation/iceboxstation_icecats_lower.dmm index 786351ecbe0..4d41238ce0b 100644 --- a/_maps/bubber/automapper/templates/IceBoxStation/iceboxstation_icecats_lower.dmm +++ b/_maps/bubber/automapper/templates/IceBoxStation/iceboxstation_icecats_lower.dmm @@ -211,14 +211,6 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"eY" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/structure/water_source/puddle, -/obj/effect/turf_decal/weather/snow/corner, -/turf/open/misc/dirt/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "fd" = ( /obj/structure/wall_torch/spawns_lit/directional/south, /obj/effect/turf_decal/weather/snow/corner{ @@ -400,6 +392,23 @@ /obj/structure/fluff/minepost, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"jY" = ( +/obj/structure/closet/crate/wooden/storage_barrel, +/obj/structure/wall_torch/spawns_lit/directional/south, +/obj/item/seeds/cotton, +/obj/item/seeds/cotton, +/obj/item/seeds/tea{ + pixel_y = 1 + }, +/obj/item/seeds/tea{ + pixel_y = 1 + }, +/obj/item/seeds/tea/catnip, +/obj/item/seeds/bamboo, +/obj/item/seeds/ambrosia, +/obj/item/seeds/eggplant, +/turf/open/misc/dirt/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "ka" = ( /obj/effect/turf_decal/siding/wood/corner{ dir = 8 @@ -598,12 +607,13 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"nq" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 6 +"nx" = ( +/obj/effect/turf_decal/weather/snow/corner{ + dir = 5 + }, +/obj/effect/turf_decal/siding/wood/corner{ + dir = 8 }, -/obj/structure/reagent_water_basin, -/obj/structure/wall_torch/spawns_lit/directional/north, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) "op" = ( @@ -660,6 +670,14 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"oN" = ( +/obj/structure/table/wood, +/obj/structure/throwing_wheel{ + pixel_y = 10 + }, +/obj/structure/wall_torch/spawns_lit/directional/west, +/turf/open/floor/wood/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "oW" = ( /obj/effect/turf_decal/siding/wood, /obj/structure/wall_torch/spawns_lit/directional/north, @@ -774,6 +792,15 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"qB" = ( +/obj/effect/turf_decal/weather/snow/corner{ + dir = 4 + }, +/obj/effect/turf_decal/siding/wood{ + dir = 8 + }, +/turf/open/misc/dirt/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "qM" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -923,6 +950,14 @@ /obj/item/food/raw_headcheese, /turf/open/floor/stone/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"uu" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 6 + }, +/obj/structure/wall_torch/spawns_lit/directional/north, +/obj/structure/reagent_dispensers/reagent_smithing_basin/prefilled, +/turf/open/misc/dirt/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "uH" = ( /obj/structure/stairs/wood{ dir = 4 @@ -951,14 +986,6 @@ /obj/item/seeds/peas, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"vr" = ( -/obj/structure/table/wood, -/obj/structure/throwing_wheel{ - pixel_y = 10 - }, -/obj/structure/wall_torch/spawns_lit/directional/west, -/turf/open/floor/wood/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "vx" = ( /obj/effect/turf_decal/weather/dirt{ dir = 1 @@ -990,23 +1017,6 @@ }, /turf/open/misc/asteroid/snow/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"wt" = ( -/obj/structure/closet/crate/wooden/storage_barrel, -/obj/structure/wall_torch/spawns_lit/directional/south, -/obj/item/seeds/cotton, -/obj/item/seeds/cotton, -/obj/item/seeds/tea{ - pixel_y = 1 - }, -/obj/item/seeds/tea{ - pixel_y = 1 - }, -/obj/item/seeds/tea/catnip, -/obj/item/seeds/bamboo, -/obj/item/seeds/ambrosia, -/obj/item/seeds/eggplant, -/turf/open/misc/dirt/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "wF" = ( /obj/effect/turf_decal/weather/dirt{ dir = 8 @@ -1069,13 +1079,6 @@ /obj/structure/stairs/wood, /turf/open/floor/stone/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"yp" = ( -/obj/machinery/primitive_stove, -/obj/item/storage/bag/tray{ - pixel_y = 4 - }, -/turf/open/floor/stone/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "yD" = ( /obj/structure/flora/ash/chilly, /turf/open/misc/asteroid/snow/icemoon, @@ -1164,28 +1167,17 @@ /obj/structure/bonfire/grill_pre_attached, /turf/open/floor/stone/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"AV" = ( -/obj/structure/closet/crate/wooden/storage_barrel, -/obj/item/stack/ore/glass/thirty{ - pixel_y = 5; - amount = 50 - }, -/obj/effect/turf_decal/weather/snow/corner{ - dir = 10 - }, -/turf/open/misc/dirt/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "Bc" = ( /obj/structure/rack/wooden, -/obj/item/forging/reagent_weapon/axe/fake_copper{ +/obj/item/melee/forged_reagent_weapon/axe/fake_copper{ pixel_x = -8; pixel_y = -4 }, -/obj/item/forging/reagent_weapon/axe/fake_copper{ +/obj/item/melee/forged_reagent_weapon/axe/fake_copper{ pixel_x = -2; pixel_y = -4 }, -/obj/item/forging/reagent_weapon/axe/fake_copper{ +/obj/item/melee/forged_reagent_weapon/axe/fake_copper{ pixel_x = 4; pixel_y = -4 }, @@ -1239,7 +1231,7 @@ /obj/item/shield/goliath{ pixel_x = -6 }, -/obj/item/forging/reagent_weapon/axe/fake_copper{ +/obj/item/melee/forged_reagent_weapon/axe/fake_copper{ pixel_x = 4; pixel_y = -4 }, @@ -1277,35 +1269,22 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"Eu" = ( -/obj/effect/turf_decal/weather/snow/corner{ - dir = 4 - }, -/obj/effect/turf_decal/siding/wood{ - dir = 8 - }, -/turf/open/misc/dirt/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "EA" = ( /obj/structure/rack/wooden, /obj/item/gun/ballistic/bow/longbow, /obj/item/storage/bag/quiver, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"EU" = ( -/obj/structure/closet/crate/wooden{ - name = "glassblowing crate" +"Fn" = ( +/obj/structure/closet/crate/wooden/storage_barrel, +/obj/item/stack/ore/glass/thirty{ + pixel_y = 5; + amount = 50 }, -/obj/item/glassblowing/blowing_rod, -/obj/item/glassblowing/jacks{ - pixel_x = 6 +/obj/effect/turf_decal/weather/snow/corner{ + dir = 10 }, -/obj/item/glassblowing/paddle{ - pixel_y = 6 - }, -/obj/item/glassblowing/shears, -/obj/item/glassblowing/metal_cup, -/turf/open/misc/asteroid/snow/icemoon, +/turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) "FB" = ( /obj/structure/mannequin/wood, @@ -1446,6 +1425,14 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"Jb" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/structure/water_source/puddle, +/obj/effect/turf_decal/weather/snow/corner, +/turf/open/misc/dirt/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "Jl" = ( /obj/effect/turf_decal/weather/dirt{ dir = 8 @@ -1666,22 +1653,6 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"Pq" = ( -/obj/effect/turf_decal/siding/wood, -/obj/structure/rack/wooden, -/obj/item/stack/sheet/mineral/stone/five{ - pixel_y = -5; - pixel_x = -4 - }, -/obj/item/stack/sheet/mineral/stone/five{ - pixel_y = -5; - pixel_x = 7 - }, -/obj/effect/turf_decal/weather/snow/corner{ - dir = 1 - }, -/turf/open/misc/dirt/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "PB" = ( /obj/effect/turf_decal/weather/snow/corner{ dir = 6 @@ -1886,6 +1857,13 @@ }, /turf/open/floor/stone/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"TH" = ( +/obj/machinery/primitive_stove, +/obj/item/storage/bag/tray{ + pixel_y = 4 + }, +/turf/open/floor/stone/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "Ug" = ( /obj/effect/turf_decal/weather/snow/corner, /turf/open/misc/dirt/icemoon, @@ -1973,6 +1951,21 @@ /obj/structure/chair/wood, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"VS" = ( +/obj/structure/closet/crate/wooden{ + name = "glassblowing crate" + }, +/obj/item/glassblowing/blowing_rod, +/obj/item/glassblowing/jacks{ + pixel_x = 6 + }, +/obj/item/glassblowing/paddle{ + pixel_y = 6 + }, +/obj/item/glassblowing/shears, +/obj/item/glassblowing/metal_cup, +/turf/open/misc/asteroid/snow/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "VT" = ( /obj/structure/closet/crate/wooden/storage_barrel, /obj/item/ammo_casing/arrow/bone, @@ -1992,6 +1985,22 @@ /obj/machinery/smartfridge/drying/rack, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) +"Wv" = ( +/obj/effect/turf_decal/siding/wood, +/obj/structure/rack/wooden, +/obj/item/stack/sheet/mineral/stone/five{ + pixel_y = -5; + pixel_x = -4 + }, +/obj/item/stack/sheet/mineral/stone/five{ + pixel_y = -5; + pixel_x = 7 + }, +/obj/effect/turf_decal/weather/snow/corner{ + dir = 1 + }, +/turf/open/misc/dirt/icemoon, +/area/ruin/unpowered/primitive_catgirl_den) "WM" = ( /obj/structure/mineral_door/wood/large_gate{ dir = 8 @@ -2001,15 +2010,6 @@ }, /turf/open/misc/dirt/icemoon, /area/ruin/unpowered/primitive_catgirl_den) -"Xn" = ( -/obj/effect/turf_decal/weather/snow/corner{ - dir = 5 - }, -/obj/effect/turf_decal/siding/wood/corner{ - dir = 8 - }, -/turf/open/misc/dirt/icemoon, -/area/ruin/unpowered/primitive_catgirl_den) "Xv" = ( /obj/structure/rack/wooden, /obj/item/plate/blowing_glass{ @@ -2388,7 +2388,7 @@ Yj oJ UV ZJ -wt +jY yF yF oJ @@ -3417,9 +3417,9 @@ Rq yF GN GN -Pq -vr -eY +Wv +oN +Jb GN pa hA @@ -3459,10 +3459,10 @@ jN GN GN GN -Xn -Eu +nx +qB aK -AV +Fn pa kF ZJ @@ -3501,7 +3501,7 @@ vZ pa GN yF -EU +VS yD JN zS @@ -3753,7 +3753,7 @@ pa qu oJ yF -nq +uu bT su cs @@ -3802,7 +3802,7 @@ mh pa ua GU -yp +TH su vx AG diff --git a/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_fortress.dmm b/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_fortress.dmm index 328f93ba40c..090ddeffc89 100644 --- a/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_fortress.dmm +++ b/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_fortress.dmm @@ -1630,14 +1630,13 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/ruin/unpowered/ash_walkers) "xb" = ( -/obj/item/forging/reagent_weapon/bokken, -/obj/item/forging/reagent_weapon/bokken{ +/obj/item/melee/forged_reagent_weapon/bokken{ pixel_x = -7 }, -/obj/item/forging/reagent_weapon/bokken{ +/obj/item/melee/forged_reagent_weapon/bokken{ pixel_x = 14 }, -/obj/item/forging/reagent_weapon/bokken{ +/obj/item/melee/forged_reagent_weapon/bokken{ pixel_x = 7 }, /obj/structure/rack/wooden, @@ -1697,8 +1696,8 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/ruin/unpowered/ash_walkers) "xF" = ( -/obj/structure/reagent_water_basin, /obj/effect/decal/cleanable/dirt, +/obj/structure/reagent_dispensers/reagent_smithing_basin/prefilled, /turf/open/floor/stone/lavaland, /area/ruin/unpowered/ash_walkers) "xW" = ( diff --git a/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_nest.dmm b/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_nest.dmm index 3772341e510..f27ba83388c 100644 --- a/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_nest.dmm +++ b/_maps/bubber/automapper/templates/lavaland/lavaland_ashwalker_nest.dmm @@ -519,7 +519,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/ruin/unpowered/ash_walkers) "sD" = ( -/obj/structure/reagent_water_basin, +/obj/structure/reagent_dispensers/reagent_smithing_basin, /obj/structure/stone_tile/slab, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/ruin/unpowered/ash_walkers) diff --git a/_maps/bubber/automapper/templates/lavaland/lavaland_smithy.dmm b/_maps/bubber/automapper/templates/lavaland/lavaland_smithy.dmm index 8a2a232d427..b9e449924db 100644 --- a/_maps/bubber/automapper/templates/lavaland/lavaland_smithy.dmm +++ b/_maps/bubber/automapper/templates/lavaland/lavaland_smithy.dmm @@ -12,7 +12,7 @@ /area/mine/blacksmith) "l" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/reagent_water_basin, +/obj/structure/reagent_dispensers/reagent_smithing_basin, /turf/open/floor/stone, /area/mine/blacksmith) "n" = ( diff --git a/_maps/map_files/biodome/biodome.dmm b/_maps/map_files/biodome/biodome.dmm index 5da93c2522d..d4f3675fe84 100644 --- a/_maps/map_files/biodome/biodome.dmm +++ b/_maps/map_files/biodome/biodome.dmm @@ -5976,6 +5976,10 @@ }, /turf/open/floor/iron/grimy, /area/station/hallway/secondary/entry) +"cfV" = ( +/obj/structure/cable, +/turf/closed/wall/r_wall, +/area/station/ai/satellite/chamber) "cgh" = ( /obj/machinery/duct, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -10766,14 +10770,6 @@ }, /turf/open/floor/carpet/green, /area/station/command/heads_quarters/nt_rep) -"dPu" = ( -/obj/effect/turf_decal/siding/blue, -/obj/effect/turf_decal/siding/blue/corner{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/chamber) "dPD" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -12438,6 +12434,14 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/department/cargo) +"ets" = ( +/obj/effect/turf_decal/siding/blue, +/obj/effect/turf_decal/siding/blue/corner{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/chamber) "etu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/item/radio/intercom/directional/south, @@ -13719,16 +13723,6 @@ /obj/structure/filingcabinet, /turf/open/floor/iron/smooth_large, /area/station/security/detectives_office) -"ePG" = ( -/obj/machinery/power/apc/auto_name/directional/east, -/obj/structure/cable, -/obj/effect/turf_decal/siding/wood{ - dir = 6 - }, -/obj/structure/reagent_water_basin, -/obj/structure/wall_torch/spawns_lit/directional/south, -/turf/open/floor/stone, -/area/station/cargo/blacksmith) "ePS" = ( /obj/effect/turf_decal/trimline/dark_green/filled/line, /obj/effect/turf_decal/trimline/dark_green/filled/mid_joiner, @@ -16663,20 +16657,6 @@ /obj/structure/cable, /turf/closed/wall, /area/station/science/breakroom) -"fVI" = ( -/obj/item/kirbyplants/photosynthetic{ - pixel_y = 6 - }, -/obj/machinery/requests_console/directional/west{ - department = "AI Chamber"; - name = "AI Chamber Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/mapping_helpers/requests_console/information, -/obj/machinery/firealarm/directional/north, -/obj/structure/cable, -/turf/open/floor/circuit, -/area/station/ai/satellite/chamber) "fVJ" = ( /obj/machinery/door/airlock/engineering{ name = "Telecommunications" @@ -20258,6 +20238,20 @@ "hmV" = ( /turf/open/floor/wood, /area/station/service/abandoned_gambling_den) +"hna" = ( +/obj/item/kirbyplants/photosynthetic{ + pixel_y = 6 + }, +/obj/machinery/requests_console/directional/west{ + department = "AI Chamber"; + name = "AI Chamber Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/mapping_helpers/requests_console/information, +/obj/machinery/firealarm/directional/north, +/obj/structure/cable, +/turf/open/floor/circuit, +/area/station/ai/satellite/chamber) "hne" = ( /obj/structure/sign/painting/library_secure{ pixel_x = -32 @@ -25002,6 +24996,16 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/security/checkpoint/customs) +"iVu" = ( +/obj/machinery/holopad/secure, +/obj/effect/turf_decal/siding/blue/corner, +/obj/effect/turf_decal/siding/blue/corner{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/chamber) "iVz" = ( /obj/structure/table, /obj/item/screwdriver, @@ -30333,14 +30337,6 @@ /obj/structure/closet/crate, /turf/open/floor/iron, /area/station/cargo/storage) -"kJv" = ( -/obj/effect/turf_decal/siding/blue{ - dir = 8 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/chamber) "kJT" = ( /obj/item/paper_bin/bundlenatural, /turf/open/misc/asteroid/airless, @@ -34303,6 +34299,11 @@ /obj/effect/mapping_helpers/airlock/access/all/security/entrance, /turf/open/floor/iron/dark/textured, /area/station/security/brig/entrance) +"lZh" = ( +/obj/structure/cable, +/obj/machinery/power/smes/super/full, +/turf/open/floor/circuit, +/area/station/ai/satellite/chamber) "lZk" = ( /obj/effect/decal/cleanable/blood/gibs, /turf/open/floor/plating/reinforced, @@ -37263,11 +37264,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/lower) -"naC" = ( -/obj/structure/cable, -/obj/machinery/power/smes/super/full, -/turf/open/floor/circuit, -/area/station/ai/satellite/chamber) "naO" = ( /obj/structure/chair/sofa/bench/right{ dir = 4 @@ -50531,10 +50527,6 @@ /obj/item/folder/yellow, /turf/open/floor/iron/dark, /area/station/tcommsat/computer) -"rCO" = ( -/obj/structure/cable, -/turf/closed/wall/r_wall, -/area/station/ai/satellite/chamber) "rCP" = ( /obj/effect/turf_decal/siding/dark/corner{ dir = 1 @@ -58723,16 +58715,6 @@ }, /turf/open/floor/iron/white/smooth_large, /area/station/science/robotics/augments) -"ulw" = ( -/obj/machinery/holopad/secure, -/obj/effect/turf_decal/siding/blue/corner, -/obj/effect/turf_decal/siding/blue/corner{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/ai/satellite/chamber) "ulx" = ( /obj/structure/cable, /turf/open/floor/iron/dark, @@ -63994,6 +63976,16 @@ /obj/effect/spawner/random/structure/girder, /turf/open/floor/plating, /area/station/maintenance/aft) +"wha" = ( +/obj/structure/reagent_dispensers/reagent_smithing_basin, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/structure/cable, +/obj/effect/turf_decal/siding/wood{ + dir = 6 + }, +/obj/structure/wall_torch/spawns_lit/directional/south, +/turf/open/floor/stone, +/area/station/cargo/blacksmith) "whg" = ( /obj/effect/turf_decal/siding/brown{ dir = 8 @@ -64218,6 +64210,14 @@ /obj/machinery/holopad, /turf/open/misc/asteroid, /area/station/cargo/miningdock) +"wkU" = ( +/obj/effect/turf_decal/siding/blue{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, +/obj/structure/cable, +/turf/open/floor/iron/dark, +/area/station/ai/satellite/chamber) "wlo" = ( /obj/effect/turf_decal/tile/brown/half/contrasted{ dir = 4 @@ -156141,7 +156141,7 @@ oCa oCa oCa sjc -fVI +hna vtb qEP qEP @@ -156398,8 +156398,8 @@ mbR mbR iKu sjc -kJv -dPu +wkU +ets puM sjc sjc @@ -156656,11 +156656,11 @@ saz nId ddy yiK -ulw +iVu npE lRp -rCO -naC +cfV +lZh uio nKg uTZ @@ -180092,7 +180092,7 @@ uHg xfB nzM xyN -ePG +wha rPl bzD dSu diff --git a/_maps/map_files/generic/CentCom_skyrat_z2.dmm b/_maps/map_files/generic/CentCom_skyrat_z2.dmm index 0e10013dcb9..b76bed8870e 100644 --- a/_maps/map_files/generic/CentCom_skyrat_z2.dmm +++ b/_maps/map_files/generic/CentCom_skyrat_z2.dmm @@ -1106,6 +1106,17 @@ /obj/structure/flora/tree/jungle/small, /turf/open/misc/grass/planet, /area/centcom/holding/cafe/park) +"azJ" = ( +/obj/structure/closet/secure_closet/personal/cabinet, +/obj/item/clothing/neck/large_scarf/green, +/obj/item/clothing/head/hats/intern{ + pixel_y = 9 + }, +/obj/item/clothing/under/rank/centcom/intern{ + pixel_y = -3 + }, +/turf/open/floor/wood/large, +/area/centcom/central_command_areas/admin/interlink) "azO" = ( /obj/effect/turf_decal/siding/white/corner{ dir = 1 @@ -2034,17 +2045,6 @@ "aXG" = ( /turf/open/floor/iron/dark, /area/centcom/interlink) -"aXQ" = ( -/obj/structure/closet/secure_closet/personal/cabinet, -/obj/item/clothing/neck/large_scarf/green, -/obj/item/clothing/head/hats/intern{ - pixel_y = 9 - }, -/obj/item/clothing/under/rank/centcom/intern{ - pixel_y = -3 - }, -/turf/open/floor/wood/large, -/area/centcom/central_command_areas/admin/interlink) "aYc" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 8 @@ -2512,6 +2512,12 @@ /obj/machinery/bookbinder, /turf/open/floor/wood, /area/centcom/holding/cafe/station/service/library) +"bAh" = ( +/obj/machinery/door/poddoor/shutters/indestructible{ + id = "admin_shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/centcom/central_command_areas/admin/interlink) "bAr" = ( /obj/effect/turf_decal/siding/wood, /obj/item/flashlight/flare/candle/infinite{ @@ -5666,6 +5672,43 @@ }, /turf/open/floor/plating/abductor, /area/centcom/holding/cafe/dorms) +"eNt" = ( +/obj/structure/table/wood, +/obj/item/stamp/denied{ + pixel_x = 8; + pixel_y = 10 + }, +/obj/machinery/requests_console/auto_name/directional/north{ + pixel_x = 3; + pixel_y = 31 + }, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/mapping_helpers/requests_console/information, +/obj/machinery/button/door/directional/north{ + name = "Admin Shutter Control"; + id = "admin_shutters"; + pixel_x = -5; + pixel_y = 21 + }, +/obj/item/folder/blue{ + pixel_x = 1; + pixel_y = -3 + }, +/obj/item/folder/red{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/item/folder/yellow{ + pixel_x = -7; + pixel_y = -3 + }, +/obj/effect/mapping_helpers/requests_console/announcement, +/obj/item/stamp/granted{ + pixel_y = 10; + pixel_x = -4 + }, +/turf/open/floor/carpet/executive, +/area/centcom/central_command_areas/admin/interlink) "eNI" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 1 @@ -5690,10 +5733,6 @@ /obj/effect/decal/remains/human, /turf/open/misc/dirt/planet, /area/centcom/holding/cafe/ruin/xenonest) -"ePo" = ( -/obj/structure/reagent_water_basin, -/turf/open/misc/dirt/planet, -/area/centcom/holding/cafe/ruin/ash_walkers) "eRz" = ( /obj/effect/turf_decal/siding/white{ dir = 4 @@ -12970,43 +13009,6 @@ /obj/machinery/light/directional/south, /turf/open/floor/iron/white, /area/centcom/interlink/dorm_rooms) -"mEr" = ( -/obj/structure/table/wood, -/obj/item/stamp/denied{ - pixel_x = 8; - pixel_y = 10 - }, -/obj/machinery/requests_console/auto_name/directional/north{ - pixel_x = 3; - pixel_y = 31 - }, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/mapping_helpers/requests_console/information, -/obj/machinery/button/door/directional/north{ - name = "Admin Shutter Control"; - id = "admin_shutters"; - pixel_x = -5; - pixel_y = 21 - }, -/obj/item/folder/blue{ - pixel_x = 1; - pixel_y = -3 - }, -/obj/item/folder/red{ - pixel_x = -3; - pixel_y = -3 - }, -/obj/item/folder/yellow{ - pixel_x = -7; - pixel_y = -3 - }, -/obj/effect/mapping_helpers/requests_console/announcement, -/obj/item/stamp/granted{ - pixel_y = 10; - pixel_x = -4 - }, -/turf/open/floor/carpet/executive, -/area/centcom/central_command_areas/admin/interlink) "mEA" = ( /obj/structure/cable/layer3, /obj/machinery/light/dim/directional/north, @@ -13085,6 +13087,11 @@ /obj/structure/fans/tiny/invisible, /turf/open/water/beach, /area/centcom/holding/cafe/beach) +"mJM" = ( +/obj/structure/bed/double, +/obj/item/bedsheet/centcom/double, +/turf/open/floor/carpet/executive, +/area/centcom/central_command_areas/admin/interlink) "mKq" = ( /obj/structure/chair/sofa/corp{ dir = 1 @@ -14541,12 +14548,6 @@ /obj/structure/cable, /turf/open/indestructible/plating, /area/centcom/holding/cafe/station/maint) -"oeg" = ( -/obj/machinery/door/poddoor/shutters/indestructible{ - id = "admin_shutters" - }, -/turf/closed/indestructible/fakeglass, -/area/centcom/central_command_areas/admin/interlink) "ogq" = ( /obj/structure/chair/comfy/brown{ dir = 4 @@ -14610,6 +14611,19 @@ }, /turf/open/floor/carpet/black, /area/centcom/interlink) +"oke" = ( +/obj/structure/table/wood, +/obj/item/paper_bin{ + papertype = /obj/item/paper/paperslip/corporate; + pixel_x = 6; + pixel_y = 2 + }, +/obj/item/pen/fourcolor{ + pixel_x = -7; + pixel_y = 7 + }, +/turf/open/floor/carpet/executive, +/area/centcom/central_command_areas/admin/interlink) "olz" = ( /obj/structure/window/reinforced/plasma/plastitanium/indestructible, /turf/open/indestructible/hoteltile{ @@ -15780,14 +15794,6 @@ /obj/structure/bookcase/random/reference, /turf/open/indestructible/hotelwood, /area/centcom/holding/cafe/station/medical/psychology) -"pFm" = ( -/obj/structure/closet/secure_closet/personal/cabinet, -/obj/item/modular_computer/pda/clear, -/obj/item/paper_bin, -/obj/item/storage/toolbox/mechanical, -/obj/item/multitool, -/turf/open/floor/carpet/executive, -/area/centcom/central_command_areas/admin/interlink) "pFr" = ( /obj/machinery/door/airlock/security{ name = "Interlink Security Processing" @@ -16147,10 +16153,6 @@ }, /turf/open/floor/plating, /area/centcom/interlink) -"pVD" = ( -/obj/structure/dresser, -/turf/open/floor/carpet/executive, -/area/centcom/central_command_areas/admin/interlink) "pWH" = ( /obj/structure/showcase/fakeid{ dir = 4; @@ -20357,11 +20359,6 @@ }, /turf/open/floor/carpet/black, /area/centcom/interlink) -"uAY" = ( -/obj/structure/bed/double, -/obj/item/bedsheet/centcom/double, -/turf/open/floor/carpet/executive, -/area/centcom/central_command_areas/admin/interlink) "uCK" = ( /obj/structure/reagent_dispensers/watertank, /turf/open/indestructible/plating, @@ -22543,6 +22540,14 @@ }, /turf/open/floor/grass, /area/centcom/interlink) +"wKu" = ( +/obj/structure/closet/secure_closet/personal/cabinet, +/obj/item/modular_computer/pda/clear, +/obj/item/paper_bin, +/obj/item/storage/toolbox/mechanical, +/obj/item/multitool, +/turf/open/floor/carpet/executive, +/area/centcom/central_command_areas/admin/interlink) "wKG" = ( /obj/structure/railing/unbreakable{ dir = 10 @@ -22967,6 +22972,10 @@ /obj/effect/turf_decal/siding/thinplating_new, /turf/open/floor/iron, /area/centcom/interlink) +"xoU" = ( +/obj/structure/dresser, +/turf/open/floor/carpet/executive, +/area/centcom/central_command_areas/admin/interlink) "xpM" = ( /obj/structure/stone_tile/center, /obj/structure/stone_tile/surrounding_tile{ @@ -23081,19 +23090,6 @@ /obj/effect/turf_decal/siding/wood, /turf/open/floor/wood/tile, /area/centcom/interlink) -"xve" = ( -/obj/structure/table/wood, -/obj/item/paper_bin{ - papertype = /obj/item/paper/paperslip/corporate; - pixel_x = 6; - pixel_y = 2 - }, -/obj/item/pen/fourcolor{ - pixel_x = -7; - pixel_y = 7 - }, -/turf/open/floor/carpet/executive, -/area/centcom/central_command_areas/admin/interlink) "xvu" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/indestructible/hoteltile{ @@ -42355,9 +42351,9 @@ rPg tvw lEu lEu -oeg +bAh dgK -oeg +bAh lEu lEu fTE @@ -42616,7 +42612,7 @@ rxZ rxZ rhf mSx -oeg +bAh vat vat vat @@ -42873,7 +42869,7 @@ hbe hbe hbe hYD -oeg +bAh vat vat fVr @@ -43897,7 +43893,7 @@ rbr tvw lEu lEu -mEr +eNt luM kHQ cAd @@ -44156,13 +44152,13 @@ aaa lEu fSE apw -xve +oke gpr lEu anH wUC -pFm -pVD +wKu +xoU lEu aaa vLx @@ -44673,10 +44669,10 @@ ycX iFA rPG lEu -aXQ +azJ fIk cAd -uAY +mJM lEu aaa vLx @@ -81142,7 +81138,7 @@ uMS sUu sUu nXK -ePo +nXK nXK nXK sUu diff --git a/_maps/map_files/moonstation/moonstation.dmm b/_maps/map_files/moonstation/moonstation.dmm index 3530abd8e36..bc6752b4f8c 100644 --- a/_maps/map_files/moonstation/moonstation.dmm +++ b/_maps/map_files/moonstation/moonstation.dmm @@ -147,13 +147,6 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) -"acj" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/air_sensor/engine_chamber, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "acp" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/table, @@ -755,6 +748,13 @@ /obj/structure/railing, /turf/open/floor/catwalk_floor/rust/moonstation, /area/moonstation/surface) +"ajy" = ( +/obj/machinery/door/poddoor/shutters/radiation/preopen{ + id = "engsm"; + name = "Radiation Chamber Shutters" + }, +/turf/open/floor/plating, +/area/station/engineering/supermatter/engine) "ajD" = ( /obj/effect/spawner/random/burgerstation/atmos, /turf/open/floor/plating, @@ -1752,21 +1752,6 @@ /obj/structure/sign/warning/directional/west, /turf/open/floor/plating/lavaland_atmos, /area/lavaland/surface/outdoors) -"avN" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 8 - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/general, -/obj/effect/mapping_helpers/airlock/locked, -/obj/machinery/door/airlock/engineering/glass/critical{ - heat_proof = 1; - name = "Supermatter Chamber"; - id_tag = "sm_west" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, -/obj/machinery/meter, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "avP" = ( /obj/effect/turf_decal/stripes/asteroid/line{ dir = 9 @@ -3408,6 +3393,11 @@ /obj/structure/chair/comfy, /turf/open/floor/carpet, /area/station/security/processing) +"aPP" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/obj/machinery/atmospherics/components/unary/delam_scram/directional/east, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "aPS" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -5189,6 +5179,11 @@ /obj/item/binoculars, /turf/open/floor/plating, /area/station/maintenance/department/prison) +"bnO" = ( +/obj/structure/window/reinforced/plasma/spawner/directional/south, +/obj/machinery/power/energy_accumulator/grounding_rod/anchored, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "bnV" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/effect/turf_decal/tile/red/half/contrasted{ @@ -5505,6 +5500,10 @@ /obj/structure/ladder, /turf/open/floor/plastic, /area/station/commons/public_xenoarch) +"bsn" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "bsC" = ( /obj/structure/table, /obj/machinery/dish_drive/bullet{ @@ -6692,15 +6691,6 @@ }, /turf/open/lava/smooth/lava_land_surface, /area/ruin/unpowered/ash_walkers) -"bJg" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 4 - }, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "bJj" = ( /obj/machinery/atmospherics/pipe/smart/simple/green/visible, /obj/effect/turf_decal/tile/blue/half/contrasted, @@ -9795,6 +9785,17 @@ }, /turf/open/floor/iron/white, /area/station/medical/medbay/lobby) +"cAX" = ( +/obj/machinery/navbeacon{ + codes_txt = "delivery;dir=8"; + location = "QM #1"; + name = "QM #1" + }, +/mob/living/basic/bot/mulebot, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/brown/anticorner/contrasted, +/turf/open/floor/iron, +/area/station/cargo/storage) "cAY" = ( /obj/effect/turf_decal/siding/dark{ dir = 4 @@ -10849,6 +10850,10 @@ /obj/structure/extinguisher_cabinet/directional/north, /turf/open/floor/wood, /area/station/service/chapel/office) +"cOX" = ( +/obj/effect/spawner/structure/window/reinforced/plasma, +/turf/open/floor/plating, +/area/station/engineering/supermatter/engine) "cPc" = ( /obj/effect/turf_decal/stripes/asteroid/line{ dir = 5 @@ -12384,13 +12389,6 @@ /obj/effect/decal/cleanable/cobweb/cobweb2, /turf/open/floor/wood, /area/station/maintenance/rus_gambling) -"dig" = ( -/obj/machinery/door/poddoor/shutters/radiation/preopen{ - id = "engsm"; - name = "Radiation Chamber Shutters" - }, -/turf/open/floor/plating, -/area/station/engineering/supermatter/engine) "dii" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -13353,6 +13351,14 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron/dark, /area/station/command/gateway) +"duq" = ( +/obj/machinery/door/poddoor/shutters/radiation/preopen{ + id = "engsm"; + name = "Radiation Chamber Shutters" + }, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/engineering/supermatter/engine) "dux" = ( /obj/machinery/light/floor, /turf/open/floor/plating, @@ -13950,6 +13956,15 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/primary/central/fore) +"dBJ" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "dBM" = ( /obj/effect/turf_decal/vg_decals/numbers/two, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -14710,6 +14725,20 @@ /obj/structure/extinguisher_cabinet/directional/west, /turf/open/floor/iron/dark, /area/station/security/checkpoint/science) +"dMT" = ( +/obj/machinery/navbeacon{ + codes_txt = "delivery;dir=8"; + location = "QM #3"; + name = "QM #3" + }, +/mob/living/basic/bot/mulebot, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/brown/half/contrasted{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt/dust, +/turf/open/floor/iron, +/area/station/cargo/storage) "dMY" = ( /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/structure/reagent_dispensers/plumbed{ @@ -16438,6 +16467,15 @@ }, /turf/open/floor/engine, /area/station/engineering/atmos/hfr_room) +"ekD" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "ekE" = ( /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 8 @@ -16664,6 +16702,15 @@ /obj/machinery/camera/autoname/toxins/directional/west, /turf/open/floor/iron/dark, /area/station/science/ordnance/testlab) +"emM" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "emO" = ( /obj/structure/closet/crate/hydroponics, /obj/effect/spawner/random/food_or_drink/seed, @@ -19057,25 +19104,6 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/plating, /area/station/maintenance/abandon_exam) -"eTC" = ( -/obj/machinery/airalarm/directional/north, -/obj/effect/mapping_helpers/airalarm/engine_access, -/obj/machinery/button/door/directional/west{ - id = "sm_east"; - name = "Supermatter Chamber Bolt Control"; - normaldoorcontrol = 1; - specialfunctions = 4; - req_access = list("engineering") - }, -/obj/effect/turf_decal/stripes/end{ - dir = 1 - }, -/obj/effect/mapping_helpers/airalarm/link{ - chamber_id = "engine" - }, -/obj/effect/mapping_helpers/airalarm/tlv_no_checks, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "eTH" = ( /obj/structure/cable, /obj/machinery/atmospherics/components/binary/pump{ @@ -20648,12 +20676,6 @@ /obj/structure/fluff/fokoff_sign, /turf/open/floor/fake_snow/safe, /area/station/science/ordnance) -"foh" = ( -/obj/structure/window/reinforced/plasma/spawner/directional/south, -/obj/machinery/power/energy_accumulator/tesla_coil/anchored, -/obj/structure/cable, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "foj" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 8 @@ -21067,6 +21089,17 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/aft) +"ftr" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/obj/machinery/meter, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "ftJ" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -22689,22 +22722,6 @@ }, /turf/open/floor/engine, /area/station/science/ordnance/burnchamber) -"fMW" = ( -/obj/structure/table/reinforced, -/obj/machinery/button/door/directional/west{ - id = "ce_officer_shutters"; - name = "Privacy Shutters Control"; - req_access = list("ce") - }, -/obj/machinery/requests_console/auto_name/directional/north, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/mapping_helpers/requests_console/supplies, -/obj/effect/mapping_helpers/requests_console/information, -/obj/effect/mapping_helpers/requests_console/announcement, -/obj/effect/turf_decal/tile/neutral, -/obj/machinery/fax/heads/ce, -/turf/open/floor/iron, -/area/station/command/heads_quarters/ce) "fNd" = ( /obj/effect/turf_decal/tile/dark_blue/opposingcorners{ dir = 8 @@ -23496,14 +23513,6 @@ }, /turf/open/floor/iron/smooth, /area/mine/laborcamp/quarters) -"fWW" = ( -/obj/machinery/light/small/red/directional/west, -/obj/structure/sign/warning/radiation/directional/west, -/obj/structure/window/reinforced/plasma/spawner/directional/south, -/obj/machinery/power/energy_accumulator/tesla_coil/anchored, -/obj/structure/cable, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "fXn" = ( /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 4 @@ -28402,6 +28411,10 @@ /obj/machinery/fax/auto_name, /turf/open/floor/wood, /area/station/command/heads_quarters/blueshield) +"hnF" = ( +/obj/effect/turf_decal/stripes/line, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "hnG" = ( /obj/effect/turf_decal/tile/yellow/half/contrasted{ dir = 8 @@ -29348,6 +29361,15 @@ }, /turf/open/floor/engine/co2, /area/station/engineering/atmos) +"hAk" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 4 + }, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "hAr" = ( /obj/structure/tank_dispenser/oxygen, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -36954,6 +36976,20 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/dark, /turf/open/floor/catwalk_floor/iron_dark, /area/station/maintenance/starboard/aft) +"juU" = ( +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/general, +/obj/effect/mapping_helpers/airlock/locked, +/obj/machinery/door/airlock/engineering/glass/critical{ + heat_proof = 1; + name = "Supermatter Chamber"; + id_tag = "sm_east" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "juW" = ( /obj/effect/turf_decal/tile/yellow/anticorner/contrasted{ dir = 8 @@ -37272,6 +37308,13 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/maintenance/port) +"jzv" = ( +/obj/machinery/light/small/directional/south, +/obj/item/radio/intercom/directional/west, +/obj/effect/turf_decal/stripes/end, +/obj/machinery/camera/autoname/engineering/directional/south, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "jzL" = ( /turf/open/floor/glass/reinforced/scrape_below, /area/station/hallway/primary/port) @@ -38273,11 +38316,6 @@ /obj/effect/mapping_helpers/requests_console/supplies, /turf/open/floor/wood/parquet, /area/station/command/heads_quarters/rd) -"jOr" = ( -/obj/effect/turf_decal/stripes/full, -/obj/machinery/power/supermatter_crystal/engine, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "jOC" = ( /obj/effect/turf_decal/bot, /obj/machinery/portable_atmospherics/canister/nitrogen, @@ -38585,11 +38623,6 @@ /obj/effect/decal/cleanable/cobweb, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"jSv" = ( -/obj/effect/spawner/structure/window/reinforced/plasma, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "jSz" = ( /turf/open/floor/stone, /area/station/service/hydroponics) @@ -39103,20 +39136,6 @@ /obj/item/flashlight/flare/candle/infinite, /turf/open/lava/smooth/lava_land_surface, /area/ruin/unpowered/ash_walkers) -"jZx" = ( -/mob/living/basic/bot/mulebot, -/obj/machinery/navbeacon{ - codes_txt = "delivery;dir=8"; - location = "QM #2"; - name = "QM #2" - }, -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt/dust, -/turf/open/floor/iron, -/area/station/cargo/storage) "jZJ" = ( /obj/effect/mapping_helpers/airlock/access/all/command/captain, /obj/machinery/door/airlock/captain{ @@ -41426,15 +41445,6 @@ /obj/structure/extinguisher_cabinet/directional/east, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/aft) -"kCy" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "kCz" = ( /obj/machinery/door/poddoor/shutters/window{ id = "armory"; @@ -43362,21 +43372,6 @@ "lbr" = ( /turf/closed/wall/mineral/wood, /area/station/hallway/secondary/service) -"lbw" = ( -/obj/structure/table/glass, -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 1 - }, -/obj/machinery/keycard_auth/wall_mounted/directional/north, -/obj/machinery/button/door/directional/north{ - id = "cmo_officer_shutters"; - name = "Privacy Shutters Control"; - pixel_y = 34; - req_access = list("cmo") - }, -/obj/machinery/fax/heads/cmo, -/turf/open/floor/iron/white, -/area/station/command/heads_quarters/cmo) "lbH" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 8 @@ -44366,6 +44361,15 @@ /obj/effect/landmark/navigate_destination/autoname, /turf/open/floor/plating, /area/station/engineering/supermatter/room) +"lnz" = ( +/obj/machinery/light/small/red/directional/east, +/obj/structure/sign/warning/radiation/directional/east, +/obj/structure/window/reinforced/plasma/spawner/directional/south, +/obj/machinery/power/energy_accumulator/tesla_coil/anchored, +/obj/structure/cable, +/obj/machinery/camera/autoname/engineering/directional/east, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "lnC" = ( /obj/machinery/porta_turret/ai, /turf/open/floor/circuit/red, @@ -45962,6 +45966,9 @@ }, /turf/open/floor/iron/checker, /area/station/hallway/primary/central/aft) +"lHI" = ( +/turf/closed/wall/r_wall, +/area/station/engineering/supermatter/engine) "lHP" = ( /obj/structure/table/wood, /obj/machinery/newscaster/directional/east, @@ -47531,6 +47538,11 @@ }, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/dorms) +"mbq" = ( +/obj/effect/spawner/structure/window/reinforced/plasma, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "mbr" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/effect/spawner/random/trash/box, @@ -47632,6 +47644,13 @@ /obj/structure/disposalpipe/trunk, /turf/open/floor/iron/showroomfloor, /area/station/terminal/cryo) +"mbV" = ( +/obj/machinery/light/small/directional/south, +/obj/item/radio/intercom/directional/east, +/obj/effect/turf_decal/stripes/end, +/obj/machinery/camera/autoname/engineering/directional/south, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "mcd" = ( /obj/machinery/meter, /obj/machinery/atmospherics/pipe/smart/manifold/general/visible{ @@ -47666,6 +47685,12 @@ /obj/structure/chair/office/light, /turf/open/floor/plastic, /area/station/commons/public_xenoarch) +"mcw" = ( +/obj/effect/turf_decal/tile/brown/half/contrasted, +/obj/structure/cable, +/obj/structure/reagent_dispensers/reagent_smithing_basin, +/turf/open/floor/iron/checker, +/area/station/cargo/blacksmith) "mcx" = ( /obj/effect/turf_decal/tile/dark_blue/half/contrasted{ dir = 8 @@ -47785,19 +47810,6 @@ /obj/machinery/autolathe, /turf/open/floor/iron, /area/station/cargo/office) -"met" = ( -/obj/structure/table/wood, -/obj/structure/cable, -/obj/machinery/fax/heads/hos, -/turf/open/floor/wood, -/area/station/command/heads_quarters/hos) -"meB" = ( -/obj/machinery/light/small/directional/south, -/obj/item/radio/intercom/directional/west, -/obj/effect/turf_decal/stripes/end, -/obj/machinery/camera/autoname/engineering/directional/south, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "meD" = ( /obj/effect/turf_decal/siding/yellow{ dir = 1 @@ -47927,20 +47939,6 @@ "mfS" = ( /turf/open/floor/iron/dark, /area/station/hallway/primary/central/aft) -"mfT" = ( -/obj/effect/turf_decal/siding/dark{ - dir = 9 - }, -/obj/structure/table/wood, -/obj/machinery/button/door/directional/north{ - id = "qm_privacy_shutters"; - name = "Privacy Shutters Control"; - req_access = list("qm") - }, -/obj/machinery/firealarm/directional/west, -/obj/machinery/fax/heads/qm, -/turf/open/floor/mineral/gold, -/area/station/command/heads_quarters/qm) "mga" = ( /obj/structure/closet/emcloset/anchored, /turf/open/floor/plating, @@ -48884,20 +48882,6 @@ /obj/machinery/light_switch/directional/east, /turf/open/floor/iron/dark, /area/station/security/corrections_officer) -"mti" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 4 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) -"mtp" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/obj/machinery/atmospherics/components/unary/delam_scram/directional/east, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "mtz" = ( /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 4 @@ -50747,17 +50731,6 @@ }, /turf/open/floor/iron, /area/station/engineering/storage_shared) -"mTq" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/obj/machinery/meter, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "mTr" = ( /obj/machinery/door/airlock/maintenance_hatch, /obj/effect/mapping_helpers/airlock/access/all/medical/surgery, @@ -53393,6 +53366,12 @@ "nFN" = ( /turf/closed/wall, /area/station/maintenance/department/medical/chemistry) +"nFO" = ( +/obj/structure/window/reinforced/plasma/spawner/directional/south, +/obj/machinery/power/energy_accumulator/tesla_coil/anchored, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "nFS" = ( /obj/effect/turf_decal/tile/green/half/contrasted, /obj/structure/disposalpipe/segment{ @@ -55065,15 +55044,6 @@ /obj/structure/extinguisher_cabinet/directional/north, /turf/open/floor/iron/dark, /area/station/commons/dorms) -"ocd" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 4 - }, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "ocg" = ( /obj/effect/turf_decal/tile/brown{ dir = 4 @@ -56632,6 +56602,21 @@ }, /turf/open/indestructible/boss, /area/lavaland/surface/outdoors) +"ouq" = ( +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/general, +/obj/effect/mapping_helpers/airlock/locked, +/obj/machinery/door/airlock/engineering/glass/critical{ + heat_proof = 1; + name = "Supermatter Chamber"; + id_tag = "sm_west" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, +/obj/machinery/meter, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "out" = ( /obj/structure/stone_tile/block{ dir = 1 @@ -58462,13 +58447,6 @@ /obj/effect/mapping_helpers/airalarm/tlv_no_checks, /turf/open/floor/circuit/telecomms, /area/station/ai/satellite/chamber) -"oUj" = ( -/obj/machinery/light/small/directional/south, -/obj/item/radio/intercom/directional/east, -/obj/effect/turf_decal/stripes/end, -/obj/machinery/camera/autoname/engineering/directional/south, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "oUn" = ( /obj/structure/table, /obj/item/radio/intercom, @@ -58484,6 +58462,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/captain) +"oUw" = ( +/obj/machinery/power/apc/auto_name/directional/east, +/obj/effect/mapping_helpers/apc/full_charge, +/obj/effect/mapping_helpers/apc/cell_10k, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "oUz" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -58873,6 +58858,14 @@ "oZt" = ( /turf/closed/wall/rust, /area/station/engineering/atmos) +"oZw" = ( +/obj/machinery/light/small/red/directional/west, +/obj/structure/sign/warning/radiation/directional/west, +/obj/structure/window/reinforced/plasma/spawner/directional/north, +/obj/machinery/power/energy_accumulator/grounding_rod/anchored, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "oZy" = ( /obj/effect/turf_decal/stripes/asteroid/line{ dir = 1 @@ -59279,6 +59272,13 @@ /obj/machinery/suit_storage_unit/atmos, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) +"pea" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/air_sensor/engine_chamber, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "peq" = ( /obj/structure/table, /obj/item/storage/toolbox/mechanical, @@ -59888,13 +59888,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/storage/eva) -"pkC" = ( -/obj/machinery/power/apc/auto_name/directional/east, -/obj/effect/mapping_helpers/apc/full_charge, -/obj/effect/mapping_helpers/apc/cell_10k, -/obj/structure/cable, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "pkE" = ( /obj/machinery/door/airlock/external{ name = "External Airlock" @@ -60297,6 +60290,13 @@ /obj/effect/turf_decal/tile/purple/half/contrasted, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/rd) +"ppD" = ( +/obj/effect/turf_decal/stripes/corner, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "ppH" = ( /obj/structure/table/wood, /obj/machinery/computer/security/telescreen/entertainment/directional/east, @@ -61705,12 +61705,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden, /turf/open/floor/plating, /area/station/maintenance/department/engine/atmos) -"pJo" = ( -/obj/structure/table/wood, -/obj/machinery/keycard_auth/wall_mounted/directional/west, -/obj/machinery/fax/heads/hop, -/turf/open/floor/wood/parquet, -/area/station/command/heads_quarters/hop) "pJr" = ( /obj/effect/turf_decal/tile/holiday/rainbow/half/contrasted{ dir = 4 @@ -62273,6 +62267,15 @@ }, /turf/open/floor/glass/reinforced/scrape_below, /area/station/hallway/secondary/exit/departure_lounge) +"pRf" = ( +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ + dir = 8 + }, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "pRm" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/machinery/power/apc/auto_name/directional/north, @@ -62358,15 +62361,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/purple/hidden/layer5, /turf/open/floor/plating, /area/station/maintenance/department/engine) -"pSI" = ( -/obj/machinery/light/small/red/directional/east, -/obj/structure/sign/warning/radiation/directional/east, -/obj/structure/window/reinforced/plasma/spawner/directional/south, -/obj/machinery/power/energy_accumulator/tesla_coil/anchored, -/obj/structure/cable, -/obj/machinery/camera/autoname/engineering/directional/east, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "pST" = ( /obj/effect/spawner/random/burgerstation/atmos, /turf/open/floor/plating, @@ -63472,6 +63466,25 @@ luminosity = 2 }, /area/station/command/vault) +"qfb" = ( +/obj/machinery/airalarm/directional/north, +/obj/effect/mapping_helpers/airalarm/engine_access, +/obj/machinery/button/door/directional/west{ + id = "sm_east"; + name = "Supermatter Chamber Bolt Control"; + normaldoorcontrol = 1; + specialfunctions = 4; + req_access = list("engineering") + }, +/obj/effect/turf_decal/stripes/end{ + dir = 1 + }, +/obj/effect/mapping_helpers/airalarm/link{ + chamber_id = "engine" + }, +/obj/effect/mapping_helpers/airalarm/tlv_no_checks, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "qfx" = ( /obj/effect/turf_decal/trimline/dark/filled/warning, /obj/structure/extinguisher_cabinet/directional/south, @@ -63936,10 +63949,6 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/plating, /area/station/terminal/interlink) -"qkO" = ( -/obj/effect/turf_decal/stripes/line, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "qkW" = ( /obj/structure/steps/directional/north, /turf/open/floor/plating, @@ -64857,6 +64866,14 @@ /obj/structure/extinguisher_cabinet/directional/east, /turf/open/floor/iron/dark/corner, /area/station/hallway/primary/aft) +"qyo" = ( +/obj/machinery/light/small/red/directional/west, +/obj/structure/sign/warning/radiation/directional/west, +/obj/structure/window/reinforced/plasma/spawner/directional/south, +/obj/machinery/power/energy_accumulator/tesla_coil/anchored, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "qys" = ( /obj/effect/turf_decal/tile/brown/half/contrasted{ dir = 8 @@ -66426,6 +66443,12 @@ }, /turf/open/lava/smooth/lava_land_surface, /area/lavaland/surface/outdoors) +"qSr" = ( +/obj/structure/table/wood, +/obj/item/radio/intercom/command/directional/west, +/obj/machinery/fax/heads/captain, +/turf/open/floor/iron/dark, +/area/station/command/heads_quarters/captain) "qSC" = ( /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 1 @@ -68386,6 +68409,18 @@ "rrl" = ( /turf/open/openspace, /area/station/service/hydroponics/upper) +"rrJ" = ( +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/machinery/door/airlock/engineering/glass/critical{ + heat_proof = 1; + name = "Supermatter Chamber" + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/general, +/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "rrU" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, /turf/open/floor/engine, @@ -68611,20 +68646,6 @@ }, /turf/open/floor/iron/dark, /area/station/security/eva) -"rvp" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/general, -/obj/effect/mapping_helpers/airlock/locked, -/obj/machinery/door/airlock/engineering/glass/critical{ - heat_proof = 1; - name = "Supermatter Chamber"; - id_tag = "sm_east" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "rvx" = ( /obj/effect/landmark/start/librarian, /turf/open/floor/carpet/red, @@ -68744,17 +68765,6 @@ /obj/machinery/light/small/directional/west, /turf/open/floor/engine, /area/station/science/ordnance/freezerchamber) -"rxi" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, -/obj/machinery/meter, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "rxl" = ( /obj/item/storage/bag/plants/portaseeder, /obj/item/plant_analyzer, @@ -69136,6 +69146,20 @@ /obj/machinery/light/directional/north, /turf/open/floor/wood, /area/station/commons/vacant_room/office) +"rCB" = ( +/obj/effect/turf_decal/siding/dark{ + dir = 9 + }, +/obj/structure/table/wood, +/obj/machinery/button/door/directional/north{ + id = "qm_privacy_shutters"; + name = "Privacy Shutters Control"; + req_access = list("qm") + }, +/obj/machinery/firealarm/directional/west, +/obj/machinery/fax/heads/qm, +/turf/open/floor/mineral/gold, +/area/station/command/heads_quarters/qm) "rCH" = ( /obj/machinery/atmospherics/pipe/multiz/scrubbers/visible/layer2{ dir = 1 @@ -69469,6 +69493,12 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/dorms) +"rHs" = ( +/obj/structure/table/glass/plasmaglass, +/obj/machinery/fax/heads/rd, +/obj/item/radio/intercom/directional/west, +/turf/open/floor/carpet/purple, +/area/station/command/heads_quarters/rd) "rHx" = ( /obj/effect/mapping_helpers/broken_floor, /obj/item/kirbyplants/random/dead, @@ -70173,6 +70203,12 @@ /obj/item/clothing/glasses/meson, /turf/open/floor/iron/dark, /area/station/engineering/main) +"rQL" = ( +/obj/structure/window/reinforced/plasma/spawner/directional/north, +/obj/machinery/power/energy_accumulator/tesla_coil/anchored, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "rQM" = ( /obj/structure/rack, /obj/item/gps, @@ -70587,10 +70623,6 @@ }, /turf/open/floor/iron/dark, /area/station/medical/surgery) -"rWv" = ( -/obj/effect/spawner/structure/window/reinforced/plasma, -/turf/open/floor/plating, -/area/station/engineering/supermatter/engine) "rWE" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/engine, @@ -72302,6 +72334,25 @@ /obj/item/toy/figure/miner, /turf/open/floor/iron/dark, /area/station/cargo/miningdock) +"sui" = ( +/obj/machinery/airalarm/directional/north, +/obj/effect/mapping_helpers/airalarm/engine_access, +/obj/machinery/button/door/directional/east{ + id = "sm_west"; + name = "Supermatter Chamber Bolt Control"; + normaldoorcontrol = 1; + specialfunctions = 4; + req_access = list("engineering") + }, +/obj/effect/turf_decal/stripes/end{ + dir = 1 + }, +/obj/effect/mapping_helpers/airalarm/link{ + chamber_id = "engine" + }, +/obj/effect/mapping_helpers/airalarm/tlv_no_checks, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "suj" = ( /obj/effect/spawner/random/maintenance, /obj/structure/cable, @@ -73368,6 +73419,18 @@ /obj/effect/spawner/random/trash/grille_or_waste, /turf/open/floor/plating, /area/station/maintenance/department/chapel) +"sKG" = ( +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 + }, +/obj/machinery/door/airlock/engineering/glass/critical{ + heat_proof = 1; + name = "Supermatter Chamber" + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/general, +/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "sKH" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -73420,14 +73483,6 @@ }, /turf/open/floor/iron/checker, /area/station/hallway/primary/aft) -"sLA" = ( -/obj/machinery/light/small/red/directional/east, -/obj/structure/sign/warning/radiation/directional/east, -/obj/structure/window/reinforced/plasma/spawner/directional/north, -/obj/machinery/power/energy_accumulator/grounding_rod/anchored, -/obj/structure/cable, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "sLE" = ( /obj/structure/sign/warning/directional/west, /turf/open/misc/moonstation_rock, @@ -73591,13 +73646,6 @@ }, /turf/open/floor/wood, /area/station/service/chapel) -"sNG" = ( -/obj/effect/turf_decal/stripes/corner, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "sNI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/tile/yellow{ @@ -75327,18 +75375,6 @@ }, /turf/open/floor/iron, /area/station/engineering/break_room) -"tlF" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 8 - }, -/obj/machinery/door/airlock/engineering/glass/critical{ - heat_proof = 1; - name = "Supermatter Chamber" - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/general, -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "tlH" = ( /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood, @@ -76053,10 +76089,6 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/general, /turf/open/floor/plating, /area/station/ai/satellite/maintenance) -"ttW" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "tug" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -76371,6 +76403,22 @@ /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron/dark, /area/station/science/server) +"tyy" = ( +/obj/structure/table/reinforced, +/obj/machinery/button/door/directional/west{ + id = "ce_officer_shutters"; + name = "Privacy Shutters Control"; + req_access = list("ce") + }, +/obj/machinery/requests_console/auto_name/directional/north, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/mapping_helpers/requests_console/supplies, +/obj/effect/mapping_helpers/requests_console/information, +/obj/effect/mapping_helpers/requests_console/announcement, +/obj/effect/turf_decal/tile/neutral, +/obj/machinery/fax/heads/ce, +/turf/open/floor/iron, +/area/station/command/heads_quarters/ce) "tyA" = ( /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/engine, @@ -77377,6 +77425,21 @@ /obj/structure/closet/toolcloset, /turf/open/floor/plating, /area/station/construction) +"tKP" = ( +/obj/structure/table/glass, +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 1 + }, +/obj/machinery/keycard_auth/wall_mounted/directional/north, +/obj/machinery/button/door/directional/north{ + id = "cmo_officer_shutters"; + name = "Privacy Shutters Control"; + pixel_y = 34; + req_access = list("cmo") + }, +/obj/machinery/fax/heads/cmo, +/turf/open/floor/iron/white, +/area/station/command/heads_quarters/cmo) "tKS" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -78263,6 +78326,20 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/dark, /area/station/commons/lounge) +"tWl" = ( +/mob/living/basic/bot/mulebot, +/obj/machinery/navbeacon{ + codes_txt = "delivery;dir=8"; + location = "QM #2"; + name = "QM #2" + }, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/brown/half/contrasted{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt/dust, +/turf/open/floor/iron, +/area/station/cargo/storage) "tWp" = ( /obj/machinery/door/airlock/maintenance_hatch, /obj/effect/mapping_helpers/airlock/access/all/medical/paramedic, @@ -78574,6 +78651,11 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"uax" = ( +/obj/effect/turf_decal/stripes/full, +/obj/machinery/power/supermatter_crystal/engine, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "uaI" = ( /obj/machinery/camera/autoname/directional/west, /obj/effect/turf_decal/stripes/line{ @@ -79750,11 +79832,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/department/engine/atmos) -"uqI" = ( -/obj/structure/window/reinforced/plasma/spawner/directional/south, -/obj/machinery/power/energy_accumulator/grounding_rod/anchored, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "uqS" = ( /obj/effect/spawner/structure/window/reinforced/plasma, /turf/open/floor/plating, @@ -79948,6 +80025,12 @@ /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, /turf/closed/wall/r_wall/rust, /area/station/science/ordnance/burnchamber) +"utx" = ( +/obj/structure/table/wood, +/obj/machinery/keycard_auth/wall_mounted/directional/west, +/obj/machinery/fax/heads/hop, +/turf/open/floor/wood/parquet, +/area/station/command/heads_quarters/hop) "utS" = ( /obj/effect/turf_decal/siding/wood, /obj/structure/table/wood, @@ -80178,6 +80261,10 @@ /obj/structure/closet/emcloset, /turf/open/floor/plating, /area/station/maintenance/department/chapel) +"uxC" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "uxH" = ( /obj/effect/turf_decal/tile/green/diagonal_centre, /obj/structure/cable, @@ -80273,6 +80360,12 @@ }, /turf/open/floor/iron/dark, /area/station/hallway/secondary/exit/departure_lounge) +"uyq" = ( +/obj/structure/table/wood, +/obj/structure/cable, +/obj/machinery/fax/heads/hos, +/turf/open/floor/wood, +/area/station/command/heads_quarters/hos) "uyA" = ( /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 1 @@ -80489,6 +80582,17 @@ }, /turf/open/floor/iron/dark, /area/station/science/ordnance/testlab) +"uAU" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, +/obj/machinery/meter, +/turf/open/floor/engine, +/area/station/engineering/supermatter/engine) "uAY" = ( /obj/effect/turf_decal/siding/dark_blue, /obj/effect/turf_decal/tile/dark_blue/opposingcorners{ @@ -80962,9 +81066,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/iron/smooth, /area/mine/laborcamp/production) -"uHs" = ( -/turf/closed/wall/r_wall, -/area/station/engineering/supermatter/engine) "uHy" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/directional/west, @@ -81119,25 +81220,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/department/chapel) -"uIZ" = ( -/obj/machinery/airalarm/directional/north, -/obj/effect/mapping_helpers/airalarm/engine_access, -/obj/machinery/button/door/directional/east{ - id = "sm_west"; - name = "Supermatter Chamber Bolt Control"; - normaldoorcontrol = 1; - specialfunctions = 4; - req_access = list("engineering") - }, -/obj/effect/turf_decal/stripes/end{ - dir = 1 - }, -/obj/effect/mapping_helpers/airalarm/link{ - chamber_id = "engine" - }, -/obj/effect/mapping_helpers/airalarm/tlv_no_checks, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "uJf" = ( /obj/machinery/mech_bay_recharge_port, /obj/effect/mapping_helpers/broken_machine, @@ -82500,10 +82582,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/plating, /area/station/maintenance/fore) -"vdZ" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "vef" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -82802,12 +82880,6 @@ "vih" = ( /turf/closed/wall/r_wall, /area/station/maintenance/department/engine) -"vik" = ( -/obj/structure/table/glass/plasmaglass, -/obj/machinery/fax/heads/rd, -/obj/item/radio/intercom/directional/west, -/turf/open/floor/carpet/purple, -/area/station/command/heads_quarters/rd) "vin" = ( /obj/machinery/atmospherics/pipe/smart/simple/supply/visible{ dir = 4 @@ -83698,14 +83770,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/control_center) -"vuw" = ( -/obj/machinery/door/poddoor/shutters/radiation/preopen{ - id = "engsm"; - name = "Radiation Chamber Shutters" - }, -/obj/structure/cable, -/turf/open/floor/plating, -/area/station/engineering/supermatter/engine) "vux" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -85699,17 +85763,6 @@ }, /turf/open/floor/iron/white, /area/station/security/medical) -"vUM" = ( -/obj/machinery/navbeacon{ - codes_txt = "delivery;dir=8"; - location = "QM #1"; - name = "QM #1" - }, -/mob/living/basic/bot/mulebot, -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/tile/brown/anticorner/contrasted, -/turf/open/floor/iron, -/area/station/cargo/storage) "vUN" = ( /obj/structure/table/wood, /obj/effect/landmark/start/hangover, @@ -87215,18 +87268,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/security/prison/upper) -"wph" = ( -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/machinery/door/airlock/engineering/glass/critical{ - heat_proof = 1; - name = "Supermatter Chamber" - }, -/obj/effect/mapping_helpers/airlock/access/all/engineering/general, -/obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "wpv" = ( /obj/machinery/holopad, /obj/structure/cable, @@ -87646,6 +87687,14 @@ /obj/item/skillchip/chefs_kiss, /turf/open/floor/iron/kitchen_coldroom/freezerfloor, /area/station/service/kitchen/coldroom) +"wvd" = ( +/obj/machinery/light/small/red/directional/east, +/obj/structure/sign/warning/radiation/directional/east, +/obj/structure/window/reinforced/plasma/spawner/directional/north, +/obj/machinery/power/energy_accumulator/grounding_rod/anchored, +/obj/structure/cable, +/turf/open/floor/catwalk_floor/iron_dark, +/area/station/engineering/supermatter/engine) "wvs" = ( /obj/machinery/washing_machine, /obj/effect/turf_decal/tile/neutral/opposingcorners, @@ -87778,14 +87827,6 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/science/robotics/mechbay) -"wxS" = ( -/obj/machinery/light/small/red/directional/west, -/obj/structure/sign/warning/radiation/directional/west, -/obj/structure/window/reinforced/plasma/spawner/directional/north, -/obj/machinery/power/energy_accumulator/grounding_rod/anchored, -/obj/structure/cable, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "wxU" = ( /obj/machinery/light/small/directional/west, /obj/machinery/button/door/directional/west{ @@ -88155,20 +88196,6 @@ /obj/effect/landmark/start/science_guard, /turf/open/floor/iron/dark, /area/station/security/checkpoint/science) -"wDe" = ( -/obj/machinery/navbeacon{ - codes_txt = "delivery;dir=8"; - location = "QM #3"; - name = "QM #3" - }, -/mob/living/basic/bot/mulebot, -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/tile/brown/half/contrasted{ - dir = 4 - }, -/obj/effect/decal/cleanable/dirt/dust, -/turf/open/floor/iron, -/area/station/cargo/storage) "wDl" = ( /obj/effect/spawner/structure/window/reinforced/plasma, /turf/open/floor/plating, @@ -88976,12 +89003,6 @@ /obj/machinery/portable_atmospherics/canister/nitrous_oxide, /turf/open/floor/iron/dark, /area/station/engineering/atmos/storage/gas) -"wOU" = ( -/obj/structure/window/reinforced/plasma/spawner/directional/north, -/obj/machinery/power/energy_accumulator/tesla_coil/anchored, -/obj/structure/cable, -/turf/open/floor/catwalk_floor/iron_dark, -/area/station/engineering/supermatter/engine) "wPk" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -92055,12 +92076,6 @@ /obj/effect/spawner/random/trash/graffiti, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"xDz" = ( -/obj/structure/table/wood, -/obj/item/radio/intercom/command/directional/west, -/obj/machinery/fax/heads/captain, -/turf/open/floor/iron/dark, -/area/station/command/heads_quarters/captain) "xDD" = ( /obj/effect/turf_decal/bot, /obj/machinery/portable_atmospherics/canister/miasma, @@ -92812,15 +92827,6 @@ /obj/effect/decal/cleanable/cobweb/cobweb2, /turf/open/floor/plating, /area/station/maintenance/department/public_mining) -"xOz" = ( -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 4 - }, -/turf/open/floor/engine, -/area/station/engineering/supermatter/engine) "xOJ" = ( /obj/structure/sign/warning/docking, /turf/closed/wall, @@ -93673,12 +93679,6 @@ }, /turf/open/floor/catwalk_floor/iron_dark, /area/station/maintenance/department/engine/atmos) -"xYe" = ( -/obj/effect/turf_decal/tile/brown/half/contrasted, -/obj/structure/cable, -/obj/structure/reagent_water_basin, -/turf/open/floor/iron/checker, -/area/station/cargo/blacksmith) "xYh" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light_switch/directional/west, @@ -246725,7 +246725,7 @@ lYr kCN nCa fAd -mfT +rCB aAE hsu bno @@ -248254,9 +248254,9 @@ tgm lge qWX iQk -wDe -jZx -vUM +dMT +tWl +cAX mQt rVk qRH @@ -250585,7 +250585,7 @@ tGu vJk vJk fhd -xYe +mcw rdH qVN tOR @@ -252560,7 +252560,7 @@ gBj xig fKe bGa -xDz +qSr gcI aoh mfG @@ -253445,7 +253445,7 @@ fxU giJ fxU tdD -pkC +oUw wtd elk gWw @@ -253698,11 +253698,11 @@ wtd wtd tiO dNU -uHs -uHs -wph -uHs -uHs +lHI +lHI +rrJ +lHI +lHI qqz ykI gWw @@ -253955,11 +253955,11 @@ wtd wtd xNF hBt -uHs -uIZ -rxi -oUj -uHs +lHI +sui +uAU +mbV +lHI eVl elk gWw @@ -254211,13 +254211,13 @@ qDR wtd wtd wtd -uHs -uHs -uHs -avN -uHs -uHs -uHs +lHI +lHI +lHI +ouq +lHI +lHI +lHI wtd gWw nur @@ -254438,7 +254438,7 @@ kzR rdH atd rdH -fMW +tyy wMc mEV deR @@ -254468,13 +254468,13 @@ uSo xLD wtd wtd -dig -fWW -vdZ -vdZ -vdZ -wxS -dig +ajy +qyo +uxC +uxC +uxC +oZw +ajy wtd gWw nur @@ -254725,13 +254725,13 @@ kZr wtd wtd sei -vuw -foh -sNG -kCy -mti -wOU -vuw +duq +nFO +ppD +ekD +pRf +rQL +duq dTF ryH mda @@ -254982,13 +254982,13 @@ kZr wtd nmH sei -uHs -uqI -qkO -jOr -acj -jSv -rWv +lHI +bnO +hnF +uax +pea +mbq +cOX nsx gWw aHl @@ -255239,13 +255239,13 @@ kZr wtd sei sei -vuw -foh -bJg -ocd -xOz -wOU -vuw +duq +nFO +emM +hAk +dBJ +rQL +duq hai srP cYu @@ -255496,13 +255496,13 @@ qaW qQW sei wtd -dig -pSI -mtp -ttW -ttW -sLA -dig +ajy +lnz +aPP +bsn +bsn +wvd +ajy sei gWw jqr @@ -255753,13 +255753,13 @@ qDR wtd sei wtd -uHs -uHs -uHs -rvp -uHs -uHs -uHs +lHI +lHI +lHI +juU +lHI +lHI +lHI sei gWw rWE @@ -256011,11 +256011,11 @@ wtd sei lYW nkI -uHs -eTC -mTq -meB -uHs +lHI +qfb +ftr +jzv +lHI vnb sei gWw @@ -256268,11 +256268,11 @@ wtd sei kAA jWl -uHs -uHs -tlF -uHs -uHs +lHI +lHI +sKG +lHI +lHI ofy sei gWw @@ -258211,7 +258211,7 @@ eEr hEi rzN vdv -pJo +utx oPE fJp mni @@ -267769,7 +267769,7 @@ xKu sUv jDL ogo -lbw +tKP txR elY cET @@ -270554,7 +270554,7 @@ stM stM stM eSx -met +uyq lLg lLg lLg @@ -275502,7 +275502,7 @@ dHE nMz jQe cJF -vik +rHs kOd lGH mUA diff --git a/code/__DEFINES/~skyrat_defines/_globalvars/bitfields.dm b/code/__DEFINES/~skyrat_defines/_globalvars/bitfields.dm index 878d62f86ed..79a71e70fa9 100644 --- a/code/__DEFINES/~skyrat_defines/_globalvars/bitfields.dm +++ b/code/__DEFINES/~skyrat_defines/_globalvars/bitfields.dm @@ -1,7 +1,3 @@ -DEFINE_BITFIELD(skyrat_obj_flags, list( - "ANVIL_REPAIR" = ANVIL_REPAIR, -)) - DEFINE_BITFIELD(chemical_flags_skyrat, list( "REAGENT_BLOOD_REGENERATING" = REAGENT_BLOOD_REGENERATING, )) diff --git a/code/__DEFINES/~skyrat_defines/atom_hud.dm b/code/__DEFINES/~skyrat_defines/atom_hud.dm index 9da86c17f4a..cfb8f434dea 100644 --- a/code/__DEFINES/~skyrat_defines/atom_hud.dm +++ b/code/__DEFINES/~skyrat_defines/atom_hud.dm @@ -10,6 +10,8 @@ //SR Nanotrasen #define SECHUD_BLUESHIELD "hudblueshield" #define SECHUD_NT_CONSULTANT "hudnanotrasenconsultant" +//blacksmith +#define SECHUD_BLACKSMITH "hudblacksmith" //SR DS2 #define SECHUD_DS2_DOCTOR "hud_ds2doctor" #define SECHUD_DS2_RESEARCHER "hud_ds2researcher" diff --git a/code/__DEFINES/~skyrat_defines/obj_flags.dm b/code/__DEFINES/~skyrat_defines/obj_flags.dm index a284b21fdc9..279a8637a03 100644 --- a/code/__DEFINES/~skyrat_defines/obj_flags.dm +++ b/code/__DEFINES/~skyrat_defines/obj_flags.dm @@ -2,6 +2,3 @@ #define GENITAL_NEVER_SHOW 1 #define GENITAL_HIDDEN_BY_CLOTHES 2 #define GENITAL_ALWAYS_SHOW 3 - -/// Whether something is repairable by the anvil -#define ANVIL_REPAIR (1<<0) diff --git a/code/__DEFINES/~~bubber_defines/traits/declarations.dm b/code/__DEFINES/~~bubber_defines/traits/declarations.dm index 996a3604681..60e61d96e5d 100644 --- a/code/__DEFINES/~~bubber_defines/traits/declarations.dm +++ b/code/__DEFINES/~~bubber_defines/traits/declarations.dm @@ -14,6 +14,11 @@ // For light weight borgs #define TRAIT_R_LIGHT_WEIGHT "lightweight_borg" +///Characters that know how to make advanced smithing equipment +#define TRAIT_KNOW_ADVANCED_SMITHING "know_advanced_smithing" +#define TRAIT_KNOW_GUNSMITHING "know_gunsmithing" +#define TRAIT_KNOW_CIRCUIT_SMITHING "know_circuit_smithing" + //For the hemophage quirk that allows hemophages to eat/drink normal food/drink. #define TRAIT_MASQUERADE_FOOD "masquerade_food" diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 9252fb0d123..281bdeb9684 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -385,6 +385,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_KNOCKEDOUT" = TRAIT_KNOCKEDOUT, "TRAIT_KNOW_ENGI_WIRES" = TRAIT_KNOW_ENGI_WIRES, "TRAIT_KNOW_ROBO_WIRES" = TRAIT_KNOW_ROBO_WIRES, + "TRAIT_KNOW_ADVANCED_SMITHING" = TRAIT_KNOW_ADVANCED_SMITHING, // BUBBER EDIT + "TRAIT_KNOW_GUNSMITHING" = TRAIT_KNOW_GUNSMITHING, // BUBBER EDIT + "TRAIT_KNOW_CIRCUIT_SMITHING" = TRAIT_KNOW_CIRCUIT_SMITHING, // BUBBER EDIT "TRAIT_LIGHTBULB_REMOVER" = TRAIT_LIGHTBULB_REMOVER, "TRAIT_LIGHT_DRINKER" = TRAIT_LIGHT_DRINKER, "TRAIT_LIGHT_STEP" = TRAIT_LIGHT_STEP, diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm index 9cb057e946e..306ec28398b 100644 --- a/code/datums/id_trim/jobs.dm +++ b/code/datums/id_trim/jobs.dm @@ -1181,6 +1181,7 @@ assignment = JOB_SECURITY_OFFICER_SUPPLY subdepartment_color = COLOR_CARGO_BROWN department_access = list( + ACCESS_BLACKSMITH, //BUBBER EDIT ACCESS_BIT_DEN, ACCESS_CARGO, ACCESS_MINING, diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 54af1a4852f..48198757ccc 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -17,6 +17,7 @@ TRAIT_HATED_BY_DOGS, TRAIT_USES_SKINTONES, TRAIT_WATER_HATER, + TRAIT_KNOW_ADVANCED_SMITHING, //BUBBER EDIT ) changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT species_language_holder = /datum/language_holder/felinid diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index 6bec6cf0ccd..0dadb131dd0 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -182,6 +182,7 @@ Lizard subspecies: ASHWALKERS inherent_traits = list( TRAIT_MUTANT_COLORS, TRAIT_VIRUSIMMUNE, + TRAIT_KNOW_ADVANCED_SMITHING, //BUBBER EDIT ) inherent_factions = list(FACTION_ASHWALKER) species_language_holder = /datum/language_holder/lizard/ash diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index 34afa3e4f74..28f61cb29a5 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -6,6 +6,7 @@ inherent_traits = list( TRAIT_MUTANT_COLORS, TRAIT_PLANT_SAFE, + TRAIT_KNOW_ADVANCED_SMITHING, //BUBBER EDIT ) mutant_organs = list( // /obj/item/organ/pod_hair = "None", // SKYRAT EDIT REMOVAL - Customization diff --git a/modular_skyrat/master_files/code/game/objects/items/stacks/sheets/sheet_types.dm b/modular_skyrat/master_files/code/game/objects/items/stacks/sheets/sheet_types.dm index 39c6a565224..46cf798d5e3 100644 --- a/modular_skyrat/master_files/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/modular_skyrat/master_files/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -30,7 +30,6 @@ GLOBAL_LIST_INIT(skyrat_metal_recipes, list( new/datum/stack_recipe("wrestling turnbuckle", /obj/structure/wrestling_corner, 3, time = 2.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_STRUCTURE), new/datum/stack_recipe("metal barricade", /obj/structure/deployable_barricade/metal, 2, time = 1 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_STRUCTURE), new/datum/stack_recipe("metal shelf", /obj/structure/rack/shelf, 1, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_STRUCTURE), - new/datum/stack_recipe("anvil", /obj/structure/reagent_anvil, 10, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), new/datum/stack_recipe("forge", /obj/structure/reagent_forge, 10, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), new/datum/stack_recipe("throwing wheel", /obj/structure/throwing_wheel, 10, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), )) @@ -71,7 +70,7 @@ GLOBAL_LIST_INIT(skyrat_rod_recipes, list( // Wood GLOBAL_LIST_INIT(skyrat_wood_recipes, list( - new/datum/stack_recipe("water basin", /obj/structure/reagent_water_basin, 5, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), + new/datum/stack_recipe("smithing quenching basin", /obj/structure/reagent_dispensers/reagent_smithing_basin, 5, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), new/datum/stack_recipe("forging work bench", /obj/structure/reagent_crafting_bench, 5, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), new/datum/stack_recipe("wooden half-barricade", /obj/structure/deployable_barricade/wooden, 5, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_STRUCTURE), new/datum/stack_recipe("sauna oven", /obj/structure/sauna_oven, 30, time = 1.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_ENTERTAINMENT), @@ -161,3 +160,13 @@ GLOBAL_LIST_INIT(skyrat_sand_recipes, list( /obj/item/stack/ore/glass/get_main_recipes() . = ..() . += GLOB.skyrat_sand_recipes + +// Plastitanium + +GLOBAL_LIST_INIT(skyrat_plastitanium_recipes, list( + new/datum/stack_recipe("anvil", /obj/structure/reagent_anvil, 10, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), +)) + +/obj/item/stack/sheet/mineral/plastitanium/get_main_recipes() + . = ..() + . += GLOB.skyrat_plastitanium_recipes diff --git a/modular_skyrat/master_files/code/game/objects/objs.dm b/modular_skyrat/master_files/code/game/objects/objs.dm deleted file mode 100644 index 24befc59031..00000000000 --- a/modular_skyrat/master_files/code/game/objects/objs.dm +++ /dev/null @@ -1,3 +0,0 @@ -/obj - ///the skyrat version of obj_flags, to prevent any potential future conflict - var/skyrat_obj_flags = null diff --git a/modular_skyrat/master_files/icons/mob/huds/hud.dmi b/modular_skyrat/master_files/icons/mob/huds/hud.dmi index be0ccdb31a8da546e11d7b7e8ddc1fab932bac91..8b04ebf7573d5c78d331e98e91257d3b1a6190c3 100644 GIT binary patch literal 13584 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 Md2qW#%kIVh03eo(D*ylh literal 14474 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

iw;eg*Z|)q3-n2ua1x^QYgu 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 literal 6094 zcmb7I2T)Vpw%!SeKu`fOqJSh6K|!ThP!SSTB#86?0#XEN3Ir68POwmvt_X;fA5@xj zr9+~0K@gE5U5YeANN9lsc+q+9-MRPOd*6F!&YW3i?>%ewto^NTeQSs7>1gai2q6Ff zyEJi^3;=+zO)$o_gZ=%bR-p#~^t3Pjn#U!(yS8_n+&r8Jt^jyLQjCjT2j%%vD23>_ z*A|a^DFbTZcnwDz&gTOePt~4E&k8->8}*E1JK%^=k8#YW%TVe1Z3pSFd^E|s?H zhLx%urYBzn*jcIiUoW)Ouj(ME5_#fJ+o9v>A7R;Xwe;E99UacDbWp_oyM~b&uOilau0I!xj zN3#!_qQ_*Y#ysbCjR+kwBnP$g=$*7Pa`3DiRduw~xGx9KJQRcK+PqUajyRvg?;M&wE=_&&QP=)kYwk`>w5MjCxm(ZrWcXOux$eP} z@-zDeV-0VGztrKbGa=pc@k#4Z$HU6^neVD2$(dy=uALVemnu9ImY^Pbgc8yw8lHCv zT0L9oi5+sk;@SO_;VK-#3w`hoWtwUXXl<&<&$hN5pb=Zm+NxGA1Q59}dTMuCfWj9Fpj-JLJF)nME%qy_9&P-BvgZbU4 z42RLD68Gt^9pO*odv~K_WZ%(9m){4mj3@Os)gn*j1~XbxZ)-PjN(&ULH6Lg_-2LX6 z;9_=dG(BUYPl605Y(1&5Ydp@I) z%G;OoLN6W7rH%=K#kzfdZM{d6@BYVU_kLBk>)4l+R-d4@|6C8)P8xJgO-fl?uj{N) zPUd!c-dj68DpK0f%6URw4W|^wx`ya)l!lsNFKJj=a@~jsPUjhmaIZs zcY@uQr?!L3YtdB;pJz6Zauy?7{dt{U`4sa*by!K#@n8w9V|jp!cs7tHJ-gyrNSdGO zDWy{e#$LO77Dm(yNp$IE>S$@!hdTj*frW`KHs9xJO{|m)Ec|2NT1{Eb9o`CG2?{se zT1Y{45l{!;l7hxpqA&N!nfhl<`5DGzHNxd)vk^p)C;)>j3(M>2s>VLF5;=qI+`3gT zv8J34I#m4RDWjvgotVYN2OD@vzW@uDYDNj?y$gy+V$8z`%u?2gxLeygdr0X;5oLYP z=9j#Tu-5Q&C)%2<$Iiml+ssvp*=SwB%KAZNub9LH!^(p6bTnmZsvLau2{aRSH9UbLF?MdGQXqf!cz`g;i z9;`{nNZ0YLVV``D^%HB2ktAl1`tpxKWt-m5Bd9lny7`%6wi{n3F(Od~Y1?TQnFjpO zL?xz^Z@K@jshbXx-$GtW)s2K~r?B)P*YJ>FBoRmcQ6D;3N2m|kl`56ar)O%KL|LWN z@94r`AxQ9;@0rqe4tDAZ$uE*;A5B|jc+MLeP)~d;qYl2Wjw-0Sb-t&M`hF-i+0VEA zbmdV=G_2^xu-Q3}Z0%@=s8It?IhPRwjR%i^O+2C0>YTfB3Q28#i`sTRR(t2RDDrYg zW@&-nsj9%~7lV#J9%jwFd5!r7M^DIEE(>e89RA)AZ32f?K^IRzHuv#KLmuAs>#FM% znthjg7H=||&V$7RcmSQ!+1a78_rR%hVvsN`;>dcX0uuYZKAtkp137sz?Mf}3^LrDa zzcfkb*1*dcP*ymAgG+=S!b4ugF@Aa)8vz+0tOv;!AkGBe`{qpfTJA>fx+z244vYBK z*VEfOXXs9m>kYR%ORDtcIjG7ylrnN+umoB$8xk&k^!mF=oFWpteI%&Mc4}&Bw);V0 zkeq!AI?qM4XDjnDy+g(w52;9E95jMt17|EeT?b@v7L2gm6UNmXL_PDMEv>B?O-(;% zO0oe=MNzl-Ehu1il~WPKr_R2dv$j`G)?M53r{0)z z_cdulVAaQ|o6`s(g*$Po(F(*f=}!Vi6 z2_+>PvReM!{}n4E_@d>yq7eQ(c{RYx7L*`jTHGP}kY~@A`v_aKTqxgl-^SO=Kbm+~ z*2U^WW|m5zd`g_*$wBQ%A|KhDzzW;3SKH`0BUlhDaEyt8JSOPPH@$c_Eg7{|Ye-0X z`6Y#T=j=!Th&KvK@60uBjvOt$dFjGE=%+rjNC+3Vb55Z0IRk=7#gaIULk^^ept-EA z^M zEq53G)si>$-S@PSG@}+@dsn`l&A8sKEV)XgR?-J{c&NHs|3HF zjM9*_UuOb0SL+qpc&dMX(GC?pQl2iWg7>@PQOgJjK444NI+qI_y87Fr2G2ci}J=0sN5ba=x=)G5-maSUH57f*!V_iNch9#_cd#_f6njwAC=&5B?3eQ z17N)q27oOZfc+d0K;F*uutdNSGC4Wv7#SHUR-N-{bFacw&8PlY z+@-wiGg}9e5{yKZnkNzzGAZvb1cXjaDR7zrI+^k&Fa^ZZHm{P={#$`OEmX!GRL$c9 z3&7gla)~L}@)GtDef&y14inSQqlR%#PC3S=ra7z+>0V1RYxPem z_=u&Qm@3laj_Tr2!Pe2&H(~kyEcm%Qjc;Ro{A4^1gNgbxjV15W(u|0o`kStKE#2qmp`Cd@2}ufsL+jOV+12+pt9e)h(cAjr;Gj4X(U{h&?$3~=A1UI0<5 zezr^#e#6zp*tiv6XIuIc8SLzSqE8B{Z!8zJ%QcEn{#Lw>)vmB3U6eFHq6t}Dhlb@` z4P08qlgoilT*EX0a`XB`&Fvr*luNQCG@(^)2WYIJBY=l1nrOWzn+dSf@c-0C=)@N4 z=7hFkpe@k1L3@+a6buqMev2sU-7vE7?;HaJ^lzb5^S@5~vt0Q*r2Jhv zMdSngrqvx~R+QCQ`>R8KlUk#J!ZdzN{=^^xJ@Qmz$X7A^DTzAOtxu81_&RNVbs9J; zU|+fAZJ$LG7+$bv&7#uwyF}=wuE*y^zqvBW*g<02QZlRpE@LUUS+ zSC+>$NEk5gJY={D3FDM}94g^Ub3Up3F!<38;gOWTP(g)VCv1OCc@<8eM3jyXFsewa zW-T?t^9-A<;uL9(wIACXB3wC9h2_D~qJ1(uMt`)?cy0-va`I8S$=qk9Xv7RZt+ZTp zo6@NdNBddFJ}i+=UHWEkUlHccrWr4zW<5wYrL)g4KZdQFeJ10o= z7W1;f=26cBIlmy0wWP$fr3U-gG<77}FDQ><*_<%+&f!Iwe2_8D32uEgFqn^M-YI>L z4!LFl&CUgrB{Q4qorLn?JVq0OjQjlIL8{e*y7hUZvOzhLk7UjqM4d_)l9GYMs}#*_ zPWhU`i?V^`^+06nq>>dhTZAK~6E}T&hugr9vP91&fdcVBJ{iz8*ydRR)$?SHQmeU&6WugGf%RTn(!oD z!oxnx1!`!*CH?%D@U#1|ii3uY*CB~{eEvLN)=BURipGOwAKT8t4MyZB!1d!n1#i0T zqRPqfEInwJI19Cmh}o{rVg>DZGjG}l;&vMAjSwNE8bX|n$17e{eQ zaI8c;XC0v@7mFhkAbVd199fCJ{!8nZuIxxKEHRU3jQPW@{gcDoob#x4pi9Z*>b!$& zFv(Ld*1y}%33dTEqzYJUEl;eJ6~-6Ne?8V6-=F5cXek4g4|4r0sF26hxzyRuShg^A z**0Ip_;kR?(50WVc|3vKrj=KfAz_mQq0{ZFyS@u6&Fx{RySjP|*RHH=l)N+(D@)=p zZin)<%EkGsH`=)1CpWwdD~I!W(YW7(LRAOF`5SI?h=V@<*6o(s^7$pCtZY&WjlL8uB@Xgr$YJgl;z;6Zspj)yF(8U0rT|thc%z#Z$m(*`Rn3vC z%LX@j3CPh36=d8#;?Z>hSC4F9gIhwRXO3WQ+&6}ViT1fB_r()uexwCq>b;ne)9+&o zk=UA%k@L|C;C388G3n+bA_A@2G-8sGfrwG5vG5<21rp(S4z?prBanmV5H$i~oP`P` z9Svgu%~NB`?PhK~9Q~A(sQ|%)-dDxox&|I3XoU2;29=IqjDUEn>_7{2N8za)VI~kR zHx%(yQ(vXR?sV&&WY&v{;``nx@Pq(qh&;P8SaxL7I$s9#z_bC)O@g)^^bx+Xe62N7 z8N%_H&IFS8Jm)WG+1pk6$}^LbXJaphF8fF$i46;i zR5^Li$iwWtC;T`h@j)8L#59?xzz9XN-`{cw_FE-k|FHePH{F>J02+`hHq`(hupZj_ zlZdWCe>{|9xBwJH45DD{OQ|Lb@9*@SSmlJkuz+_Ye+yMIw4^7$CB}1)U3{die?1a2 zJ3Sa$QGOBX7{rHV_lfn(N59z}WTkZ$3swlMWt5^J?=W0wH#Xog9_gF&N&(}kF#F>; zOAusnhgC9VF3@(-##ATYQm%}j>pF8@55$ITb{OHkG2Vy9pPacfa6p&1mx#Npuwn)t z6pRR_Sk?KTeN_JgEA@u<($8lr0MmiyK}$h|8hwLsY^}hmfO+-yY4cQi{_$bh@bi`CG@ zD_AFeNcQQ%PoE@)WkC`v97kr1r0<4qd|%QS49VeG9yfp?u`l~C*Yrnw>?7i=!;6LF zZJuAOG6HJ*IcM4v(t|}f^afhKvi6{pQmyt(BG7y;^(VPLmqCj%N+Uk)&;~Xs-AwSd z333x|{W78LzDF)NLu7))G-5XB5I0-reP65(NoSvslb3{^;{N7cdhEXg!QksJ$ z&4!UX(^d|UlS@hzdz*P7pcnfaENnf_hQ+@&wNn*O{j`sGz1Ala?uVwH8iGxpV2gY< zOQHf7*1rvX!UF?(JuISV_~P-Z{I zPZV^Ol$K<)X@CCwZZY+8)mGBonTCa=Zz{^syHEF&B?o#IJ=zz9DZcU}WyMMh%Vk|( z_1wSfEdxuRc>I-R3fGK zMCja{a0axcUG|M7M~FdvuQuh)vG+aCp@(NSJ>^INebbb(1MnCeyKAs5iPQ}<-hAh{ zRLZFFK8=En6+z`OV(&|hz{>k-#XC#B<*yCcVND1pyi_7d#UjWVJy`OZyM;@VHqzqm1NnL}=(Qz>2MB)h)I9GtJ1;1EJYgw1DI6uXUp1cRH*23~1A$OP<1 NQ(fm$!3FF4{{e`>>(T%K diff --git a/modular_skyrat/master_files/icons/obj/clothing/belts.dmi b/modular_skyrat/master_files/icons/obj/clothing/belts.dmi index 3f0c755739ef4a81a147a4923860500703f9883a..4470f293b1618ac4740e29cff03bc97cfdf4d913 100644 GIT binary patch literal 8694 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 SKILL_LEVEL_MASTER) +#define USER_CAN_SEE_SMITHING_INFO(user) (user.mind.get_skill_level(/datum/skill/smithing) >= SKILL_LEVEL_JOURNEYMAN) +/// Minimum and maximum force multiplier if a weapon contains incomplete parts +#define MIN_INCOMPLETE_FORGING_SCALING_PENALTY 0.1 +#define MAX_INCOMPLETE_FORGING_SCALING_PENALTY 0.5 +/// The maximum force that can be given to a weapon via perfect hits +#define MAX_PERFECT_FORCE_BONUS 3 +/// maximum force that can be given to a reagent staff via perfect hits +#define MAX_PERFECT_STAFF_INTEG_BONUS 20 +///amount of chems that can be stored into the result +#define MAX_PRE_IMBUE_STORAGE 60 +///amount of chems that the result reads as +#define DEFAULT_IMBUE_STORAGE 10 +#define REAGENT_CLOTHING_INJECT_AMOUNT 0.5 +#define REAGENT_WEAPON_INJECT_AMOUNT 4 +#define REAGENT_STAFF_INJECT_AMOUNT 10 +#define MAX_OIL_AP_AMOUNT 10 +#define PERFECT_ACCESSORY_DURABILITY_BONUS 50 +#define PERFECT_HANDCUFFS_UNLOCK_SPEED_BONUS 2 SECONDS + +#define MAX_QUENCH_HEAT 600 +#define HEAT_GIVEN_FROM_QUENCHING_METAL 300 +#define MIN_VOLUME_TO_QUENCH 300 +#define SMITHING_BASIN_HEATLOSS_COEFFICIENT 0.7 + +#define FORGE_EFFECT_FORCE "forge_effect_force" +#define FORGE_EFFECT_ARMORPEN "forge_effect_armorpen" +#define FORGE_EFFECT_ARMOR "forge_effect_armor" +#define FORGE_EFFECT_DURABILITY "forge_effect_durability" +#define FORGE_EFFECT_BLOCKCHANCE "forge_effect_block" +#define FORGE_EFFECT_REAGENT_INJECT "forge_effect_reagent_inject" +#define FORGE_EFFECT_TOOLSPEED "forge_effect_toolspeed" + +///helper function - given two ratios, a forge effect, an item, and a maximal value, it gives a new modifier and resets the old modifier to the item +/proc/give_added_modifying_effect_to_item(forge_effect, old_modifier, new_modifier, obj/item/item, max_effect) + switch(forge_effect) + if(FORGE_EFFECT_ARMOR) + var/datum/armor/indexed_armor + if(ispath(max_effect)) + indexed_armor = get_armor_by_type(max_effect) + else + indexed_armor = max_effect + + ///WHY does the tgstation implementation have 0-multipler return the source armor? and not return armor/none? + var/datum/armor/new_armor_modifier + if(new_modifier == 0) + new_armor_modifier = new /datum/armor/none + else + new_armor_modifier = indexed_armor.generate_new_with_multipliers(list(ARMOR_ALL = new_modifier)) + var/datum/armor/previous_armor_modifier + if(old_modifier == 0) + previous_armor_modifier = new /datum/armor/none + else + previous_armor_modifier = indexed_armor.generate_new_with_modifiers(list(ARMOR_ALL = new_modifier)) + + var/datum/armor/newarmor = item.get_armor() + newarmor = newarmor.add_other_armor(new_armor_modifier) + newarmor = newarmor.subtract_other_armor(previous_armor_modifier) + item.set_armor(newarmor) + if(FORGE_EFFECT_ARMORPEN) + var/new_armorpen_modifier = new_modifier * max_effect + var/previous_armorpen_modifier = old_modifier * max_effect + item.armour_penetration += new_armorpen_modifier - previous_armorpen_modifier + if(FORGE_EFFECT_BLOCKCHANCE) + var/new_blockchance_modifier = new_modifier * max_effect + var/previous_blockchance_modifier = old_modifier * max_effect + item.block_chance += new_blockchance_modifier - previous_blockchance_modifier + if(FORGE_EFFECT_DURABILITY) + var/new_durability_modifier = new_modifier * max_effect + var/previous_durability_modifier = old_modifier * max_effect + item.modify_max_integrity(item.max_integrity + new_durability_modifier - previous_durability_modifier) + if(FORGE_EFFECT_FORCE) + var/new_force_modifier = new_modifier * max_effect + var/previous_force_modifier = old_modifier * max_effect + item.force += new_force_modifier - previous_force_modifier + if(FORGE_EFFECT_TOOLSPEED) + var/new_toolspeed_modifier = new_modifier * max_effect + var/previous_toolspeed_modifier = old_modifier * max_effect + item.toolspeed += new_toolspeed_modifier - previous_toolspeed_modifier + if(FORGE_EFFECT_REAGENT_INJECT) + var/datum/component/reagent_imbued/reagent_component = item.GetComponent(/datum/component/reagent_imbued/) + if(isnull(reagent_component)) + stack_trace("Reagent inject amount was increased via forging for an item that does not have reagent imbuing!") + + var/new_inject_modifier = new_modifier * max_effect + var/previous_inject_modifier = old_modifier * max_effect + reagent_component.inject_amount += new_inject_modifier - previous_inject_modifier + else + stack_trace("Tried to modify [item] with an invalid effect [forge_effect]!") + +/proc/blacksmithing_change_material_integrity(obj/item/my_item, datum/material/material, amount, multiplier, removing = FALSE) + var/base_modifier = material.get_property(MATERIAL_INTEGRITY) + if(isnull(base_modifier)) + return + + var/integrity_mod = GET_MATERIAL_MODIFIER(base_modifier, multiplier) + var/integrity_change = removing ? floor(my_item.max_integrity / integrity_mod) : ceil(my_item.max_integrity * integrity_mod) + my_item.modify_max_integrity(integrity_change) + + var/list/armor_mods = material.get_armor_modifiers(multiplier) + + //with blacksmithing everything has to be addition based + var/datum/armor/new_armor = get_armor_by_type(my_item.get_initial_armor_type()) + new_armor = new_armor.generate_new_with_multipliers(armor_mods) + new_armor = new_armor.subtract_other_armor(my_item.get_initial_armor_type()) + new_armor = new_armor.generate_new_with_multipliers(list(ARMOR_ALL = 0.3)) //reduce effect because existing armor mults can be too much + + // Invert if we're removing our material + if(!removing) + new_armor = my_item.get_armor().add_other_armor(new_armor) + else + new_armor = my_item.get_armor().subtract_other_armor(new_armor) + my_item.set_armor(new_armor) + +/proc/blacksmithing_change_material_strength(obj/item/my_item, datum/material/material, mat_amount, multiplier, remove = FALSE) + var/density = material.get_property(MATERIAL_DENSITY) + var/hardness = material.get_property(MATERIAL_HARDNESS) + var/flexibility = material.get_property(MATERIAL_FLEXIBILITY) + if(isnull(density) || isnull(hardness) || isnull(flexibility)) + return + + // Item force calculation depends on its initial (assumed to be main) sharpness + // Transforming component doesn't work with materials at all and will need a refactor to change that, so we don't care about it here. + + var/force_mod = 1 + var/throwforce_mod = 1 + + switch (my_item.sharpness) + if (NONE) + // Blunt items are really hurt by all the flexing + force_mod = (1 + (density - 4) * 0.1) / (1 + flexibility * 0.1) + throwforce_mod = 1 + (density - 4) * 0.1 - flexibility * 0.1 + + if (SHARP_EDGED) + // Sharp items don't care about density and need high hardness to get a real bonus, but can tolerate (and benefit from) some flex + force_mod = 1 + (hardness - 4) * 0.1 + throwforce_mod = 1 + (hardness - 4) * 0.1 + + // Peaks out at 20% at flexibility of 1, drops off up to -80% at 10 + if (flexibility < 2) + force_mod *= 1 + (1 - abs(1 - flexibility)) * 0.2 + throwforce_mod += (1 - abs(1 - flexibility)) * 0.2 + else + force_mod *= 1 - (flexibility - 2) * 0.1 + throwforce_mod -= (flexibility - 2) * 0.1 + + if (SHARP_POINTY) + // Pointy items care about both density and hardness + force_mod = 1 + MATERIAL_PROPERTY_DIVERGENCE(density, 4, 6) * 0.05 + (hardness - 4) * 0.1 + throwforce_mod = 1 + MATERIAL_PROPERTY_DIVERGENCE(density, 4, 6) * 0.05 * 0.05 + (hardness - 4) * 0.1 + // But are not affected by flexibility until higher values, although they don't benefit from it either + if (flexibility > 4) + force_mod *= (1 - (flexibility - 4) * 0.2) + throwforce_mod -= (flexibility - 4) * 0.2 + + // Just for sanity in case something breaks + force_mod = clamp(force_mod, MATERIAL_MIN_FORCE_MULTIPLIER, MATERIAL_MAX_FORCE_MULTIPLIER) + throwforce_mod = clamp(throwforce_mod, MATERIAL_MIN_FORCE_MULTIPLIER, MATERIAL_MAX_FORCE_MULTIPLIER) + + // change the forcemod to an added factor instead of a whole multiplier, then reduce the sum effect of the force mod because they currently go too fuggin hard + force_mod = round((force_mod - 1) / 3.5, 0.01) + throwforce_mod = round((throwforce_mod - 1) / 3.5, 0.01) + if (!remove) + my_item.force += initial(my_item.force) * GET_MATERIAL_MODIFIER(force_mod, multiplier) + my_item.throwforce += initial(my_item.force) * GET_MATERIAL_MODIFIER(throwforce_mod, multiplier) + else + my_item.force -= initial(my_item.force) * GET_MATERIAL_MODIFIER(force_mod, multiplier) + my_item.throwforce -= initial(my_item.force) * GET_MATERIAL_MODIFIER(throwforce_mod, multiplier) + +/proc/get_material_quality_points_mult(datum/material/item_material) + //material properties affect forging; high thermal conductivity and flexibility are a bonus while high density and hardness take away + var/density = item_material.get_property(MATERIAL_DENSITY) + var/hardness = item_material.get_property(MATERIAL_HARDNESS) + var/flexibility = item_material.get_property(MATERIAL_FLEXIBILITY) + var/thermal = item_material.get_property(MATERIAL_THERMAL) + + if(isnull(density) || isnull(hardness) || isnull(flexibility) || isnull(thermal)) + return 1 + else + return ((flexibility * 3) + thermal) / (density + hardness) + +///urgh tg needs a get_armor_initial proc, working around protected/private vars is really annoying here +/atom/proc/get_initial_armor_type() + RETURN_TYPE(/datum/armor) + return initial(armor_type) diff --git a/modular_skyrat/modules/reagent_forging/code/anvil.dm b/modular_skyrat/modules/reagent_forging/code/anvil.dm index 1a48e3e4db6..2f954f2ea3f 100644 --- a/modular_skyrat/modules/reagent_forging/code/anvil.dm +++ b/modular_skyrat/modules/reagent_forging/code/anvil.dm @@ -1,3 +1,5 @@ +#define AUTO_SMITHING_SPEED_PENALTY 8 DECISECONDS + /obj/structure/reagent_anvil name = "smithing anvil" desc = "Essentially a big block of metal that you can hammer other metals on top of, crucial for anyone working metal by hand." @@ -6,11 +8,12 @@ anchored = TRUE density = TRUE - custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10) + custom_materials = list(/datum/material/alloy/plastitanium = SHEET_MATERIAL_AMOUNT * 10) /obj/structure/reagent_anvil/Initialize(mapload) . = ..() - + if(!mapload) + anchored = FALSE AddElement(/datum/element/falling_hazard, damage = 40, wound_bonus = 10, hardhat_safety = FALSE, crushes = TRUE) /obj/structure/reagent_anvil/update_appearance() @@ -26,10 +29,35 @@ /obj/structure/reagent_anvil/examine(mob/user) . = ..() . += span_notice("You can place hot metal objects on this using some tongs.") - . += span_notice("It can be (un)secured with Right Click") + . += span_notice("It can be (un)secured by Right Clicking with your bare hand.") if(length(contents)) - . += span_notice("It has [contents[1]] sitting on it.") + var/obj/my_contents = contents[1] + . += span_notice("It has [my_contents] sitting on it.") + . += span_notice("Left Click with a forging mallet to hammer the metal with precise strikes.") + if(USER_CAN_SEE_SMITHING_INFO(user) && !isnull(my_contents?.GetComponent(/datum/component/forge_smithable))) + var/datum/component/forge_smithable/my_component = my_contents.GetComponent(/datum/component/forge_smithable) + . += span_notice("You'd estimate that it's about [round(my_component.get_completion_ratio() * 100)]% complete.") + . += span_notice("The careful smithing makes it look about [round(my_component.get_perfect_ratio() * 100)]% perfected.") + if(HAS_TRAIT(user, TRAIT_KNOW_ADVANCED_SMITHING)) + . += span_notice("If you strike precisely enough, you can get a perfect hit!") + else + . += span_notice("If you had the right skillchip, you could work the metal to a higher quality...") + . += span_notice("Right Click with a forging mallet to hammer the metal with a steady tempo.") + . += span_notice("You could remove [contents[1]] with some tongs, or your bare hands.") + . += span_notice("You could cut it apart with a welder.") + +/obj/structure/reagent_anvil/attack_hand(mob/living/user, list/modifiers) + . = ..() + if(user.combat_mode) + return SECONDARY_ATTACK_CALL_NORMAL + + if(length(contents) > 0) + var/obj/item/contained_item = contents[1] + user.put_in_hands(contained_item) + balloon_alert(user, "[contained_item] retrieved") + update_appearance() + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/structure/reagent_anvil/attack_hand_secondary(mob/user, list/modifiers) . = ..() @@ -39,33 +67,49 @@ if(!can_interact(user) || !user.can_perform_action(src)) return + add_fingerprint(user) set_anchored(!anchored) balloon_alert_to_viewers(anchored ? "secured" : "unsecured") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN -/obj/structure/reagent_anvil/wrench_act(mob/living/user, obj/item/tool) - balloon_alert_to_viewers("deconstructing...") - - if(!do_after(user, 2 SECONDS, src)) - balloon_alert_to_viewers("stopped deconstructing") +/obj/structure/reagent_anvil/welder_act(mob/living/user, obj/item/tool) + if(!tool.tool_start_check(user, amount=2)) return TRUE + add_fingerprint(user) + + if(tool.use_tool(src, user, 2 SECONDS, volume=2)) + new /obj/item/sliced_pipe(drop_location()) + user.visible_message( \ + "[user] welds \the [src] into base components.", \ + span_notice("You weld \the [src] into base components."), \ + span_hear("You hear welding.")) + + deconstruct(TRUE) - tool.play_tool_sound(src) - deconstruct(TRUE) return TRUE /obj/structure/reagent_anvil/atom_deconstruct(disassembled = TRUE) - new /obj/item/stack/sheet/iron/ten(get_turf(src)) + var/obj/item/stack/sheet/my_drop = new /obj/item/stack/sheet/mineral/plastitanium(get_turf(src)) + my_drop.add(9) + if(length(contents)) + for(var/obj/contained in contents) + contained.forceMove(get_turf(src)) return ..() +/obj/structure/reagent_anvil/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(user.combat_mode) + return NONE + + if(!isnull(tool.GetComponent(/datum/component/forge_smithable))) + tool.forceMove(src) + update_appearance() + return ITEM_INTERACT_SUCCESS + return NONE + /obj/structure/reagent_anvil/tong_act(mob/living/user, obj/item/tool) var/obj/item/forging/forge_item = tool + add_fingerprint(user) var/obj/obj_anvil_search = locate() in contents - - if(forge_item.in_use) - balloon_alert(user, "already in use") - return ITEM_INTERACT_SUCCESS - var/obj/obj_tong_search = locate() in forge_item.contents if(obj_anvil_search && !obj_tong_search) obj_anvil_search.forceMove(forge_item) @@ -74,87 +118,57 @@ return ITEM_INTERACT_SUCCESS if(!obj_anvil_search && obj_tong_search) - obj_tong_search.forceMove(src) - update_appearance() - forge_item.icon_state = "tong_empty" - return ITEM_INTERACT_SUCCESS + var/returner = item_interaction(user, obj_tong_search) + if(length(tool.contents) < 1) + forge_item.icon_state = "tong_empty" + return returner + return NONE /obj/structure/reagent_anvil/hammer_act(mob/living/user, obj/item/tool) - conditional_pref_sound(src, 'modular_skyrat/modules/reagent_forging/sound/forge.ogg', vol = 35, vary = TRUE, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE, pref_to_check = /datum/preference/numeric/volume/sound_ambience_volume) - - //do we have an incomplete item to hammer out? if so, here is our block of code - var/obj/item/forging/incomplete/locate_incomplete = locate() in contents - if(locate_incomplete) - if(locate_incomplete.times_hit >= locate_incomplete.average_hits) //to prevent people from getting perfect perfects - user.balloon_alert(user, "[locate_incomplete] sounds ready") - return ITEM_INTERACT_SUCCESS - - if(COOLDOWN_FINISHED(locate_incomplete, heating_remainder)) - balloon_alert(user, "metal too cool") - locate_incomplete.times_hit -= 3 - return ITEM_INTERACT_SUCCESS - - if(COOLDOWN_FINISHED(locate_incomplete, striking_cooldown)) - var/skill_modifier = user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_SPEED_MODIFIER) * locate_incomplete.average_wait - COOLDOWN_START(locate_incomplete, striking_cooldown, skill_modifier) - locate_incomplete.times_hit++ - if(prob(user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_PROBS_MODIFIER))) - balloon_alert(user, "perfect hit!") - locate_incomplete.current_perfects++ - user.mind.adjust_experience(/datum/skill/smithing, 10) //A perfect hit gives good experience - return ITEM_INTERACT_SUCCESS - - balloon_alert(user, "good hit") - user.mind.adjust_experience(/datum/skill/smithing, 1) //A good hit gives minimal experience - return ITEM_INTERACT_SUCCESS - - locate_incomplete.times_hit -= 3 - balloon_alert(user, "bad hit") - - if(locate_incomplete.times_hit <= -locate_incomplete.average_hits) - balloon_alert_to_viewers("[locate_incomplete] breaks") - qdel(locate_incomplete) - update_appearance() - - return ITEM_INTERACT_SUCCESS - - //okay, so we didn't find an incomplete item to hammer, do we have a hammerable item? + //do we have a hammerable item? + add_fingerprint(user) var/obj/locate_obj = locate() in contents - if(locate_obj && (locate_obj.skyrat_obj_flags & ANVIL_REPAIR)) - // Repairing weapons with imbued reagents takes skill - // God this code is so terrible who the fuck wrote it in two different components that dont share a parent - // I doubt this can be made pretty without skyrat refactoring whatever the fuck all of the stuff around this codeblock is - // BUBBER TODO - Refactor this into two components with same parent! - // - // I'll give you hugs if you refactor it yourself - // ~Waterpig - if(locate_obj.GetComponent(/datum/component/reagent_clothing)) - var/datum/component/reagent_clothing/reagent_component = locate_obj.GetComponent(/datum/component/reagent_clothing) - if(length(reagent_component.imbued_reagent) && user.mind.get_skill_level(/datum/skill/smithing) < SKILL_LEVEL_EXPERT) - to_chat(user, span_danger("You need more experience to repair imbued weapons!")) - return ITEM_INTERACT_SUCCESS - else if(locate_obj.GetComponent(/datum/component/reagent_weapon)) - var/datum/component/reagent_weapon/reagent_component = locate_obj.GetComponent(/datum/component/reagent_weapon) - if(length(reagent_component.imbued_reagent) && user.mind.get_skill_level(/datum/skill/smithing) < SKILL_LEVEL_EXPERT) - to_chat(user, span_danger("You need more experience to repair imbued weapons!")) - return ITEM_INTERACT_SUCCESS - if(locate_obj.get_integrity() >= locate_obj.max_integrity) - balloon_alert(user, "already repaired") + if(!isnull(locate_obj)) + var/datum/component/forge_smithable/smith_component = locate_obj.GetComponent(/datum/component/forge_smithable/) + if(!isnull(smith_component)) + smith_component.anvil_work(user, tool) + update_appearance() return ITEM_INTERACT_SUCCESS - - while(locate_obj.get_integrity() < locate_obj.max_integrity) - if(!do_after(user, 1 SECONDS, src)) - balloon_alert(user, "stopped repairing") - return ITEM_INTERACT_SUCCESS - - locate_obj.repair_damage(locate_obj.get_integrity() + 10) - user.mind.adjust_experience(/datum/skill/smithing, 5) //repairing does give some experience - conditional_pref_sound(src, 'modular_skyrat/modules/reagent_forging/sound/forge.ogg', vol = 35, vary = TRUE, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE, pref_to_check = /datum/preference/numeric/volume/sound_ambience_volume) - - return ITEM_INTERACT_SUCCESS + update_appearance() + return ITEM_INTERACT_BLOCKING /obj/structure/reagent_anvil/hammer_act_secondary(mob/living/user, obj/item/tool) - hammer_act(user, tool) + if(DOING_INTERACTION(user, DOAFTER_SMITHING_ANVIL)) + return + + var/obj/item/my_anvil_item = contents[1] + var/datum/component/forge_smithable/smith_component = my_anvil_item.GetComponent(/datum/component/forge_smithable) + if(!isnull(my_anvil_item) && !isnull(smith_component)) + if(!should_stop_autohammering()) + balloon_alert_to_viewers("hammering steadily...") + while(!should_stop_autohammering()) + var/wait_between_swings = COOLDOWN_TIMELEFT(user, striking_cooldown) DECISECONDS + wait_between_swings += AUTO_SMITHING_SPEED_PENALTY + + if(!do_after(user, wait_between_swings, target = src, interaction_key = DOAFTER_SMITHING_ANVIL)) + balloon_alert_to_viewers("stopped hammering") + break + else + hammer_act(user, tool) + + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + +/obj/structure/reagent_anvil/proc/should_stop_autohammering() + var/obj/item/forging/incomplete/my_incomplete_item = contents[1] + var/datum/component/forge_smithable/my_forge_component = my_incomplete_item.GetComponent(/datum/component/forge_smithable/) + if(!isnull(my_forge_component)) + if(my_forge_component.is_finished_smithing()) + return TRUE + if(COOLDOWN_FINISHED(my_forge_component, heating_remainder)) + return TRUE + return FALSE + return TRUE /obj/structure/reagent_anvil/onZImpact(turf/impacted_turf, levels, message = TRUE) var/mob/living/poor_target = locate(/mob/living) in impacted_turf @@ -177,3 +191,5 @@ playsound(poor_target, 'sound/effects/magic/clockwork/fellowship_armory.ogg', 50, TRUE) add_memory_in_range(poor_target, 7, /datum/memory/witness_vendor_crush, protagonist = poor_target, antognist = src) return TRUE + +#undef AUTO_SMITHING_SPEED_PENALTY diff --git a/modular_skyrat/modules/reagent_forging/code/cargo_orders.dm b/modular_skyrat/modules/reagent_forging/code/cargo_orders.dm new file mode 100644 index 00000000000..37b748add60 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/cargo_orders.dm @@ -0,0 +1,28 @@ +/datum/supply_pack/misc/smithing_skillchips + name = "Smithing Skillchips Pack" + desc = "Learn the art of metalworking and build your own weapons! Contains two smithing skillchips." + cost = CARGO_CRATE_VALUE * 3 + access = list(ACCESS_WEAPONS) + contains = list(/obj/item/storage/box/skillchips/supply,) + +/datum/supply_pack/misc/singular_smithing_chip + name = "Single Smithing Skillchip" + desc = "Learn the art of metalworking and build your own weapons! Contains one smithing skillchip." + cost = CARGO_CRATE_VALUE * 2 + order_flags = ORDER_GOODY + access = list(ACCESS_WEAPONS) + contains = list(/obj/item/skillchip/job/blacksmith,) + +/datum/supply_pack/misc/blacksmith_starter_pack + name = "Blacksmithing Starter Pack" + desc = "Almost everything one person needs to get to a basic blacksmithing setup. Contains a forge, anvil, cooling basin, crafting bench, two jerrycans of smithing oil, a wall-mounted A/C unit, and a wrench to secure everything to the floor." + cost = CARGO_CRATE_VALUE * 4 + access = list(ACCESS_WEAPONS) + crate_type = /obj/structure/closet/crate/wooden/blacksmith + +/datum/supply_pack/misc/smithing_oil + name = "Smithing Oil Jerrycans" + desc = "Two jerrycans of smithing oil, perfect for quenching freshly-forged metal items." + cost = CARGO_CRATE_VALUE * 2 + access = list(ACCESS_WEAPONS) + contains = list(/obj/item/reagent_containers/cup/jerrycan/smithing_oil,/obj/item/reagent_containers/cup/jerrycan/smithing_oil,) diff --git a/modular_skyrat/modules/reagent_forging/code/container_boxes.dm b/modular_skyrat/modules/reagent_forging/code/container_boxes.dm new file mode 100644 index 00000000000..4b01ca6ecf8 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/container_boxes.dm @@ -0,0 +1,35 @@ +/obj/structure/closet/crate/large/smithing + name = "smithing equipment crate" + desc = "A hefty wooden crate full of smithing equipment. You'll need a crowbar to get it open." + +/obj/structure/closet/crate/large/smithing/PopulateContents() + new /obj/structure/reagent_anvil(src) + new /obj/structure/reagent_crafting_bench(src) + new /obj/structure/reagent_dispensers/reagent_smithing_basin(src) + new /obj/structure/reagent_forge(src) + new /obj/item/reagent_containers/cup/jerrycan/smithing_oil(src) + new /obj/item/reagent_containers/cup/jerrycan/smithing_oil(src) + new /obj/item/wallframe/wall_heater(src) + new /obj/item/wrench + +/obj/structure/closet/crate/wooden/blacksmith + name = "blacksmith's equipment crate" + +/obj/structure/closet/crate/wooden/blacksmith/PopulateContents() + new /obj/item/forging/tongs(src) + new /obj/item/forging/hammer(src) + new /obj/item/forging/billow(src) + var/obj/item/stack/sheet/tempstack = new /obj/item/stack/sheet/mineral/wood(src) + tempstack.add(9) + new /obj/item/stack/sheet/mineral/coal/five(src) + tempstack = new /obj/item/stack/sheet/glass(src) + tempstack.add(9) + new /obj/item/stack/sheet/iron/ten(src) + +/obj/item/storage/box/blacksmith + name = "box of smithing tools" + +/obj/item/storage/box/blacksmith/PopulateContents() + new /obj/item/forging/tongs(src) + new /obj/item/forging/hammer(src) + new /obj/item/forging/billow(src) diff --git a/modular_skyrat/modules/reagent_forging/code/crafting_bench.dm b/modular_skyrat/modules/reagent_forging/code/crafting_bench.dm index cbcf6d536f8..fbfdf7040ee 100644 --- a/modular_skyrat/modules/reagent_forging/code/crafting_bench.dm +++ b/modular_skyrat/modules/reagent_forging/code/crafting_bench.dm @@ -4,6 +4,11 @@ /// The number of hits you are set back when a bad hit is made #define BAD_HIT_PENALTY 3 +#define WEAPON_ASSEMBLY_SPEED 3 SECONDS + +/// how many chains/smithing plates can be contained? +#define MAX_FORGING_COMPLETE_ITEMS 50 + /obj/structure/reagent_crafting_bench name = "forging workbench" desc = "A crafting bench fitted with tools, securing mechanisms, and a steady surface for blacksmithing." @@ -16,113 +21,158 @@ /// What the currently picked recipe is var/datum/crafting_bench_recipe/selected_recipe - /// How many successful hits towards completion of the item have we done - var/current_hits_to_completion = 0 /// Is this bench able to complete forging items? Exists to allow non-forging workbenches to exist var/finishes_forging_weapons = TRUE /// The cooldown from the last hit before we allow another 'good hit' to happen COOLDOWN_DECLARE(hit_cooldown) + /// holds stackables + var/list/stack_item_container = list() + /// holds misc. forging items + var/list/forging_complete_container = list(/obj/item/forging/complete/chain = list(), /obj/item/forging/complete/plate = list()) + /// holds forge item placed on the table via hand, or placed on it via weapon completion + var/obj/item/forged_item_on_surface + /// What recipes are we allowed to choose from? var/list/allowed_choices = list( - /datum/crafting_bench_recipe/plate_helmet, - /datum/crafting_bench_recipe/plate_vest, - /datum/crafting_bench_recipe/plate_gloves, - /datum/crafting_bench_recipe/plate_boots, - /datum/crafting_bench_recipe/horse_shoes, - /datum/crafting_bench_recipe/ring, - /datum/crafting_bench_recipe/collar, - /datum/crafting_bench_recipe/handcuffs, - /datum/crafting_bench_recipe/borer_cage, + /datum/crafting_bench_recipe/wearable/plate_armor/plate_helmet, + /datum/crafting_bench_recipe/wearable/plate_armor/plate_vest, + /datum/crafting_bench_recipe/wearable/plate_armor/plate_gloves, + /datum/crafting_bench_recipe/wearable/plate_armor/plate_boots, + /datum/crafting_bench_recipe/wearable/plate_armor/horse_shoes, + /datum/crafting_bench_recipe/wearable/ring, + /datum/crafting_bench_recipe/wearable/collar, + /datum/crafting_bench_recipe/cowboy_holster, + /datum/crafting_bench_recipe/charging_holster, + /datum/crafting_bench_recipe/crusader_belt, + /datum/crafting_bench_recipe/multi_scabbard, + /datum/crafting_bench_recipe/repairing_scabbard, + /datum/crafting_bench_recipe/knifethrower, + /datum/crafting_bench_recipe/bluespace_plants, /datum/crafting_bench_recipe/pavise, /datum/crafting_bench_recipe/buckler, + /datum/crafting_bench_recipe/bow, + /datum/crafting_bench_recipe/wearable/handcuffs, + /datum/crafting_bench_recipe/revolver, + ////datum/crafting_bench_recipe/borer_cage, /datum/crafting_bench_recipe/coil, /datum/crafting_bench_recipe/seed_mesh, /datum/crafting_bench_recipe/centrifuge, /datum/crafting_bench_recipe/bokken, - /datum/crafting_bench_recipe/bow, /datum/crafting_bench_recipe/empty_circuit, ) /// Radial options for recipes in the allowed_choices list, populated by populate_radial_choice_list var/list/radial_choice_list = list() /// An associative list of names --> recipe path that the radial recipe picker will choose from later var/list/recipe_names_to_path = list() + /// Filters the radial choice list by required skill + var/list/choice_list_skill_filter = list() + /// Filters the radial choice list by required level in its skill; true means corresponding element requires it + var/list/choice_list_skill_level_filter = list() + /// Filters the radial choice list by if it requires the smithing skillchip; true means corresponding element requires it + var/list/choice_list_trait_filter = list() /obj/structure/reagent_crafting_bench/Initialize(mapload) . = ..() + if(!mapload) + anchored = FALSE populate_radial_choice_list() /obj/structure/reagent_crafting_bench/proc/populate_radial_choice_list() - if(!length(allowed_choices)) - return - - if(length(radial_choice_list) && length(recipe_names_to_path)) // We already have both of these and don't need it, if this is called after these are generated for some reason - return + var/datum/radial_menu_choice/option for(var/recipe in allowed_choices) var/datum/crafting_bench_recipe/recipe_to_take_from = new recipe() var/obj/recipe_resulting_item = recipe_to_take_from.resulting_item - radial_choice_list[recipe_to_take_from.recipe_name] = image(icon = initial(recipe_resulting_item.icon), icon_state = initial(recipe_resulting_item.icon_state)) + + option = new + option.image = image(icon = initial(recipe_resulting_item.icon), icon_state = initial(recipe_resulting_item.icon_state)) + option.name = initial(recipe_resulting_item.name) + option.info = initial(recipe_resulting_item.desc) + radial_choice_list[recipe_to_take_from.recipe_name] = option + recipe_names_to_path[recipe_to_take_from.recipe_name] = recipe + choice_list_skill_filter[recipe_to_take_from.recipe_name] = initial(recipe_to_take_from.relevant_skill) + choice_list_skill_level_filter[recipe_to_take_from.recipe_name] = initial(recipe_to_take_from.relevant_skill_level) + choice_list_trait_filter[recipe_to_take_from.recipe_name] = initial(recipe_to_take_from.required_traits) qdel(recipe_to_take_from) /obj/structure/reagent_crafting_bench/examine(mob/user) . = ..() + . += span_notice("You could secure or unsecure it with a wrench.") + . += span_notice("You could pry it apart with a crowbar.") + . += span_notice("You could insert a stack of material into the drawers, or right click to retrieve it later.") - if(length(contents)) - if(istype(contents[1], /obj/item/forging/complete)) - var/obj/item/forging/complete/contained_forge_item = contents[1] - - . += span_notice("[src] has a [initial(contained_forge_item.name)] sitting on it, awaiting completion.
") - var/obj/item/completion_item = contained_forge_item.spawning_item + if(!isnull(forged_item_on_surface)) + if(istype(forged_item_on_surface, /obj/item/forging/complete)) + var/obj/item/forging/complete/complete_item + . += span_notice("[src] has a [initial(complete_item.name)] sitting on it, awaiting completion.
") + var/obj/item/completion_item = complete_item.spawning_item . += span_notice("With [WEAPON_COMPLETION_WOOD_AMOUNT] sheets of wood nearby, and some hammering, it could be completed into a [initial(completion_item.name)].") return // We don't want to show any selected recipes if there's weapon head on the bench + else + if(selected_recipe) + var/obj/resulting_item = selected_recipe.resulting_item + . += span_notice("The selected recipe's resulting item is: [initial(resulting_item.name)]
") + . += span_notice("Gather the required materials, listed below, near the bench, then start hammering to complete it!
") - if(!selected_recipe) - return - - var/obj/resulting_item = selected_recipe.resulting_item - . += span_notice("The selected recipe's resulting item is: [initial(resulting_item.name)]
") - . += span_notice("Gather the required materials, listed below, near the bench, then start hammering to complete it!
") - - if(!length(selected_recipe.recipe_requirements)) - . += span_boldwarning("Somehow, this recipe has no requirements, report this as this shouldn't happen.") - return - - for(var/obj/requirement_item as anything in selected_recipe.recipe_requirements) - if(!selected_recipe.recipe_requirements[requirement_item]) - . += span_boldwarning("[requirement_item] does not have an amount required set, this should not happen, report it.") - continue - - . += span_notice("[selected_recipe.recipe_requirements[requirement_item]] - [initial(requirement_item.name)]") + if(!length(selected_recipe.recipe_requirements)) + . += span_boldwarning("Somehow, this recipe has no requirements, report this as this shouldn't happen.") + return + . += selected_recipe.get_recipe_requirements_description() return . /obj/structure/reagent_crafting_bench/update_appearance(updates) . = ..() cut_overlays() - if(!length(contents)) + if(isnull(forged_item_on_surface)) return - var/image/overlayed_item = image(icon = contents[1].icon, icon_state = contents[1].icon_state) + var/image/overlayed_item + if(isnull(forged_item_on_surface.icon_preview)) + overlayed_item = image(icon = forged_item_on_surface.icon, icon_state = forged_item_on_surface.icon_state) + else + overlayed_item = image(icon = forged_item_on_surface.icon_preview, icon_state = forged_item_on_surface.icon_state_preview) add_overlay(overlayed_item) /obj/structure/reagent_crafting_bench/attack_hand(mob/living/user, list/modifiers) . = ..() select_recipe(user) +/obj/structure/reagent_crafting_bench/attack_hand_secondary(mob/living/user, list/modifiers) + var/temp_list = generate_stack_held_list_radial() + var/option = show_radial_menu(user, src, temp_list, radius = 38, require_near = TRUE, tooltips = TRUE) + + if(!isnull(forging_complete_container[option])) + for(var/obj/item/stored_item in forging_complete_container[option]) + if(stored_item.loc == src) + stored_item.forceMove(get_turf(src)) + else if(!isnull(stack_item_container[option])) + var/obj/item/stack/sheet/output_stack = stack_item_container[option] + stack_item_container[option] = null + if(output_stack.loc == src) + output_stack.forceMove(get_turf(src)) + + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + + /obj/structure/reagent_crafting_bench/attack_robot(mob/living/user) . = ..() select_recipe(user) +/obj/structure/reagent_crafting_bench/proc/add_completed_forged_item(obj/item/new_item) + forged_item_on_surface = new_item + forged_item_on_surface.forceMove(src) + /obj/structure/reagent_crafting_bench/proc/select_recipe(mob/living/user) update_appearance() - if(length(contents)) - var/obj/item/contained_item = contents[1] - user.put_in_hands(contained_item) - balloon_alert(user, "[contained_item] retrieved") + if(!isnull(forged_item_on_surface)) + user.put_in_hands(forged_item_on_surface) + forged_item_on_surface = null + balloon_alert(user, "[forged_item_on_surface] retrieved") update_appearance() return @@ -132,7 +182,7 @@ update_appearance() return - var/chosen_recipe = show_radial_menu(user, src, radial_choice_list, radius = 38, require_near = TRUE, tooltips = TRUE) + var/chosen_recipe = show_radial_menu(user, src, get_filtered_radial_choices(user), radius = 38, require_near = TRUE, tooltips = TRUE) if(!chosen_recipe) balloon_alert(user, "no recipe choice") @@ -144,18 +194,86 @@ balloon_alert(user, "recipe chosen") update_appearance() +///Exclusively gives all the radial choices that the user can know how to make. +/obj/structure/reagent_crafting_bench/proc/get_filtered_radial_choices(mob/living/user) + var/returner = list() + if(isnull(user?.mind)) + return returner + + for(var/key,value in radial_choice_list) + if(user_can_craft(user, key)) + returner[key] = value + + return returner + +/obj/structure/reagent_crafting_bench/proc/user_can_craft(mob/living/user, key) + if(isnull(user?.mind)) + return FALSE + if(!isnull(choice_list_skill_filter[key]) && user.mind.get_skill_level(choice_list_skill_filter[key]) < choice_list_skill_level_filter[key]) + return FALSE + if(!isnull(choice_list_trait_filter[key])) + for(var/my_trait in choice_list_trait_filter[key]) + if (!HAS_TRAIT(user, my_trait)) + return FALSE + return TRUE + /// Clears the current recipe and sets hits to completion to zero /obj/structure/reagent_crafting_bench/proc/clear_recipe() QDEL_NULL(selected_recipe) - current_hits_to_completion = 0 -/obj/structure/reagent_crafting_bench/attackby(obj/item/attacking_item, mob/user, params) - if(istype(attacking_item, /obj/item/forging/complete)) - attempt_place(attacking_item, user) +/obj/structure/reagent_crafting_bench/item_interaction(mob/living/user, obj/item/attacking_item, params) + if(istype(attacking_item, /obj/item/forging/complete) && user.combat_mode == FALSE) + var/obj/item/forging/complete/complete_item = attacking_item + if(!isnull(complete_item.spawning_item)) + attempt_place(attacking_item, user) + return TRUE + + if(istype(attacking_item, /obj/item/stack/sheet)) + attempt_stack_storage(attacking_item, user) + return TRUE + + if(!isnull(forging_complete_container[attacking_item.type])) + attempt_complete_item_storage(attacking_item, user) return TRUE return ..() +/obj/structure/reagent_crafting_bench/tong_act(mob/living/user, obj/item/tool) + var/obj/item/forging/forge_item = tool + add_fingerprint(user) + var/obj/obj_tong_search = locate() in forge_item.contents + if(obj_tong_search) + var/returner = item_interaction(user, obj_tong_search) + if(length(tool.contents) < 1) + forge_item.icon_state = "tong_empty" + return returner + else + if(!isnull(forged_item_on_surface)) + if(forged_item_on_surface.loc != src) + forged_item_on_surface.forceMove(forge_item) + forge_item.icon_state = "tong_full" + balloon_alert(user, "took [forged_item_on_surface]") + forged_item_on_surface = null + return ITEM_INTERACT_SUCCESS + else + var/temp_list = generate_stack_held_list_radial() + var/option = show_radial_menu(user, src, temp_list, radius = 38, require_near = TRUE, tooltips = TRUE) + if(user.get_active_held_item() != tool) + if(!isnull(stack_item_container[option])) + var/obj/item/stack/sheet/output_stack = stack_item_container[option] + if(!isnull(output_stack) && output_stack.loc == src) + output_stack.tong_act(user, tool) + return ITEM_INTERACT_SUCCESS + else if(!isnull(forging_complete_container[option]) && length(forging_complete_container[option]) > 0) + var/obj/item/stack/sheet/output_complete = forging_complete_container[option][1] + if(!isnull(output_complete) && output_complete.loc == src) + output_complete.tong_act(user, tool) + return ITEM_INTERACT_SUCCESS + else + balloon_alert(user, "you let go of [tool]!") + + return NONE + /obj/structure/reagent_crafting_bench/mouse_drop_receive(atom/movable/attacking_item, mob/living/user, params) . = ..() if(!isliving(user)) @@ -168,10 +286,11 @@ attempt_place(attacking_item, user) /obj/structure/reagent_crafting_bench/proc/attempt_place(obj/item/attacking_item, mob/user) - if(length(contents)) + if(!isnull(forged_item_on_surface)) balloon_alert(user, "already full") return + forged_item_on_surface = attacking_item attacking_item.forceMove(src) balloon_alert_to_viewers("placed [attacking_item]") update_appearance() @@ -179,76 +298,97 @@ /obj/structure/reagent_crafting_bench/wrench_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src) - deconstruct(disassembled = TRUE) - return ITEM_INTERACT_SUCCESS + set_anchored(!anchored) + balloon_alert_to_viewers(anchored ? "secured" : "unsecured") + return TRUE + +/obj/structure/reagent_crafting_bench/crowbar_act(mob/living/user, obj/item/tool) + tool.play_tool_sound(src) + balloon_alert(user, "pried apart") + deconstruct(TRUE) + return TRUE + +/obj/structure/reagent_crafting_bench/atom_deconstruct(disassembled = TRUE) + if(length(contents)) + for(var/obj/contained in contents) + contained.forceMove(get_turf(src)) + return ..() /obj/structure/reagent_crafting_bench/hammer_act(mob/living/user, obj/item/tool) - conditional_pref_sound(src, 'modular_skyrat/modules/reagent_forging/sound/forge.ogg', vol = 35, vary = TRUE, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE, pref_to_check = /datum/preference/numeric/volume/sound_ambience_volume) - if(length(contents)) - if(!istype(contents[1], /obj/item/forging/complete)) - balloon_alert(user, "invalid item") - return ITEM_INTERACT_SUCCESS + if(DOING_INTERACTION(user, DOAFTER_SMITHING_ANVIL)) + return - var/obj/item/forging/complete/weapon_to_finish = contents[1] + if(!isnull(forged_item_on_surface)) + return try_weapon_completion(user, tool) - if(!weapon_to_finish.spawning_item) - balloon_alert(user, "[weapon_to_finish] cannot be completed") - return ITEM_INTERACT_SUCCESS + if(check_craftability_general(user, tool) == ITEM_INTERACT_BLOCKING) + return ITEM_INTERACT_BLOCKING - var/list/wood_required_for_weapons = list( - /obj/item/stack/sheet/mineral/wood = WEAPON_COMPLETION_WOOD_AMOUNT, - ) + var/skill_modifier = user.mind.get_skill_modifier(selected_recipe.relevant_skill, SKILL_SPEED_MODIFIER) - if(!can_we_craft_this(wood_required_for_weapons)) - balloon_alert(user, "not enough wood") - return ITEM_INTERACT_SUCCESS - - var/list/things_to_use = can_we_craft_this(wood_required_for_weapons, TRUE) - var/obj/thing_just_made = create_thing_from_requirements(things_to_use, user = user, skill_to_grant = /datum/skill/smithing, skill_amount = 30, completing_a_weapon = TRUE) - - if(!thing_just_made) - message_admins("[src] just tried to finish a weapon but somehow created nothing! This is not working as intended!") - return ITEM_INTERACT_SUCCESS - - balloon_alert_to_viewers("[thing_just_made] created") - update_appearance() - return ITEM_INTERACT_SUCCESS - - if(!selected_recipe) - balloon_alert(user, "no recipe selected") - return ITEM_INTERACT_SUCCESS - - if(!can_we_craft_this(selected_recipe.recipe_requirements)) - balloon_alert(user, "missing ingredients") - return ITEM_INTERACT_SUCCESS - - var/skill_modifier = user.mind.get_skill_modifier(selected_recipe.relevant_skill, SKILL_SPEED_MODIFIER) * 1 SECONDS - - if(!COOLDOWN_FINISHED(src, hit_cooldown)) // If you hit it before the cooldown is done, you get a bad hit, setting you back three good hits - current_hits_to_completion -= BAD_HIT_PENALTY - - if(current_hits_to_completion <= -(selected_recipe.required_good_hits)) - balloon_alert_to_viewers("recipe failed") - clear_recipe() - return ITEM_INTERACT_SUCCESS - - balloon_alert(user, "bad hit") - return ITEM_INTERACT_SUCCESS - - COOLDOWN_START(src, hit_cooldown, skill_modifier) - - if((current_hits_to_completion >= selected_recipe.required_good_hits) && !length(contents)) + playsound(src, 'sound/items/hammering_wood.ogg', 50, vary = TRUE) + if(do_after(user, selected_recipe.time_to_assemble * skill_modifier,target = src, interaction_key = DOAFTER_SMITHING_TABLE) && isnull(forged_item_on_surface)) var/list/things_to_use = can_we_craft_this(selected_recipe.recipe_requirements, TRUE) create_thing_from_requirements(things_to_use, selected_recipe, user, selected_recipe.relevant_skill, selected_recipe.relevant_skill_reward) return ITEM_INTERACT_SUCCESS - current_hits_to_completion++ - balloon_alert(user, "good hit") - user.mind.adjust_experience(selected_recipe.relevant_skill, selected_recipe.relevant_skill_reward / 15) // Good hits towards the current item grants experience in that skill + return ITEM_INTERACT_BLOCKING +/// Takes the given list of item requirements and checks the surroundings for them, returns TRUE unless return_ingredients_list is set, in which case a list of all the items to use is returned +/obj/structure/reagent_crafting_bench/proc/try_weapon_completion(mob/living/user, obj/item/tool) + if(DOING_INTERACTION(user, DOAFTER_SMITHING_TABLE)) + return + + if(!istype(forged_item_on_surface, /obj/item/forging/complete)) + balloon_alert(user, "invalid item") + return ITEM_INTERACT_BLOCKING + + var/obj/item/forging/complete/complete_item = forged_item_on_surface + if(!complete_item.spawning_item) + balloon_alert(user, "[forged_item_on_surface] cannot be completed") + return ITEM_INTERACT_BLOCKING + + var/list/wood_required_for_weapons = list( + /obj/item/stack/sheet/mineral/wood = WEAPON_COMPLETION_WOOD_AMOUNT, + ) + + if(!can_we_craft_this(wood_required_for_weapons)) + balloon_alert(user, "not enough wood") + return ITEM_INTERACT_BLOCKING + + playsound(src, 'sound/items/hammering_wood.ogg', 50, vary = TRUE) + if(!do_after(user, WEAPON_ASSEMBLY_SPEED, target = src, interaction_key = DOAFTER_SMITHING_TABLE)) + return ITEM_INTERACT_BLOCKING + + var/list/things_to_use = can_we_craft_this(wood_required_for_weapons, TRUE) + if(!can_we_craft_this(wood_required_for_weapons)) + balloon_alert(user, "not enough wood") + return ITEM_INTERACT_BLOCKING + + var/obj/thing_just_made = create_thing_from_requirements(things_to_use, user = user, skill_to_grant = /datum/skill/smithing, skill_amount = 30, completing_a_weapon = TRUE) + + if(!thing_just_made) + message_admins("[src] just tried to finish a weapon but somehow created nothing! This is not working as intended!") + return ITEM_INTERACT_SUCCESS + + balloon_alert_to_viewers("[thing_just_made] created") + update_appearance() return ITEM_INTERACT_SUCCESS -/// Takes the given list of item requirements and checks the surroundings for them, returns TRUE unless return_ingredients_list is set, in which case a list of all the items to use is returned +/obj/structure/reagent_crafting_bench/proc/check_craftability_general(mob/living/user, obj/item/tool) + if(!selected_recipe) + balloon_alert(user, "no recipe selected") + return ITEM_INTERACT_BLOCKING + + if(!user_can_craft(user, selected_recipe.recipe_name)) + balloon_alert(user, "you're not skilled enough!") + return ITEM_INTERACT_BLOCKING + + if(!can_we_craft_this(selected_recipe.recipe_requirements)) + balloon_alert(user, "missing ingredients") + return ITEM_INTERACT_BLOCKING + + return ITEM_INTERACT_SUCCESS /obj/structure/reagent_crafting_bench/proc/can_we_craft_this(list/required_items, return_ingredients_list = FALSE) if(!length(required_items)) message_admins("[src] just tried to check for ingredients nearby without having a list of items to check for!") @@ -292,7 +432,7 @@ message_admins("[src] just tried to complete a recipe without having a recipe, and without it being the completion of a forging weapon!") return FALSE - if(completing_a_weapon && (!length(contents) || !istype(contents[1], /obj/item/forging/complete))) + if(completing_a_weapon && isnull(forged_item_on_surface)) message_admins("[src] just tried to complete a forge weapon without there being a weapon head inside it to complete!") return FALSE @@ -303,76 +443,79 @@ if(completing_a_weapon) recipe_to_follow = new /datum/crafting_bench_recipe/weapon_completion_recipe - var/materials_to_transfer = list() - var/list/temporary_materials_list = use_or_delete_recipe_requirements(things_to_use, recipe_to_follow) - for(var/material in temporary_materials_list) - materials_to_transfer[material] += temporary_materials_list[material] - var/obj/newly_created_thing if(completing_a_weapon) - var/obj/item/forging/complete/completed_forge_item = contents[1] - newly_created_thing = new completed_forge_item.spawning_item(src) - if(newly_created_thing.force > 0) //we don't want the staff to get added damage - newly_created_thing.force += min(round(completed_forge_item.current_perfects * INVERSE(10)), 3) //adds a maximum of 3 force, and 6 if dual-wielded - - if(completed_forge_item.custom_materials) // We need to add the weapon head's materials to the completed item, too - for(var/custom_material in completed_forge_item.custom_materials) - materials_to_transfer[custom_material] += completed_forge_item.custom_materials[custom_material] - - qdel(completed_forge_item) // And then we also need to 'use' the item - - else - newly_created_thing = new recipe_to_follow.resulting_item(src) + things_to_use.Add(forged_item_on_surface) + newly_created_thing = recipe_to_follow.create_using_item_list(things_to_use, user, src) if(!newly_created_thing) message_admins("[src] just failed to create something while crafting!") return FALSE - if(recipe_to_follow.transfers_materials) - newly_created_thing.set_custom_materials(materials_to_transfer, multiplier = 1) - user.mind.adjust_experience(skill_to_grant, skill_amount) clear_recipe() update_appearance() return newly_created_thing -/// Takes the given list, things_to_use, compares it to recipe_to_follow's requirements, then either uses items from a stack, or deletes them otherwise. Returns custom material of forge items in the end. -/obj/structure/reagent_crafting_bench/proc/use_or_delete_recipe_requirements(list/things_to_use, datum/crafting_bench_recipe/recipe_to_follow) - var/list/materials_to_transfer = list() - - for(var/obj/requirement_item as anything in things_to_use) - if(isstack(requirement_item)) - var/stack_type - for(var/recipe_thing_to_reference in recipe_to_follow.recipe_requirements) - if(!istype(requirement_item, recipe_thing_to_reference)) - continue - stack_type = recipe_thing_to_reference - break - - var/obj/item/stack/requirement_stack = requirement_item - - if(requirement_stack.amount < recipe_to_follow.recipe_requirements[stack_type]) - recipe_to_follow.recipe_requirements[stack_type] -= requirement_stack.amount - requirement_stack.use(requirement_stack.amount) - continue - - requirement_stack.use(recipe_to_follow.recipe_requirements[stack_type]) - - else if(istype(requirement_item, /obj/item/forging/complete)) - if(!requirement_item.custom_materials || !recipe_to_follow.transfers_materials) - qdel(requirement_item) - continue - - for(var/custom_material in requirement_item.custom_materials) - materials_to_transfer += custom_material - qdel(requirement_item) - +/obj/structure/reagent_crafting_bench/proc/attempt_stack_storage(obj/item/stack/sheet/my_stack, mob/living/user) + var/obj/item/stack/sheet/existing_stack = stack_item_container[my_stack.merge_type] + if(isnull(existing_stack)) + stack_item_container[my_stack.merge_type] = my_stack + my_stack.forceMove(src) + balloon_alert(user, "stashed [my_stack]") + else + if(existing_stack.amount >= existing_stack.max_amount) + balloon_alert(user, "[my_stack] drawer is full!") else - qdel(requirement_item) + my_stack.merge(stack_item_container[my_stack.merge_type]) + balloon_alert(user, "stashed [my_stack]") - return materials_to_transfer +/obj/structure/reagent_crafting_bench/proc/attempt_complete_item_storage(obj/item/forging/complete/complete_item, mob/living/user) + if(length(forging_complete_container[complete_item.type]) >= MAX_FORGING_COMPLETE_ITEMS) + balloon_alert(user, "[initial(complete_item.name)] drawer is full!") + else + forging_complete_container[complete_item.type] += complete_item + complete_item.forceMove(src) + balloon_alert(user, "stashed [initial(complete_item.name)]") + +/obj/structure/reagent_crafting_bench/proc/clear_empty_stacks() + var/obj/item/stack/sheet/my_stack + var/list/my_list + for(var/stack_type in stack_item_container) + my_stack = stack_item_container[stack_type] + if(isnull(my_stack) || my_stack.amount < 1) + stack_item_container.Remove(stack_type) + + for(var/complete_item_type in forging_complete_container) + for(var/obj/item/my_item in forging_complete_container[complete_item_type]) + if(isnull(my_item) || my_item.loc != src) + my_list = forging_complete_container[complete_item_type] + my_list.Remove(my_item) + +/obj/structure/reagent_crafting_bench/proc/generate_stack_held_list_radial() + clear_empty_stacks() + var/list/returner = list() + var/datum/radial_menu_choice/option + var/obj/item/stack/sheet/my_sheet + var/obj/item/forging/complete/my_complete + for(var/complete_type in forging_complete_container) + if(length(forging_complete_container[complete_type]) > 0) + option = new + my_complete = forging_complete_container[complete_type][1] + option.image = image(icon = initial(my_complete.icon), icon_state = initial(my_complete.icon_state)) + option.name = initial(my_complete.name) + option.info = length(forging_complete_container[complete_type]) + returner[complete_type] = option + for(var/stack_type in stack_item_container) + option = new + my_sheet = stack_item_container[stack_type] + option.image = image(icon = initial(my_sheet.icon), icon_state = initial(my_sheet.icon_state)) + option.name = initial(my_sheet.name) + option.info = my_sheet.amount + returner[stack_type] = option + return returner /// Gets movable atoms within one tile of range of the crafting bench /obj/structure/reagent_crafting_bench/proc/get_environment() @@ -389,3 +532,5 @@ #undef WEAPON_COMPLETION_WOOD_AMOUNT #undef BAD_HIT_PENALTY +#undef WEAPON_ASSEMBLY_SPEED +#undef MAX_FORGING_COMPLETE_ITEMS diff --git a/modular_skyrat/modules/reagent_forging/code/crafting_bench_recipes.dm b/modular_skyrat/modules/reagent_forging/code/crafting_bench_recipes.dm index 3699a85ac1f..ae1fb0c215b 100644 --- a/modular_skyrat/modules/reagent_forging/code/crafting_bench_recipes.dm +++ b/modular_skyrat/modules/reagent_forging/code/crafting_bench_recipes.dm @@ -1,18 +1,190 @@ /datum/crafting_bench_recipe + abstract_type = /datum/crafting_bench_recipe /// The name of the recipe to show var/recipe_name = "generic debug recipe" + /// What appears in the infobox when viewed in the crafting menu + var/recipe_desc = "generic debug recipe" /// The items required to create the resulting item var/list/recipe_requirements /// What the end result of this recipe should be var/resulting_item = /obj/item/forging /// If we use the materials from the component parts var/transfers_materials = TRUE - /// How many times should you have to swing the hammer to finish this item - var/required_good_hits = 6 + /// How long it takes at the crafting bench to assemble + var/time_to_assemble = 2 SECONDS /// What skill is relevant to the creation of this item? var/relevant_skill = /datum/skill/smithing + /// How much EXP to give to relevant_skill? + var/exp_give = 5 + /// What skill level is required in that creation? + var/relevant_skill_level = 0 + /// Does the recipe also require a specific trait? + var/required_traits = null /// How much experience in our relevant skill do we give upon completion? var/relevant_skill_reward = 30 + /// Do the crafting pieces go into the result item, or get qdel'd? + var/insert_ingredients_into_product_contents + /// What memory to give upon completion + var/completion_memory_given = null + /// Exp bonus if the player doesn't have a memory of making this + var/first_time_completion_exp_bonus = 70 + /// Is this a multiple item product? + var/multiple_item_result = FALSE + +///Creates the item using the given list of ingredients -- it's assumed that this is only called after the list is verified to contain all ingredients. Probably shouldn't be parent called due to ingredient deletion. +/datum/crafting_bench_recipe/proc/create_using_item_list(list/item_list, mob/living/user, construction_location) + var/obj/item/returner = new resulting_item(construction_location) + apply_perfect_and_completion_bonuses(item_list, returner) + transfer_reagent_imbues_from_ingredients_to_product(item_list, returner, user) + put_materials_in_product_from_ingredients(item_list, returner) + consume_crafting_ingredients(item_list, returner) + move_to_world(returner, construction_location) + give_experience(user) + return returner + +/datum/crafting_bench_recipe/proc/transfer_reagent_imbues_from_ingredients_to_product(list/ingredients, obj/item/product, mob/living/user) + //imbued of the final product should be approx. imbued of all containing products + var/datum/component/reagent_imbued/output_reagent_component = product.GetComponent(/datum/component/reagent_imbued) + var/datum/reagents/my_reagents = combine_reagent_imbues(ingredients) + if(!isnull(output_reagent_component) && USER_CAN_REAGENT_IMBUE(user)) + output_reagent_component.set_reagent_imbue(my_reagents, clear_source_reagents = TRUE) + +/datum/crafting_bench_recipe/proc/combine_reagent_imbues(list/reagent_imbued_items) + var/datum/reagents/reagents_sum = new(maximum = 4096, new_flags = NO_REACT) + var/datum/component/reagent_imbued/current_reagent_component + for(var/obj/item in reagent_imbued_items) + current_reagent_component = item.GetComponent(/datum/component/reagent_imbued) + if(!isnull(current_reagent_component)) + current_reagent_component.imbued_reagent.trans_to(reagents_sum, current_reagent_component.imbued_reagent.total_volume, copy_only = TRUE) + reagents_sum.maximum_volume = reagents_sum.total_volume + return reagents_sum + +/datum/crafting_bench_recipe/proc/put_materials_in_product_from_ingredients(list/ingredients, obj/item/product) + if(transfers_materials) + var/materials_to_transfer = list() + for(var/obj/requirement_item as anything in ingredients) + if(istype(requirement_item, /obj/item/forging/complete)) + if(!requirement_item.custom_materials || !transfers_materials) + continue + + for(var/custom_material in requirement_item.custom_materials) + materials_to_transfer[custom_material] += requirement_item.custom_materials[custom_material] + + product.set_custom_materials(materials_to_transfer, multiplier = 1) + + +/datum/crafting_bench_recipe/proc/consume_crafting_ingredients(list/things_to_use, obj/item/product) + for(var/obj/item/thing in things_to_use) + if(isstack(thing)) + var/obj/item/stack/stack_thing + stack_thing = thing + var/stack_type = stack_thing.merge_type + var/amount_to_subtract = recipe_requirements[stack_type] + if(insert_ingredients_into_product_contents) + var/obj/item/stack/temp_stack = stack_thing.split_stack(amount_to_subtract) + temp_stack.forceMove(product) + else + stack_thing.use(amount_to_subtract, transfer = FALSE, check = TRUE) + else + if(insert_ingredients_into_product_contents) + thing.forceMove(product) + else + qdel(thing) + +/datum/crafting_bench_recipe/proc/count_forgeable_items(things_to_use) + var/total_forge_items = 0 + for(var/obj/item/i in things_to_use) + if(istype(i, /obj/item/forging/complete)) + total_forge_items ++ + else if(!isnull(i.GetComponent(/datum/component/forge_smithable))) + total_forge_items ++ + return total_forge_items + + +/datum/crafting_bench_recipe/proc/get_total_completion_amount(list/things_to_use) + var/total_completion = 0 + for(var/obj/item/i in things_to_use) + if(istype(i, /obj/item/forging/complete)) + var/obj/item/forging/complete/complete_forging_item = i + total_completion += complete_forging_item.hammer_completion_amount + else if(!isnull(i.GetComponent(/datum/component/forge_smithable))) + var/datum/component/forge_smithable/smithing_component = i.GetComponent(/datum/component/forge_smithable) + total_completion += smithing_component.quality_points + return total_completion + +/datum/crafting_bench_recipe/proc/get_total_completion_ratio(list/things_to_use) + if (count_forgeable_items(things_to_use) == 0) + return 1 + return get_total_completion_amount(things_to_use) / count_forgeable_items(things_to_use) + +/datum/crafting_bench_recipe/proc/get_total_perfection_amount(list/things_to_use) + var/total_perfection = 0 + for(var/obj/item/i in things_to_use) + if(istype(i, /obj/item/forging/complete)) + var/obj/item/forging/complete/complete_forging_item = i + total_perfection += complete_forging_item.perfect_ratio + else if(!isnull(i.GetComponent(/datum/component/forge_smithable))) + var/datum/component/forge_smithable/smithing_component = i.GetComponent(/datum/component/forge_smithable) + total_perfection += smithing_component.get_perfect_ratio() + return total_perfection + +/datum/crafting_bench_recipe/proc/get_total_perfection_ratio(list/things_to_use) + if (count_forgeable_items(things_to_use) == 0) + return 1 + return get_total_perfection_amount(things_to_use) / count_forgeable_items(things_to_use) + +/datum/crafting_bench_recipe/proc/find_from_list(list/item_list, type) + for(var/obj/item/forging/complete/temp_item in item_list) + if(istype(temp_item, type)) + return temp_item + return null + +/datum/crafting_bench_recipe/proc/apply_perfect_and_completion_bonuses(list/things_to_use, obj/item/product) + var/pieces_completion_ratio = get_total_completion_ratio(things_to_use) + var/pieces_perfection_ratio = get_total_perfection_ratio(things_to_use) + var/datum/component/forge_smithable/smith_component = product.GetComponent(/datum/component/forge_smithable) + if(!isnull(smith_component)) + smith_component.set_completion_and_perfection_ratios(pieces_completion_ratio, pieces_perfection_ratio) + +/datum/crafting_bench_recipe/proc/get_recipe_requirements_description() + var/list/returner = list() + for(var/obj/requirement_item as anything in recipe_requirements) + if(!recipe_requirements[requirement_item]) + returner += span_boldwarning("[requirement_item] does not have an amount required set, this should not happen, report it.") + continue + + returner += get_ingredient_description(requirement_item) + return returner + +/datum/crafting_bench_recipe/proc/get_ingredient_description(obj/requirement_item) + return span_notice("[recipe_requirements[requirement_item]] - [initial(requirement_item.name)]") + +/datum/crafting_bench_recipe/proc/get_smithing_memory(obj/item/product) + return completion_memory_given + +/datum/crafting_bench_recipe/proc/give_experience(mob/user, item_list, obj/item/product) + if(!isnull(user?.mind)) + var/completion = get_total_completion_ratio(item_list) + var/memory_type = get_smithing_memory(product) + var/memory_bonus_exp = 0 + if(completion >= 1) + if(!isnull(memory_type) && isnull(user?.mind?.memories[memory_type])) + user.add_mob_memory(memory_type, protagonist = user) + memory_bonus_exp = first_time_completion_exp_bonus + + var/exp_give_mult = completion * get_material_quality_points_mult(product.get_master_material()) + user.mind.adjust_experience(relevant_skill, exp_give * exp_give_mult + memory_bonus_exp) + +/datum/crafting_bench_recipe/proc/move_to_world(obj/item/product, place_to_move_to) + if(istype(place_to_move_to, /obj/structure/reagent_crafting_bench)) + var/obj/structure/reagent_crafting_bench/my_bench = place_to_move_to + my_bench.add_completed_forged_item(product) + else + product.forceMove(place_to_move_to) + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// WEAPON COMPLETION ///////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// /datum/crafting_bench_recipe/weapon_completion_recipe //Exists so I don't have to modify the code too much for weapon completion recipe_name = "generic weapon completion recipe (should not be visible)" @@ -20,140 +192,377 @@ /obj/item/stack/sheet/mineral/wood = 2, ) -/datum/crafting_bench_recipe/plate_helmet +/datum/crafting_bench_recipe/weapon_completion_recipe/create_using_item_list(list/item_list, mob/living/user, construction_location) + //apparently i fuggin have to write my own version of is_type_in_list + var/obj/item/forging/complete/weapon_head = find_from_list(item_list, /obj/item/forging/complete) + if(isnull(weapon_head)) + stack_trace("[src] didn't contain a valid reagent smithing weapon head when its recipe was completed!") + return + var/obj/item/returner = new weapon_head.spawning_item(construction_location) + apply_perfect_and_completion_bonuses(item_list, returner) + transfer_reagent_imbues_from_ingredients_to_product(item_list, returner, user) + put_materials_in_product_from_ingredients(item_list, returner, user) + give_experience(user, item_list, returner) + move_to_world(returner, construction_location) + + consume_crafting_ingredients(item_list, returner) + return returner + +/datum/crafting_bench_recipe/weapon_completion_recipe/get_smithing_memory(obj/item/product) + //o, the humanity (of code debt that i don't want to bother refactoring) + var/datum/memory/smithing/actual_memory + for(var/smithing_subtype in subtypesof(/datum/memory/smithing)) + actual_memory = new smithing_subtype + if(istype(product, initial(actual_memory.smithed_item_type))) + qdel(actual_memory) + return smithing_subtype + else + qdel(actual_memory) + stack_trace("[product] doesn't have an assigned brain memory type in modular_skyrat/modules/reagent_forging/code/memories.dm !") + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// ARMOR COMPLETION ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/crafting_bench_recipe/wearable/plate_armor/plate_helmet recipe_name = "plate helmet" + recipe_desc = "Protective headgear. Smithing oil and perfected metalworking will make it even more protective." recipe_requirements = list( - /obj/item/forging/complete/plate = 4, + /obj/item/stack/sheet/leather = 1, + /obj/item/forging/complete/plate = 1, ) resulting_item = /obj/item/clothing/head/helmet/forging_plate_helmet - required_good_hits = 8 + time_to_assemble = 1.5 SECONDS + completion_memory_given = /datum/memory/smithing/helmet -/datum/crafting_bench_recipe/plate_vest + +/datum/crafting_bench_recipe/wearable/plate_armor/plate_vest recipe_name = "plate vest" + recipe_desc = "Protective chestplating. Smithing oil and perfected metalworking will make it even more protective." recipe_requirements = list( - /obj/item/forging/complete/plate = 6, + /obj/item/stack/sheet/leather = 1, + /obj/item/forging/complete/plate = 1, ) resulting_item = /obj/item/clothing/suit/armor/forging_plate_armor - required_good_hits = 12 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/plate_vest -/datum/crafting_bench_recipe/plate_gloves +/datum/crafting_bench_recipe/wearable/plate_armor/plate_gloves recipe_name = "plate gloves" + recipe_desc = "Protective bracers. Smithing oil and perfected metalworking will make it even more protective." recipe_requirements = list( - /obj/item/forging/complete/plate = 2, + /obj/item/stack/sheet/leather = 1, + /obj/item/forging/complete/plate = 1, ) resulting_item = /obj/item/clothing/gloves/forging_plate_gloves - required_good_hits = 4 + time_to_assemble = 2 SECONDS + completion_memory_given = /datum/memory/smithing/gloves -/datum/crafting_bench_recipe/plate_boots +/datum/crafting_bench_recipe/wearable/plate_armor/plate_boots recipe_name = "plate boots" + recipe_desc = "Protective greaves. Smithing oil and perfected metalworking will make it even more protective." recipe_requirements = list( - /obj/item/forging/complete/plate = 4, + /obj/item/stack/sheet/leather = 1, + /obj/item/forging/complete/plate = 1, ) resulting_item = /obj/item/clothing/shoes/forging_plate_boots - required_good_hits = 8 + time_to_assemble = 2 SECONDS + completion_memory_given = /datum/memory/smithing/boots -/datum/crafting_bench_recipe/horse_shoes +/datum/crafting_bench_recipe/wearable/plate_armor/horse_shoes recipe_name = "horse shoes" + recipe_desc = "Protective... horse shoes? Smithing oil and perfected metalworking will make it even more protective." recipe_requirements = list( - /obj/item/forging/complete/chain = 4, + /obj/item/forging/complete/chain = 1, + /obj/item/stack/sheet/iron = 2, ) resulting_item = /obj/item/clothing/shoes/horseshoe/reagent_clothing - required_good_hits = 8 + time_to_assemble = 1.5 SECONDS + completion_memory_given = /datum/memory/smithing/horseshoes -/datum/crafting_bench_recipe/ring +/datum/crafting_bench_recipe/wearable/ring recipe_name = "ring" + recipe_desc = "A small ring that imbues the wearer with reagents. Perfected metalworking will make it imbue more reagents at once. Smithing oil increases its durability." recipe_requirements = list( - /obj/item/forging/complete/chain = 2, + /obj/item/forging/complete/chain = 1, ) resulting_item = /obj/item/clothing/gloves/ring/reagent_clothing - required_good_hits = 4 + time_to_assemble = 4 SECONDS + completion_memory_given = /datum/memory/smithing/ring -/datum/crafting_bench_recipe/collar +/datum/crafting_bench_recipe/wearable/collar recipe_name = "collar" + recipe_desc = "A small collar that imbues the wearer with reagents. Perfected metalworking will make it imbue more reagents at once. Smithing oil increases its durability." recipe_requirements = list( - /obj/item/forging/complete/chain = 3, + /obj/item/forging/complete/chain = 1, ) resulting_item = /obj/item/clothing/neck/collar/reagent_clothing - required_good_hits = 6 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/collar -/datum/crafting_bench_recipe/handcuffs +/datum/crafting_bench_recipe/wearable/handcuffs recipe_name = "handcuffs" + recipe_desc = "A pair of handcuffs; they need to be purged of reagents before they can be used." recipe_requirements = list( - /obj/item/forging/complete/chain = 5, + /obj/item/forging/complete/chain = 1, ) resulting_item = /obj/item/restraints/handcuffs/reagent_clothing - required_good_hits = 10 + time_to_assemble = 5 SECONDS -/datum/crafting_bench_recipe/borer_cage - recipe_name = "cortical borer cage" - recipe_requirements = list( - /obj/item/forging/complete/plate = 6, - ) - resulting_item = /obj/item/cortical_cage - required_good_hits = 12 - -/datum/crafting_bench_recipe/pavise - recipe_name = "pavise" - recipe_requirements = list( - /obj/item/forging/complete/plate = 8, - ) - resulting_item = /obj/item/shield/buckler/reagent_weapon/pavise - required_good_hits = 16 +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// SPECIAL WEAPONS /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// /datum/crafting_bench_recipe/buckler recipe_name = "buckler" + recipe_desc = "A small shield. Perfected metalworking will make it more protective. Smithing oil will enhance its stopping power." recipe_requirements = list( - /obj/item/forging/complete/plate = 5, + /obj/item/forging/complete/plate = 1, + /obj/item/stack/sheet/mineral/wood = 2, ) resulting_item = /obj/item/shield/buckler/reagent_weapon - required_good_hits = 10 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/buckler -/datum/crafting_bench_recipe/coil - recipe_name = "coil" - recipe_requirements = list( - /obj/item/forging/complete/chain = 2, - ) - resulting_item = /obj/item/forging/coil - required_good_hits = 4 - -/datum/crafting_bench_recipe/seed_mesh - recipe_name = "seed mesh" +/datum/crafting_bench_recipe/pavise + recipe_name = "pavise" + recipe_desc = "A large shield. Perfected metalworking will make it more protective. Smithing oil will enhance its stopping power." recipe_requirements = list( /obj/item/forging/complete/plate = 1, - /obj/item/forging/complete/chain = 2, + /obj/item/stack/sheet/mineral/wood = 4, ) - resulting_item = /obj/item/seed_mesh - required_good_hits = 10 - -/datum/crafting_bench_recipe/centrifuge - recipe_name = "centrifuge" - recipe_requirements = list( - /obj/item/forging/complete/plate = 1, - ) - resulting_item = /obj/item/reagent_containers/cup/primitive_centrifuge - required_good_hits = 4 + resulting_item = /obj/item/shield/buckler/reagent_weapon/pavise + time_to_assemble = 6 SECONDS + completion_memory_given = /datum/memory/smithing/pavise /datum/crafting_bench_recipe/bokken recipe_name = "bokken" + recipe_desc = "A wooden sword, typically meant for practice swordfighting." recipe_requirements = list( /obj/item/stack/sheet/mineral/wood = 4, ) - resulting_item = /obj/item/forging/reagent_weapon/bokken - required_good_hits = 8 + resulting_item = /obj/item/melee/forged_reagent_weapon/bokken + time_to_assemble = 3 SECONDS /datum/crafting_bench_recipe/bow recipe_name = "bow" + recipe_desc = "A length of curved wood attached at both ends with string. Launches arrows." recipe_requirements = list( /obj/item/stack/sheet/mineral/wood = 4, ) resulting_item = /obj/item/forging/incomplete_bow - required_good_hits = 8 + time_to_assemble = 4 SECONDS + +/datum/crafting_bench_recipe/revolver + recipe_name = "revolver" + recipe_desc = "A classical single-action revolver." + recipe_requirements = list( + /obj/item/stack/sheet/mineral/wood = 1, + /obj/item/forging/complete/revolver_cylinder = 1, + /obj/item/forging/complete/revolver_frame = 1 + ) + resulting_item = /obj/item/gun/ballistic/revolver/handcrafted_single_action + time_to_assemble = 4 SECONDS + completion_memory_given = /datum/memory/smithing/revolver + relevant_skill_level = 7 + required_traits = list(TRAIT_KNOW_GUNSMITHING) + +/datum/crafting_bench_recipe/revolver/consume_crafting_ingredients(list/things_to_use, obj/item/product) + var/obj/item/gun/ballistic/revolver/handcrafted_single_action/gun = product + for(var/obj/item/thing in things_to_use) + if(isstack(thing)) + var/obj/item/stack/stack_thing + stack_thing = thing + var/stack_type = stack_thing.type + var/amount_to_subtract = recipe_requirements[stack_type] + if(insert_ingredients_into_product_contents) + var/obj/item/stack/temp_stack = stack_thing.split_stack(amount_to_subtract) + temp_stack.forceMove(product) + else + stack_thing.use(amount_to_subtract, transfer = FALSE, check = TRUE) + else if(istype(thing, /obj/item/forging/complete/revolver_cylinder)) + thing.forceMove(product) + gun.cylinder = thing + else if(istype(thing, /obj/item/forging/complete/revolver_frame)) + thing.forceMove(product) + gun.frame = thing + else + qdel(thing) + product.update_appearance() + +/datum/crafting_bench_recipe/revolver/put_materials_in_product_from_ingredients(list/ingredients, obj/item/product) + if(transfers_materials) + var/materials_to_transfer = list() + for(var/obj/requirement_item as anything in ingredients) + if(istype(requirement_item, /obj/item/forging/complete)) + if(!requirement_item.custom_materials || !transfers_materials) + continue + if(istype(requirement_item, /obj/item/forging/complete/revolver_cylinder)) + product.set_material_slot(/datum/material_slot/revolver/cylinder, requirement_item.get_master_material()) + else + for(var/custom_material in requirement_item.custom_materials) + materials_to_transfer[custom_material] += requirement_item.custom_materials[custom_material] + + product.set_custom_materials(materials_to_transfer, multiplier = 1) + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// BELT COMPLETION /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/datum/crafting_bench_recipe/cowboy_holster + recipe_name = "cowboy holster" + recipe_desc = "A quickdraw holster-belt that can additionally store extra ammo boxes." + recipe_requirements = list( + /obj/item/stack/sheet/leather = 3, + /obj/item/stack/sheet/mineral/gold = 1, + /obj/item/stack/sheet/mineral/silver = 1, + ) + resulting_item = /obj/item/storage/belt/hip_holster/cowboy + relevant_skill_level = 5 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/cowboy_holster + required_traits = list(TRAIT_KNOW_GUNSMITHING) + +/datum/crafting_bench_recipe/charging_holster + recipe_name = "charging holster" + recipe_desc = "A bluespace holster-belt that can charge electric guns stored in it." + recipe_requirements = list( + /obj/item/stack/sheet/plastic = 1, + /obj/item/stack/sheet/mineral/gold = 1, + /obj/item/stack/sheet/bluespace_crystal = 1, + /obj/item/circuitboard/machine/recharger = 1, + ) + resulting_item = /obj/item/storage/belt/hip_holster/charging + relevant_skill_level = 7 + exp_give = 20 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/charging_holster + required_traits = list(TRAIT_KNOW_GUNSMITHING, TRAIT_KNOW_CIRCUIT_SMITHING) + +/datum/crafting_bench_recipe/charging_holster/get_ingredient_description(requirement_item) + //machine boards for weapon rechargers are just called "weapon recharger" so this needs to be clarified + if(requirement_item != /obj/item/circuitboard/machine/recharger) + . = ..() + else + return span_notice("[recipe_requirements[requirement_item]] - machine board (weapon recharger)") + +/datum/crafting_bench_recipe/crusader_belt + recipe_name = "scabbard-utility belt" + recipe_desc = "A belt that holds one sword and has a number of side-pouches for holding miscellaneous items." + recipe_requirements = list( + /obj/item/stack/sheet/leather = 3, + /obj/item/stack/sheet/cloth = 2, + /obj/item/stack/sheet/mineral/gold = 1, + ) + resulting_item = /obj/item/storage/belt/crusader + relevant_skill_level = 4 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/crusader_belt + required_traits = list(TRAIT_KNOW_ADVANCED_SMITHING) + +/datum/crafting_bench_recipe/multi_scabbard + recipe_name = "multi-scabbard harness" + recipe_desc = "A set of harnesses that allow the wearer to carry multiple bulky weapons." + recipe_requirements = list( + /obj/item/stack/sheet/leather = 6, + /obj/item/stack/sheet/cloth = 6, + ) + resulting_item = /obj/item/storage/belt/sheath/multi + relevant_skill_level = 4 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/multi_scabbard + required_traits = list(TRAIT_KNOW_ADVANCED_SMITHING) + +/datum/crafting_bench_recipe/repairing_scabbard + recipe_name = "repairing scabbard" + recipe_desc = "A belt that holds one weapon and slowly restores its integrity over time." + recipe_requirements = list( + /obj/item/stack/sheet/leather = 3, + /obj/item/stack/sheet/mineral/silver = 1, + /obj/item/stack/sheet/bluespace_crystal = 1, + ) + resulting_item = /obj/item/storage/belt/sheath/repairing + relevant_skill_level = 5 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/repairing_scabbard + required_traits = list(TRAIT_KNOW_ADVANCED_SMITHING) + +/datum/crafting_bench_recipe/knifethrower + recipe_name = "knifethrower's belt" + recipe_desc = "A belt that can hold a ton of knives." + recipe_requirements = list( + /obj/item/stack/sheet/leather = 3, + /obj/item/stack/sheet/cloth = 2, + ) + resulting_item = /obj/item/storage/belt/knifethrowers_belt + relevant_skill_level = 4 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/knifethrower + required_traits = list(TRAIT_KNOW_ADVANCED_SMITHING) + +/datum/crafting_bench_recipe/bluespace_plants + recipe_name = "bluespace plant bag" + recipe_desc = "A bluespace bag specially designed for holding plants." + recipe_requirements = list( + /obj/item/stack/sheet/cloth = 5, + /obj/item/grown/bananapeel/bluespace = 1, + /obj/item/stack/sheet/bluespace_crystal = 1, + ) + exp_give = 30 + resulting_item = /obj/item/storage/bag/plants/bluespace + relevant_skill_level = 2 + time_to_assemble = 3 SECONDS + completion_memory_given = /datum/memory/smithing/bluespace_plants + required_traits = list(TRAIT_KNOW_CIRCUIT_SMITHING) + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// MISC COMPLETION ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/crafting_bench_recipe/borer_cage + recipe_name = "cortical borer cage" + recipe_requirements = list( + /obj/item/forging/complete/plate = 3, + ) + resulting_item = /obj/item/cortical_cage + time_to_assemble = 2 SECONDS + + +/datum/crafting_bench_recipe/coil + recipe_name = "coil" + recipe_desc = "A helically-shaped length of metal. Purged of reagents, it is used for crafting other materials." + recipe_requirements = list( + /obj/item/forging/complete/chain = 1, + ) + resulting_item = /obj/item/forging/coil + time_to_assemble = 2 SECONDS + +/datum/crafting_bench_recipe/seed_mesh + recipe_name = "seed mesh" + recipe_desc = "A sifting mesh used to pull seeds from sand. Reagents have no effect on this." + recipe_requirements = list( + /obj/item/forging/complete/plate = 1, + /obj/item/forging/complete/chain = 1, + ) + resulting_item = /obj/item/seed_mesh + time_to_assemble = 10 SECONDS + +/datum/crafting_bench_recipe/centrifuge + recipe_name = "centrifuge" + recipe_desc = "A centrifuge is used to separate reagents in a solution. It typically must be purged of reagents to keep the solution clean." + recipe_requirements = list( + /obj/item/forging/complete/plate = 1, + ) + resulting_item = /obj/item/reagent_containers/cup/primitive_centrifuge + time_to_assemble = 7 SECONDS /datum/crafting_bench_recipe/empty_circuit recipe_name = "circuit" + recipe_desc = "Hand-made circuitry that can be used in electronic devices. Reagents are usually cleaned out before it can function correctly." recipe_requirements = list( /obj/item/forging/complete/plate = 1, /obj/item/forging/coil = 1, ) resulting_item = /obj/item/empty_circuit - required_good_hits = 4 + time_to_assemble = 20 SECONDS + required_traits = list(TRAIT_KNOW_CIRCUIT_SMITHING) diff --git a/modular_skyrat/modules/reagent_forging/code/forge.dm b/modular_skyrat/modules/reagent_forging/code/forge.dm index 7e4263a402f..f0781de4cf0 100644 --- a/modular_skyrat/modules/reagent_forging/code/forge.dm +++ b/modular_skyrat/modules/reagent_forging/code/forge.dm @@ -28,6 +28,10 @@ /// The chance per piece of wood added that charcoal will form later #define CHARCOAL_CHANCE 45 +/// Stats for the expelled gas from running the forge +#define FORGE_FUMES_HEAT FIRE_MINIMUM_TEMPERATURE_TO_EXIST +#define FORGE_FUMES_VOLUME 1 + /// The minimum units of a reagent rerquired to imbue it into a weapon #define MINIMUM_IMBUING_REAGENT_AMOUNT 100 @@ -64,8 +68,6 @@ var/reagent_forging = FALSE /// Cooldown time for processing on the forge COOLDOWN_DECLARE(forging_cooldown) - /// Is the forge in use or not? If true, prevents most interactions with the forge - var/in_use = FALSE /// The current 'level' of the forge, how upgraded is it from zero to three var/forge_level = FORGE_LEVEL_YOU_PLAY_LIKE_A_NOOB /// What smoke particles should be coming out of the forge @@ -76,26 +78,46 @@ var/static/list/choice_list = list( "Chain" = /obj/item/forging/incomplete/chain, "Plate" = /obj/item/forging/incomplete/plate, + "Rapier" = /obj/item/forging/incomplete/rapier, "Sword" = /obj/item/forging/incomplete/sword, "Katana" = /obj/item/forging/incomplete/katana, "Dagger" = /obj/item/forging/incomplete/dagger, - "Rapier" = /obj/item/forging/incomplete/rapier, - "Staff" = /obj/item/forging/incomplete/staff, "Spear" = /obj/item/forging/incomplete/spear, - "Axe" = /obj/item/forging/incomplete/axe, "Hammer" = /obj/item/forging/incomplete/hammer, + "Axe" = /obj/item/forging/incomplete/axe, + "Arrowhead" = /obj/item/forging/incomplete/arrowhead, + "Revolver Frame" = /obj/item/forging/incomplete/revolver_frame, + "Revolver Cylinder" = /obj/item/forging/incomplete/revolver_cylinder, + "Staff" = /obj/item/forging/incomplete/staff, "Pickaxe" = /obj/item/forging/incomplete/pickaxe, "Shovel" = /obj/item/forging/incomplete/shovel, - "Arrowhead" = /obj/item/forging/incomplete/arrowhead, "Rail Nail" = /obj/item/forging/incomplete/rail_nail, "Rail Cart" = /obj/item/forging/incomplete/rail_cart, ) /// List of possible choices for the selection radial var/list/radial_choice_list = list() + /// Filters the radial choice list by required skill + var/list/choice_list_skill_filter = list( + "Revolver Frame" = /datum/skill/smithing, + "Revolver Cylinder" = /datum/skill/smithing, + ) + /// Filters the radial choice list by required level in its skill; true means corresponding element requires it + var/list/choice_list_skill_level_filter = list( + "Revolver Frame" = 7, + "Revolver Cylinder" = 7, + ) + /// Filters the radial choice list by if it requires the smithing skillchip; true means corresponding element requires it + var/list/choice_list_trait_filter = list( + "Revolver Frame" = list(TRAIT_KNOW_GUNSMITHING), + "Revolver Cylinder" = list(TRAIT_KNOW_GUNSMITHING), + ) + /// Descriptions of the forging items in hint bubbles for the radial menu + var/list/radial_choice_hinttext = list() /obj/structure/reagent_forge/examine(mob/user) . = ..() - + . += span_notice("You could secure or unsecure it with a wrench.") + . += span_notice("You could pry it apart with a crowbar.") if(used_tray) . += span_notice("It has [used_tray] in it, which can be removed with an empty hand.") else @@ -149,6 +171,8 @@ /obj/structure/reagent_forge/Initialize(mapload) . = ..() + if(!mapload) + anchored = FALSE START_PROCESSING(SSobj, src) populate_radial_choice_list() update_appearance() @@ -162,9 +186,38 @@ if(length(radial_choice_list)) return + var/obj/resulting_item + var/datum/radial_menu_choice/option for(var/forge_option in choice_list) - var/obj/resulting_item = choice_list[forge_option] - radial_choice_list[forge_option] = image(icon = initial(resulting_item.icon), icon_state = initial(resulting_item.icon_state)) + resulting_item = choice_list[forge_option] + option = new + option.image = image(icon = initial(resulting_item.icon), icon_state = initial(resulting_item.icon_state)) + option.name = initial(resulting_item.name) + option.info = initial(resulting_item.desc) + radial_choice_list[forge_option] = option + +///Exclusively gives all the radial choices that the user can know how to make. +/obj/structure/reagent_forge/proc/get_filtered_radial_choices(mob/living/user) + var/returner = list() + if(isnull(user?.mind)) + return returner + + for(var/key,value in radial_choice_list) + if(user_can_craft(user, key)) + returner[key] = value + + return returner + +/obj/structure/reagent_forge/proc/user_can_craft(mob/living/user, key) + if(isnull(user?.mind)) + return FALSE + if(!isnull(choice_list_skill_filter[key]) && user.mind.get_skill_level(choice_list_skill_filter[key]) < choice_list_skill_level_filter[key]) + return FALSE + if(!isnull(choice_list_trait_filter[key])) + for(var/my_trait in choice_list_trait_filter[key]) + if (!HAS_TRAIT(user, my_trait)) + return FALSE + return TRUE /obj/structure/reagent_forge/Destroy() STOP_PROCESSING(SSobj, src) @@ -182,30 +235,23 @@ add_overlay(gold_overlay) if(used_tray) // If we have a tray inside, check if the forge is on or not, then give the corresponding tray overlay - var/image/tray_overlay = image(icon = icon, icon_state = "forge_tray_[check_fuel(just_checking = TRUE) ? "active" : "inactive"]") + var/image/tray_overlay = image(icon = icon, icon_state = "forge_tray_[check_fuel(0) ? "active" : "inactive"]") add_overlay(tray_overlay) /// Checks if the forge has fuel, if so what type. If it has either type of fuel, returns TRUE, otherwise returns FALSE. just_checking will check if there is fuel without taking actions -/obj/structure/reagent_forge/proc/check_fuel(just_checking = FALSE) - if(forge_fuel_strong) // Check for strong fuel (coal) first, as it has more power over weaker fuels - if(just_checking) - return TRUE +/obj/structure/reagent_forge/proc/check_fuel(time_to_reduce_by = 0) + if(forge_fuel_strong > 0) // Check for strong fuel (coal) first, as it has more power over weaker fuels - forge_fuel_strong -= 5 SECONDS + forge_fuel_strong -= time_to_reduce_by target_temperature = 100 return TRUE - if(forge_fuel_weak) // If there's no strong fuel, maybe we have weak fuel (wood) - if(just_checking) - return TRUE + if(forge_fuel_weak > 0) // If there's no strong fuel, maybe we have weak fuel (wood) - forge_fuel_weak -= 5 SECONDS + forge_fuel_weak -= time_to_reduce_by target_temperature = 50 return TRUE - if(just_checking) - return FALSE - target_temperature = minimum_target_temperature // If the forge has no fuel, then we should lowly return to the minimum lowest temp we can do return FALSE @@ -216,32 +262,17 @@ reagent_forging = TRUE update_appearance() -/// Creates both a fail message balloon alert, and sets in_use to false -/obj/structure/reagent_forge/proc/fail_message(mob/living/user, message) - balloon_alert(user, message) - in_use = FALSE /// Adjust the temperature to head towards the target temperature, changing icon and creating light if the temperature is rising -/obj/structure/reagent_forge/proc/check_temp() +/obj/structure/reagent_forge/proc/check_temp(seconds_per_tick) if(forge_temperature > target_temperature) // Being above the target temperature will cause the forge to cool down - forge_temperature -= (FORGE_DEFAULT_TEMPERATURE_CHANGE - temperature_loss_reduction) + forge_temperature -= (FORGE_DEFAULT_TEMPERATURE_CHANGE - temperature_loss_reduction) * seconds_per_tick return else if((forge_temperature < target_temperature) && (forge_fuel_weak || forge_fuel_strong)) // Being below the target temp, and having fuel, will cause the temp to rise - forge_temperature += FORGE_DEFAULT_TEMPERATURE_CHANGE + forge_temperature += FORGE_DEFAULT_TEMPERATURE_CHANGE * seconds_per_tick return -/// If the forge is in use, checks if there is an oven tray, then if there are any mobs actually in use range. If not sets the forge to not be in use. -/obj/structure/reagent_forge/proc/check_in_use() - if(!in_use) - return - - if(used_tray) // We check if there's a tray because trays inside of the forge count as it being in use, even if nobody is around - return - - for(var/mob/living/living_mob in range(1,src)) - if(!living_mob) - in_use = FALSE /// Spawns a piece of coal at the forge and renames it to charcoal /obj/structure/reagent_forge/proc/spawn_coal() @@ -249,26 +280,29 @@ spawn_coal.name = "charcoal" /obj/structure/reagent_forge/process(seconds_per_tick) + /* if(!COOLDOWN_FINISHED(src, forging_cooldown)) return - COOLDOWN_START(src, forging_cooldown, 5 SECONDS) - check_fuel() - check_temp() - check_in_use() // This is here to ensure the forge doesn't remain in_use if it really isn't + COOLDOWN_START(src, forging_cooldown, 5 SECONDS) */ + check_fuel(seconds_per_tick) + check_temp(seconds_per_tick) + try_expel_gas(seconds_per_tick) - - - if(!used_tray && check_fuel(just_checking = TRUE)) + if(!used_tray && check_fuel()) set_smoke_state(SMOKE_STATE_NOT_COOKING) // If there is no tray but we have fuel, use the not cooking smoke state return - if(!check_fuel(just_checking = TRUE)) // If there's no fuel, remove it all + if(!check_fuel()) // If there's no fuel, remove it all set_smoke_state(SMOKE_STATE_NONE) return handle_baking_things(seconds_per_tick) +/obj/structure/reagent_forge/proc/try_expel_gas(seconds_per_tick) + if(check_fuel()) + atmos_spawn_air("[GAS_CO2]=[FORGE_FUMES_VOLUME * seconds_per_tick];[TURF_TEMPERATURE(FORGE_FUMES_HEAT)]") + /// Sends signals to bake and items on the used tray, setting the smoke state of the forge according to the most cooked item in it /obj/structure/reagent_forge/proc/handle_baking_things(seconds_per_tick) if(forge_temperature < MIN_FORGE_TEMP) // If we are below minimum forge temp, don't continue on to cooking @@ -422,12 +456,8 @@ smelt_ore(attacking_item, user) return - if(attacking_item.GetComponent(/datum/component/reagent_weapon)) - handle_weapon_imbue(attacking_item, user) - return - - if(attacking_item.GetComponent(/datum/component/reagent_clothing)) - handle_clothing_imbue(attacking_item, user) + if(attacking_item.GetComponent(/datum/component/reagent_imbued/weapon)) + handle_reagent_imbue(attacking_item, user) return /obj/structure/reagent_forge/attackby(obj/item/attacking_item, mob/living/user, params) @@ -435,10 +465,8 @@ add_tray_to_forge(user, attacking_item) return TRUE - if(in_use) // If the forge is currently in use by someone (or there is a tray in it) then we cannot use it - if(used_tray) - balloon_alert(user, "remove [used_tray] first") - balloon_alert(user, "forge busy") + if(used_tray) + balloon_alert(user, "remove [used_tray] first") return TRUE if(istype(attacking_item, /obj/item/stack/sheet/mineral/wood)) // Wood is a weak fuel, and will only get the forge up to 50 temperature @@ -453,12 +481,14 @@ smelt_ore(attacking_item, user) return TRUE - if(attacking_item.GetComponent(/datum/component/reagent_weapon)) - handle_weapon_imbue(attacking_item, user) + if(attacking_item.GetComponent(/datum/component/reagent_imbued) && attacking_item.reagents.total_volume > 1) + handle_reagent_imbue(attacking_item, user) return TRUE - if(attacking_item.GetComponent(/datum/component/reagent_clothing)) - handle_clothing_imbue(attacking_item, user) + if(attacking_item.GetComponent(/datum/component/forge_smithable)) + var/datum/component/forge_smithable/mycomponent = attacking_item.GetComponent(/datum/component/forge_smithable) + mycomponent.heat_for_smithing(FORGE_HEATING_DURATION) + balloon_alert(user, "heated [attacking_item]") return TRUE if(istype(attacking_item, /obj/item/ceramic)) @@ -473,6 +503,9 @@ handle_metal_cup_melting(attacking_item, user) return TRUE + if(istype(attacking_item, /obj/item/stack)) + stack_item_to_forgeable(user, attacking_item, burn_hand = TRUE) + return TRUE return ..() /// Take the given tray and place it inside the forge, updating everything relevant to that @@ -490,7 +523,6 @@ balloon_alert_to_viewers("put [tray] in [src]") used_tray = tray - in_use = TRUE // You can't use the forge if there's a tray sitting in it update_appearance() /// Take the used_tray and spit it out, updating everything relevant to that @@ -506,34 +538,28 @@ else used_tray.forceMove(get_turf(src)) used_tray = null - in_use = FALSE /// Adds to either the strong or weak fuel timers from the given stack /obj/structure/reagent_forge/proc/refuel(obj/item/stack/refueling_stack, mob/living/user, is_strong_fuel = FALSE) - in_use = TRUE if(is_strong_fuel) if(forge_fuel_strong >= 5 MINUTES) - fail_message(user, "[src] is full on coal") + balloon_alert(user, "[src] is full on coal") return if(forge_fuel_weak >= 5 MINUTES) - fail_message(user, "[src] is full on wood") + balloon_alert(user, "[src] is full on wood") return - balloon_alert_to_viewers("refueling...") - var/obj/item/stack/sheet/stack_sheet = refueling_stack - if(!do_after(user, 3 SECONDS, target = src) || !stack_sheet.use(1)) - fail_message(user, "stopped fueling") + if(!stack_sheet.use(1)) + balloon_alert(user, "not enough fuel") return if(is_strong_fuel) forge_fuel_strong += 5 MINUTES else forge_fuel_weak += 5 MINUTES - in_use = FALSE balloon_alert(user, "fueled [src]") - user.mind.adjust_experience(/datum/skill/smithing, 5) // You gain small amounts of experience from useful fueling if(prob(CHARCOAL_CHANCE) && !is_strong_fuel) to_chat(user, span_notice("[src]'s fuel is packed densely enough to have made some charcoal!")) @@ -541,22 +567,22 @@ /// Takes given ore and smelts it, possibly producing extra sheets if upgraded /obj/structure/reagent_forge/proc/smelt_ore(obj/item/stack/ore/ore_item, mob/living/user) - in_use = TRUE - + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return if(forge_temperature < MIN_FORGE_TEMP) - fail_message(user, "forge too cool") + balloon_alert(user, "forge too cool") return var/skill_modifier = user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_SPEED_MODIFIER) if(!ore_item.refined_type) - fail_message(user, "cannot smelt [ore_item]") + balloon_alert(user, "cannot smelt [ore_item]") return balloon_alert_to_viewers("smelting...") - if(!do_after(user, skill_modifier * 3 SECONDS, target = src)) - fail_message(user, "stopped smelting [ore_item]") + if(!do_after(user, skill_modifier * 3 SECONDS, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) + balloon_alert(user, "stopped smelting [ore_item]") return var/src_turf = get_turf(src) @@ -566,119 +592,60 @@ for(var/spawn_ore in 1 to ore_to_sheet_amount) new spawning_item(src_turf) - in_use = FALSE qdel(ore_item) return -/// Handles weapon reagent imbuing -/obj/structure/reagent_forge/proc/handle_weapon_imbue(obj/attacking_item, mob/living/user) - if(user.mind.get_skill_level(/datum/skill/smithing) < SKILL_LEVEL_MASTER) - to_chat(user, span_danger("You need more experience to understand the fine workings of imbuing!")) - return - //This code will refuse all non-ashwalkers & non-icecats from imbuing - if(!ishuman(user)) - to_chat(user, span_danger("It is impossible for you to imbue!")) //maybe remove (ashwalkers & icecats only) after some time +/// Handles reagent imbuing +/obj/structure/reagent_forge/proc/handle_reagent_imbue(obj/attacking_item, mob/living/user) + if(!USER_CAN_REAGENT_IMBUE(user)) + to_chat(user, span_danger("You don't know the right trick to imbue this weapon!")) return - in_use = TRUE - balloon_alert_to_viewers("imbuing...") + if(attacking_item?.reagents?.total_volume < 1) + to_chat(user, span_danger("You need to coat the blade in a reagent before you can imbue it!")) + return - var/obj/item/attacking_weapon = attacking_item + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return - var/datum/component/reagent_weapon/weapon_component = attacking_weapon.GetComponent(/datum/component/reagent_weapon) + var/datum/component/reagent_imbued/weapon_component = attacking_item.GetComponent(/datum/component/reagent_imbued) if(!weapon_component) - fail_message(user, "cannot imbue") + balloon_alert(user, "[attacking_item] is unimbueable!") return - if(length(weapon_component.imbued_reagent)) - fail_message(user, "already imbued") - return - - if(!do_after(user, 10 SECONDS, target = src)) - fail_message(user, "stopped imbuing") - return - - for(var/datum/reagent/weapon_reagent as anything in attacking_weapon.reagents.reagent_list) - if(weapon_reagent.volume < MINIMUM_IMBUING_REAGENT_AMOUNT) - attacking_weapon.reagents.remove_reagent(weapon_reagent.type) - continue - - weapon_component.imbued_reagent += weapon_reagent.type - attacking_weapon.name = "[weapon_reagent.name] [attacking_weapon.name]" - - attacking_weapon.color = mix_color_from_reagents(attacking_weapon.reagents.reagent_list) - if(attacking_weapon.armour_penetration < 10) - attacking_weapon.armour_penetration = 0 - else - attacking_weapon.armour_penetration -= 10 - balloon_alert_to_viewers("imbued [attacking_weapon]") - user.mind.adjust_experience(/datum/skill/smithing, 60) - playsound(src, 'sound/effects/magic/demon_consume.ogg', 50, TRUE) - in_use = FALSE - return TRUE - -/// Handles clothing imbuing, extremely similar to weapon imbuing but not in the same proc because of how uhh... goofy the way this has to be done is -/obj/structure/reagent_forge/proc/handle_clothing_imbue(obj/attacking_item, mob/living/user) - if(user.mind.get_skill_level(/datum/skill/smithing) < SKILL_LEVEL_MASTER) - to_chat(user, span_danger("You need more experience to understand the fine workings of imbuing!")) - return - //This code will refuse all non-ashwalkers & non-icecats from imbuing - if(!ishuman(user)) - to_chat(user, span_danger("It is impossible for you to imbue!")) //maybe remove (ashwalkers & icecats only) after some time - return - - in_use = TRUE balloon_alert_to_viewers("imbuing...") - var/obj/item/attacking_clothing = attacking_item - - var/datum/component/reagent_clothing/clothing_component = attacking_clothing.GetComponent(/datum/component/reagent_clothing) - if(!clothing_component) - fail_message(user, "cannot imbue") + if(!do_after(user, 10 SECONDS, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) + balloon_alert(user, "stopped imbuing") return - if(length(clothing_component.imbued_reagent)) - fail_message(user, "already imbued") - return + if(USER_CAN_REAGENT_IMBUE(user)) + weapon_component.set_reagent_imbue(attacking_item.reagents, clear_source_reagents = TRUE) - if(!do_after(user, 10 SECONDS, target = src)) - fail_message(user, "stopped imbuing") - return - - for(var/datum/reagent/clothing_reagent as anything in attacking_clothing.reagents.reagent_list) - if(clothing_reagent.volume < MINIMUM_IMBUING_REAGENT_AMOUNT) - attacking_clothing.reagents.remove_reagent(clothing_reagent.type, include_subtypes = TRUE) - continue - - clothing_component.imbued_reagent += clothing_reagent.type - attacking_clothing.name = "[clothing_reagent.name] [attacking_clothing.name]" - - attacking_clothing.color = mix_color_from_reagents(attacking_clothing.reagents.reagent_list) - balloon_alert_to_viewers("imbued [attacking_clothing]") - user.mind.adjust_experience(/datum/skill/smithing, 60) + balloon_alert_to_viewers("imbued [attacking_item]") playsound(src, 'sound/effects/magic/demon_consume.ogg', 50, TRUE) - in_use = FALSE return TRUE /// Sets ceramic items from their unusable state into their finished form /obj/structure/reagent_forge/proc/handle_ceramics(obj/attacking_item, mob/living/user) - in_use = TRUE + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return if(forge_temperature < MIN_FORGE_TEMP) - fail_message(user, "forge too cool") + balloon_alert(user, "forge too cool") return var/obj/item/ceramic/ceramic_item = attacking_item var/ceramic_speed = user.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) * BASELINE_ACTION_TIME if(!ceramic_item.forge_item) - fail_message(user, "cannot set [ceramic_item]") + balloon_alert(user, "cannot set [ceramic_item]") return balloon_alert_to_viewers("setting [ceramic_item]") - if(!do_after(user, ceramic_speed, target = src)) - fail_message("stopped setting [ceramic_item]") + if(!do_after(user, ceramic_speed, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) + balloon_alert(user, "stopped setting [ceramic_item]") return balloon_alert(user, "finished setting [ceramic_item]") @@ -686,14 +653,13 @@ user.mind.adjust_experience(/datum/skill/production, 50) spawned_ceramic.color = ceramic_item.color qdel(ceramic_item) - in_use = FALSE /// Handles the creation of molten glass from glass sheets /obj/structure/reagent_forge/proc/handle_glass_sheet_melting(obj/attacking_item, mob/living/user) - in_use = TRUE - + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return if(forge_temperature < MIN_FORGE_TEMP) - fail_message(user, "forge too cool") + balloon_alert(user, "forge too cool") return var/obj/item/stack/sheet/glass/glass_item = attacking_item @@ -702,11 +668,10 @@ balloon_alert_to_viewers("heating...") - if(!do_after(user, glassblowing_speed, target = src) || !glass_item.use(1)) - fail_message(user, "stopped heating [glass_item]") + if(!do_after(user, glassblowing_speed, target = src, interaction_key = DOAFTER_SMITHING_FORGE) || !glass_item.use(1)) + balloon_alert(user, "stopped heating [glass_item]") return - in_use = FALSE var/obj/item/glassblowing/molten_glass/spawned_glass = new /obj/item/glassblowing/molten_glass(get_turf(src)) user.mind.adjust_experience(/datum/skill/production, 10) COOLDOWN_START(spawned_glass, remaining_heat, glassblowing_amount) @@ -714,10 +679,10 @@ /// Handles creating molten glass from a metal cup filled with sand /obj/structure/reagent_forge/proc/handle_metal_cup_melting(obj/attacking_item, mob/living/user) - in_use = TRUE - + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return if(forge_temperature < MIN_FORGE_TEMP) - fail_message(user, "forge too cool") + balloon_alert(user, "forge too cool") return var/obj/item/glassblowing/metal_cup/metal_item = attacking_item @@ -725,16 +690,15 @@ var/glassblowing_amount = BASELINE_HEATING_DURATION / user.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) if(!metal_item.has_sand) - fail_message(user, "[metal_item] has no sand") + balloon_alert(user, "[metal_item] has no sand") return balloon_alert_to_viewers("heating...") - if(!do_after(user, glassblowing_speed, target = src)) - fail_message(user, "stopped heating [metal_item]") + if(!do_after(user, glassblowing_speed, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) + balloon_alert(user, "stopped heating [metal_item]") return - in_use = FALSE metal_item.has_sand = FALSE metal_item.icon_state = "metal_cup_empty" // This should be handled a better way but presently this is how it works var/obj/item/glassblowing/molten_glass/spawned_glass = new /obj/item/glassblowing/molten_glass(get_turf(src)) @@ -743,160 +707,183 @@ spawned_glass.total_time = glassblowing_amount /obj/structure/reagent_forge/billow_act(mob/living/user, obj/item/tool) - if(in_use) // Preventing billow use if the forge is in use to prevent spam - fail_message(user, "forge busy") - return ITEM_INTERACT_SUCCESS + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return var/skill_modifier = user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_SPEED_MODIFIER) var/obj/item/forging/forge_item = tool - in_use = TRUE - if(!forge_fuel_strong && !forge_fuel_weak) - fail_message(user, "no fuel in [src]") + balloon_alert(user, "no fuel in [src]") return ITEM_INTERACT_SUCCESS if(forge_temperature >= MAX_FORGE_TEMP) - fail_message(user, "[src] cannot heat further") + balloon_alert(user, "[src] cannot heat further") return ITEM_INTERACT_SUCCESS balloon_alert_to_viewers("billowing...") while(forge_temperature < 91) - if(!do_after(user, skill_modifier * forge_item.toolspeed, target = src)) + if(!do_after(user, skill_modifier * forge_item.toolspeed * 15 DECISECONDS, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) balloon_alert_to_viewers("stopped billowing") return ITEM_INTERACT_SUCCESS forge_temperature += 10 - user.mind.adjust_experience(/datum/skill/smithing, 5) // Billowing, like fueling, gives you some experience in forging - in_use = FALSE balloon_alert(user, "successfully heated [src]") return ITEM_INTERACT_SUCCESS /obj/structure/reagent_forge/tong_act(mob/living/user, obj/item/tool) - var/skill_modifier = user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_SPEED_MODIFIER) - var/obj/item/forging/forge_item = tool - - if(in_use || forge_item.in_use) - fail_message(user, "forge busy") - return ITEM_INTERACT_SUCCESS - - in_use = TRUE - forge_item.in_use = TRUE + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return + var/obj/item/tongs_contents = locate(/obj/item/) in tool.contents if(forge_temperature < MIN_FORGE_TEMP) - fail_message(user, "forge too cool") - forge_item.in_use = FALSE - return ITEM_INTERACT_SUCCESS + balloon_alert(user, "forge too cool") + return ITEM_INTERACT_BLOCKING // Here we check the item used on us (tongs) for an incomplete forge item of some kind to heat - var/obj/item/forging/incomplete/search_incomplete = locate(/obj/item/forging/incomplete) in forge_item.contents - if(search_incomplete) - balloon_alert_to_viewers("heating [search_incomplete]") - - if(!do_after(user, skill_modifier * forge_item.toolspeed, target = src)) - balloon_alert_to_viewers("stopped heating [search_incomplete]") - forge_item.in_use = FALSE - return ITEM_INTERACT_SUCCESS - - COOLDOWN_START(search_incomplete, heating_remainder, FORGE_HEATING_DURATION) - in_use = FALSE - forge_item.in_use = FALSE - user.mind.adjust_experience(/datum/skill/smithing, 5) // Heating up forge items grants some experience - balloon_alert(user, "successfully heated [search_incomplete]") + var/datum/component/forge_smithable/smithable = tongs_contents?.GetComponent(/datum/component/forge_smithable) + if(!isnull(smithable)) + smithable.heat_for_smithing(FORGE_HEATING_DURATION) + balloon_alert(user, "heated [tongs_contents]") return ITEM_INTERACT_SUCCESS // Here we check the item used on us (tongs) for a stack of some kind to create an object from - var/obj/item/stack/search_stack = locate(/obj/item/stack) in forge_item.contents + var/obj/item/stack/search_stack = locate(/obj/item/stack) in tool.contents if(search_stack) - var/user_choice = show_radial_menu(user, src, radial_choice_list, radius = 38, require_near = TRUE, tooltips = TRUE) - if(!user_choice) - fail_message(user, "nothing chosen") - forge_item.in_use = FALSE - return ITEM_INTERACT_SUCCESS + return stack_item_to_forgeable(user, search_stack, tool) - // Sets up a list of the materials to give to the item later - var/list/material_list = list() - - if(search_stack.material_type) - material_list[SSmaterials.get_material(search_stack.material_type)] = SHEET_MATERIAL_AMOUNT - - else - for(var/material in search_stack.custom_materials) - material_list[material] = SHEET_MATERIAL_AMOUNT - - if(!search_stack.use(1)) - fail_message(user, "not enough of [search_stack]") - forge_item.in_use = FALSE - return ITEM_INTERACT_SUCCESS - - balloon_alert_to_viewers("heating [search_stack]") - - if(!do_after(user, skill_modifier * forge_item.toolspeed, target = src)) - balloon_alert_to_viewers("stopped heating [search_stack]") - forge_item.in_use = FALSE - return ITEM_INTERACT_SUCCESS - - var/spawn_item = choice_list[user_choice] - var/obj/item/forging/incomplete/incomplete_item = new spawn_item(get_turf(src)) - - if(material_list) - incomplete_item.set_custom_materials(material_list) - - COOLDOWN_START(incomplete_item, heating_remainder, FORGE_HEATING_DURATION) - in_use = FALSE - forge_item.in_use = FALSE - balloon_alert(user, "prepared [search_incomplete] into [user_choice]") - search_stack = locate(/obj/item/stack) in forge_item.contents - - if(!search_stack) - forge_item.icon_state = "tong_empty" - return ITEM_INTERACT_SUCCESS - - in_use = FALSE - forge_item.in_use = FALSE return ITEM_INTERACT_SUCCESS +/obj/structure/reagent_forge/proc/stack_item_to_forgeable(mob/living/user, obj/item/stack/search_stack, obj/item/tool = null, burn_hand = FALSE) + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return + + if(forge_temperature < MIN_FORGE_TEMP) + balloon_alert(user, "forge too cool") + return ITEM_INTERACT_BLOCKING + + if(values_sum(search_stack.mats_per_unit) < SHEET_MATERIAL_AMOUNT) + balloon_alert(user, "not enough material in [search_stack]") + return ITEM_INTERACT_BLOCKING + + var/list/my_list = get_filtered_radial_choices(user) + var/user_choice = show_radial_menu(user, src, my_list, radius = 38, require_near = TRUE, tooltips = TRUE) + var/skill_modifier = user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_SPEED_MODIFIER) + + if(!user_choice) + balloon_alert(user, "nothing chosen") + return ITEM_INTERACT_BLOCKING + + // Sets up a list of the materials to give to the item later + var/list/material_list = list() + + if(search_stack.material_type) + material_list[SSmaterials.get_material(search_stack.material_type)] = SHEET_MATERIAL_AMOUNT + + else + for(var/material in search_stack.custom_materials) + material_list[material] = SHEET_MATERIAL_AMOUNT + + if(search_stack.amount < 1) + balloon_alert(user, "not enough of [search_stack]") + return ITEM_INTERACT_BLOCKING + + balloon_alert_to_viewers("heating [search_stack]") + + if(!burn_hand) + if(!isnull(tool)) + if(!do_after(user, skill_modifier * tool.toolspeed * 2 SECONDS, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) + balloon_alert_to_viewers("stopped heating [search_stack]") + return ITEM_INTERACT_BLOCKING + else + stack_trace("[src] had its 'make forgeable item' function called, with burn_hand = false and tool = null! This should never happen!") + else + to_chat(user, span_warning("Heating the [search_stack] without equipment seems like a bad idea...")) + if(!do_after(user, skill_modifier * 5 SECONDS, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) + balloon_alert_to_viewers("stopped heating [search_stack]") + return ITEM_INTERACT_BLOCKING + + var/hand_protected = FALSE + var/mob/living/carbon/human/human_user = user + if(!istype(human_user) || HAS_TRAIT(human_user, TRAIT_RESISTHEAT) || HAS_TRAIT(human_user, TRAIT_RESISTHEATHANDS)) + hand_protected = TRUE + else if(!istype(human_user.gloves, /obj/item/clothing/gloves)) + hand_protected = FALSE + else + var/obj/item/clothing/gloves/gloves = human_user.gloves + if(gloves.max_heat_protection_temperature) + hand_protected = (gloves.max_heat_protection_temperature > 360) + if(!hand_protected) + var/hitzone = user.held_index_to_dir(user.active_hand_index) == "r" ? BODY_ZONE_PRECISE_R_HAND : BODY_ZONE_PRECISE_L_HAND + user.apply_damage(35, BURN, hitzone) + playsound(src, 'sound/effects/wounds/sizzle1.ogg', 50, TRUE) + to_chat(user, span_danger("You burn your hand putting [search_stack] in [src]!")) + user.add_mood_event("burnt_thumb", /datum/mood_event/burnt_thumb) + + if(!search_stack.use(1)) + balloon_alert(user, "not enough of [search_stack]") + return ITEM_INTERACT_BLOCKING + + var/spawn_item = choice_list[user_choice] + var/obj/item/forging/incomplete/incomplete_item = new spawn_item(get_turf(src)) + + if(material_list) + incomplete_item.set_custom_materials(material_list) + + var/datum/component/forge_smithable/smith_component = incomplete_item.GetComponent(/datum/component/forge_smithable) + if(!isnull(smith_component)) + COOLDOWN_START(smith_component, heating_remainder, FORGE_HEATING_DURATION) + + balloon_alert(user, "prepared [search_stack] into [user_choice]") + if(!isnull(tool) && length(tool.contents) > 0) + tool.icon_state = "tong_empty" + + return ITEM_INTERACT_SUCCESS + + /obj/structure/reagent_forge/blowrod_act(mob/living/user, obj/item/tool) + if(DOING_INTERACTION(user, DOAFTER_SMITHING_FORGE)) + return var/obj/item/glassblowing/blowing_rod/blowing_item = tool var/glassblowing_speed = user.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) * BASELINE_ACTION_TIME var/glassblowing_amount = BASELINE_HEATING_DURATION / user.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) - if(in_use) - to_chat(user, span_warning("You cannot do multiple things at the same time!")) - return ITEM_INTERACT_SUCCESS - in_use = TRUE - if(forge_temperature < MIN_FORGE_TEMP) - fail_message(user, "The temperature is not hot enough to start heating [blowing_item].") + balloon_alert(user, "not hot enough to start heating [blowing_item]") return ITEM_INTERACT_SUCCESS var/obj/item/glassblowing/molten_glass/find_glass = locate() in blowing_item.contents if(!find_glass) - fail_message(user, "[blowing_item] does not have any glass to heat up.") + balloon_alert(user, "[blowing_item] does not have any glass to heat up.") return ITEM_INTERACT_SUCCESS if(!COOLDOWN_FINISHED(find_glass, remaining_heat)) - fail_message(user, "[find_glass] is still has remaining heat.") + balloon_alert(user, "[find_glass] is still has remaining heat.") return ITEM_INTERACT_SUCCESS to_chat(user, span_notice("You begin heating up [blowing_item].")) - if(!do_after(user, glassblowing_speed, target = src)) - fail_message(user, "[blowing_item] is interrupted in its heating process.") + if(!do_after(user, glassblowing_speed, target = src, interaction_key = DOAFTER_SMITHING_FORGE)) + balloon_alert(user, "[blowing_item] is interrupted in its heating process.") return ITEM_INTERACT_SUCCESS COOLDOWN_START(find_glass, remaining_heat, glassblowing_amount) find_glass.total_time = glassblowing_amount to_chat(user, span_notice("You finish heating up [blowing_item].")) - user.mind.adjust_experience(/datum/skill/smithing, 5) user.mind.adjust_experience(/datum/skill/production, 10) - in_use = FALSE return ITEM_INTERACT_SUCCESS /obj/structure/reagent_forge/wrench_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src) + set_anchored(!anchored) + balloon_alert_to_viewers(anchored ? "secured" : "unsecured") + return TRUE + +/obj/structure/reagent_forge/crowbar_act(mob/living/user, obj/item/tool) + tool.play_tool_sound(src) + balloon_alert(user, "pried apart") deconstruct(TRUE) return TRUE @@ -959,3 +946,6 @@ #undef SMOKE_STATE_NEUTRAL #undef SMOKE_STATE_BAD #undef SMOKE_STATE_NOT_COOKING + +#undef FORGE_FUMES_HEAT +#undef FORGE_FUMES_VOLUME diff --git a/modular_skyrat/modules/reagent_forging/code/forge_belts.dm b/modular_skyrat/modules/reagent_forging/code/forge_belts.dm new file mode 100644 index 00000000000..78a031cc7d5 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/forge_belts.dm @@ -0,0 +1,542 @@ +//////////////////////////////////////////////////////////////////////////// +////////////////////////////// HOLSTERS //////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// + +//can't be a subtype of item/storage/belt/holster -- that can be suit storaged per \code\__DEFINES\inventory.dm +//hip holsters SHOULDN'T be able to be suit storaged. +/obj/item/storage/belt/hip_holster + name = "hip holster" + desc = "you shouldn't be seeing this." + abstract_type = /obj/item/storage/belt/hip_holster + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi' + icon_state = "cowboy_holster" + inhand_icon_state = "holster" + worn_icon_state = "holster" + alternate_worn_layer = null + storage_type = /datum/storage/holster + +/obj/item/storage/belt/hip_holster/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + update_label() +/obj/item/storage/belt/hip_holster/Exited(atom/movable/gone, direction) + . = ..() + update_label() +/obj/item/storage/belt/hip_holster/proc/update_label(list/contents_to_check = null) + if(isnull(contents_to_check)) + contents_to_check = contents + var/list/noteworthy_contents = list() + for(var/obj/item/I in contents_to_check) + if(!istype(I, /obj/item/ammo_box)) + noteworthy_contents += I + + if(length(noteworthy_contents) > 0) + name = "[src::name] containing [english_list(noteworthy_contents)]" + else + name = src::name + +//copy of the holster/equipped proc. +/obj/item/storage/belt/hip_holster/equipped(mob/user, slot) + . = ..() + if(slot & (ITEM_SLOT_BELT|ITEM_SLOT_SUITSTORE)) + ADD_CLOTHING_TRAIT(user, TRAIT_GUNFLIP) + +/obj/item/storage/belt/hip_holster/update_overlays() + . = ..() + . += get_guns_contained_overlays() + +/obj/item/storage/belt/hip_holster/proc/get_guns_contained_overlays(list/contents_to_check = null) + var/list/returner = list() + if(contents_to_check == null) + contents_to_check = contents + for(var/obj/item/I in contents_to_check) + if(istype(I, /obj/item/gun/ballistic/revolver)) + returner += mutable_appearance('modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi', "belt_gun_wood") + return returner + if(istype(I, /obj/item/gun)) + returner += mutable_appearance('modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi', "belt_gun_black") + return returner + if(istype(I, /obj/item/melee/baton/security)) + returner += mutable_appearance('modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi', "belt_gun_baton") + return returner + +/obj/item/storage/belt/hip_holster/cowboy + name = "quickdraw holster" + desc = "A rugged leather belt. Can carry a handgun; the holster pouch makes it like reflex to draw your gun. Also comes with some side pockets for speedloaders and magazines." + icon_state = "cowboy_holster" + inhand_icon_state = "utility" + worn_icon_state = "utility" + storage_type = /datum/storage/cowboy_holster + +/datum/storage/cowboy_holster + max_slots = 4 + max_total_storage = 6 + open_sound = 'sound/items/handling/holster_open.ogg' + open_sound_vary = TRUE + +/datum/storage/cowboy_holster/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound, list/holdables) + . = ..() + if(length(holdables)) + set_holdable(holdables) + return + + set_holdable(list( + /obj/item/food/grown/banana, + /obj/item/gun/ballistic/automatic/pistol, + /obj/item/gun/ballistic/revolver, + /obj/item/gun/energy/disabler, + /obj/item/gun/energy/dueling, + /obj/item/gun/energy/e_gun/hos, + /obj/item/gun/energy/e_gun/mini, + /obj/item/gun/energy/laser/captain, + /obj/item/gun/energy/laser/pistol, + /obj/item/gun/energy/laser/thermal, + /obj/item/ammo_box/advanced/s12gauge, + /obj/item/ammo_box/magazine, + /obj/item/ammo_box/speedloader, + )) + +/datum/storage/cowboy_holster/attempt_insert(obj/item/to_insert, mob/user, override = FALSE, force = STORAGE_NOT_LOCKED, messages = TRUE) + . = ..() + sort_contents() + refresh_views() + //parent.update_appearance() + +//resorts the contents so that guns are always the first thing pulled. +/datum/storage/cowboy_holster/proc/sort_contents() + var/list/gunz = list() + var/list/everything_else = list() + var/obj/parent_obj = parent + if(!isnull(parent_obj)) + for(var/obj/item/i in parent_obj.contents) + if(istype(i, /obj/item/gun/)) + gunz += i + else + everything_else += i + parent_obj.contents = everything_else + gunz + +///////////////////////////////////////////////// + +/obj/item/storage/belt/hip_holster/charging + name = "charging holster" + desc = "A sophisticated plastic holster belt. Bluespace tech allows it to store almost anything a standard weapon charger can; it can slowly charge that item." + icon_state = "charger_belt" + inhand_icon_state = "security" + worn_icon_state = "security" + storage_type = /datum/storage/charging_holster //null //datum/storage/charging_holster + var/obj/machinery/recharger/belt_charger/my_charger + var/obj/item/storage/box/real_storage + +/obj/item/storage/belt/hip_holster/charging/Initialize(mapload) + my_charger = new(src) + my_charger.my_belt = src + real_storage = new(src) + . = ..() + +/obj/item/storage/belt/hip_holster/charging/Destroy() + QDEL_NULL(my_charger) + QDEL_NULL(real_storage) + . = ..() + +/obj/item/storage/belt/hip_holster/charging/update_label(list/contents_to_check = null) + . = ..(real_storage.contents) + +/obj/item/storage/belt/hip_holster/charging/get_guns_contained_overlays(list/contents_to_check = null) + . = ..(real_storage.contents) + +/obj/item/storage/belt/hip_holster/charging/update_overlays() + . = ..() + if(length(real_storage?.contents) > 0) + if(!isnull(my_charger?.charging)) + var/icon_to_use = "charger_belt_[(my_charger.using_power ? "charging" : "fullcharge")]" + . += mutable_appearance(icon, icon_to_use, alpha = src.alpha) + else + . += mutable_appearance(icon, "charger_belt_precharging") + +/datum/storage/charging_holster + max_slots = 1 + max_total_storage = 12 + open_sound = 'sound/items/handling/holster_open.ogg' + open_sound_vary = TRUE + max_specific_storage = WEIGHT_CLASS_BULKY + var/delay_before_charging = 20 SECONDS + var/timer_id + var/obj/machinery/recharger/belt_charger/my_charger + +/datum/storage/charging_holster/New() + . = ..() + if(istype(parent, /obj/item/storage/belt/hip_holster/charging)) + var/obj/item/storage/belt/hip_holster/charging/my_belt = parent + my_charger = my_belt.my_charger + set_real_location(my_belt.real_storage) + set_holdable(list( + /obj/item/gun/energy, + /obj/item/melee/baton/security, + /obj/item/ammo_box/magazine/recharge, + /obj/item/modular_computer,)) + +/datum/storage/charging_holster/can_insert(obj/item/to_insert, mob/user, messages = TRUE, force = STORAGE_NOT_LOCKED) + if(istype(to_insert, /obj/item/gun/energy)) + var/obj/item/gun/energy/energy_gun = to_insert + if(!energy_gun.can_charge) + to_chat(user, span_notice("Your gun has no external power connector.")) + return FALSE + . = ..() + +/datum/storage/charging_holster/attempt_insert(obj/item/to_insert, mob/user, override = FALSE, force = STORAGE_NOT_LOCKED, messages = TRUE) + . = ..() + if(.) + parent.update_overlays() + timer_id = addtimer(CALLBACK(my_charger, TYPE_PROC_REF(/obj/machinery/recharger/belt_charger, activate_with_item)), delay_before_charging, TIMER_STOPPABLE | TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_DELETE_ME) + +/datum/storage/charging_holster/attempt_remove(obj/item/thing, atom/remove_to_loc, silent = FALSE, visual_updates = TRUE) + . = ..() + if(.) + my_charger.charging = null + parent.update_overlays() + deltimer(timer_id) + +/obj/machinery/recharger/belt_charger + name = "belt charger" + desc = "the fact that you can see this means there's an error, call a dev!" + recharge_coeff = 0.2 + var/obj/item/storage/belt/hip_holster/charging/my_belt + +/obj/machinery/recharger/belt_charger/Destroy() + my_belt = null + . = ..() + +/obj/machinery/recharger/belt_charger/RefreshParts() + . = ..() + recharge_coeff = initial(recharge_coeff) + +/obj/machinery/recharger/belt_charger/proc/activate_with_item() + if(length(my_belt?.real_storage?.contents) > 0) + say("charging...") + charging = my_belt.real_storage.contents[1] + playsound(src, 'sound/machines/ping.ogg', 30, TRUE, frequency = 0.8) + START_PROCESSING(SSmachines, src) + update_use_power(ACTIVE_POWER_USE) + using_power = TRUE + update_appearance() + my_belt.update_overlays() + +/obj/machinery/recharger/belt_charger/process(seconds_per_tick) + if(length(my_belt?.real_storage?.contents) != 1 || my_belt?.real_storage.contents[1] != charging) + charging = null + return PROCESS_KILL + . = ..() + my_belt.update_appearance() + charging.update_appearance() + +/obj/machinery/recharger/belt_charger/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + to_chat(user, span_warning("You should insert the [tool] into the connected holster pouch, not the charging device itself!")) + return ITEM_INTERACT_BLOCKING + +//////////////////////////////////////////////////////////////////////////// +////////////////////////////// SHEATHS ///////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// + +/obj/item/storage/belt/crusader //Belt + sheath combination - still only holds one sword at a time though + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi' + worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/belt.dmi' + name = "scabbard-utility belt" + desc = "Holds an assortment of equipment for whatever situation an adventurer may encounter, as well as having an attached scabbard to hold a sword or bladed weapon." + icon_state = "crusader_belt" + worn_icon_state = "crusader_belt" + inhand_icon_state = "utility" + w_class = WEIGHT_CLASS_BULKY //Cant fit a sheath in your bag + interaction_flags_click = NEED_DEXTERITY + custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT) + +/obj/item/storage/belt/crusader/Initialize(mapload) + . = ..() + + create_storage( + max_slots = 2, + max_specific_storage = WEIGHT_CLASS_BULKY, //This makes sure swords and the pouches can fit in here - the whitelist keeps the bad stuff out + storage_type = /datum/storage/belt/crusader, + canhold = list( + /obj/item/storage/belt/storage_pouch, + /obj/item/melee/forged_reagent_weapon/sword, + /obj/item/melee/forged_reagent_weapon/rapier, + /obj/item/melee/forged_reagent_weapon/dagger, + /obj/item/melee/forged_reagent_weapon/katana, + /obj/item/melee/forged_reagent_weapon/bokken, + /obj/item/melee/sabre, + /obj/item/melee/parsnip_sabre, + /obj/item/claymore, + /obj/item/melee/cleric_mace, + /obj/item/knife, + /obj/item/melee/baton, + /obj/item/nullrod, //holds any subset of nullrod in the sheath-storage - - - + /obj/item/melee/energy/sword/nullrod, + ), + canthold = list( // - - - except the second list's items (no fedora in the sheath) + /obj/item/nullrod/armblade, + /obj/item/toy/plush/carpplushie/nullrod, + /obj/item/nullrod/chainsaw, + /obj/item/nullrod/bostaff, + /obj/item/nullrod/hammer, + /obj/item/nullrod/pitchfork, + /obj/item/nullrod/pride_hammer, + /obj/item/nullrod/spear, + /obj/item/nullrod/staff, + /obj/item/nullrod/fedora, + /obj/item/nullrod/godhand, + /obj/item/nullrod/whip, + ), + ) + atom_storage.allow_big_nesting = TRUE // Lets the pouch work + AddElement(/datum/element/update_icon_updates_onmob) + +//Overrides normal dumping code to instead dump from the pouch item inside +/datum/storage/belt/crusader/dump_content_at(atom/dest_object, mob/dumping_mob) + var/atom/used_belt = parent + if(!used_belt) + return + var/obj/item/storage/belt/storage_pouch/pouch = locate() in real_location + if(!pouch) + pouch.balloon_alert(dumping_mob, "no pouch!") + return //oopsie!! If we don't have a pouch! You're fucked! + if(locked) + pouch.balloon_alert(dumping_mob, "locked!") + return + pouch.atom_storage.dump_content_at(dest_object, dumping_mob) + +/obj/item/storage/belt/crusader/item_ctrl_click(mob/user) //Makes ctrl-click also open the inventory, so that you can open it with full hands without dropping the sword + . = ..() + atom_storage.show_contents(user) + return + +/obj/item/storage/belt/crusader/click_alt(mob/user) //This is basically the same as the normal sheath, but because there's always an item locked in the first slot it uses the second slot for swords + if(contents.len == 2) + var/obj/item/drawn_item = contents[2] + add_fingerprint(user) + playsound(src, 'sound/items/unsheath.ogg', 50, TRUE, -5) + if(!user.put_in_hands(drawn_item)) + to_chat(user, span_notice("You fumble for [drawn_item] and it falls on the floor.")) + update_appearance() + return CLICK_ACTION_SUCCESS + user.visible_message( + span_notice("[user] takes [drawn_item] out of [src]."), + span_notice("You take [drawn_item] out of [src].")) + update_appearance() + else + to_chat(user, span_warning("[src] is empty!")) + return CLICK_ACTION_SUCCESS + +/obj/item/storage/belt/crusader/update_icon(updates) + if(contents.len == 2) //Checks for a sword/rod in the sheath slot, changes the sprite accordingly + icon_state = "crusader_belt_sheathed" + worn_icon_state = "crusader_belt_sheathed" + else + icon_state = "crusader_belt" + worn_icon_state = "crusader_belt" + . = ..() + +/obj/item/storage/belt/crusader/examine(mob/user) + . = ..() + .+= span_notice("Ctrl-click it to easily open its inventory.") + if(contents.len == 2) //If there's no sword/rod in the sheath slot it doesnt display the alt-click instruction + . += span_notice("Alt-click it to quickly draw the blade.") + return + + +/obj/item/storage/belt/crusader/PopulateContents() + . = ..() + new /obj/item/storage/belt/storage_pouch(src) + +/obj/item/storage/belt/storage_pouch //seperate mini-storage inside the belt, leaving room for only one sword. Inspired by a (very poorly implemented) belt on Desert Rose + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi' + worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/belt.dmi' + name = "storage pouch" + desc = span_notice("Click on this to open your belt's inventory!") + icon_state = "storage_pouch_icon" + worn_icon_state = "no_name" //Intentionally sets the worn icon to an error + w_class = WEIGHT_CLASS_BULKY //Still cant put it in your bags, its technically a belt + anchored = 1 //Dont want people taking it out with their hands + +/obj/item/storage/belt/storage_pouch/attack_hand(mob/user, list/modifiers) //Opens the bag on click - considering it's already anchored, this makes it function similar to how ghosts can open all nested inventories + . = ..() + atom_storage.show_contents(user) + +/obj/item/storage/belt/storage_pouch/Initialize(mapload) + . = ..() + atom_storage.max_slots = 6 + atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL //Rather than have a huge whitelist, the belt can simply hold anything a pocket can hold - Can easily be changed if it somehow becomes an issue + +///////////////////////////////////////////////// + +/obj/item/storage/belt/sheath/multi + name = "multi-scabbard" + desc = "A set of harnesses that enable carrying multiple bulky swords and/or shields." + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi' + icon_state = "multiscabbard_swords_0" + inhand_icon_state = "sheath" + worn_icon_state = "sheath" + actions_types = null + storage_type = /datum/storage/multi_scabbard + +/obj/item/storage/belt/sheath/multi/update_icon(updates) + . = ..() + var/numswords = 0 + if(!isnull(contents) && contents.len > 0) + for(var/obj/item/i in contents) + if(istype(i, /obj/item/melee)) + numswords ++ + numswords = clamp(numswords, 0, 2) + icon_state = "multiscabbard_swords_[numswords]" + +/obj/item/storage/belt/sheath/multi/update_overlays() + . = ..() + var/obj/item/shield/my_shield = null + for(var/obj/item/i in contents) + if(istype(i, /obj/item/shield)) + my_shield = i + break + if(!isnull(my_shield)) + . += mutable_appearance(my_shield.icon, my_shield.icon_state) + +/datum/storage/multi_scabbard + max_slots = 2 + do_rustle = TRUE + max_specific_storage = WEIGHT_CLASS_HUGE + click_alt_open = FALSE + +/datum/storage/multi_scabbard/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(list( + /obj/item/melee/forged_reagent_weapon, + /obj/item/shield, + /obj/item/melee/sabre, + /obj/item/melee/parsnip_sabre, + /obj/item/claymore, + /obj/item/melee/cleric_mace, + /obj/item/knife, + /obj/item/melee/baton, + /obj/item/nullrod, + /obj/item/melee/energy/sword/nullrod, + )) + +////////////////////////////////////////////////////////////////// + +/obj/item/storage/belt/sheath/repairing + name = "repairing scabbard" + desc = "A bluespace crystal in this scabbard causes it to slowly rejuvenate whatever's stored inside it." + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi' + icon_state = "repairsheath" + inhand_icon_state = "sheath" + worn_icon_state = "sheath" + actions_types = null + storage_type = /datum/storage/repairing_scabbard + + var/integ_restoration_amount_per_seconds = 0.5 + +/obj/item/storage/belt/sheath/repairing/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + START_PROCESSING(SSdcs, src) + +/obj/item/storage/belt/sheath/repairing/process(seconds_per_tick) + if(contents.len == 0) + return PROCESS_KILL + + var/obj/item/my_item = contents[1] + if(isnull(my_item)) + return PROCESS_KILL + + if(my_item.get_integrity() < my_item.max_integrity) + my_item.repair_damage(integ_restoration_amount_per_seconds * seconds_per_tick) + +/datum/storage/repairing_scabbard + max_slots = 1 + do_rustle = FALSE + max_specific_storage = WEIGHT_CLASS_BULKY + click_alt_open = FALSE + +/datum/storage/repairing_scabbard/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(list( + /obj/item/melee/forged_reagent_weapon, + /obj/item/shield, + /obj/item/melee/sabre, + /obj/item/melee/parsnip_sabre, + /obj/item/claymore, + /obj/item/melee/cleric_mace, + /obj/item/knife, + /obj/item/melee/baton, + /obj/item/nullrod, + /obj/item/melee/energy/sword/nullrod, + )) + +///////////////////////////////////////////////// + +/obj/item/storage/belt/knifethrowers_belt + name = "knifethrower's belt" + desc = "Stores a frankly ridiculous number of knives and comparable short, bladed weapons. The shallow pocket depth makes it poor at storing other objects." + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi' + icon_state = "knifethrowers" + inhand_icon_state = "utility" + worn_icon_state = "utility" + drop_sound = 'sound/items/handling/toolbelt_drop.ogg' + pickup_sound = 'sound/items/handling/toolbelt_pickup.ogg' + storage_type = /datum/storage/knifethrowers + +/obj/item/storage/belt/knifethrowers_belt/update_overlays() + . = ..() + if(contents.len > 0) + var/icon_to_use = "belt_knives_[(contents.len > 8 ? 8 : contents.len)]" + . += mutable_appearance(icon, icon_to_use, alpha = src.alpha) + +/datum/storage/knifethrowers + max_slots = 14 + max_total_storage = 60 + open_sound = 'sound/items/handling/holster_open.ogg' + open_sound_vary = TRUE + +/datum/storage/knifethrowers/New() + . = ..() + set_holdable(list( + /obj/item/melee/forged_reagent_weapon/dagger, + /obj/item/knife, + /obj/item/pen, + /obj/item/scalpel, + /obj/item/shard, + /obj/item/spess_knife, + /obj/item/toy/toy_dagger, + )) + +///////////////////////////////////////////////// + +/obj/item/storage/bag/plants/bluespace + name = "bluespace plant bag" + desc = "A bluespace banana peel grants much larger storage capacity here. It malfunctions when trying to store specific mutant plants -- ironically, bluespace bananas are one such plant..." + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi' + icon_state = "bluespace_botany_bag" + worn_icon_state = "plantbag" + storage_type = /datum/storage/bag/plants_bluespace + +/datum/storage/bag/plants_bluespace + max_total_storage = 1000 + max_slots = 300 + +/datum/storage/bag/plants_bluespace/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(list( + /obj/item/food/grown, + /obj/item/graft, + /obj/item/grown, + /obj/item/food/honeycomb, + /obj/item/seeds, + ), list( + /obj/item/food/grown/banana/bluespace, + /obj/item/grown/bananapeel/bluespace, + /obj/item/food/grown/cherry_bomb, + /obj/item/food/grown/firelemon, + /obj/item/food/grown/gatfruit, + /obj/item/food/grown/holymelon, + /obj/item/food/grown/barrelmelon, + /obj/item/food/grown/tomato/blue/bluespace, + /obj/item/food/grown/tomato/killer, + ) + ) + diff --git a/modular_skyrat/modules/reagent_forging/code/forge_clothing.dm b/modular_skyrat/modules/reagent_forging/code/forge_clothing.dm index 781ca88788a..93ee36fbc95 100644 --- a/modular_skyrat/modules/reagent_forging/code/forge_clothing.dm +++ b/modular_skyrat/modules/reagent_forging/code/forge_clothing.dm @@ -1,3 +1,24 @@ +/datum/armor/perfect_forged_armor_bonus + melee = 20 + bullet = 20 + fire = 25 + laser = 10 + wound = 20 + +/datum/armor/smithing_oil_armor_bonus + melee = 10 + bullet = 10 + +/datum/armor/armor_forging_plate_armor + melee = 20 + bullet = 10 + fire = 50 + wound = 10 + laser = 15 + bomb = 25 + acid = 50 + wound = 50 + // Vests /obj/item/clothing/suit/armor/forging_plate_armor name = "reagent plate vest" @@ -10,22 +31,27 @@ icon_state = "plate_vest" supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON resistance_flags = FIRE_PROOF - skyrat_obj_flags = ANVIL_REPAIR armor_type = /datum/armor/armor_forging_plate_armor - material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR - -/datum/armor/armor_forging_plate_armor - melee = 40 - bullet = 40 - fire = 50 - wound = 30 + material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS /obj/item/clothing/suit/armor/forging_plate_armor/Initialize(mapload) . = ..() - AddComponent(/datum/component/armor_plate, 4) - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_OCLOTHING) + allowed += /obj/item/melee/forged_reagent_weapon + AddComponent(/datum/component/reagent_imbued/clothing, list(FORGE_EFFECT_ARMOR = /datum/armor/smithing_oil_armor_bonus), set_slot = ITEM_SLOT_OCLOTHING) + AddComponent(/datum/component/forge_smithable, \ + FORGING_CLOTHING_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS, \ + FORGING_CLOTHING_REFORGING_MAX_BAD_HITS, \ + FORGING_CLOTHING_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_ARMOR = /datum/armor/perfect_forged_armor_bonus), \ + incompletion_effects = list(FORGE_EFFECT_ARMOR, FORGE_EFFECT_DURABILITY)) - allowed += /obj/item/forging/reagent_weapon +/obj/item/clothing/suit/armor/forging_plate_armor/change_material_integrity(datum/material/material, amount, multiplier, removing = FALSE) + blacksmithing_change_material_integrity(src, material, amount, multiplier, removing) + +/obj/item/clothing/suit/armor/forging_plate_armor/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) // Gloves /obj/item/clothing/gloves/forging_plate_gloves @@ -38,22 +64,28 @@ worn_icon_teshari = 'modular_skyrat/modules/reagent_forging/icons/mob/clothing/forge_clothing_teshari.dmi' icon_state = "plate_gloves" resistance_flags = FIRE_PROOF - skyrat_obj_flags = ANVIL_REPAIR body_parts_covered = parent_type::body_parts_covered | ARMS - armor_type = /datum/armor/gloves_forging_plate_gloves - material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR - -/datum/armor/gloves_forging_plate_gloves - melee = 40 - bullet = 40 - fire = 50 - wound = 30 + armor_type = /datum/armor/armor_forging_plate_armor + material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS /obj/item/clothing/gloves/forging_plate_gloves/Initialize(mapload) . = ..() - AddComponent(/datum/component/armor_plate, 4) - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_GLOVES) + AddComponent(/datum/component/reagent_imbued/clothing, list(FORGE_EFFECT_ARMOR = /datum/armor/smithing_oil_armor_bonus), set_slot = ITEM_SLOT_GLOVES) + AddComponent(/datum/component/forge_smithable, \ + FORGING_CLOTHING_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS, \ + FORGING_CLOTHING_REFORGING_MAX_BAD_HITS, \ + FORGING_CLOTHING_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_ARMOR = /datum/armor/perfect_forged_armor_bonus), \ + incompletion_effects = list(FORGE_EFFECT_ARMOR, FORGE_EFFECT_DURABILITY)) + +/obj/item/clothing/gloves/forging_plate_gloves/change_material_integrity(datum/material/material, amount, multiplier, removing = FALSE) + blacksmithing_change_material_integrity(src, material, amount, multiplier, removing) + +/obj/item/clothing/gloves/forging_plate_gloves/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) // Helmets /obj/item/clothing/head/helmet/forging_plate_helmet @@ -68,20 +100,26 @@ supports_variations_flags = CLOTHING_SNOUTED_VARIATION_NO_NEW_ICON resistance_flags = FIRE_PROOF flags_inv = null - skyrat_obj_flags = ANVIL_REPAIR - armor_type = /datum/armor/helmet_forging_plate_helmet - material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR - -/datum/armor/helmet_forging_plate_helmet - melee = 40 - bullet = 40 - fire = 50 - wound = 30 + armor_type = /datum/armor/armor_forging_plate_armor + material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS /obj/item/clothing/head/helmet/forging_plate_helmet/Initialize(mapload) . = ..() - AddComponent(/datum/component/armor_plate, 4) - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_HEAD) + AddComponent(/datum/component/reagent_imbued/clothing, list(FORGE_EFFECT_ARMOR = /datum/armor/smithing_oil_armor_bonus), set_slot = ITEM_SLOT_HEAD) + AddComponent(/datum/component/forge_smithable, \ + FORGING_CLOTHING_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS, \ + FORGING_CLOTHING_REFORGING_MAX_BAD_HITS, \ + FORGING_CLOTHING_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_ARMOR = /datum/armor/perfect_forged_armor_bonus), \ + incompletion_effects = list(FORGE_EFFECT_ARMOR, FORGE_EFFECT_DURABILITY)) + +/obj/item/clothing/head/helmet/forging_plate_helmet/change_material_integrity(datum/material/material, amount, multiplier, removing = FALSE) + blacksmithing_change_material_integrity(src, material, amount, multiplier, removing) + +/obj/item/clothing/head/helmet/forging_plate_helmet/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) // Boots /obj/item/clothing/shoes/forging_plate_boots @@ -97,39 +135,57 @@ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION body_parts_covered = parent_type::body_parts_covered | LEGS - armor_type = /datum/armor/shoes_forging_plate_boots - material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR + armor_type = /datum/armor/armor_forging_plate_armor + material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS resistance_flags = FIRE_PROOF - skyrat_obj_flags = ANVIL_REPAIR fastening_type = SHOES_SLIPON -/datum/armor/shoes_forging_plate_boots - melee = 20 - bullet = 20 - /obj/item/clothing/shoes/forging_plate_boots/Initialize(mapload) . = ..() - AddComponent(/datum/component/armor_plate, 2) - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_FEET) + AddComponent(/datum/component/reagent_imbued/clothing, list(FORGE_EFFECT_ARMOR = /datum/armor/smithing_oil_armor_bonus), set_slot = ITEM_SLOT_FEET) + AddComponent(/datum/component/forge_smithable, \ + FORGING_CLOTHING_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS, \ + FORGING_CLOTHING_REFORGING_MAX_BAD_HITS, \ + FORGING_CLOTHING_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_ARMOR = /datum/armor/perfect_forged_armor_bonus), \ + incompletion_effects = list(FORGE_EFFECT_ARMOR, FORGE_EFFECT_DURABILITY)) + +/obj/item/clothing/shoes/forging_plate_boots/change_material_integrity(datum/material/material, amount, multiplier, removing = FALSE) + blacksmithing_change_material_integrity(src, material, amount, multiplier, removing) + +/obj/item/clothing/shoes/forging_plate_boots/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) /obj/item/clothing/shoes/horseshoe/reagent_clothing name = "reagent horseshoes" desc = "A pair of horseshoes made out of chains." - - armor_type = /datum/armor/shoes_horseshoe - material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR - skyrat_obj_flags = ANVIL_REPAIR - -/datum/armor/shoes_horseshoe - melee = 20 - bullet = 20 + armor_type = /datum/armor/armor_forging_plate_armor + material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS /obj/item/clothing/shoes/horseshoe/reagent_clothing/Initialize(mapload) . = ..() - AddComponent(/datum/component/armor_plate, 2) - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_FEET) + AddComponent(/datum/component/reagent_imbued/clothing, list(FORGE_EFFECT_ARMOR = /datum/armor/smithing_oil_armor_bonus), set_slot = ITEM_SLOT_FEET) + AddComponent(/datum/component/forge_smithable, \ + FORGING_CLOTHING_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS, \ + FORGING_CLOTHING_REFORGING_MAX_BAD_HITS, \ + FORGING_CLOTHING_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_ARMOR = /datum/armor/perfect_forged_armor_bonus), \ + incompletion_effects = list(FORGE_EFFECT_ARMOR, FORGE_EFFECT_DURABILITY)) + +/obj/item/clothing/shoes/horseshoe/reagent_clothing/change_material_integrity(datum/material/material, amount, multiplier, removing = FALSE) + blacksmithing_change_material_integrity(src, material, amount, multiplier, removing) + +/obj/item/clothing/shoes/horseshoe/reagent_clothing/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) + +//////////////////////////////////////////////////////////////////////////// +////////////////////////////// MISC //////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// -// Misc /obj/item/clothing/gloves/ring/reagent_clothing name = "reagent ring" desc = "A tiny ring, sized to wrap around a finger." @@ -137,15 +193,26 @@ worn_icon_state = "sring" inhand_icon_state = null material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR - skyrat_obj_flags = ANVIL_REPAIR + var/current_perfection_bonus = 0 + var/current_completion_penalty = 0 /obj/item/clothing/gloves/ring/reagent_clothing/Initialize(mapload) . = ..() - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_GLOVES) + AddComponent(/datum/component/reagent_imbued/clothing, list(FORGE_EFFECT_DURABILITY = 50), set_slot = ITEM_SLOT_GLOVES) + AddComponent(/datum/component/forge_smithable, \ + FORGING_CLOTHING_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS, \ + FORGING_CLOTHING_REFORGING_MAX_BAD_HITS, \ + FORGING_CLOTHING_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_REAGENT_INJECT = 0.2), \ + incompletion_effects = list(FORGE_EFFECT_REAGENT_INJECT, FORGE_EFFECT_DURABILITY)) + +//////////////////////////////////////////////////////////////////////////// /obj/item/clothing/neck/collar/reagent_clothing name = "reagent collar" - desc = "A collar that is ready to be worn for certain individuals." + desc = "A collar that is ready to be worn for... certain individuals." icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_clothing/lewd_neck.dmi' worn_icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_clothing/lewd_neck.dmi' icon_state = "collar_cyan" @@ -154,18 +221,24 @@ slot_flags = ITEM_SLOT_NECK w_class = WEIGHT_CLASS_SMALL material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR - skyrat_obj_flags = ANVIL_REPAIR /obj/item/clothing/neck/collar/reagent_clothing/Initialize(mapload) . = ..() - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_NECK) + AddComponent(/datum/component/reagent_imbued/clothing, list(FORGE_EFFECT_DURABILITY = 50), set_slot = ITEM_SLOT_NECK) + AddComponent(/datum/component/forge_smithable, \ + FORGING_CLOTHING_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS, \ + FORGING_CLOTHING_REFORGING_MAX_BAD_HITS, \ + FORGING_CLOTHING_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_REAGENT_INJECT = 0.2), \ + incompletion_effects = list(FORGE_EFFECT_REAGENT_INJECT, FORGE_EFFECT_DURABILITY)) /obj/item/restraints/handcuffs/reagent_clothing name = "reagent handcuffs" desc = "A pair of handcuffs that are ready to keep someone captive." material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR - skyrat_obj_flags = ANVIL_REPAIR /obj/item/restraints/handcuffs/reagent_clothing/Initialize(mapload) . = ..() - AddComponent(/datum/component/reagent_clothing, ITEM_SLOT_HANDCUFFED) + AddComponent(/datum/component/reagent_imbued/clothing, ITEM_SLOT_HANDCUFFED) diff --git a/modular_skyrat/modules/reagent_forging/code/forge_equipment.dm b/modular_skyrat/modules/reagent_forging/code/forge_equipment.dm new file mode 100644 index 00000000000..2335385bde2 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/forge_equipment.dm @@ -0,0 +1,60 @@ +/obj/item/forging + abstract_type = /obj/item/forging + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi' + lefthand_file = 'modular_skyrat/modules/reagent_forging/icons/mob/forge_weapon_l.dmi' + righthand_file = 'modular_skyrat/modules/reagent_forging/icons/mob/forge_weapon_r.dmi' + toolspeed = 1 + +/obj/item/forging/tongs + name = "forging tongs" + desc = "A set of tongs specifically crafted for use in forging. A wise man once said 'I lift things up and put them down.'" + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi' + icon_state = "tong_empty" + tool_behaviour = TOOL_TONG + +/obj/item/forging/tongs/primitive + name = "primitive forging tongs" + toolspeed = 1.2 + custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5) + +/obj/item/forging/tongs/attack_self(mob/user, modifiers) + . = ..() + var/obj/search_obj = locate(/obj) in contents + if(search_obj) + search_obj.forceMove(get_turf(src)) + icon_state = "tong_empty" + return + +/obj/item/forging/hammer + name = "forging mallet" + desc = "A mallet specifically crafted for use in forging. Used to slowly shape metal; careful, you could break something with it!" + icon_state = "hammer" + inhand_icon_state = "hammer" + worn_icon_state = "hammer_back" + tool_behaviour = TOOL_HAMMER + ///the list of things that, if attacked, will set the attack speed to rapid + var/static/list/fast_attacks = list( + /obj/structure/reagent_anvil, + /obj/structure/reagent_crafting_bench + ) + + +/obj/item/forging/hammer/primitive + name = "primitive forging hammer" + custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5) + +/obj/item/forging/hammer/debug + name = "debugging fast forging hammer" + toolspeed = 0.1 + +/obj/item/forging/billow + name = "forging billow" + desc = "A billow specifically crafted for use in forging. Used to stoke the flames and keep the forge lit." + icon_state = "billow" + tool_behaviour = TOOL_BILLOW + +/obj/item/forging/billow/primitive + name = "primitive forging billow" + toolspeed = 1.2 + custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5) + diff --git a/modular_skyrat/modules/reagent_forging/code/forge_items.dm b/modular_skyrat/modules/reagent_forging/code/forge_items.dm index 197120e2411..0fa93ec1a94 100644 --- a/modular_skyrat/modules/reagent_forging/code/forge_items.dm +++ b/modular_skyrat/modules/reagent_forging/code/forge_items.dm @@ -1,187 +1,262 @@ -/obj/item/forging - icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi' - lefthand_file = 'modular_skyrat/modules/reagent_forging/icons/mob/forge_weapon_l.dmi' - righthand_file = 'modular_skyrat/modules/reagent_forging/icons/mob/forge_weapon_r.dmi' - toolspeed = 1 SECONDS - ///whether the item is in use or not - var/in_use = FALSE - -/obj/item/forging/tongs - name = "forging tongs" - desc = "A set of tongs specifically crafted for use in forging. A wise man once said 'I lift things up and put them down.'" - icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi' - icon_state = "tong_empty" - tool_behaviour = TOOL_TONG - -/obj/item/forging/tongs/primitive - name = "primitive forging tongs" - toolspeed = 2 SECONDS - custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5) - -/obj/item/forging/tongs/attack_self(mob/user, modifiers) - . = ..() - var/obj/search_obj = locate(/obj) in contents - if(search_obj) - search_obj.forceMove(get_turf(src)) - icon_state = "tong_empty" - return - -/obj/item/forging/hammer - name = "forging mallet" - desc = "A mallet specifically crafted for use in forging. Used to slowly shape metal; careful, you could break something with it!" - icon_state = "hammer" - inhand_icon_state = "hammer" - worn_icon_state = "hammer_back" - tool_behaviour = TOOL_HAMMER - ///the list of things that, if attacked, will set the attack speed to rapid - var/static/list/fast_attacks = list( - /obj/structure/reagent_anvil, - /obj/structure/reagent_crafting_bench - ) - -/obj/item/forging/hammer/primitive - name = "primitive forging hammer" - custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5) - -/obj/item/forging/billow - name = "forging billow" - desc = "A billow specifically crafted for use in forging. Used to stoke the flames and keep the forge lit." - icon_state = "billow" - tool_behaviour = TOOL_BILLOW - -/obj/item/forging/billow/primitive - name = "primitive forging billow" - toolspeed = 2 SECONDS - custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5) - //incomplete pre-complete items /obj/item/forging/incomplete - name = "parent dev item" - desc = "An incomplete forge item, continue to work hard to be rewarded for your efforts." - //the time remaining that you can hammer before too cool - COOLDOWN_DECLARE(heating_remainder) - //the time between each strike - COOLDOWN_DECLARE(striking_cooldown) - ///the amount of times it takes for the item to become ready - var/average_hits = 30 - ///the amount of times the item has been hit currently - var/times_hit = 0 + name = "incomplete forging item" + desc = "you shouldn't be seeing this!" + abstract_type = /obj/item/forging/incomplete + + ///the quality points required for it to be considered usable for crafting + var/completion_quality_points = 40 ///the required time before each strike to prevent spamming var/average_wait = 1 SECONDS - ///the number of current perfect hits (really only impacts weapons atm) - var/current_perfects = 0 + ///the bad hits required for it to break; exceeding this will break the item + var/bad_hit_maximum = 5 + ///maximum number of perfect hits before perfect hits no longer improve the quality + var/max_perfect_hits = 20 + ///the path of the item that will be spawned upon completion var/spawn_item + ///does this item break if it's not finished hammering when it's quenched? + //this is mostly for working on stuff that has no effect based on completion amount, preventing cheesing + var/break_on_early_quench = FALSE //because who doesn't want to have a plasma sword? material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR -/obj/item/forging/incomplete/tong_act(mob/living/user, obj/item/tool) +/obj/item/forging/incomplete/Initialize(mapload) . = ..() + AddComponent(/datum/component/forge_smithable, completion_quality_points, TRUE, max_perfect_hits, bad_hit_maximum, average_wait, CALLBACK(src, TYPE_PROC_REF(/obj/item/forging/incomplete, quench_item)), color = "")// TYPE_PROC_REF(/obj/item/forging/incomplete/, quench_item)) + +/obj/item/forging/incomplete/proc/quench_item(datum/reagents/dunk_reagents, dunk_object, mob/living/quencher) + SIGNAL_HANDLER + + var/datum/component/forge_smithable/forge_component = GetComponent(/datum/component/forge_smithable) + if(forge_component.is_finished_smithing()) + to_chat(quencher, span_notice("You cool down [src].")) + quencher.mind.adjust_experience(/datum/skill/smithing, 5) + else + if(break_on_early_quench) + to_chat(quencher, span_warning("The [src] breaks from the thermal shock! Metalworking this type of object requires it to be hammered into completion.")) + qdel(src) + return + else + to_chat(quencher, span_warning("You cool down [src]. You're not sure if it was ready yet...")) + + var/obj/spawned_obj = new spawn_item(get_turf(dunk_object)) + if(custom_materials) + spawned_obj.set_custom_materials(custom_materials, 1) //lets set its material + + if(istype(spawned_obj, /obj/item/forging/complete)) + var/obj/item/forging/complete/complete_spawned = spawned_obj + complete_spawned.perfect_ratio = forge_component.get_perfect_ratio() + complete_spawned.hammer_completion_amount = forge_component.get_completion_ratio() + + var/datum/component/reagent_imbued/new_reagent_component = spawned_obj.GetComponent(/datum/component/reagent_imbued) + if(!isnull(new_reagent_component) && USER_CAN_REAGENT_IMBUE(quencher)) + new_reagent_component.set_reagent_imbue(dunk_reagents, FALSE, TRUE) + to_chat(quencher, span_notice("The [spawned_obj] is imbued with reagents.")) + + qdel(src) + return spawned_obj + +/obj/item/forging/incomplete/tong_act(mob/living/user, obj/item/tool) if(length(tool.contents) > 0) user.balloon_alert(user, "tongs are full already!") return forceMove(tool) tool.icon_state = "tong_full" + +/obj/item/forging/incomplete/pickup(mob/living/user) + var/hand_protected = FALSE + var/mob/living/carbon/human/human_user = user + if(!istype(human_user) || HAS_TRAIT(human_user, TRAIT_RESISTHEAT) || HAS_TRAIT(human_user, TRAIT_RESISTHEATHANDS)) + hand_protected = TRUE + else if(!istype(human_user.gloves, /obj/item/clothing/gloves)) + hand_protected = FALSE + else + var/obj/item/clothing/gloves/gloves = human_user.gloves + if(gloves.max_heat_protection_temperature) + hand_protected = (gloves.max_heat_protection_temperature > 360) + + ..() + if(!hand_protected) + var/hitzone = user.held_index_to_dir(user.active_hand_index) == "r" ? BODY_ZONE_PRECISE_R_HAND : BODY_ZONE_PRECISE_L_HAND + user.apply_damage(5, BURN, hitzone) + to_chat(user, span_danger("You burn your hand trying to pick up [src]!")) + user.add_mood_event("burnt_thumb", /datum/mood_event/burnt_thumb) + user.dropItemToGround(src) + +/obj/item/forging/incomplete/can_be_pulled(user, force) // no drag memes + return FALSE + /obj/item/forging/incomplete/chain name = "incomplete chain" icon_state = "hot_chain" - average_hits = 10 - average_wait = 0.5 SECONDS + completion_quality_points = 40 + max_perfect_hits = 15 spawn_item = /obj/item/forging/complete/chain + break_on_early_quench = TRUE + desc = "A length of chain. Used in crafting recipes at your crafting table." /obj/item/forging/incomplete/plate name = "incomplete plate" icon_state = "hot_plate" - average_hits = 10 - average_wait = 0.5 SECONDS + completion_quality_points = 40 + max_perfect_hits = 15 spawn_item = /obj/item/forging/complete/plate + desc = "A sturdy plate of material. Used in many crafting recipies at your crafting table, most notably that of armor." /obj/item/forging/incomplete/sword name = "incomplete sword blade" icon_state = "hot_blade" spawn_item = /obj/item/forging/complete/sword + desc = "A longsword. Practically requires a two-handed stance for effective use, but is very adept at blocking strikes." /obj/item/forging/incomplete/katana name = "incomplete katana blade" icon_state = "hot_katanablade" spawn_item = /obj/item/forging/complete/katana + desc = "A katana blade. Unwieldy if not carried with both hands, but careful metalworking makes it cut through armor more effectively." /obj/item/forging/incomplete/rapier name = "incomplete rapier blade" icon_state = "hot_rapierblade" spawn_item = /obj/item/forging/complete/rapier + desc = "A rapier blade. A classical go-to self-defense weapon, it requires only one hand to use." /obj/item/forging/incomplete/dagger name = "incomplete dagger blade" icon_state = "hot_daggerblade" + completion_quality_points = 22 + max_perfect_hits = 10 spawn_item = /obj/item/forging/complete/dagger + desc = "A small, sleek dagger. Can be easily hid, and can critially-strike vulnerable opponents." /obj/item/forging/incomplete/staff name = "incomplete staff head" icon_state = "hot_staffhead" spawn_item = /obj/item/forging/complete/staff + desc = "A staff that can be imbued with reagents. Not very useful in direct combat." /obj/item/forging/incomplete/spear name = "incomplete spear head" + completion_quality_points = 18 ///crazy low completion required (spears are historically an easy weapon to make that are used for widespread arming) + max_perfect_hits = 8 icon_state = "hot_spearhead" spawn_item = /obj/item/forging/complete/spear + desc = "A short spearhead. The weapon of commoners, it strikes from further away and can be made extremely quickly compared to other weapons." /obj/item/forging/incomplete/axe name = "incomplete axe head" icon_state = "hot_axehead" + completion_quality_points = 22 + max_perfect_hits = 14 spawn_item = /obj/item/forging/complete/axe + desc = "A heavy axehead. Hits from this can potentially bypass shields and other forms of protection." /obj/item/forging/incomplete/hammer name = "incomplete hammer head" icon_state = "hot_hammerhead" spawn_item = /obj/item/forging/complete/hammer + desc = "A heavy hammerhead. Can be used like a forging hammer -- and a two-handed stance can bust through doors." /obj/item/forging/incomplete/pickaxe name = "incomplete pickaxe head" icon_state = "hot_pickaxehead" spawn_item = /obj/item/forging/complete/pickaxe + desc = "A pickaxe. Used for digging." /obj/item/forging/incomplete/shovel name = "incomplete shovel head" icon_state = "hot_shovelhead" spawn_item = /obj/item/forging/complete/shovel + desc = "A shovel. Used for digging." /obj/item/forging/incomplete/arrowhead name = "incomplete arrowhead" icon_state = "hot_arrowhead" - average_hits = 12 - average_wait = 0.5 SECONDS + completion_quality_points = 12 spawn_item = /obj/item/forging/complete/arrowhead + desc = "A number of small arrowheads, used to create arrows." + +/obj/item/forging/incomplete/revolver_cylinder + name = "incomplete revolver cylinder" + icon_state = "hot_revolver_cylinder" + completion_quality_points = 60 + max_perfect_hits = 70 + spawn_item = /obj/item/forging/complete/revolver_cylinder + desc = "The cylinder of a classical single-action revolver." + +/obj/item/forging/incomplete/revolver_frame + name = "incomplete revolver frame" + icon_state = "hot_revolver_frame" + completion_quality_points = 90 + max_perfect_hits = 110 + spawn_item = /obj/item/forging/complete/revolver_frame + desc = "The frame of a classical single-action revolver." /obj/item/forging/incomplete/rail_nail name = "incomplete rail nail" icon = 'modular_skyrat/modules/ashwalkers/icons/railroad.dmi' icon_state = "hot_nail" - average_hits = 10 - average_wait = 0.5 SECONDS + completion_quality_points = 10 spawn_item = /obj/item/forging/complete/rail_nail + break_on_early_quench = TRUE + desc = "A rail nail used to build rail tracks." /obj/item/forging/incomplete/rail_cart name = "incomplete rail cart" icon = 'modular_skyrat/modules/ashwalkers/icons/railroad.dmi' icon_state = "hot_cart" spawn_item = /obj/vehicle/ridden/rail_cart + break_on_early_quench = TRUE + desc = "A minecart that can be ridden along rail tracks." //"complete" pre-complete items /obj/item/forging/complete + abstract_type = /obj/item/forging/complete ///the path of the item that will be created var/spawning_item - ///the amount of perfect hits on the item, if it was allowed - var/current_perfects = 0 + ///how many perfect hits did we get, out of the max? + var/perfect_ratio = 0 + ///when this was worked at an anvil, how many hits were actually applied of the max? + var/hammer_completion_amount = 0 //because who doesn't want to have a plasma sword? material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR + ///does it cut the user's hand when used as a weapon? + var/double_edged_damage = 0 + var/has_reagent_component = TRUE + +/obj/item/forging/complete/Initialize(mapload) + . = ..() + if(has_reagent_component) + AddComponent(/datum/component/reagent_imbued) /obj/item/forging/complete/examine(mob/user) . = ..() if(spawning_item) . += span_notice("
In order to finish this item, a workbench will be necessary!") +/obj/item/forging/complete/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers) + if(double_edged_damage <= 0) + return + + if(!iscarbon(user) || !user.is_holding(src)) + return + + var/mob/living/carbon/jab = user + if(jab.get_all_covered_flags() & HANDS) + return + + to_chat(user, span_warning("[src] cuts into your hand!")) + jab.apply_damage(double_edged_damage * hammer_completion_amount, BRUTE, user.get_active_hand(), attacking_item = src) + +/obj/item/forging/complete/tong_act(mob/living/user, obj/item/tool) + if(length(tool.contents) > 0) + user.balloon_alert(user, "tongs are full already!") + return + forceMove(tool) + tool.icon_state = "tong_full" + /obj/item/forging/complete/chain name = "chain" desc = "A singular chain, best used in combination with multiple chains." @@ -196,55 +271,68 @@ name = "sword blade" desc = "A sword blade, ready to get some wood for completion." icon_state = "blade" - spawning_item = /obj/item/forging/reagent_weapon/sword + spawning_item = /obj/item/melee/forged_reagent_weapon/sword + force = 5 + double_edged_damage = 3 /obj/item/forging/complete/katana name = "katana blade" desc = "A katana blade, ready to get some wood for completion." icon_state = "katanablade" - spawning_item = /obj/item/forging/reagent_weapon/katana + spawning_item = /obj/item/melee/forged_reagent_weapon/katana + force = 5 + double_edged_damage = 3 /obj/item/forging/complete/rapier name = "rapier blade" desc = "A rapier blade, ready to get some wood for completion." icon_state = "rapierblade" - spawning_item = /obj/item/forging/reagent_weapon/rapier + spawning_item = /obj/item/melee/forged_reagent_weapon/rapier + force = 5 + double_edged_damage = 3 /obj/item/forging/complete/dagger name = "dagger blade" desc = "A dagger blade, ready to get some wood for completion." icon_state = "daggerblade" - spawning_item = /obj/item/forging/reagent_weapon/dagger + spawning_item = /obj/item/melee/forged_reagent_weapon/dagger + force = 3 + double_edged_damage = 2 /obj/item/forging/complete/staff name = "staff head" desc = "A staff head, ready to get some wood for completion." icon_state = "staffhead" - spawning_item = /obj/item/forging/reagent_weapon/staff + spawning_item = /obj/item/melee/forged_reagent_weapon/staff /obj/item/forging/complete/spear name = "spear head" desc = "A spear head, ready to get some wood for completion." icon_state = "spearhead" - spawning_item = /obj/item/forging/reagent_weapon/spear + spawning_item = /obj/item/melee/forged_reagent_weapon/spear + force = 3 + double_edged_damage = 2 /obj/item/forging/complete/axe name = "axe head" desc = "An axe head, ready to get some wood for completion." icon_state = "axehead" - spawning_item = /obj/item/forging/reagent_weapon/axe + spawning_item = /obj/item/melee/forged_reagent_weapon/axe + force = 5 /obj/item/forging/complete/hammer name = "hammer head" desc = "A hammer head, ready to get some wood for completion." icon_state = "hammerhead" - spawning_item = /obj/item/forging/reagent_weapon/hammer + spawning_item = /obj/item/melee/forged_reagent_weapon/hammer + force = 5 /obj/item/forging/complete/pickaxe name = "pickaxe head" desc = "A pickaxe head, ready to get some wood for completion." icon_state = "pickaxehead" spawning_item = /obj/item/pickaxe/reagent_weapon + force = 5 /obj/item/forging/complete/shovel name = "shovel head" @@ -257,6 +345,16 @@ desc = "An arrowhead, ready to get some wood for completion." icon_state = "arrowhead" spawning_item = /obj/item/arrow_spawner + force = 3 + double_edged_damage = 2 + +/obj/item/forging/complete/revolver_cylinder + name = "single-action revolver cylinder" + icon_state = "revolver_cylinder" + +/obj/item/forging/complete/revolver_frame + name = "single-action revolver frame" + icon_state = "revolver_frame" /obj/item/forging/complete/rail_nail name = "rail nail" @@ -264,6 +362,7 @@ icon = 'modular_skyrat/modules/ashwalkers/icons/railroad.dmi' icon_state = "nail" spawning_item = /obj/item/stack/rail_track/ten + force = 3 /obj/item/forging/coil name = "coil" @@ -284,7 +383,7 @@ return ..() /obj/item/arrow_spawner - name = "arrow spawner" + name = "arrows" desc = "You shouldn't see this." /// the amount of arrows that are spawned from the spawner var/spawning_amount = 4 @@ -307,24 +406,38 @@ return ..() /obj/item/stack/tong_act(mob/living/user, obj/item/tool) - . = ..() + if(amount < 1) + user.balloon_alert(user, "not enough material in the stack!") + return FALSE if(length(tool.contents) > 0) user.balloon_alert(user, "tongs are full already!") return FALSE if(!material_type && !custom_materials) user.balloon_alert(user, "invalid material!") return - forceMove(tool) - tool.icon_state = "tong_full" + + var/obj/item/stack/stack_to_move + if(amount == 1) + stack_to_move = src + else + stack_to_move = split_stack(1) + + if(!isnull(stack_to_move)) + stack_to_move.forceMove(tool) + user.balloon_alert(user, "took one") + tool.icon_state = "tong_full" + else + stack_trace("[src] was grabbed by [tool] and couldn't pull one sheet!") /obj/tong_act(mob/living/user, obj/item/tool) . = ..() if(length(tool.contents)) user.balloon_alert(user, "tongs are full already!") return FALSE - if(skyrat_obj_flags & ANVIL_REPAIR) + if(!isnull(GetComponent(/datum/component/forge_smithable/)) || !isnull(GetComponent(/datum/component/reagent_imbued))) forceMove(tool) tool.icon_state = "tong_full" + user.balloon_alert(user, "took [src]") /obj/item/empty_circuit name = "empty circuit" @@ -365,3 +478,5 @@ return return ..() + +/obj/item/ diff --git a/modular_skyrat/modules/reagent_forging/code/forge_revolver.dm b/modular_skyrat/modules/reagent_forging/code/forge_revolver.dm new file mode 100644 index 00000000000..94dabfe6a37 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/forge_revolver.dm @@ -0,0 +1,225 @@ +// COMSIG_ATOM_WAS_ATTACKED check out this signal's implementation + +/obj/item/gun/ballistic/revolver/handcrafted_single_action + name = "\improper handcrafted revolver" + desc = "A traditional-style cowboy revolver. It's single-action, so the hammer must be manually cocked back to cycle the round." + icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi' + icon_state = "revolver_preview" + icon_preview = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi' + icon_state_preview = "revolver_preview" + accepted_magazine_type = /obj/item/ammo_box/magazine/internal/cylinder/handcrafted_single_action + fire_sound_volume = 90 + bolt_type = BOLT_TYPE_NO_BOLT + semi_auto = FALSE + mag_display = FALSE + bolt_wording = "hammer" + rack_delay = 1 + fire_delay = 1 + //just a little extra damage as a treat + projectile_damage_multiplier = 1.2 + ///below stats should match or exceed the murphy's + throwforce = 14 + force = 8 + material_flags = MATERIAL_EFFECTS | MATERIAL_AFFECT_STATISTICS | MATERIAL_ADD_PREFIX + + //is the hammer primed (ready to fire) or released (safe)? + var/hammer_is_primed = FALSE + var/hammer_pull_speed_fanning = 0 DECISECONDS + var/hammer_pull_speed_onehanded = 5 DECISECONDS + var/static/holster_misfire_chance = 70 + + //forging base components + var/obj/item/forging/complete/revolver_frame/frame + var/obj/item/forging/complete/revolver_cylinder/cylinder + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/Initialize(mapload) + . = ..() + AddComponent(/datum/component/reagent_imbued/weapon, oil_effects = list(FORGE_EFFECT_FORCE = 4)) + AddComponent(/datum/component/forge_smithable, \ + FORGING_WEAPON_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_WEAPON_REFORGING_MAX_PERFECT_HITS, \ + FORGING_WEAPON_REFORGING_MAX_BAD_HITS, \ + FORGING_WEAPON_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_FORCE = MAX_PERFECT_FORCE_BONUS)) + + RegisterSignal(src, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(try_throw_misfire)) + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) + + var/density = material.get_property(MATERIAL_DENSITY) + var/hardness = material.get_property(MATERIAL_HARDNESS) + var/flexibility = material.get_property(MATERIAL_FLEXIBILITY) + var/inaccuracy_modifier = max(0, ((flexibility * 2) - (density + hardness)) * 40 ) + var/damage_modifier = min(0, ((density + hardness) - (flexibility * 2)) / 20) + + if(!remove) + spread += inaccuracy_modifier + projectile_damage_multiplier += damage_modifier + else + spread -= inaccuracy_modifier + projectile_damage_multiplier -= damage_modifier + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/give_manufacturer_examine() + return + + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/update_icon(updates) + . = ..() + icon_state = "revolver_handle" + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/update_overlays() + . = ..() + + var/mutable_appearance/temp_mutable + if(hammer_is_primed) + temp_mutable = mutable_appearance(icon, "revolver_frame_primed") + else + temp_mutable = mutable_appearance(icon, "revolver_frame") + + if(!isnull(frame)) + temp_mutable.color = frame?.color + . += temp_mutable + + temp_mutable = mutable_appearance(icon,"revolver_cylinder_inserted") + if(!isnull(cylinder)) + temp_mutable.color = cylinder?.color + . += temp_mutable + +///Can't be toggled safety -- you need to manage the hammer +/obj/item/gun/ballistic/revolver/handcrafted_single_action/give_gun_safeties() + return + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/shoot_with_empty_chamber() + . = ..() + set_hammer_cock(FALSE) + +///go my snowflake code for handling the revolver's hammer!!! +/obj/item/gun/ballistic/revolver/handcrafted_single_action/attack_self(mob/living/user) + if(recent_rack > world.time) + return + + var/rack_delay_to_use + var/obj/item/bodypart/other_hand = user.has_hand_for_held_index(user.get_inactive_hand_index()) // + if(user.get_inactive_held_item() || !other_hand) //if both hands occupied, one-handed rack + rack_delay_to_use = hammer_pull_speed_onehanded + else + rack_delay_to_use = hammer_pull_speed_fanning + recent_rack = world.time + rack_delay_to_use + rack(user) + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/rack(mob/user = null) + if(DOING_INTERACTION(user, DOAFTER_REVOLVER_HAMMER_COCK)) + return + + if(!hammer_is_primed) + set_hammer_cock(TRUE) + balloon_alert(user, "cocked the hammer") + playsound(src, 'sound/items/weapons/gun_mode_switch1.ogg', vol = 35, vary = TRUE, frequency = 1.8, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) + if(!istype(magazine,/obj/item/ammo_box/magazine/internal/cylinder)) + stack_trace("[usr] has a magazine that isn't a revolver cylinder!") + var/obj/item/ammo_box/magazine/internal/cylinder/my_cylinder = magazine + my_cylinder.rotate() + chamber_round() + else + set_hammer_cock(FALSE) + balloon_alert(user, "decocked the hammer") + playsound(src, 'sound/items/weapons/gun_mode_switch1.ogg', vol = 35, vary = TRUE, frequency = 1.6, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/proc/set_hammer_cock(cock) + hammer_is_primed = cock + update_appearance() + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/try_fire_gun(atom/target, mob/living/user, params) + if(hammer_is_primed) + return ..() + else + balloon_alert(user, "hammer isn't cocked!") + return FALSE + +////////////////////////// MISFIRE WHEN SHOVED OR ATTACKED /////////////////////////////////////// + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread) + set_hammer_cock(FALSE) + . = ..() + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/equipped(mob/user, slot, initial) + . = ..() + var/static/list/connections = list( + COMSIG_ATOM_WAS_ATTACKED = PROC_REF(try_attack_misfire)//, + ///COMSIG_CARBON_HELP_ACT = PROC_REF(try_hug_misfire), + //COMSIG_LIVING_Z_IMPACT = PROC_REF(try_fall_misfire), + ) + if(slot != ITEM_SLOT_HANDS) + AddComponent(/datum/component/connect_inventory, user, connections, allowed_slots = ALL ^ ITEM_SLOT_HANDS) + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/dropped(mob/user, silent) + . = ..() + qdel(GetComponent(/datum/component/connect_inventory)) + + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/proc/try_attack_misfire(mob/victim, mob/attacker, attack_flags) + if(attack_flags & ATTACKER_SHOVING && prob(50)) + if(holster_misfire(victim, attacker) == TRUE) + victim.visible_message(span_warning("[victim]'s [src] goes off from [attacker]'s attack!"), + span_danger("Your primed [src] triggers from [attacker]'s strike! "), ignored_mobs = victim) + +/* +/obj/item/gun/ballistic/revolver/handcrafted_single_action/proc/try_hug_misfire(mob/living/source) + if(prob(3)) + if(holster_misfire(victim, attacker) == TRUE) + user.visible_message(span_warning("[victim]'s [src] goes off from [attacker]'s hug!"), + span_danger("Your primed [src] triggers from [attacker]'s hug! "), ignored_mobs = victim) + + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/proc/try_fall_misfire(datum/source, levels, turf/fell_on) + if(prob(70)) + if(holster_misfire(victim, attacker) == TRUE) + user.visible_message(span_warning("[victim]'s [src] goes off from [attacker]'s hug!"), + span_danger("Your primed [src] triggers from the sharp fall! "), ignored_mobs = victim) +*/ +/obj/item/gun/ballistic/revolver/handcrafted_single_action/proc/holster_misfire(mob/victim, mob/attacker) + return try_fire_gun(victim, attacker, null, FALSE) + +/obj/item/gun/ballistic/revolver/handcrafted_single_action/proc/try_throw_misfire(datum/source, datum/thrownthing/throwingdatum) + SIGNAL_HANDLER + //todo: gun fires in random direction if it's thrown + //if(prob(100)) + return + +////////////////////////////////////// MISC RELATED STUFF ////////////////////////////////////// + +/obj/item/ammo_box/magazine/internal/cylinder/handcrafted_single_action + name = "handcrafted revolver cylinder" + start_empty = TRUE + ammo_type = /obj/item/ammo_casing/c38 + caliber = CALIBER_38 + max_ammo = 6 + +///cause apparently tgcode doesn't have a bug check against if start_empty = false +/obj/item/ammo_box/magazine/internal/cylinder/handcrafted_single_action/Initialize(mapload) + . = ..() + while(stored_ammo.len < max_ammo) + stored_ammo += null + +///get round doesn't spin the cylinder; cocking the hammer (from rack()) must be used to spin the cylinder +/obj/item/ammo_box/magazine/internal/cylinder/handcrafted_single_action/get_round() + var/casing = null + if(length(stored_ammo) >= 1) + casing = stored_ammo[1] + if (ispath(casing)) + casing = new casing(src) + stored_ammo[1] = casing + return casing + +/obj/item/ammo_box/magazine/internal/cylinder/handcrafted_single_action/spin() + . = ..() + +/datum/material_slot/revolver/frame + name = "frame" + material_amount = 1 + +/datum/material_slot/revolver/cylinder + name = "frame" + material_amount = 0.9 diff --git a/modular_skyrat/modules/reagent_forging/code/forge_smithable_component.dm b/modular_skyrat/modules/reagent_forging/code/forge_smithable_component.dm new file mode 100644 index 00000000000..ac712bf42ee --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/forge_smithable_component.dm @@ -0,0 +1,376 @@ +#define ANVIL_HAMMER_HIT_GOOD 1 +#define ANVIL_HAMMER_HIT_BAD 0 +#define ANVIL_HAMMER_HIT_PERFECT 2 +#define ANVIL_HAMMER_HIT_CANNOT_WORK 3 +#define ANVIL_SMITHING_CHIP_QUALITY_BONUS 0.5 + +/datum/component/forge_smithable + ///the item that the component is attached to + var/obj/item/parent_item + ///required type to attach to + var/obj/item/required_type = /obj/item + + ///the quality points of the incomplete item; goes up on good/perfect hits + var/quality_points = 0 + ///the quality points required for it to be considered usable for crafting + var/completion_quality_points = 1 + ///the required time before each strike to prevent spamming + var/average_wait = 100 SECONDS + ///total current bad hits + var/bad_hits_total = 0 + ///the bad hits required for it to break; exceeding this will break the item + var/bad_hit_maximum = 1 + ///the number of current perfect hits + var/current_perfects = 0 + ///maximum number of perfect hits before perfect hits no longer improve the quality + var/max_perfect_hits = 1 + var/can_perfect_hit = TRUE + //what color does the parent item turn when heated? if null, then parent item doesn't change color when heated + var/heat_color = null + + //what happens when quenched or cooled? + var/datum/callback/quench_callback = null + var/datum/callback/passive_cool_callback = null + //do we change the reagents imbued? + var/quench_causes_reimbue = TRUE + //index: what type of effect (ex. increase armor, increase AP, increase force) + //element: by how much to increase + var/list/quench_effects_perfection = list() + var/list/quench_effects_incompletion = list() + //use this to track the completion/perfection amounts last applied upon quench + var/perfection_ratio_applied = 0 + var/completion_applied_multiplier = 0 + COOLDOWN_DECLARE(heating_remainder) + +/* Gives a given item the smithable component -- which allows it to be picked up with tongs, smithed with a hammer, and quenched in reagent. + * + * completion_needed: The number of quality points (increased by hammer strikes) required to finish the item. + * can_perfect: If perfect strikes are possible on this equipment piece. + * max_perfection: Maximum perfection points to fully perfect this piece of equipment. + * max_breakage: How many bad hits can be done before it breaks + * wait_time: Time between strikes needed for it to no longer be considered a bad hit + * on_quench: What function on the parent item to call when this item is quenched. + * on_passive_cool: What function on the parent item to call when this item loses all heat. + * on_forging_heat: What function on the parent item to call when this item is heated in a forge. + * color: The color to use when the parent item is heated. + * perfection_effects: What effects to apply based on the % perfection. (Associative list.) + * incompletion_effects: What penalties to apply based on how incomplete this item is at quench. (Associative list.) + */ +/datum/component/forge_smithable/Initialize(completion_needed, can_perfect, max_perfection, max_breakage, wait_time, datum/callback/on_quench = null, datum/callback/on_passive_cool = null, datum/callback/on_forging_heat = null, color = "#FF4400", list/perfection_effects = null, list/incompletion_effects = null) + if(!istype(parent, required_type)) + return COMPONENT_INCOMPATIBLE + parent_item = parent + average_wait = wait_time + completion_quality_points = completion_needed + max_perfect_hits = max_perfection + bad_hit_maximum = max_breakage + can_perfect_hit = can_perfect + + quench_callback = on_quench + passive_cool_callback = on_passive_cool + if(!isnull(on_quench)) + RegisterSignal(parent_item, COMSIG_SMITHING_QUENCH, on_quench) + if(!isnull(on_passive_cool)) + RegisterSignal(parent_item, COMSIG_SMITHING_PASSIVE_COOLED, on_passive_cool)//TYPE_PROC_REF(parent_item.type, on_passive_cool)) + if(length(color) > 0) + heat_color = color + if(!isnull(perfection_effects)) + quench_effects_perfection = perfection_effects + if(!isnull(incompletion_effects)) + quench_effects_incompletion = incompletion_effects + RegisterSignal(parent_item, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + +/datum/component/forge_smithable/proc/on_examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + examine_list += span_notice("It can be safely picked up with tongs.") + examine_list += span_notice("It can be heated by being inserted into a forge.") + examine_list += span_notice("It can be quenched in any open container that's large enough.") + if(USER_CAN_SEE_SMITHING_INFO(user)) + examine_list += span_notice("You'd estimate that it's about [round(get_completion_ratio() * 100)]% complete.") + examine_list += span_notice("The careful smithing makes it look about [round(get_perfect_ratio() * 100)]% perfected.") + if(COOLDOWN_FINISHED(src, heating_remainder)) + examine_list += span_notice("The metal needs to be reheated before it is malleable again.") + else + examine_list += span_notice("The metal is hot enough to work.") + + else + examine_list += span_notice("If you were more skilled at smithing you could discern more information from it...") + + +/datum/component/forge_smithable/proc/good_hit(amount = 1, playsound = FALSE) + quality_points = clamp(amount + quality_points, 0, completion_quality_points) + if(playsound) + playsound(parent_item, 'sound/items/weapons/parry.ogg', vol = 45, vary = TRUE, frequency = 1.3, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, volume_preference = /datum/preference/numeric/volume/sound_ambience_volume) + +/datum/component/forge_smithable/proc/perfect_hit(amount = 1, playsound = FALSE) + good_hit(amount, FALSE) + if(playsound) + playsound(parent_item, 'sound/items/weapons/parry.ogg', vol = 45, vary = TRUE, frequency = 1.0, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, volume_preference = /datum/preference/numeric/volume/sound_ambience_volume) + if(current_perfects < max_perfect_hits) + current_perfects = clamp(current_perfects + amount, 0, max_perfect_hits) + +/datum/component/forge_smithable/proc/bad_hit(amount = 2, playsound = FALSE) + bad_hits_total += amount + if(check_for_breakage()) + forging_breakage() + else + if(playsound) + playsound(parent_item, 'sound/items/weapons/parry.ogg', vol = 35, vary = TRUE, frequency = 0.4, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, volume_preference = /datum/preference/numeric/volume/sound_ambience_volume) + +/datum/component/forge_smithable/proc/check_for_breakage() + if(bad_hits_total > bad_hit_maximum) + return TRUE + return FALSE + +/datum/component/forge_smithable/proc/forging_breakage(playsound = TRUE) + if(playsound) + conditional_pref_sound(parent_item, 'modular_skyrat/modules/reagent_forging/sound/forge.ogg', vol = 35, vary = TRUE, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE, pref_to_check = /datum/preference/numeric/volume/sound_ambience_volume) + + parent_item.balloon_alert_to_viewers("the [parent_item] shattered!") + qdel(parent_item) + qdel(src) + +/datum/component/forge_smithable/proc/is_finished_smithing() + if(quality_points >= completion_quality_points) + return TRUE + return FALSE + +/datum/component/forge_smithable/proc/get_completion_ratio() + return quality_points / completion_quality_points + +/datum/component/forge_smithable/proc/is_perfected() + if(current_perfects >= max_perfect_hits) + return TRUE + return FALSE + +/datum/component/forge_smithable/proc/get_perfect_ratio() + return current_perfects / max_perfect_hits + +/datum/component/forge_smithable/proc/show_balloon_alert(mob/living/user, hit_quality) + if(!HAS_TRAIT(user, TRAIT_DEAF)) + switch(hit_quality) + if(ANVIL_HAMMER_HIT_CANNOT_WORK) + parent_item.balloon_alert(user,"can't work!") + if(ANVIL_HAMMER_HIT_BAD) + parent_item.balloon_alert(user,"bad hit") + if(ANVIL_HAMMER_HIT_GOOD) + if(is_finished_smithing()) + if(is_perfected()) + parent_item.balloon_alert(user,"[parent_item] is perfected!") + else + parent_item.balloon_alert(user,"[parent_item] sounds done") + else + parent_item.balloon_alert(user, "good hit") + if(ANVIL_HAMMER_HIT_PERFECT) + if(is_finished_smithing() && is_perfected()) + parent_item.balloon_alert(user,"[parent_item] is perfected!") + else + parent_item.balloon_alert(user, "perfect hit!") + +/datum/component/forge_smithable/proc/get_hit_quality(mob/living/user, obj/item/forging/hammer/tool) + if(parent_item.GetComponent(/datum/component/reagent_imbued)) + var/datum/component/reagent_imbued/reagent_component = parent_item.GetComponent(/datum/component/reagent_imbued) + if(reagent_component.imbued_reagent.reagent_list.len >= 1 && !USER_CAN_REAGENT_IMBUE(user)) + return ANVIL_HAMMER_HIT_CANNOT_WORK + + if(!COOLDOWN_FINISHED(user, striking_cooldown)) + return ANVIL_HAMMER_HIT_BAD + + if(can_perfect_hit) + if(user.mind.get_skill_level(/datum/skill/smithing) >= SKILL_LEVEL_LEGENDARY) + return ANVIL_HAMMER_HIT_PERFECT + if(!COOLDOWN_FINISHED(user, perfect_strike_window) && COOLDOWN_FINISHED(user, striking_cooldown)) + return ANVIL_HAMMER_HIT_PERFECT + + return ANVIL_HAMMER_HIT_GOOD + +/datum/component/forge_smithable/proc/get_quality_points(mob/living/user, obj/item/tool) + var/total_quality_points = 1 + + total_quality_points *= get_material_quality_points_mult(parent_item.get_master_material()) + + //toolspeed doesn't affect the speed at which you strike the anvil, but it DOES affect the quality points gained (and thus how quickly the item is finished completion) + total_quality_points /= tool.toolspeed + + //if the user has advanced smithing, then they also get a smithing hit bonus + if(HAS_TRAIT(user, TRAIT_KNOW_ADVANCED_SMITHING)) + total_quality_points += ANVIL_SMITHING_CHIP_QUALITY_BONUS + + //finally, all hits have a minimum quality amount + total_quality_points = max(MINIMUM_SMITHING_QUALITY_POINTS, total_quality_points) + + return total_quality_points + +/datum/component/forge_smithable/proc/reset(reset_perfects = FALSE) + quality_points = 0 + bad_hits_total = 0 + if(reset_perfects) + current_perfects = 0 + +////////////////////// FOR ACTUAL SMITHING ACTIONS ///////////////////////// + +/* Tries to apply a hit onto the item using the tool. Will calculate the appropriate hit quality and increase the completion based on that. + * + * user: The user who is trying to smith. + * tool: The tool they are using to smith with. + */ +/datum/component/forge_smithable/proc/anvil_work(mob/living/user, obj/item/tool) + if(COOLDOWN_FINISHED(src, heating_remainder)) + bad_hit() + user.balloon_alert(user, "metal too cool") + return ITEM_INTERACT_SUCCESS + + var/quality_points_to_give = get_quality_points(user, tool) + var/hit_quality = get_hit_quality(user, tool) + switch(hit_quality) + if(ANVIL_HAMMER_HIT_CANNOT_WORK) + bad_hit(playsound = TRUE) + to_chat(user, span_warning("You don't know how to work with reagent imbued items!")) + if(ANVIL_HAMMER_HIT_BAD) + bad_hit(playsound = TRUE) + if(ANVIL_HAMMER_HIT_GOOD) + good_hit(amount = quality_points_to_give, playsound = TRUE) + user.mind.adjust_experience(/datum/skill/smithing, 1) //A good hit gives mild experience + do_sparks(1, FALSE, src) + if(ANVIL_HAMMER_HIT_PERFECT) + perfect_hit(amount = quality_points_to_give, playsound = TRUE) + user.mind.adjust_experience(/datum/skill/smithing, 5) //A perfect hit gives good experience + do_sparks(2, FALSE, parent_item) + + show_balloon_alert(user, hit_quality) + + var/skill_modifier = user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_SPEED_MODIFIER) * average_wait + COOLDOWN_START(user, striking_cooldown, skill_modifier) + COOLDOWN_START(user, perfect_strike_window, skill_modifier + (user.mind.get_skill_level(/datum/skill/smithing) DECISECONDS / 2)) + +/* Attempts to quench the item using the given reagents. The reagents source must fill certain criteria to be elligible to do this -- and various effects can apply depending on the reagents used and their temp. + * + * dunk_reagents: The reagents source to try and use. + * dunk_object: The container of the reagents source. + * user: The person trying to quench the item. + * show_message: Should we show a message of the quench outcome? + */ +/datum/component/forge_smithable/proc/try_quench(datum/reagents/dunk_reagents, dunk_object, mob/living/user, show_message = TRUE) + if(COOLDOWN_FINISHED(src, heating_remainder)) + if(show_message) + parent_item.balloon_alert(user, "[parent_item] is too cool to successfully quench!") + return FALSE + if(!(dunk_reagents.flags & DUNKABLE)) + if(show_message) + parent_item.balloon_alert(user, "[dunk_object] doesn't have a large enough hole to immerse [parent_item]!") + return FALSE + if(dunk_reagents.chem_temp > MAX_QUENCH_HEAT) + if(show_message) + parent_item.balloon_alert(user, "[dunk_object] is too hot to cool [parent_item]!") + return FALSE + if(dunk_reagents.total_volume < MIN_VOLUME_TO_QUENCH) + if(show_message) + parent_item.balloon_alert(user, "[dunk_object] doesn't contain enough fluid to immerse [parent_item]!") + return FALSE + + dunk_reagents.expose_temperature(HEAT_GIVEN_FROM_QUENCHING_METAL) + COOLDOWN_RESET(src, heating_remainder) + playsound(parent_item, 'modular_skyrat/modules/reagent_forging/sound/hot_hiss.ogg', 50, TRUE) + + if(!isnull(heat_color)) + parent_item.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY) + parent_item.update_integrity(max(round(lerp(0, parent_item.max_integrity, get_completion_ratio())), parent_item.get_integrity())) + if(quench_causes_reimbue && USER_CAN_REAGENT_IMBUE(user)) + var/datum/component/reagent_imbued/my_imbue = parent_item.GetComponent(/datum/component/reagent_imbued) + if(!isnull(my_imbue)) + my_imbue.set_reagent_imbue(dunk_reagents, FALSE, TRUE) + if(show_message) + parent_item.balloon_alert(user, "[parent_item] is imbued!") + else if(show_message) + parent_item.balloon_alert(user, "[parent_item] is quenched!") + + if(!isnull(quench_callback)) + quench_callback.Invoke(dunk_reagents, dunk_object, user) + return TRUE + +/* Heats the item up for smithing -- and also colors the item with heat colors if applicable. + * + * heat_time: The time until the item cools again. + * additive: If reheating an already-hot item extends the time or not. + */ +/datum/component/forge_smithable/proc/heat_for_smithing(heat_time, additive = FALSE) + if(!isnull(heat_color)) + parent_item.add_atom_colour(color_transition_filter(heat_color, SATURATION_OVERRIDE), TEMPORARY_COLOUR_PRIORITY) + if(additive) + COOLDOWN_START(src, heating_remainder, heat_time + COOLDOWN_TIMELEFT(src, heating_remainder)) + else + COOLDOWN_START(src, heating_remainder, heat_time) + +/* Sets the item's completion and perfection amount -- and applies the bonuses and penalties based on that. + * + * completion_amount: The completion points amount to set. + * perfection_amount: The perfection points amount to set. + */ +/datum/component/forge_smithable/proc/set_completion_and_perfection(completion_amount, perfection_amount) + quality_points = completion_amount + current_perfects = perfection_amount + apply_completion_perfection_modifiers(get_completion_ratio(), get_perfect_ratio()) + +/* Sets the item's completion and perfection amount perentage -- and applies the bonus and penalties based on that. + * + * completion_ratio: The completion amount to set (as a decimal from 0.0 - 1.0 inclusive) + * perfection_ratio: The perfection amount to set (as a decimal from 0.0 - 1.0 inclusive) + */ +/datum/component/forge_smithable/proc/set_completion_and_perfection_ratios(completion_ratio, perfection_ratio) + quality_points = round(completion_ratio * completion_quality_points) + current_perfects = round(perfection_ratio * max_perfect_hits) + apply_completion_perfection_modifiers(completion_ratio, perfection_ratio) + +/* Applies the incompletion penalty and perfection bonus to the item with the given amounts. + * + * completion_ratio: The completion amount to set (as a decimal from 0.0 - 1.0 inclusive) + * perfection_ratio: The perfection amount to set (as a decimal from 0.0 - 1.0 inclusive) + */ +/datum/component/forge_smithable/proc/apply_completion_perfection_modifiers(completion_ratio, perfection_ratio) + for(var/index in quench_effects_perfection) + give_added_modifying_effect_to_item(index, perfection_ratio_applied, perfection_ratio, parent_item, quench_effects_perfection[index]) + + var/incompletion_multiplier + var/incomplete_maximum_penalty + if(completion_ratio < 1) + incompletion_multiplier = 1 - lerp(MIN_INCOMPLETE_FORGING_SCALING_PENALTY, MAX_INCOMPLETE_FORGING_SCALING_PENALTY, completion_ratio) + else + incompletion_multiplier = 0 + for(var/index in quench_effects_incompletion) + switch(index) + if(FORGE_EFFECT_ARMOR) + var/datum/armor/temp_armor = get_armor_by_type(parent_item.get_initial_armor_type()) + incomplete_maximum_penalty = temp_armor.generate_new_with_multipliers(list(ARMOR_ALL = -1)) + if(FORGE_EFFECT_ARMORPEN) + incomplete_maximum_penalty = initial(parent_item.armour_penetration) * -1 + if(FORGE_EFFECT_BLOCKCHANCE) + incomplete_maximum_penalty = initial(parent_item.block_chance) * -1 + if(FORGE_EFFECT_DURABILITY) + incomplete_maximum_penalty = initial(parent_item.max_integrity) * -1 + if(FORGE_EFFECT_FORCE) + incomplete_maximum_penalty = initial(parent_item.force) * -1 + if(FORGE_EFFECT_REAGENT_INJECT) + var/datum/component/reagent_imbued/reagent_component = parent_item.GetComponent(/datum/component/reagent_imbued) + if(!isnull(reagent_component)) + stack_trace("[parent_item] has an invalid reagent imbue-enhancing effect, because it has no reagent component!") + incomplete_maximum_penalty = initial(reagent_component.inject_amount) * -1 + if(FORGE_EFFECT_TOOLSPEED) + incomplete_maximum_penalty = initial(parent_item.toolspeed) * 2 + else + stack_trace("Tried to modify [parent_item] with an invalid effect [index]!") + give_added_modifying_effect_to_item(index, completion_applied_multiplier, incompletion_multiplier, parent_item, incomplete_maximum_penalty) + + perfection_ratio_applied = perfection_ratio + completion_applied_multiplier = incompletion_multiplier + +/mob/living + //the time between each strike + COOLDOWN_DECLARE(striking_cooldown) + //the time it takes to prepare a perfect strike. should always be > striking_cooldown + COOLDOWN_DECLARE(perfect_strike_window) + +#undef ANVIL_HAMMER_HIT_GOOD +#undef ANVIL_HAMMER_HIT_BAD +#undef ANVIL_HAMMER_HIT_PERFECT +#undef ANVIL_HAMMER_HIT_CANNOT_WORK +#undef ANVIL_SMITHING_CHIP_QUALITY_BONUS diff --git a/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm b/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm index c93172c14d8..a8179943b37 100644 --- a/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm +++ b/modular_skyrat/modules/reagent_forging/code/forge_weapons.dm @@ -1,18 +1,196 @@ +#define STUN_ATTACK "stun attack" + // Damage is technically 10 by default since that's the max you get from perfect hits when crafting. Find a non-third-of-sth value for the multiplier to get to 18, I'll wait -/obj/item/forging/reagent_weapon +/obj/item/melee/forged_reagent_weapon + name = "forged reagent weapon" icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi' lefthand_file = 'modular_skyrat/modules/reagent_forging/icons/mob/forge_weapon_l.dmi' righthand_file = 'modular_skyrat/modules/reagent_forging/icons/mob/forge_weapon_r.dmi' worn_icon = 'modular_skyrat/modules/reagent_forging/icons/mob/forge_weapon_worn.dmi' - material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR + integrity_failure = 0.5 + material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS obj_flags = UNIQUE_RENAME - skyrat_obj_flags = ANVIL_REPAIR + abstract_type = /obj/item/melee/forged_reagent_weapon -/obj/item/forging/reagent_weapon/examine(mob/user) + //keeps track of how much force was taken away from incomplete forging + var/completion_force_penalty = 0 + //keeps track of how much force was given from perfect hammering + var/perfect_forging_bonus = 0 + + //chance to self damage from being clumsy + var/clumsy_backfire_chance = 70 + + //vars for nonlethal strikes + //can this item nonlethal attack? + var/can_nonlethal_altfire = TRUE + //time between nonlethal attacks + var/nonlethal_strike_cooldown = 4 SECONDS + COOLDOWN_DECLARE(nonlethal_strike_cooldown_timer) + /// The path of the default sound to play when we stun something. + var/on_stun_sound = 'sound/effects/woodhit.ogg' + //verbs to use when secondary attacking + var/list/secondary_attack_verb_continuous = list("shaft-strikes") + var/list/secondary_attack_verb_simple = list("shaft-strike") + + //below numbers are multiplied by the weapon's force + //how much stamina damage to apply on a rightclick + var/stamina_damage_multiplier = 2 + //how long the victim should be knockdowned + var/knockdown_time_multiplier = 0.5 DECISECONDS + + +/obj/item/melee/forged_reagent_weapon/examine(mob/user) . = ..() - . += span_notice("Using a hammer on [src] will repair its damage!") + var/healthpercent = round((atom_integrity/max_integrity) * 100, 1) + switch(healthpercent) + if(50 to 99) + . += span_info("It looks slightly damaged.") + if(25 to 50) + . += span_info("It appears heavily damaged.") + if(0 to 25) + . += span_warning("It's falling apart!") -/obj/item/forging/reagent_weapon/sword +/obj/item/melee/forged_reagent_weapon/Initialize(mapload) + . = ..() + if (length(secondary_attack_verb_continuous) != length(secondary_attack_verb_simple)) + stack_trace("[src] doesn't have equal secondary attack verb lengths, please fix!") + apply_reagent_component() + apply_smithing_component() + +/* needs updating to the new bane component, todo later by the person who actually knows how to handle such a thing +/obj/item/melee/forged_reagent_weapon/set_custom_materials(list/materials, multiplier = 1) + if(/datum/material/silver in materials) + AddComponent(/datum/component/bane, affected_biotypes = MOB_MINING, added_damage = 80) //For killing really big monsters. + AddElement(/datum/element/bane, /datum/species/lycan, damage_multiplier = 3, requires_combat_mode = FALSE) + else + RemoveComponent(/datum/component/bane) + */ + +/obj/item/melee/forged_reagent_weapon/change_material_integrity(datum/material/material, amount, multiplier, removing = FALSE) + blacksmithing_change_material_integrity(src, material, amount, multiplier, removing) + +/obj/item/melee/forged_reagent_weapon/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) + +/obj/item/melee/forged_reagent_weapon/proc/apply_reagent_component() + AddComponent(/datum/component/reagent_imbued/weapon, oil_effects = list(FORGE_EFFECT_ARMORPEN = 10)) + +/obj/item/melee/forged_reagent_weapon/proc/apply_smithing_component() + AddComponent(/datum/component/forge_smithable, \ + FORGING_WEAPON_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_WEAPON_REFORGING_MAX_PERFECT_HITS, \ + FORGING_WEAPON_REFORGING_MAX_BAD_HITS, \ + FORGING_WEAPON_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_FORCE = MAX_PERFECT_FORCE_BONUS), \ + incompletion_effects = list(FORGE_EFFECT_FORCE, FORGE_EFFECT_ARMORPEN)) + +// nonlethal secondary attack +/obj/item/melee/forged_reagent_weapon/pre_attack(atom/target, mob/living/user, list/modifiers, list/attack_modifiers) + . = ..() + if(.) + return TRUE + if(!ishuman(target)) + return FALSE // bashing objects + + // clumsy people redirect this attack - yes, this bypasses IWASBATONED and such + if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) + user.visible_message( + span_danger("[user] accidentally hits [user.p_them()]self over the head with [src]! What a doofus!"), + span_userdanger("You accidentally hit yourself over the head with [src]!"), + visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE, + ) + + finalize_nonlethal_strike(user, user, clumsy = TRUE) + user.apply_damage(2 * force, BRUTE, BODY_ZONE_HEAD, attacking_item = src) + log_combat(user, user, "accidentally stun attacked [user.p_them()]self due to their clumsiness", src) + user.do_attack_animation(user) + user.changeNext_move(attack_speed) + return TRUE // you hit yourself, nerd + + var/right_clicked = LAZYACCESS(modifiers, RIGHT_CLICK) + if(can_nonlethal_altfire && right_clicked) + if(COOLDOWN_FINISHED(src, nonlethal_strike_cooldown_timer)) + // when we continue to attack, deal 0 (brute) damage (just stun) + SET_ATTACK_FORCE(attack_modifiers, 0) + MUTE_ATTACK_HITSOUND(attack_modifiers) + HIDE_ATTACK_MESSAGES(attack_modifiers) + LAZYSET(attack_modifiers, STUN_ATTACK, TRUE) + else + return TRUE //if we cannot nonlethal and the player is attempting, cancel the attack + return FALSE + +/obj/item/melee/forged_reagent_weapon/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers) + if(QDELETED(target) || !LAZYACCESS(attack_modifiers, STUN_ATTACK)) + return + + finalize_nonlethal_strike(target, user) + + var/list/desc = get_stun_description(target, user) + + if(desc) + target.visible_message(desc["visible"], desc["local"], visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE) + +/// Wrapper for calling "stun()" and doing relevant vfx/sfx +/obj/item/melee/forged_reagent_weapon/proc/finalize_nonlethal_strike(mob/living/target, mob/living/user, clumsy = FALSE) + COOLDOWN_START(src, nonlethal_strike_cooldown_timer, nonlethal_strike_cooldown) + if(on_stun_sound) + playsound(src, on_stun_sound, 30, TRUE, -1) + if(baton_effect(target, user, null, clumsy) && user) + set_batoned(target, user, nonlethal_strike_cooldown) + log_combat(user, target, "stunned", src.name) + +/obj/item/melee/forged_reagent_weapon/proc/baton_effect(mob/living/target, mob/living/user, stun_override, clumsy) + if(iscyborg(target)) + return + + var/trait_check = HAS_TRAIT(target, TRAIT_BATON_RESISTANCE) + if(ishuman(target)) + var/mob/living/carbon/human/human_target = target + if(prob(70)) + human_target.force_say() + + var/stamina_damage = force * stamina_damage_multiplier + var/knockdown_time = force * knockdown_time_multiplier + var/armour_block = target.run_armor_check(BODY_ZONE_CHEST, MELEE, null, null, armour_penetration) + + target.apply_damage(stamina_damage, STAMINA, blocked = armour_block) + if(!trait_check) + target.Knockdown((isnull(stun_override) ? knockdown_time : stun_override)) + SEND_SIGNAL(target, COMSIG_MOB_BATONED, user, src) + return TRUE + +/// Used in marking a target as being hit by a baton +/obj/item/melee/forged_reagent_weapon/proc/set_batoned(mob/living/target, mob/living/user, cooldown) + PRIVATE_PROC(TRUE) + if(!cooldown) + return + var/user_ref = REF(user) // avoids harddels. + ADD_TRAIT(target, TRAIT_IWASBATONED, user_ref) + addtimer(TRAIT_CALLBACK_REMOVE(target, TRAIT_IWASBATONED, user_ref), cooldown) + +/obj/item/melee/forged_reagent_weapon/proc/get_stun_description(mob/living/target, mob/living/user) + PROTECTED_PROC(TRUE) + . = list() + if(length(secondary_attack_verb_continuous) >= 1) + var/random = rand(1, length(secondary_attack_verb_continuous)) + .["visible"] = span_danger("[user] [secondary_attack_verb_continuous[random]] [target] with [src]!") + .["local"] = span_userdanger("[user] [secondary_attack_verb_continuous[random]] you with [src]!") + +/obj/item/melee/forged_reagent_weapon/add_item_context(datum/source, list/context, atom/target, mob/living/user) + if (isturf(target)) + return NONE + + if (isobj(target)) + context[SCREENTIP_CONTEXT_LMB] = "Attack" + else + context[SCREENTIP_CONTEXT_LMB] = "Attack" + if(can_nonlethal_altfire && ishuman(target)) + context[SCREENTIP_CONTEXT_RMB] = "Non-lethally Attack" + + return CONTEXTUAL_SCREENTIP_SET + +/obj/item/melee/forged_reagent_weapon/sword name = "reagent sword" desc = "A sharp, maneuverable bastard sword most adept at blocking opposing melee strikes." force = 7 @@ -32,24 +210,28 @@ attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") sharpness = SHARP_EDGED max_integrity = 150 + stamina_damage_multiplier = 1.75 + knockdown_time_multiplier = 1 DECISECONDS + secondary_attack_verb_continuous = list("pommel-strikes") + secondary_attack_verb_simple = list("pommel-strike") var/wielded = FALSE var/unwielded_block_chance = 30 var/wielded_block_chance = 45 -/obj/item/forging/reagent_weapon/sword/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/sword/Initialize(mapload) . = ..() AddComponent(/datum/component/two_handed, force_multiplier = 2) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) -/obj/item/forging/reagent_weapon/sword/proc/on_wield() +/obj/item/melee/forged_reagent_weapon/sword/proc/on_wield() wielded = TRUE block_chance = wielded_block_chance -/obj/item/forging/reagent_weapon/sword/proc/on_unwield() +/obj/item/melee/forged_reagent_weapon/sword/proc/on_unwield() wielded = FALSE block_chance = unwielded_block_chance -/obj/item/forging/reagent_weapon/katana +/obj/item/melee/forged_reagent_weapon/katana name = "reagent katana" desc = "A katana sharp enough to penetrate body armor, but not quite million-times-folded sharp." force = 7 @@ -67,13 +249,17 @@ attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") sharpness = SHARP_EDGED + stamina_damage_multiplier = 1.5 + knockdown_time_multiplier = 0.6 DECISECONDS + secondary_attack_verb_continuous = list("pommel-strikes") + secondary_attack_verb_simple = list("pommel-strike") -/obj/item/forging/reagent_weapon/katana/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/katana/Initialize(mapload) . = ..() AddComponent(/datum/component/two_handed, force_multiplier = 2) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) -/obj/item/forging/reagent_weapon/dagger +/obj/item/melee/forged_reagent_weapon/dagger name = "reagent dagger" desc = "A lightweight dagger historically used to stab at the gaps in armour of fallen knights of old." force = 5 @@ -90,6 +276,7 @@ attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") sharpness = SHARP_EDGED + can_nonlethal_altfire = FALSE wound_bonus = 5 exposed_wound_bonus = 30 var/bonus_damage = 10 @@ -99,7 +286,7 @@ fall_chance = 1 pain_mult = 2 -/obj/item/forging/reagent_weapon/dagger/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/dagger/Initialize(mapload) . = ..() AddComponent(/datum/component/butchering, speed = 10 SECONDS, effectiveness = 70) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) @@ -107,7 +294,7 @@ //We're not reinventing the wheel, give it extra wounding if you land it, either you do or you dont, no dmg ups. Nullblade code but modified -/obj/item/forging/reagent_weapon/dagger/attack(atom/target, mob/user, list/modifiers, list/attack_modifiers) +/obj/item/melee/forged_reagent_weapon/dagger/attack(atom/target, mob/user, list/modifiers, list/attack_modifiers) . = ..() //Wounds scale off damage applied, so we're going to give the illusion of damaging people with the swing, when in reality it's all in the proc. if(!isliving(target)) @@ -124,7 +311,7 @@ if(check_for_sneak_attack(living_target, user) == TRUE) critical_hit(living_target) -/obj/item/forging/reagent_weapon/dagger/proc/check_for_sneak_attack(mob/living/carbon/carbon_target, mob/user) +/obj/item/melee/forged_reagent_weapon/dagger/proc/check_for_sneak_attack(mob/living/carbon/carbon_target, mob/user) // Check chaplain_nullrod.dm for original comments, I'm only leaving new ones in var/successful_sneak_attack = FALSE @@ -162,7 +349,7 @@ return FALSE return TRUE -/obj/item/forging/reagent_weapon/dagger/proc/critical_hit(mob/living/carbon/carbon_target, mob/user) +/obj/item/melee/forged_reagent_weapon/dagger/proc/critical_hit(mob/living/carbon/carbon_target, mob/user) var/obj/item/bodypart/affecting = carbon_target.get_bodypart(user.get_random_valid_zone(user.zone_selected)) var/armor_block = carbon_target.run_armor_check(affecting, MELEE, armour_penetration = armour_penetration) @@ -170,12 +357,11 @@ carbon_target.balloon_alert(user, "sneak attack!") playsound(carbon_target, 'sound/items/weapons/guillotine.ogg', 50, TRUE) -/obj/item/forging/reagent_weapon/rapier +/obj/item/melee/forged_reagent_weapon/rapier name = "reagent rapier" - desc = "A lightweight rapier with a light and quick swing, even while being so thin, you feel like you can stop all harm with this" - force = 12 - armour_penetration = 25 - block_chance = 35 + desc = "A lightweight rapier. Usually kept as a self-defense weapon; good at parrying attacks but cannot be two-handed for extra power." + force = 10 + block_chance = 30 icon_state = "rapier" inhand_icon_state = "sabre" icon_angle = -45 @@ -192,30 +378,51 @@ attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") attack_verb_simple = list("attack", "pierces", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") sharpness = SHARP_POINTY + stamina_damage_multiplier = 1.5 + knockdown_time_multiplier = 1.4 + secondary_attack_verb_continuous = list("hilt-strikes") + secondary_attack_verb_simple = list("hilt-strike") -/obj/item/forging/reagent_weapon/rapier/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/rapier/Initialize(mapload) . = ..() AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) -/obj/item/forging/reagent_weapon/staff //doesn't do damage. Useful for healing reagents. +/obj/item/melee/forged_reagent_weapon/rapier/apply_reagent_component() + AddComponent(/datum/component/reagent_imbued/weapon, list(FORGE_EFFECT_BLOCKCHANCE = 5)) + +/obj/item/melee/forged_reagent_weapon/staff //doesn't do damage. Useful for healing reagents. name = "reagent staff" desc = "A staff most notably capable of being imbued with reagents, especially useful alongside its otherwise harmless nature." force = 0 icon_state = "staff" inhand_icon_state = "staff" worn_icon_state = "staff_back" + hitsound = 'sound/effects/magic/staff_healing.ogg' throwforce = 0 slot_flags = ITEM_SLOT_BACK w_class = WEIGHT_CLASS_NORMAL resistance_flags = FIRE_PROOF - attack_verb_continuous = list("bonks", "bashes", "whacks", "pokes", "prods") - attack_verb_simple = list("bonk", "bash", "whack", "poke", "prod") + attack_verb_continuous = list("reagent casts on", "waves a staff over") + attack_verb_simple = list("reagent cast on", "wave a staff over") -/obj/item/forging/reagent_weapon/staff/attack(mob/living/M, mob/living/user, params) +/obj/item/melee/forged_reagent_weapon/staff/apply_reagent_component() + AddComponent(/datum/component/reagent_imbued/weapon, list(FORGE_EFFECT_REAGENT_INJECT = 3), 0.7) + +/obj/item/melee/forged_reagent_weapon/staff/apply_smithing_component() + AddComponent(/datum/component/forge_smithable, \ + FORGING_WEAPON_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_WEAPON_REFORGING_MAX_PERFECT_HITS, \ + FORGING_WEAPON_REFORGING_MAX_BAD_HITS, \ + FORGING_WEAPON_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_REAGENT_INJECT = 3), \ + incompletion_effects = list(FORGE_EFFECT_REAGENT_INJECT, FORGE_EFFECT_DURABILITY)) + +/obj/item/melee/forged_reagent_weapon/staff/attack(mob/living/M, mob/living/user, params) . = ..() user.changeNext_move(CLICK_CD_RANGE) -/obj/item/forging/reagent_weapon/spear +/obj/item/melee/forged_reagent_weapon/spear name = "reagent spear" desc = "A long spear that can be wielded in two hands to boost damage at the cost of single-handed versatility." force = 7 @@ -235,8 +442,12 @@ wound_bonus = -10 exposed_wound_bonus = 20 sharpness = SHARP_POINTY + stamina_damage_multiplier = 2 + knockdown_time_multiplier = 0.2 + secondary_attack_verb_continuous = list("shaft-strikes") + secondary_attack_verb_simple = list("shaft-strike") -/obj/item/forging/reagent_weapon/spear/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/spear/Initialize(mapload) . = ..() AddComponent(/datum/component/jousting, max_tile_charge = 9, min_tile_charge = 6) AddComponent(/datum/component/butchering, speed = 10 SECONDS, effectiveness = 70) @@ -244,13 +455,13 @@ AddComponent(/datum/component/two_hand_reach, unwield_reach = 2, wield_reach = 1) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) -/obj/item/forging/reagent_weapon/spear/proc/on_wield(obj/item/source, mob/living/carbon/user) +/obj/item/melee/forged_reagent_weapon/spear/proc/on_wield(obj/item/source, mob/living/carbon/user) armour_penetration *= 2 -/obj/item/forging/reagent_weapon/spear/proc/on_unwield(obj/item/source, mob/living/carbon/user) +/obj/item/melee/forged_reagent_weapon/spear/proc/on_unwield(obj/item/source, mob/living/carbon/user) armour_penetration /= 2 -/obj/item/forging/reagent_weapon/axe +/obj/item/melee/forged_reagent_weapon/axe name = "reagent axe" desc = "An axe especially balanced for throwing and embedding into fleshy targets, yet also effective at destroying shields of all sorts." force = 7 @@ -268,20 +479,24 @@ attack_verb_continuous = list("slashes", "bashes") attack_verb_simple = list("slash", "bash") sharpness = SHARP_EDGED + stamina_damage_multiplier = 3 + knockdown_time_multiplier = 0.7 + secondary_attack_verb_continuous = list("blunt-strikes") + secondary_attack_verb_simple = list("blunt-strike") /datum/embedding/forged_axe embed_chance = 65 fall_chance = 10 pain_mult = 2 -/obj/item/forging/reagent_weapon/axe/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/axe/Initialize(mapload) . = ..() AddComponent(/datum/component/butchering, speed = 10 SECONDS, effectiveness = 70) AddComponent(/datum/component/two_handed, force_multiplier = 2) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) -/obj/item/forging/reagent_weapon/axe/pre_attack(mob/living/M, mob/living/user, params) +/obj/item/melee/forged_reagent_weapon/axe/pre_attack(mob/living/M, mob/living/user, params) . = ..() if(ishuman(M)) var/mob/living/carbon/human/H = M @@ -295,17 +510,18 @@ playsound(src, 'sound/effects/tableheadsmash.ogg', 30) H.apply_damage(15, STAMINA) -/obj/item/forging/reagent_weapon/hammer +/obj/item/melee/forged_reagent_weapon/hammer name = "reagent hammer" desc = "A heavy, weighted hammer that packs an incredible punch but can prove to be unwieldy. Useful for forging!" - force = 7 //strong when wielded, but boring. + force = 6 //strong when wielded, but boring. armour_penetration = 10 wound_bonus = -5 icon_state = "crush_hammer" inhand_icon_state = "crush_hammer" worn_icon_state = "hammer_back" throwforce = 10 - slot_flags = ITEM_SLOT_BACK + demolition_mod = 1.5 + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT w_class = WEIGHT_CLASS_BULKY resistance_flags = FIRE_PROOF attack_verb_continuous = list("bashes", "whacks") @@ -316,14 +532,38 @@ /obj/structure/reagent_anvil, /obj/structure/reagent_crafting_bench ) + stamina_damage_multiplier = 1.8 + knockdown_time_multiplier = 0.6 + secondary_attack_verb_continuous = list("shaft-strikes") + secondary_attack_verb_simple = list("shaft-strike") -/obj/item/forging/reagent_weapon/hammer/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/hammer/Initialize(mapload) . = ..() AddElement(/datum/element/kneejerk) AddComponent(/datum/component/two_handed, force_multiplier = 2.4) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) -/obj/item/forging/reagent_weapon/hammer/attack(mob/living/M, mob/living/user, params) +/obj/item/melee/forged_reagent_weapon/hammer/apply_smithing_component() + AddComponent(/datum/component/forge_smithable, \ + FORGING_WEAPON_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_WEAPON_REFORGING_MAX_PERFECT_HITS, \ + FORGING_WEAPON_REFORGING_MAX_BAD_HITS, \ + FORGING_WEAPON_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_FORCE = MAX_PERFECT_FORCE_BONUS, FORGE_EFFECT_TOOLSPEED = -0.2), \ + incompletion_effects = list(FORGE_EFFECT_FORCE, FORGE_EFFECT_ARMORPEN, FORGE_EFFECT_TOOLSPEED)) + +/obj/item/melee/forged_reagent_weapon/hammer/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + . = ..() + var/density = material.get_property(MATERIAL_DENSITY) + var/hardness = material.get_property(MATERIAL_HARDNESS) + var/toolspeed_bonus = (density + hardness - 12) / 12 + if(!remove) + toolspeed -= toolspeed_bonus + else + toolspeed += toolspeed_bonus + +/obj/item/melee/forged_reagent_weapon/hammer/attack(mob/living/M, mob/living/user, params) . = ..() user.changeNext_move(CLICK_CD_SLOW) //The hammer attacks slower but has more damage, that's it's thing now @@ -343,67 +583,74 @@ transparent = FALSE max_integrity = 150 //over double that of a wooden one w_class = WEIGHT_CLASS_NORMAL - material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_AFFECT_STATISTICS - skyrat_obj_flags = ANVIL_REPAIR + material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_AFFECT_STATISTICS | MATERIAL_COLOR shield_break_sound = 'sound/effects/bang.ogg' shield_break_leftover = /obj/item/forging/complete/plate /obj/item/shield/buckler/reagent_weapon/Initialize(mapload) . = ..() - AddComponent(/datum/component/reagent_weapon) + AddComponent(/datum/component/reagent_imbued/weapon, oil_effects = list(FORGE_EFFECT_BLOCKCHANCE = 5, FORGE_EFFECT_DURABILITY = 20)) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) + apply_smithing_component() -/obj/item/shield/buckler/reagent_weapon/examine(mob/user) - . = ..() - . += span_notice("Using a hammer on [src] will repair its damage!") +/obj/item/shield/buckler/reagent_weapon/change_material_integrity(datum/material/material, amount, multiplier, removing = FALSE) + blacksmithing_change_material_integrity(src, material, amount, multiplier, removing) -/obj/item/shield/buckler/reagent_weapon/attackby(obj/item/attacking_item, mob/user, params) - if(atom_integrity >= max_integrity) - return ..() - if(istype(attacking_item, /obj/item/forging/hammer)) - var/obj/item/forging/hammer/attacking_hammer = attacking_item - var/skill_modifier = user.mind.get_skill_modifier(/datum/skill/smithing, SKILL_SPEED_MODIFIER) * attacking_hammer.toolspeed - while(atom_integrity < max_integrity) - if(!do_after(user, skill_modifier, src)) - return - var/fixing_amount = min(max_integrity - atom_integrity, 5) - atom_integrity += fixing_amount - user.mind.adjust_experience(/datum/skill/smithing, 5) //useful heating means you get some experience - balloon_alert(user, "partially repaired!") - return - return ..() +/obj/item/shield/buckler/reagent_weapon/change_material_strength(datum/material/material, mat_amount, multiplier, remove = FALSE) + blacksmithing_change_material_strength(src, material, mat_amount, multiplier, remove) + +/obj/item/shield/buckler/reagent_weapon/proc/apply_smithing_component() + AddComponent(/datum/component/forge_smithable, \ + FORGING_WEAPON_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_WEAPON_REFORGING_MAX_PERFECT_HITS, \ + FORGING_WEAPON_REFORGING_MAX_BAD_HITS, \ + FORGING_WEAPON_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_BLOCKCHANCE = 5), \ + incompletion_effects = list(FORGE_EFFECT_BLOCKCHANCE, FORGE_EFFECT_FORCE)) /obj/item/shield/buckler/reagent_weapon/pavise //similar to the adamantine shield. Huge, slow, lets you soak damage and packs a wallop. name = "reagent plated pavise shield" - desc = "An oblong shield used by ancient crossbowmen as cover while reloading. Probably just as useful with an actual gun." + desc = "An large wall-like shield. Very heavy, but extremely protective." force = 10 icon_state = "pavise" inhand_icon_state = "pavise" worn_icon_state = "pavise_back" - block_chance = 75 - item_flags = SLOWS_WHILE_IN_HAND + block_chance = 55 + item_flags = SLOWS_WHILE_IN_HAND | IMMUTABLE_SLOW + slowdown = 2.0 + drag_slowdown = 3.5 w_class = WEIGHT_CLASS_HUGE slot_flags = ITEM_SLOT_BACK max_integrity = 300 //tanky /obj/item/shield/buckler/reagent_weapon/pavise/Initialize(mapload) . = ..() - AddComponent(/datum/component/two_handed, require_twohands = TRUE, force_multiplier = 1.5) AddComponent(/datum/component/mindless_killer, mindless_force_override = 0, mindless_multiplier_override = 2) +/obj/item/shield/buckler/reagent_weapon/pavise/apply_smithing_component() + AddComponent(/datum/component/forge_smithable, \ + FORGING_WEAPON_REFORGING_MAX_QUALITY, \ + TRUE, \ + FORGING_WEAPON_REFORGING_MAX_PERFECT_HITS, \ + FORGING_WEAPON_REFORGING_MAX_BAD_HITS, \ + FORGING_WEAPON_REFORGING_AVERAGE_WAIT, \ + perfection_effects = list(FORGE_EFFECT_BLOCKCHANCE = 10), \ + incompletion_effects = list(FORGE_EFFECT_BLOCKCHANCE, FORGE_EFFECT_FORCE)) + /obj/item/pickaxe/reagent_weapon name = "reagent pickaxe" /obj/item/pickaxe/reagent_weapon/Initialize(mapload) . = ..() - AddComponent(/datum/component/reagent_weapon) + AddComponent(/datum/component/reagent_imbued/weapon) /obj/item/shovel/reagent_weapon name = "reagent shovel" /obj/item/shovel/reagent_weapon/Initialize(mapload) . = ..() - AddComponent(/datum/component/reagent_weapon) + AddComponent(/datum/component/reagent_imbued/weapon) /obj/item/ammo_casing/arrow/attackby(obj/item/attacking_item, mob/user, params) var/spawned_item @@ -429,7 +676,7 @@ user.put_in_hands(converted_arrow) qdel(src) -/obj/item/forging/reagent_weapon/bokken +/obj/item/melee/forged_reagent_weapon/bokken name = "bokken" desc = "A wooden sword that is capable of wielded in two hands. It seems to be made to prevent permanent injuries." force = 10 @@ -443,13 +690,15 @@ slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK w_class = WEIGHT_CLASS_BULKY resistance_flags = FLAMMABLE + stamina_damage_multiplier = 1.5 + knockdown_time_multiplier = 0.6 DECISECONDS attack_verb_continuous = list("bonks", "bashes", "whacks", "pokes", "prods") attack_verb_simple = list("bonk", "bash", "whack", "poke", "prod") var/wielded = FALSE var/unwielded_block_chance = 20 var/wielded_block_chance = 40 -/obj/item/forging/reagent_weapon/bokken/Initialize(mapload) +/obj/item/melee/forged_reagent_weapon/bokken/Initialize(mapload) . = ..() AddComponent(/datum/component/two_handed,\ force_multiplier = 2, \ @@ -457,15 +706,15 @@ unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ ) -/obj/item/forging/reagent_weapon/bokken/proc/on_wield() +/obj/item/melee/forged_reagent_weapon/bokken/proc/on_wield() wielded = TRUE block_chance = wielded_block_chance -/obj/item/forging/reagent_weapon/bokken/proc/on_unwield() +/obj/item/melee/forged_reagent_weapon/bokken/proc/on_unwield() wielded = FALSE block_chance = unwielded_block_chance -/obj/item/forging/reagent_weapon/bokken/attack(mob/living/carbon/target_mob, mob/living/user, params) +/obj/item/melee/forged_reagent_weapon/bokken/attack(mob/living/carbon/target_mob, mob/living/user, params) . = ..() if(!iscarbon(target_mob)) user.visible_message(span_warning("The [src] seems to be ineffective against the [target_mob]!")) @@ -476,3 +725,5 @@ /obj/item/spear/Initialize(mapload) . = ..() AddComponent(/datum/component/two_hand_reach, unwield_reach = 1, wield_reach = 2) + +#undef STUN_ATTACK diff --git a/modular_skyrat/modules/reagent_forging/code/memories.dm b/modular_skyrat/modules/reagent_forging/code/memories.dm new file mode 100644 index 00000000000..718da4c5ed1 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/memories.dm @@ -0,0 +1,125 @@ +/// Keeps track of smithing items & what was made by who +/datum/memory/smithing + story_value = STORY_VALUE_MEH + // Protagonist - The smith making the item + /// What type of crafting it was + var/smithed_item_type = null + /// name of the item for strings + var/item_name = null + +/datum/memory/smithing/New( + datum/mind/memorizer_mind, + atom/protagonist, + atom/deuteragonist, + atom/antagonist, +) + return ..() + +/datum/memory/smithing/get_names() + return list("The time [protagonist_name] made a [initial(item_name)].") + +/datum/memory/smithing/get_starts() + return list( + "[protagonist_name] carefully assembling a [initial(item_name)].", + ) + +/datum/memory/smithing/get_moods() + return list( + "[protagonist_name] [mood_verb] after finishing [initial(item_name)].", + ) + +/datum/memory/smithing/sword + smithed_item_type = /obj/item/melee/forged_reagent_weapon/sword + item_name = "sword" + +/datum/memory/smithing/katana + smithed_item_type = /obj/item/melee/forged_reagent_weapon/katana + item_name = "katana" + +/datum/memory/smithing/dagger + smithed_item_type = /obj/item/melee/forged_reagent_weapon/dagger + item_name = "dagger" + +/datum/memory/smithing/rapier + smithed_item_type = /obj/item/melee/forged_reagent_weapon/rapier + item_name = "rapier" + +/datum/memory/smithing/spear + smithed_item_type = /obj/item/melee/forged_reagent_weapon/spear + item_name = "spear" + +/datum/memory/smithing/axe + smithed_item_type = /obj/item/melee/forged_reagent_weapon/axe + item_name = "axe" + +/datum/memory/smithing/hammer + smithed_item_type = /obj/item/melee/forged_reagent_weapon/hammer + item_name = "hammer" + +/datum/memory/smithing/buckler + smithed_item_type = /obj/item/shield/buckler/reagent_weapon + item_name = "buckler" + +/datum/memory/smithing/pavise + smithed_item_type = /obj/item/shield/buckler/reagent_weapon/pavise + item_name = "pavise" + +/datum/memory/smithing/revolver + smithed_item_type = /obj/item/gun/ballistic/revolver/handcrafted_single_action + item_name = "revolver" + +/datum/memory/smithing/helmet + smithed_item_type = /obj/item/clothing/head/helmet/forging_plate_helmet + item_name = "hammer" + +/datum/memory/smithing/plate_vest + smithed_item_type = /obj/item/clothing/suit/armor/forging_plate_armor + item_name = "plate vest" + +/datum/memory/smithing/gloves + smithed_item_type = /obj/item/clothing/gloves/forging_plate_gloves + item_name = "pair of plate gloves" + +/datum/memory/smithing/boots + smithed_item_type = /obj/item/clothing/shoes/forging_plate_boots + item_name = "pair of plate boots" + +/datum/memory/smithing/horseshoes + smithed_item_type = /obj/item/clothing/shoes/horseshoe/reagent_clothing + item_name = "set of horseshoes" + +/datum/memory/smithing/ring + smithed_item_type = /obj/item/clothing/gloves/ring/reagent_clothing + item_name = "ring" + +/datum/memory/smithing/collar + smithed_item_type = /obj/item/clothing/neck/collar/reagent_clothing + item_name = "collar" + +/datum/memory/smithing/cowboy_holster + smithed_item_type = /obj/item/storage/belt/hip_holster/cowboy + item_name = "holster" + +/datum/memory/smithing/charging_holster + smithed_item_type = /obj/item/storage/belt/hip_holster/charging + item_name = "charging holster" + +/datum/memory/smithing/crusader_belt + smithed_item_type = /obj/item/storage/belt/crusader + item_name = "utility-scabbard belt" + +/datum/memory/smithing/multi_scabbard + smithed_item_type = /obj/item/storage/belt/sheath/multi + item_name = "multi-scabbard belt" + +/datum/memory/smithing/repairing_scabbard + smithed_item_type = /obj/item/storage/belt/sheath/repairing + item_name = "repairing scabbard" + +/datum/memory/smithing/knifethrower + smithed_item_type = /obj/item/storage/belt/knifethrowers_belt + item_name = "knifethrower's belt" + +/datum/memory/smithing/bluespace_plants + smithed_item_type = /obj/item/storage/bag/plants/bluespace + item_name = "bluespace plants bag" diff --git a/modular_skyrat/modules/reagent_forging/code/mindless_killer.dm b/modular_skyrat/modules/reagent_forging/code/mindless_killer.dm index 0420c7c7d5d..e7b40e88469 100644 --- a/modular_skyrat/modules/reagent_forging/code/mindless_killer.dm +++ b/modular_skyrat/modules/reagent_forging/code/mindless_killer.dm @@ -29,6 +29,8 @@ return if(target_mob.mind) return + if(modifiers[FORCE_OVERRIDE] == 0) + return var/extra_damage = (obj_parent.force + mindless_force) * mindless_multiplier target_mob.apply_damage(extra_damage, forced = TRUE, spread_damage = TRUE) return diff --git a/modular_skyrat/modules/reagent_forging/code/reagent_component.dm b/modular_skyrat/modules/reagent_forging/code/reagent_component.dm index ca186d156dd..bbb98146634 100644 --- a/modular_skyrat/modules/reagent_forging/code/reagent_component.dm +++ b/modular_skyrat/modules/reagent_forging/code/reagent_component.dm @@ -1,116 +1,203 @@ -#define MAX_IMBUE_STORAGE 250 -#define REAGENT_CLOTHING_INJECT_AMOUNT 0.5 -#define REAGENT_WEAPON_INJECT_AMOUNT 1 -#define REAGENT_WEAPON_DAMAGE_MULTIPLIER 3 + + +///Parent component that stores shared info +/datum/component/reagent_imbued/ + ///the item that the component is attached to + var/obj/item/parent_item + ///required type to attach to + var/obj/item/required_type = /obj/item + //the imbued reagents + var/datum/reagents/imbued_reagent = new(10, NO_REACT) + ///the text to show the player what happens + var/examine_imbued_description = "It is currently imbued with the following:" + ///required parent item integrity% for reagent effects + var/integrity_required + ///how much reagent to inject per tic/proc + var/inject_amount = 0 + ///what smithing oil does to this (at 100% oil imbuing it has 100% of the listed effects) + var/list/smithing_oil_effects = list() + ///keeps track of how effective last smithing oil imbue did (% of total imbue capacity) + var/last_smithing_oil_ratio_applied = 0 + ///damage mult on parent item when reagent procs + var/integrity_loss_per_inject = 1 + +/// * set_slot: Used for clothing only, ignore if this isn't the case. +/// * integrity: what integ % is required to use the reagent imbue? +/datum/component/reagent_imbued/Initialize(list/oil_effects = list(), integrity = 0.85, set_slot = null) + if(!istype(parent, required_type)) + return COMPONENT_INCOMPATIBLE //they need to be clothing, I already said this + parent_item = parent + parent_item.create_reagents(MAX_PRE_IMBUE_STORAGE, INJECTABLE | REFILLABLE) + integrity_required = integrity + RegisterSignal(parent_item, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent_item, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examine_more)) + smithing_oil_effects = oil_effects + +/datum/component/reagent_imbued/proc/on_examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + if(USER_CAN_REAGENT_IMBUE(user)) + examine_list += span_notice("[parent_item] is able to be imbued by dipping it into a container such as a quenching trough, or pouring chemicals into it and then tempering it at a forge!") + else + examine_list += span_notice("If you knew a certain smithing trick, you could imbue special chemicals into this...") + if(parent_item.get_integrity_percentage() < integrity_required) + examine_list += span_notice("It must be repaired before its imbued reagent effects can work...") + if(imbued_reagent.reagent_list.len >= 1 && (USER_CAN_REAGENT_IMBUE(user) || HAS_TRAIT(user, TRAIT_REAGENT_SCANNER))) + examine_list += span_notice("You could inspect this closer to see what is imbued into it, and what smithing oil does...") + + +/datum/component/reagent_imbued/proc/on_examine_more(obj/item/source, mob/examiner, list/examine_list) + if(imbued_reagent.reagent_list.len >= 1 && (HAS_TRAIT(examiner, TRAIT_KNOW_ADVANCED_SMITHING) || HAS_TRAIT(examiner, TRAIT_REAGENT_SCANNER))) + add_reagent_imbue_description(examine_list) + + add_oil_imbue_effect_description(examine_list) + +/datum/component/reagent_imbued/proc/add_reagent_imbue_description(list/examine_list) + examine_list += span_notice(examine_imbued_description) + for (var/datum/reagent/reagent in imbued_reagent.reagent_list) + examine_list += span_notice("[reagent.volume] units of [reagent.name]") + +/datum/component/reagent_imbued/proc/add_oil_imbue_effect_description(list/examine_list) + if(smithing_oil_effects.len > 0) + var/list/effects_list = list() + for(var/i in smithing_oil_effects) + switch(i) + if(FORGE_EFFECT_ARMOR) + effects_list += "cushion blows more effectively" + if(FORGE_EFFECT_ARMORPEN) + effects_list += "pierce through armor more efficiently" + if(FORGE_EFFECT_BLOCKCHANCE) + effects_list += "better at deflecting blows" + if(FORGE_EFFECT_DURABILITY) + effects_list += "reinforce its durability" + if(FORGE_EFFECT_FORCE) + effects_list += "enhance its destructive potential" + if(FORGE_EFFECT_REAGENT_INJECT) + effects_list += "inflict more of the other containing reagents" + if(FORGE_EFFECT_TOOLSPEED) + effects_list += "work more efficiently in its intended use" + + examine_list += span_notice("Smithing oil will make it [english_list(effects_list)].") + else + examine_list += span_notice("Smithing oil has no effect on [parent_item], but may affect things crafted with it.") + +///Replaces the imbued_reagent with the given new_reagents. +/datum/component/reagent_imbued/proc/set_reagent_imbue(datum/reagents/new_reagents, clear_source_reagents = TRUE) + //copy in the new reagents to replace the old + imbued_reagent.clear_reagents() + new_reagents.trans_to(imbued_reagent, amount = new_reagents.total_volume, copy_only = TRUE, no_react = TRUE) + + apply_smithing_oil_bonus() + parent_item.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) + if(length(imbued_reagent.reagent_list) > 0) + var/new_color = mix_color_from_reagents(imbued_reagent.reagent_list) + if(length(new_color) > 7) //remove the transparency stuff + new_color = copytext(new_color, 1, 8) + "FF" + parent_item.add_atom_colour(color_transition_filter(new_color, SATURATION_OVERRIDE), WASHABLE_COLOUR_PRIORITY) + + if(clear_source_reagents) + new_reagents.clear_reagents() + +///This function modifies the parent item based on how much smithing oil is in our imbued reagent list. Needs to be defined in child subtypes. +/datum/component/reagent_imbued/proc/apply_smithing_oil_bonus() + if(length(smithing_oil_effects) > 0) + var/new_oil_ratio = imbued_reagent.remove_reagent(/datum/reagent/fuel/oil/smithing, imbued_reagent.maximum_volume, include_subtypes = FALSE) + new_oil_ratio /= imbued_reagent.maximum_volume + for(var/index in smithing_oil_effects) + give_added_modifying_effect_to_item(index, last_smithing_oil_ratio_applied, new_oil_ratio, parent_item, smithing_oil_effects[index]) + last_smithing_oil_ratio_applied = new_oil_ratio + +/datum/component/reagent_imbued/proc/try_inject(mob/living/target, mob/living/user = null) + if(isnull(user)) + user = target + //don't have the weapon or any imbued reagents? don't try + if(isnull(parent_item) || imbued_reagent?.reagent_list?.len < 1) + return FALSE + if(imbued_reagent.total_volume == 0) + return FALSE + if(parent_item.get_integrity_percentage() < integrity_required) + return FALSE + + //lets inject that target + var/mob/living_target = target + var/new_amount_to_inject = min(inject_amount, imbued_reagent.total_volume) + imbued_reagent.trans_to(target = living_target, amount = new_amount_to_inject, transferred_by = user, methods = INJECT, copy_only = TRUE) + parent_item.take_damage(integrity_loss_per_inject * new_amount_to_inject, sound_effect = FALSE) + return TRUE //the component that is attached to clothes that allows them to be imbued //ONLY USE THIS FOR CLOTHING -/datum/component/reagent_clothing - ///the item that the component is attached to - var/obj/item/parent_clothing +/datum/component/reagent_imbued/clothing ///the slot that the item will check var/checking_slot - ///the human that is wearing the parent_clothing + ///the human that is wearing the parent_item var/mob/living/carbon/human/cloth_wearer - ///the container that will apply the chemicals - var/obj/item/reagent_containers/applying_container - ///the list of imbued reagents that will given to the human owner - var/list/imbued_reagent = list() + imbued_reagent = new(maximum = REAGENT_CLOTHING_INJECT_AMOUNT, new_flags = NO_REACT) + examine_imbued_description = "It will slowly administer the following to its wearer:" + inject_amount = REAGENT_CLOTHING_INJECT_AMOUNT //the cooldown between each imbue COOLDOWN_DECLARE(imbue_cooldown) -/datum/component/reagent_clothing/Initialize(set_slot = null) - if(!istype(parent, /obj/item)) - return COMPONENT_INCOMPATIBLE //they need to be clothing, I already said this +/datum/component/reagent_imbued/clothing/Initialize(list/oil_effects = list(), integrity = 0.85, set_slot = null) + . = ..() + if(. != COMPONENT_INCOMPATIBLE) + if(set_slot) + checking_slot = set_slot + RegisterSignal(parent_item, COMSIG_ITEM_EQUIPPED, PROC_REF(set_wearer)) + RegisterSignal(parent_item, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(remove_wearer)) + START_PROCESSING(SSdcs, src) - if(set_slot) - checking_slot = set_slot - - parent_clothing = parent - parent_clothing.create_reagents(MAX_IMBUE_STORAGE, INJECTABLE | REFILLABLE) - applying_container = new /obj/item/reagent_containers(src) - RegisterSignal(parent_clothing, COMSIG_ITEM_EQUIPPED, PROC_REF(set_wearer)) - RegisterSignal(parent_clothing, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(remove_wearer)) - RegisterSignal(parent_clothing, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) - START_PROCESSING(SSdcs, src) - -/datum/component/reagent_clothing/Destroy(force, silent) - parent_clothing = null +/datum/component/reagent_imbued/clothing/Destroy(force, silent) + parent_item = null cloth_wearer = null - QDEL_NULL(applying_container) STOP_PROCESSING(SSdcs, src) return ..() -/datum/component/reagent_clothing/proc/on_examine(datum/source, mob/user, list/examine_list) - SIGNAL_HANDLER - examine_list += span_notice("[parent_clothing] is able to be inbued with a chemical at a reagent forge!") -/datum/component/reagent_clothing/proc/set_wearer() +/datum/component/reagent_imbued/clothing/proc/set_wearer() SIGNAL_HANDLER - if(!ishuman(parent_clothing.loc)) + if(!ishuman(parent_item.loc)) return - cloth_wearer = parent_clothing.loc + cloth_wearer = parent_item.loc -/datum/component/reagent_clothing/proc/remove_wearer() +/datum/component/reagent_imbued/clothing/proc/remove_wearer() SIGNAL_HANDLER cloth_wearer = null -/datum/component/reagent_clothing/process(seconds_per_tick) - if(!parent_clothing || !cloth_wearer || !length(imbued_reagent)) +/datum/component/reagent_imbued/clothing/process(seconds_per_tick) + if(!cloth_wearer) return - if(parent_clothing != cloth_wearer.get_item_by_slot(checking_slot)) + if(parent_item != cloth_wearer.get_item_by_slot(checking_slot)) return if(!COOLDOWN_FINISHED(src, imbue_cooldown)) return - COOLDOWN_START(src, imbue_cooldown, 3 SECONDS) - - for(var/create_reagent in imbued_reagent) - applying_container.reagents.add_reagent(create_reagent, REAGENT_CLOTHING_INJECT_AMOUNT) - applying_container.reagents.trans_to(target = cloth_wearer, amount = REAGENT_CLOTHING_INJECT_AMOUNT, methods = INJECT) + if(try_inject(cloth_wearer)) + COOLDOWN_START(src, imbue_cooldown, 3 SECONDS) //the component that is attached to weapons that allows them to be imbued //ONLY USE THIS FOR WEAPONS -/datum/component/reagent_weapon - ///the item that the component is attached to - var/obj/item/parent_weapon - ///the list of imbued reagents that will given to the human owner - var/list/imbued_reagent = list() +/datum/component/reagent_imbued/weapon + imbued_reagent = new(REAGENT_WEAPON_INJECT_AMOUNT, NO_REACT) + examine_imbued_description = "Upon hit, it will inject the following to its victim:" + var/extra_force_oil_bonus = 0 + inject_amount = REAGENT_WEAPON_INJECT_AMOUNT -/datum/component/reagent_weapon/Initialize(...) - if(!istype(parent, /obj/item)) - return COMPONENT_INCOMPATIBLE //they need to be weapons, I already said this - parent_weapon = parent - parent_weapon.create_reagents(MAX_IMBUE_STORAGE, INJECTABLE | REFILLABLE) - RegisterSignal(parent_weapon, COMSIG_ITEM_ATTACK, PROC_REF(inject_attacked)) - RegisterSignal(parent_weapon, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) +/datum/component/reagent_imbued/weapon/Initialize(list/oil_effects = list(), integrity = 0.85, set_slot = null) + . = ..() + if(. != COMPONENT_INCOMPATIBLE) + RegisterSignal(parent_item, COMSIG_ITEM_ATTACK, PROC_REF(inject_attacked)) -/datum/component/reagent_weapon/Destroy(force, silent) - parent_weapon = null +/datum/component/reagent_imbued/weapon/Destroy(force, silent) + parent_item = null return ..() -/datum/component/reagent_weapon/proc/on_examine(datum/source, mob/user, list/examine_list) +/datum/component/reagent_imbued/weapon/proc/inject_attacked(datum/source, mob/living/target, mob/living/user, list/modifiers) SIGNAL_HANDLER - examine_list += span_notice("[parent_weapon] is able to be imbued with a chemical at a reagent forge!") - -/datum/component/reagent_weapon/proc/inject_attacked(datum/source, mob/living/target, mob/living/user, list/modifiers) - SIGNAL_HANDLER - - //don't have the weapon or any imbued reagents? don't try - if(!parent_weapon || !length(imbued_reagent)) - return - - //lets inject that target - var/mob/living_target = target - for(var/create_reagent in imbued_reagent) - living_target.reagents.add_reagent(create_reagent, REAGENT_WEAPON_INJECT_AMOUNT) - parent_weapon.take_damage(length(imbued_reagent) * REAGENT_WEAPON_DAMAGE_MULTIPLIER) - - -#undef MAX_IMBUE_STORAGE + try_inject(target) +#undef MAX_PRE_IMBUE_STORAGE #undef REAGENT_CLOTHING_INJECT_AMOUNT #undef REAGENT_WEAPON_INJECT_AMOUNT -#undef REAGENT_WEAPON_DAMAGE_MULTIPLIER +#undef REAGENT_STAFF_INJECT_AMOUNT diff --git a/modular_skyrat/modules/reagent_forging/code/reagent_smithing_oil.dm b/modular_skyrat/modules/reagent_forging/code/reagent_smithing_oil.dm new file mode 100644 index 00000000000..d4bea0cf4a3 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/reagent_smithing_oil.dm @@ -0,0 +1,16 @@ +/datum/reagent/fuel/oil/smithing + name = "Smithing Oil" + description = "A specialized type of oil with various blacksmithing applications, from quenching hot metal, to enhancing the hardness of blades and armor. Has a very high burning point, unless it's exposed to fire..." + color = "#2D2D2D" + taste_description = "oil" + burning_temperature = 6000 + burning_volume = 0.05 + chemical_flags = NONE + addiction_types = null + default_container = /obj/effect/decal/cleanable/blood/splatter/oil + +/obj/item/reagent_containers/cup/jerrycan/smithing_oil + name = "Jarnsmiour Blacksteel Foundation-brand smithing oil" + cap_type = "red" + desc = "Jarnsmiour Blacksteel's finest smithing oil; a favored ingredient of the intergalactic spacesmith's union, thanks to its ability to enhance the inherrent capabilities of nearly every form of metal." + list_reagents = list(/datum/reagent/fuel/oil/smithing = 200) diff --git a/modular_skyrat/modules/reagent_forging/code/smith_skill.dm b/modular_skyrat/modules/reagent_forging/code/smith_skill.dm index 5deb7374755..94b48d87286 100644 --- a/modular_skyrat/modules/reagent_forging/code/smith_skill.dm +++ b/modular_skyrat/modules/reagent_forging/code/smith_skill.dm @@ -3,7 +3,7 @@ title = "Smithy" desc = "The desperate artist who strives after the flames of the forge." modifiers = list( - SKILL_SPEED_MODIFIER = list(1, 0.95, 0.9, 0.85, 0.75, 0.6, 0.5), + SKILL_SPEED_MODIFIER = list(1, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7), SKILL_PROBS_MODIFIER = list(0, 5, 10, 20, 40, 80, 100) ) skill_item_path = /obj/item/clothing/neck/cloak/skill_reward/smithing diff --git a/modular_skyrat/modules/reagent_forging/code/smithing_trait_and_chip.dm b/modular_skyrat/modules/reagent_forging/code/smithing_trait_and_chip.dm new file mode 100644 index 00000000000..dda0323b127 --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/smithing_trait_and_chip.dm @@ -0,0 +1,21 @@ +/obj/item/skillchip/job/blacksmith + name = "Smithing F0RG-3M4573-R skillchip" + desc = "Grants skills related to blacksmithing and metalworking." + auto_traits = list(TRAIT_KNOW_ADVANCED_SMITHING, TRAIT_KNOW_GUNSMITHING, TRAIT_KNOW_CIRCUIT_SMITHING) + skill_name = "Advanced Smithing" + skill_description = "Contains schematics for more advanced equipment forging, as well as how to best apply it." + skill_icon = "gavel" + activate_message = span_notice("You suddenly know how to make advanced metalworking components out of raw materials.") + deactivate_message = span_notice("The memories of how to construct advanced metalworking components fade from your mind.") + +/obj/item/storage/box/skillchips/supply + name = "box of supply job skillchips" + desc = "Contains spares of every supply job skillchip." + +/obj/item/storage/box/skillchips/supply/PopulateContents() + new /obj/item/skillchip/job/blacksmith(src) + new /obj/item/skillchip/job/blacksmith(src) + +/obj/structure/closet/secure_closet/quartermaster/PopulateContents() + . = ..() + new /obj/item/storage/box/skillchips/supply(src) diff --git a/modular_skyrat/modules/reagent_forging/code/water_basin.dm b/modular_skyrat/modules/reagent_forging/code/water_basin.dm index 67f59ee903e..e889bfa13e6 100644 --- a/modular_skyrat/modules/reagent_forging/code/water_basin.dm +++ b/modular_skyrat/modules/reagent_forging/code/water_basin.dm @@ -1,110 +1,116 @@ -/obj/structure/reagent_water_basin - name = "water basin" - desc = "A basin full of water, ready to quench the hot metal." +/obj/structure/reagent_dispensers/reagent_smithing_basin + name = "smithing trough" + desc = "A basin meant to quench heated smithing equipment and cool it." icon = 'modular_skyrat/modules/reagent_forging/icons/obj/forge_structures.dmi' icon_state = "water_basin" anchored = TRUE density = TRUE + can_be_tanked = FALSE custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5) + reagent_id = null + //deliberate choice -- matches the volume of a bluespace beaker + tank_volume = 300 /// Tracks if you can fish from this basin var/datum/component/fishing_spot/fishable -/obj/structure/reagent_water_basin/Initialize(mapload) - . = ..() +/obj/structure/reagent_dispensers/reagent_smithing_basin/prefilled + reagent_id = /datum/reagent/fuel/oil/smithing -/obj/structure/reagent_water_basin/Destroy() +/obj/structure/reagent_dispensers/reagent_smithing_basin/update_overlays() + . = ..() + if(reagents.total_volume >= tank_volume) + var/mutable_appearance/filling = mutable_appearance(icon, "water_basin_reagent_overlay") + filling.color = mix_color_from_reagents(reagents.reagent_list) + . += filling + return + +/obj/structure/reagent_dispensers/reagent_smithing_basin/Initialize(mapload) + . = ..() + if(!mapload) + anchored = FALSE + reagents.flags = reagents.flags | REFILLABLE | DUNKABLE + check_fishable() + START_PROCESSING(SSdcs, src) + update_appearance() + +/obj/structure/reagent_dispensers/reagent_smithing_basin/proc/check_fishable() + if(isnull(fishable) && reagents.total_volume >= tank_volume) + fishable = AddComponent(/datum/component/fishing_spot, /datum/fish_source/water_basin) + else if(!isnull(fishable) && reagents.total_volume < tank_volume) + RemoveComponentSource(src, /datum/component/fishing_spot) + +/obj/structure/reagent_dispensers/reagent_smithing_basin/Destroy() QDEL_NULL(fishable) return ..() -/obj/structure/reagent_water_basin/examine(mob/user) +/obj/structure/reagent_dispensers/reagent_smithing_basin/examine(mob/user) . = ..() - if(!fishable) - . += span_notice("[src] can be upgraded through a bluespace crystal or a journeyman smithy!") - + if(HAS_TRAIT(user, TRAIT_KNOW_ADVANCED_SMITHING)) + . += span_notice("Dipping smithed items in this trough will imbue it with its chemicals!") else - . += span_notice("[src] looks to be a bottomless basin of water... You can even see fish swimming around down there!") + . += span_notice("You could use this for imbuing reagents into equipment if you knew the right trick...") + . += span_notice("You could secure or unsecure it with a wrench.") + . += span_notice("You could pry it apart with a crowbar.") -/obj/structure/reagent_water_basin/attack_hand(mob/living/user, list/modifiers) - . = ..() - attempt_upgrade(user) +//heat from the contained reagents need to go into the atmosphere over time +/obj/structure/reagent_dispensers/reagent_smithing_basin/process(seconds_per_tick) + var/datum/gas_mixture/current_air = return_air() + if(reagents.total_volume < 1 || reagents.chem_temp == current_air.temperature) + return PROCESS_KILL + var/temp_difference = reagents.chem_temp - current_air.temperature + current_air.temperature += temp_difference * seconds_per_tick * SMITHING_BASIN_HEATLOSS_COEFFICIENT + reagents.chem_temp -= temp_difference * seconds_per_tick * SMITHING_BASIN_HEATLOSS_COEFFICIENT + return -/obj/structure/reagent_water_basin/attack_robot(mob/living/user) - . = ..() - attempt_upgrade(user) - -/obj/structure/reagent_water_basin/proc/attempt_upgrade(mob/living/user) - var/smithing_skill = user.mind.get_skill_level(/datum/skill/smithing) - if(smithing_skill < SKILL_LEVEL_JOURNEYMAN || fishable) - return - - balloon_alert(user, "the water deepens!") - fishable = AddComponent(/datum/component/fishing_spot, /datum/fish_source/water_basin) - -/obj/structure/reagent_water_basin/attackby(obj/item/attacking_item, mob/living/user, params) - if(istype(attacking_item, /obj/item/stack/ore/glass)) - var/obj/item/stack/ore/glass/glass_obj = attacking_item - if(!glass_obj.use(1)) - return - - new /obj/item/stack/clay(get_turf(src)) - user.mind.adjust_experience(/datum/skill/production, 1) - return - - if(istype(attacking_item, /obj/item/stack/ore/bluespace_crystal)) - if(fishable) - return - var/obj/item/stack/ore/bluespace_crystal/bs_crystal = attacking_item - - if(!bs_crystal.use(1)) - return - - balloon_alert(user, "the water deepens!") - fishable = AddComponent(/datum/component/fishing_spot, /datum/fish_source/water_basin) - return +/obj/structure/reagent_dispensers/attackby(obj/item/attacking_item, mob/living/user, params) + START_PROCESSING(SSdcs, src) + var/datum/component/forge_smithable/smith_component = attacking_item.GetComponent(/datum/component/forge_smithable/) + if(!isnull(smith_component)) + smith_component.try_quench(reagents, src, user) + return ITEM_INTERACT_SUCCESS return ..() -/obj/structure/reagent_water_basin/wrench_act(mob/living/user, obj/item/tool) +/obj/structure/reagent_dispensers/reagent_smithing_basin/wrench_act(mob/living/user, obj/item/tool) tool.play_tool_sound(src) - - for(var/i in 1 to 5) - new /obj/item/stack/sheet/mineral/wood(get_turf(src)) - - qdel(src) + set_anchored(!anchored) + balloon_alert_to_viewers(anchored ? "secured" : "unsecured") return TRUE -/obj/structure/reagent_water_basin/tong_act(mob/living/user, obj/item/tool) - var/obj/item/forging/incomplete/search_incomplete = locate(/obj/item/forging/incomplete) in tool.contents - if(!search_incomplete) - return ITEM_INTERACT_SUCCESS +/obj/structure/reagent_dispensers/reagent_smithing_basin/crowbar_act(mob/living/user, obj/item/tool) + if(DOING_INTERACTION(user, DOAFTER_SMITHING_WATER_BASIN)) + return - playsound(src, 'modular_skyrat/modules/reagent_forging/sound/hot_hiss.ogg', 50, TRUE) + tool.play_tool_sound(src) - if(search_incomplete?.times_hit < search_incomplete.average_hits) - to_chat(user, span_warning("You cool down [search_incomplete], but it wasn't ready yet.")) - COOLDOWN_RESET(search_incomplete, heating_remainder) - return ITEM_INTERACT_SUCCESS + if (reagents.total_volume < 75) + to_chat(user, span_notice("You begin to pry apart \the [src]...")) + else + to_chat(user, span_warning("As you begin prying apart \the [src] you notice that it's full of fluid... maybe you should reconsider?")) - if(search_incomplete?.times_hit >= search_incomplete.average_hits) - to_chat(user, span_notice("You cool down [search_incomplete] and it's ready.")) - user.mind.adjust_experience(/datum/skill/smithing, 10) //using the water basin on a ready item gives decent experience. + if(!do_after(user, 5 SECONDS, src, interaction_key = DOAFTER_SMITHING_WATER_BASIN)) + return FALSE - var/obj/spawned_obj = new search_incomplete.spawn_item(get_turf(src)) - if(search_incomplete.custom_materials) - spawned_obj.set_custom_materials(search_incomplete.custom_materials, 1) //lets set its material + //spill contents, then deconstruct + if(reagents.total_volume > 0) + knock_down() + deconstruct(TRUE) + return TRUE - if(istype(spawned_obj, /obj/item/forging/complete)) - var/obj/item/forging/complete/complete_spawned = spawned_obj - complete_spawned.current_perfects = search_incomplete.current_perfects - - qdel(search_incomplete) - tool.icon_state = "tong_empty" - return ITEM_INTERACT_SUCCESS +/obj/structure/reagent_dispensers/tong_act(mob/living/user, obj/item/tool) + var/obj/item/tongs_contents = locate(/obj/item) in tool.contents + if(!tongs_contents) + to_chat(user, span_notice("No item to dip!")) + return ITEM_INTERACT_BLOCKING + else + . = attackby(tongs_contents, user) + if(tool.contents.len == 0) + tool.icon_state = "tong_empty" /// Fishing source for fishing out of basins that have been upgraded, contains saltwater fish (lizard fish fall under this too!) /datum/fish_source/water_basin - catalog_description = "Bottomless Water Basins" + catalog_description = "Filled Blacksmithing Quenching Troughs" fish_table = list( /obj/item/fish/clownfish = 15, /obj/item/fish/pufferfish = 10, @@ -115,6 +121,7 @@ /obj/item/fish/gunner_jellyfish = 15, /obj/item/fish/needlefish = 10, /obj/item/fish/armorfish = 10, + /obj/item/fish/swordfish = 10, /obj/effect/spawner/random/maintenance = 10, /obj/effect/spawner/random/trash/garbage = 15, ) diff --git a/modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi b/modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi index ee31a1c28d9c816cf1865eb0dac3358abe471976..f42db09c58d37550fec820c4cea05702a3195941 100644 GIT binary patch literal 10407 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%0AYrKvpN!f;*eE3z~QRsGsR3`I5Swf?2Z z4(wPGAU+zlB(>d6DY?LNXrO=SU)n!M)McSIx<3wss+u1CHdxa1Q!;|8J(O^H_-)c# zIjBWvY|myOaHX=ffz_4zHH4=#grPqRe>dm-G!{mI^vENYX`8*QdDAprUNa>h*6CgJ z@uRwgl!Vil2}&MgD}sIz{dQG05Z5YTkW6@DTQeVXHOz9R!aHTbE>tx39_$EU{tD&3 z7*9=7TjNv8Pp_@Mi4Z0O24zSwzIuEv=8j+iDzH`#5inX&6OR^?2c7wty+-Y#K7SUh z-Wl^}-E8=1K2o$IJPN9Mc}5au)f(mTM1a_k5t^?^Y{Z%gU!cZ3VQVz=y`9;-05%Tu z&%IeEZ}SRYv~uDAv{c&I-+ExmK%0dveXyg-flsp~juQif7f3eF##ref+VgIE$n6#? zOM=}Vvv>r2ft>N#fQ&_VQEA-u^12|CCV) zGt`hHW=?EkVaH^2reTxBqpWXD5%OEmd`rCfpznrEJOHJ0li1)PyvPCV#T*6hx)6&& z$#06lx~Bj_?7&TTVFkJmjN|>dXqmVc68onN6*=boD&Ra##Hboqm?A$W_Ov(ZOHB7T zpiPgRZ4Pz3oA})L6#SYc8|8UzGRUud$9q(gD3fll2uk-`ACtdOG#e=BPb3(3;rDZW z1>MNAaDXo(PxkU{ete?~zzr{r!dZtm7@A4|m5K?%zOdFo5Y!=>f zT}JcC`4b^@J>xTo2JP7%X)Ru;HWS09UQ(3jUBue(x871q5wnCg;pM+Ai;b^-;bPH4 z8Lk8_*@Su95~%0Cn}`**p5Ac3Tdp$LUrA~-V<#jbbE_g>b~_~<(`S}o(YUNiu_@zEv3i3U@_L;&#jOmm0%kvUWrpCoI{#VGKdF}MJ#yhP~Pe*Px z2)JY|7V$^7!YMgOk+(?__i?!NB;(VOM8ao`>udCTMuaqxMwbDJ`PO>u8O{qLQxp#? zcd5G{#I{%zy4-44YK@Tl*-J6Jdolo78e}evhrM2-W5!(a>Sl^42aMJeiS~+C*`DGo zdQLvJzs8n>665o14Kz&&o~jx}EmyLY>&tc0uS^e0Xp%@GUcg@ZjHTZdQ<6nQsTi`d z&%8$V7@pisH`*PsXONcpzT{Z>b8gGDDvmU7C$8ejqxc4c;;}x=emM@nTUk)`OCh#N`)bg=)k#q^j4< z;@9XPE5AH+atO&n?ZIc=LN!d>B^(M5n3_hD$7-3$pk$HVu{gtNODdwlm%B}=w=9{pcV->l@ z4sm4Isq1USOrQfMmq_AGqHRC3H~%j0G#772yq0E*kGVR2lF0l-mjju6w|3d@D*iOG zlUO$t0?rM^3;G^l;Xm+N$h3mk(%wL3j?m@J)~l-0p2dW<8DZ$& z(7MsH(ZkXtBViMo^e;?UjKjyJXF_Lt5C6h`Ck~# BDMA1M literal 1863 zcmZuycQo6J7yoJzd$mSm#NMM?o;@3@vGs{krH|ICNYEgZ7$rs=>#2H*M_a8lc=e2G zNO0_mwni2CnW+&qQX|&$%b&mV`~Cj7_jB&Kvht%QzO^P)N#&zf>yk=LA6X3}r7D~(aKN7# z>j5FpF8TogH_{1eb1S~$#~rIsX}Hw6zlI#uBq>Wl)dDIi*5lC;MvAEl#Z$sa#w6PSL*BObXe1R(G zV~5+Tof9WAh39lN9Z^vZpg8SCUyUfuJ4)t{4PvDEzIIxKFjC1%0`E+Xx1i#osZN1@3hiRL{KL~))_wSl#iWSl^G2+XB zd0y%i_+-AbYJ4QSeQtb12p!!nz_~sn!zDkT9E504>(OtGwJE~d*REnZ8i@7r?^@^) z0`g3a{yJ9WDN-A(|E)Y+nq`^GZbE9?>AfCrEQrCFD<v<4Ah9SN6WG`5x#5B+r{PXYSv2B zTr-zxBe@L=>L8vvKkDc0;%w3_ffc=|qfbf?yq+qdh~48wBveh7FO}68Z|sx%dGn8;7&C zB;22hR%(Hd{tED&c;-5F?z6Xfyi0u#i@B2^Sp)X)SRw$ z$_Y(%*l&MAsCjRGb8D~}$fD=G`Q6JPSo^E=048rs(wj-kFb+eXr999X@Q^hPfK!jf z+~^)@1mN=5d1q>EyT5HG>TL>8mY+MT@)&J8m{bipy49c+vX-L(3*Ly$tF$W!SP`-Y8 z)M*c@tgU{zcL6@98@Bfo`=}Vak7bL8nAJXtWh>+ex{2QxBOOm*Z!n?3L%Xa*>t-LPi{bfih~b zp!Jx2SVw<;{8TQpt0;yx*r|SG&+gOc&1->fd(~1}99Z2UtchSnG&5cC{93m!Fn@g! mrJPWrf1vf-^8df<=RkSrw}0+gIDTP&M!?C=7204MnD%c{WqK$8 diff --git a/modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi b/modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi index 4d80230251947dc11a4d6d9d353b920db99f646c..76c3adbad53c4142cd27e799f2c30983f42550cd 100644 GIT binary patch literal 8770 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 diff --git a/modular_skyrat/modules/reagent_forging/icons/obj/forge_structures.dmi b/modular_skyrat/modules/reagent_forging/icons/obj/forge_structures.dmi index c5d8e84977983fd9f9b1011f4e8ddf934a14e4d2..be2fa4d4399d9b12f2bdca7530697d972041373b 100644 GIT binary patch literal 4395 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 literal 3464 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 C!A}YR literal 15116 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 diff --git a/tgstation.dme b/tgstation.dme index 24105fbacda..57b0b011e26 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -7231,7 +7231,6 @@ #include "modular_skyrat\master_files\code\game\machinery\limbgrower.dm" #include "modular_skyrat\master_files\code\game\machinery\suit_storage.dm" #include "modular_skyrat\master_files\code\game\objects\items.dm" -#include "modular_skyrat\master_files\code\game\objects\objs.dm" #include "modular_skyrat\master_files\code\game\objects\effects\decals\remains.dm" #include "modular_skyrat\master_files\code\game\objects\effects\decals\cleanable\humans.dm" #include "modular_skyrat\master_files\code\game\objects\effects\decals\cleanable\misc.dm" @@ -8850,17 +8849,27 @@ #include "modular_skyrat\modules\quirks\neutral.dm" #include "modular_skyrat\modules\quirks\echolocation\echolocation.dm" #include "modular_skyrat\modules\radiosound\code\headset.dm" +#include "modular_skyrat\modules\reagent_forging\code\_DEFINES.dm" #include "modular_skyrat\modules\reagent_forging\code\anvil.dm" +#include "modular_skyrat\modules\reagent_forging\code\cargo_orders.dm" +#include "modular_skyrat\modules\reagent_forging\code\container_boxes.dm" #include "modular_skyrat\modules\reagent_forging\code\crafting_bench.dm" #include "modular_skyrat\modules\reagent_forging\code\crafting_bench_recipes.dm" #include "modular_skyrat\modules\reagent_forging\code\forge.dm" +#include "modular_skyrat\modules\reagent_forging\code\forge_belts.dm" #include "modular_skyrat\modules\reagent_forging\code\forge_clothing.dm" +#include "modular_skyrat\modules\reagent_forging\code\forge_equipment.dm" #include "modular_skyrat\modules\reagent_forging\code\forge_items.dm" #include "modular_skyrat\modules\reagent_forging\code\forge_recipes.dm" +#include "modular_skyrat\modules\reagent_forging\code\forge_revolver.dm" +#include "modular_skyrat\modules\reagent_forging\code\forge_smithable_component.dm" #include "modular_skyrat\modules\reagent_forging\code\forge_weapons.dm" +#include "modular_skyrat\modules\reagent_forging\code\memories.dm" #include "modular_skyrat\modules\reagent_forging\code\mindless_killer.dm" #include "modular_skyrat\modules\reagent_forging\code\reagent_component.dm" +#include "modular_skyrat\modules\reagent_forging\code\reagent_smithing_oil.dm" #include "modular_skyrat\modules\reagent_forging\code\smith_skill.dm" +#include "modular_skyrat\modules\reagent_forging\code\smithing_trait_and_chip.dm" #include "modular_skyrat\modules\reagent_forging\code\tool_override.dm" #include "modular_skyrat\modules\reagent_forging\code\two_hand_range.dm" #include "modular_skyrat\modules\reagent_forging\code\water_basin.dm" diff --git a/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx b/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx index 9f8fb56aa92..14ba803ad27 100644 --- a/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx +++ b/tgui/packages/tgui/interfaces/Orbit/JobIcon.tsx @@ -7,6 +7,7 @@ import type { Antagonist, Observable } from './types'; const customJobs = [ 'Telecomms Specialist', 'Barber', + 'Blacksmith', 'Blueshield', 'Bouncer', 'Corrections Officer', diff --git a/tools/UpdatePaths/Scripts_Bubber/5360_smithing_repath.txt b/tools/UpdatePaths/Scripts_Bubber/5360_smithing_repath.txt new file mode 100644 index 00000000000..b1d57fdd99f --- /dev/null +++ b/tools/UpdatePaths/Scripts_Bubber/5360_smithing_repath.txt @@ -0,0 +1,2 @@ +/obj/structure/reagent_water_basin : /obj/structure/reagent_dispensers/reagent_smithing_basin{@OLD} +/obj/item/forging/reagent_weapon : /obj/item/melee/forged_reagent_weapon