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 be0ccdb31a8..8b04ebf7573 100644 Binary files a/modular_skyrat/master_files/icons/mob/huds/hud.dmi and b/modular_skyrat/master_files/icons/mob/huds/hud.dmi differ diff --git a/modular_skyrat/master_files/icons/obj/card.dmi b/modular_skyrat/master_files/icons/obj/card.dmi index 66706f77d53..8bb14b4a792 100644 Binary files a/modular_skyrat/master_files/icons/obj/card.dmi and b/modular_skyrat/master_files/icons/obj/card.dmi differ diff --git a/modular_skyrat/master_files/icons/obj/clothing/belts.dmi b/modular_skyrat/master_files/icons/obj/clothing/belts.dmi index 3f0c755739e..4470f293b16 100644 Binary files a/modular_skyrat/master_files/icons/obj/clothing/belts.dmi and b/modular_skyrat/master_files/icons/obj/clothing/belts.dmi differ diff --git a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm index 387fe427343..12ae730e5f5 100644 --- a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm +++ b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm @@ -52,13 +52,13 @@ desc = "Summons a knife that places a tracking curse on unsuspecting miners who destroy our marked tendrils." required_components = list( "north" = /obj/item/organ/monster_core/regenerative_core, - "south" = /obj/item/forging/reagent_weapon/dagger, + "south" = /obj/item/melee/forged_reagent_weapon/dagger, "east" = /obj/item/stack/sheet/bone, "west" = /obj/item/stack/sheet/sinew, ) consumed_components = list( /obj/item/organ/monster_core/regenerative_core, - /obj/item/forging/reagent_weapon/dagger, + /obj/item/melee/forged_reagent_weapon/dagger, /obj/item/stack/sheet/bone, /obj/item/stack/sheet/sinew, ) diff --git a/modular_skyrat/modules/borgs/code/robot_items.dm b/modular_skyrat/modules/borgs/code/robot_items.dm index a6ff81dd2f5..6577a3e746c 100644 --- a/modular_skyrat/modules/borgs/code/robot_items.dm +++ b/modular_skyrat/modules/borgs/code/robot_items.dm @@ -887,7 +887,7 @@ if("Anvil") new /obj/structure/reagent_anvil(src_turf) if("Water Basin") - new /obj/structure/reagent_water_basin(src_turf) + new /obj/structure/reagent_dispensers/reagent_smithing_basin(src_turf) if("Crafting Bench") new /obj/structure/reagent_crafting_bench(src_turf) diff --git a/modular_skyrat/modules/colony_fabricator/code/appliances/space_heater.dm b/modular_skyrat/modules/colony_fabricator/code/appliances/space_heater.dm index 61a8275f7ad..804f2323331 100644 --- a/modular_skyrat/modules/colony_fabricator/code/appliances/space_heater.dm +++ b/modular_skyrat/modules/colony_fabricator/code/appliances/space_heater.dm @@ -1,7 +1,7 @@ /obj/machinery/space_heater/wall_mounted - name = "mounted heater" - desc = "A compact heating and cooling device for small scale applications, made to mount onto walls up and out of the way. \ - Like other, more free-standing space heaters however, these still require cell power to function." + name = "wall mounted A/C unit" + desc = "A compact heating and cooling device for large scale applications, made to mount onto walls up and out of the way. \ + It charges with the nearest APC; the internal cell buffers the power to the heating/cooling mechanism." icon = 'modular_skyrat/modules/colony_fabricator/icons/space_heater.dmi' anchored = TRUE density = FALSE @@ -14,6 +14,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/space_heater/wall_mounted, 29) +/obj/machinery/space_heater/wall_mounted/with_cell + cell = /obj/item/stock_parts/power_store/cell/high + +MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/space_heater/wall_mounted/with_cell, 29) + /obj/machinery/space_heater/wall_mounted/Initialize(mapload) . = ..() if(mapload) @@ -21,12 +26,26 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/space_heater/wall_mounted, 29) AddElement(/datum/element/manufacturer_examine, COMPANY_FRONTIER) RemoveElement(/datum/element/elevation, pixel_shift = 8) RemoveElement(/datum/element/climbable) + START_PROCESSING(SSobj, src) /obj/machinery/space_heater/wall_mounted/RefreshParts() . = ..() heating_energy = STANDARD_CELL_RATE * 0.2 efficiency = 30 +/obj/machinery/space_heater/wall_mounted/process_atmos(seconds_per_tick) + ..() + //charge the cell -- should be less than the heat energy expended + if(!isnull(cell)) + var/main_draw = STANDARD_CELL_RATE * 0.15 * seconds_per_tick + if(!main_draw) + return + var/charge_given = charge_cell(main_draw, cell, grid_only = TRUE) + if(charge_given) + use_energy(charge_given * 0.5) //uses extra over head of energy to charge + return + + /obj/machinery/space_heater/wall_mounted/default_deconstruction_crowbar() return @@ -51,7 +70,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/space_heater/wall_mounted, 29) // Wallmount for creating the heaters /obj/item/wallframe/wall_heater - name = "unmounted wall heater" + name = "unmounted A/C unit" desc = "A compact heating and cooling device for small scale applications, made to mount onto walls up and out of the way. \ Like other, more free-standing space heaters however, these still require cell power to function." icon = 'modular_skyrat/modules/colony_fabricator/icons/space_heater.dmi' diff --git a/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm b/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm index 9deefd3e211..4b334d80d1a 100644 --- a/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm +++ b/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm @@ -1,139 +1,3 @@ -/obj/item/storage/belt/crusader //Belt + sheath combination - still only holds one sword at a time though - icon = 'modular_skyrat/master_files/icons/obj/clothing/belts.dmi' - worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/belt.dmi' - name = "crusader belt" - desc = "Holds an assortment of equipment for whatever situation an adventurer may encounter, as well as having an attached sheath." - 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/forging/reagent_weapon/sword, - /obj/item/melee/sabre, - /obj/item/forging/reagent_weapon/rapier, - /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/master_files/icons/obj/clothing/belts.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/holster/cowboy - icon = 'modular_skyrat/master_files/icons/obj/clothing/belts.dmi' - worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/belt.dmi' - name = "cowboy belt" - desc = "Yee haw! The holster on the side of the hip is leather stamped with swirling lines, all leading back to a deer's antlers." - icon_state = "cowboy_belt" - worn_icon_state = "cowboy_belt" - inhand_icon_state = "utility" - /obj/item/storage/belt/medbandolier icon = 'modular_skyrat/master_files/icons/obj/clothing/belts.dmi' worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/belt.dmi' diff --git a/modular_skyrat/modules/goofsec/code/department_guards.dm b/modular_skyrat/modules/goofsec/code/department_guards.dm index 10a13d39360..08bd4d101fa 100644 --- a/modular_skyrat/modules/goofsec/code/department_guards.dm +++ b/modular_skyrat/modules/goofsec/code/department_guards.dm @@ -572,6 +572,7 @@ ACCESS_WEAPONS, ) minimal_access = list( + ACCESS_BLACKSMITH, ACCESS_BRIG_ENTRANCE, ACCESS_CARGO, ACCESS_MAINT_TUNNELS, diff --git a/modular_skyrat/modules/modular_items/code/tailoring.dm b/modular_skyrat/modules/modular_items/code/tailoring.dm index fc554a7e488..48d2bae5bed 100644 --- a/modular_skyrat/modules/modular_items/code/tailoring.dm +++ b/modular_skyrat/modules/modular_items/code/tailoring.dm @@ -14,15 +14,6 @@ reqs = list(/obj/item/paper = 5) category = CAT_CLOTHING -/datum/crafting_recipe/crusader_belt - name = "Crusader Belt and Sheath" - result = /obj/item/storage/belt/crusader - reqs = list(/obj/item/storage/belt/utility = 1, /obj/item/stack/sheet/leather = 3, /obj/item/stack/sheet/cloth = 2, /obj/item/stack/sheet/mineral/gold = 1) - tool_behaviors = list(TOOL_WIRECUTTER, TOOL_SCREWDRIVER, TOOL_WELDER) //To cut the leather and fasten/weld the sheath detailing - time = 30 - category = CAT_CLOTHING - delete_contents = FALSE // Otherwise the storage pouch gets deleted when crafted. - /datum/crafting_recipe/crusader_satchel name = "Crusader Satchel" result = /obj/item/storage/backpack/satchel/crusader diff --git a/modular_skyrat/modules/primitive_catgirls/code/clothing.dm b/modular_skyrat/modules/primitive_catgirls/code/clothing.dm index d3357fe4302..49408f328b4 100644 --- a/modular_skyrat/modules/primitive_catgirls/code/clothing.dm +++ b/modular_skyrat/modules/primitive_catgirls/code/clothing.dm @@ -11,7 +11,7 @@ suit = /obj/item/clothing/suit/jacket/primitive_catgirl_coat neck = /obj/item/clothing/neck/scarf/primitive_catgirl_scarf - back = /obj/item/forging/reagent_weapon/axe/fake_copper + back = /obj/item/melee/forged_reagent_weapon/axe/fake_copper // Under @@ -147,5 +147,5 @@ // Misc Items -/obj/item/forging/reagent_weapon/axe/fake_copper +/obj/item/melee/forged_reagent_weapon/axe/fake_copper custom_materials = list(/datum/material/copporcitite = SHEET_MATERIAL_AMOUNT) diff --git a/modular_skyrat/modules/reagent_forging/code/_DEFINES.dm b/modular_skyrat/modules/reagent_forging/code/_DEFINES.dm new file mode 100644 index 00000000000..4d78e89483a --- /dev/null +++ b/modular_skyrat/modules/reagent_forging/code/_DEFINES.dm @@ -0,0 +1,210 @@ + +//the list of possible things people can make if they have maxed forging skill +#define COMSIG_SMITHING_QUENCH "smithing_done" +#define COMSIG_SMITHING_PASSIVE_COOLED "smithing_passive_cooled" + +#define DOAFTER_SMITHING_FORGE "smithing_forging_doafter" +#define DOAFTER_SMITHING_ANVIL "smithing_anvil_doafter" +#define DOAFTER_SMITHING_WATER_BASIN "smithing_waterbasin_doafter" +#define DOAFTER_SMITHING_TABLE "smithing_table_doafter" + +#define DOAFTER_REVOLVER_HAMMER_COCK "smithing_revolver_hammer_cock_doafter" + +/// smithing hits are advanced by "quality points" depending on various factors. What's the lowest that the quality points given can be? +#define MINIMUM_SMITHING_QUALITY_POINTS + +#define FORGING_WEAPON_REFORGING_MAX_QUALITY 30 +#define FORGING_WEAPON_REFORGING_AVERAGE_WAIT 1 SECONDS +#define FORGING_WEAPON_REFORGING_MAX_PERFECT_HITS 15 +#define FORGING_WEAPON_REFORGING_MAX_BAD_HITS 6 + +#define FORGING_CLOTHING_REFORGING_MAX_QUALITY 30 +#define FORGING_CLOTHING_REFORGING_AVERAGE_WAIT 1 SECONDS +#define FORGING_CLOTHING_REFORGING_MAX_PERFECT_HITS 15 +#define FORGING_CLOTHING_REFORGING_MAX_BAD_HITS 6 + +#define USER_CAN_REAGENT_IMBUE(user) (HAS_TRAIT(user, TRAIT_KNOW_ADVANCED_SMITHING) || user.mind.get_skill_level(/datum/skill/smithing) >= 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 ee31a1c28d9..f42db09c58d 100644 Binary files a/modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi and b/modular_skyrat/modules/reagent_forging/icons/obj/forge_clothing.dmi differ 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 4d802302519..76c3adbad53 100644 Binary files a/modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi and b/modular_skyrat/modules/reagent_forging/icons/obj/forge_items.dmi differ 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 c5d8e849779..be2fa4d4399 100644 Binary files a/modular_skyrat/modules/reagent_forging/icons/obj/forge_structures.dmi and b/modular_skyrat/modules/reagent_forging/icons/obj/forge_structures.dmi differ diff --git a/modular_skyrat/modules/reagent_forging/readme.md b/modular_skyrat/modules/reagent_forging/readme.md index 1833dd50082..68eef5702e6 100644 --- a/modular_skyrat/modules/reagent_forging/readme.md +++ b/modular_skyrat/modules/reagent_forging/readme.md @@ -23,6 +23,12 @@ imbued reagents into the targets. - N/A +### Todo: + +- Change the reagent component and forge smithable component to (instead of having referenced procs) have defined "X effect upon imbuing smithing oil/quenching with X completion and Y perfection. The current + implementation relies too much on calling a type's proc, so there's a lot of code reuse... + ### Credits: Jake Park +Arris Fairburne diff --git a/modular_zubbers/code/_globalvars/lists/maintenance_loot_common.dm b/modular_zubbers/code/_globalvars/lists/maintenance_loot_common.dm index 8b210be21cb..846aab8ec42 100644 --- a/modular_zubbers/code/_globalvars/lists/maintenance_loot_common.dm +++ b/modular_zubbers/code/_globalvars/lists/maintenance_loot_common.dm @@ -39,7 +39,7 @@ GLOBAL_LIST_INIT(common_loot, list( //common: basic items /obj/item/storage/belt/fannypack/white = 5, /obj/item/storage/belt/fannypack/yellow = 5, /obj/item/storage/belt/holster = 1, - /obj/item/storage/belt/holster/cowboy = 1, + /obj/item/storage/belt/hip_holster/cowboy = 1, /obj/item/storage/belt/janitor = 15, /obj/item/storage/belt/janitor/full = 1, /obj/item/storage/belt/mailbelt = 5, diff --git a/modular_zubbers/code/modules/cargo/bounties/blacksmith.dm b/modular_zubbers/code/modules/cargo/bounties/blacksmith.dm index 21c512cabec..fcc30065050 100644 --- a/modular_zubbers/code/modules/cargo/bounties/blacksmith.dm +++ b/modular_zubbers/code/modules/cargo/bounties/blacksmith.dm @@ -1,3 +1,34 @@ +/* +Todo: Refactor upstream code so that item deletion is decided at the bounty level rather than at the machine running the bounty + +/datum/bounty/item/blacksmith + //if not null, bounty items require have required_material in its materials list + var/datum/material/required_material = null + //if not null, bounty items cannot contain this material + var/list/blacklisted_materials = null + //if true, redeeming a bounty item does not remove it (instead sets it as non-viable for future bounties) + + +/datum/bounty/item/applies_to(obj/shipped) + if(shipped.GetComponent(/datum/component/block_redeemed_item_in_bounty)) + return FALSE + else + return ..() + +/datum/bounty/item/ship(obj/shipped) + if(!applies_to(shipped)) + return FALSE + if(istype(shipped,/obj/item/stack)) + var/obj/item/stack/shipped_is_a_stack = shipped + shipped_count += shipped_is_a_stack.amount + else + shipped_count += 1 + return TRUE + +/datum/component/block_redeemed_item_in_bounty + +*/ + /datum/bounty/item/blacksmith/cage name = "Cortical Borer Cage" description = "One of Nanotrasen's partner stations is undergoing a borer infestation and would like to capture some life specimen for research. Ship some off to CentCom right away." @@ -16,14 +47,14 @@ name = "Staff" description = "A respected religious figure is visiting CentCom, but lost their staff on the way there! Send a replacement as soon as possible." reward = CARGO_CRATE_VALUE * 5 - wanted_types = list(/obj/item/forging/reagent_weapon/staff = TRUE) + wanted_types = list(/obj/item/melee/forged_reagent_weapon/staff = TRUE) /datum/bounty/item/blacksmith/swords name = "Swords" description = "Our interns' mosins have broken down, ship some swords so they have something to fight with." required_count = 3 reward = CARGO_CRATE_VALUE * 24 - wanted_types = list(/obj/item/forging/reagent_weapon/sword = TRUE) + wanted_types = list(/obj/item/melee/forged_reagent_weapon/sword = TRUE) /datum/bounty/item/blacksmith/armor name = "Armor piece" @@ -40,4 +71,4 @@ name = "Katana" description = "One of our Researchers is going to a Space Anime Convention and wants to show off a real katana! Ship one so he stops pestering us." reward = CARGO_CRATE_VALUE * 6 - wanted_types = list(/obj/item/forging/reagent_weapon/katana = TRUE) + wanted_types = list(/obj/item/melee/forged_reagent_weapon/katana = TRUE) diff --git a/modular_zubbers/code/modules/cargo/packs/goodies/company_imports/blacksteel.dm b/modular_zubbers/code/modules/cargo/packs/goodies/company_imports/blacksteel.dm index 0c51c040989..5cdb4195c05 100644 --- a/modular_zubbers/code/modules/cargo/packs/goodies/company_imports/blacksteel.dm +++ b/modular_zubbers/code/modules/cargo/packs/goodies/company_imports/blacksteel.dm @@ -15,7 +15,7 @@ /datum/supply_pack/company_import/blacksteel/blade/shamshir_sabre item_type = /obj/item/storage/belt/sheath/sabre/cargo - cost = PAYCHECK_COMMAND * 6 + cost = PAYCHECK_COMMAND * 15 /datum/supply_pack/company_import/blacksteel/forging_tools/billows item_type = /obj/item/forging/billow diff --git a/modular_zubbers/code/modules/clothing/outfits/outlast.dm b/modular_zubbers/code/modules/clothing/outfits/outlast.dm index 8bf1ac5a110..ded31bcbab2 100644 --- a/modular_zubbers/code/modules/clothing/outfits/outlast.dm +++ b/modular_zubbers/code/modules/clothing/outfits/outlast.dm @@ -1,10 +1,11 @@ /obj/item/clothing/suit/leatherapron //Sprite by pacertest on discord name = "leather apron" - desc = "A somewhat sterile apron made from thick leather, perfect for sawing limbs off without getting your uniform bloody." + desc = "An apron made from thick leather." icon = 'modular_zubbers/icons/obj/clothing/outlast.dmi' worn_icon = 'modular_zubbers/icons/mob/clothing/outlast.dmi' worn_icon_digi = 'modular_zubbers/icons/mob/clothing/outlast.dmi' icon_state = "leatherapron" + body_parts_covered = CHEST|GROIN allowed = list( /obj/item/bonesetter, /obj/item/cautery, @@ -21,6 +22,7 @@ /obj/item/surgical_drapes, /obj/item/storage/pill_bottle, /obj/item/tank/internals/emergency_oxygen, + /obj/item/melee/forged_reagent_weapon, /obj/item/forging/hammer, /obj/item/forging/tongs, /obj/item/forging/billow, diff --git a/modular_zubbers/code/modules/jobs/job_types/blacksmith.dm b/modular_zubbers/code/modules/jobs/job_types/blacksmith.dm index a84a2e1d8b8..990c09eef87 100644 --- a/modular_zubbers/code/modules/jobs/job_types/blacksmith.dm +++ b/modular_zubbers/code/modules/jobs/job_types/blacksmith.dm @@ -43,12 +43,84 @@ uniform = /obj/item/clothing/under/rank/cargo/tech/skyrat/long suit = /obj/item/clothing/suit/leatherapron backpack_contents = list( + /obj/item/crowbar = 1, /obj/item/forging/hammer = 1, /obj/item/forging/tongs = 1, /obj/item/forging/billow = 1, /obj/item/stack/sheet/mineral/wood = 25, + /obj/item/choice_beacon/blacksmith = 1, + /obj/item/paper/pamphlet/blacksmith_tutorial = 1, ) + glasses = /obj/item/clothing/glasses/hud/gun_permit belt = /obj/item/modular_computer/pda/cargo ears = /obj/item/radio/headset/headset_cargo shoes = /obj/item/clothing/shoes/workboots gloves = /obj/item/clothing/gloves/color/brown + skillchips = list(/obj/item/skillchip/job/blacksmith) + +/obj/item/paper/pamphlet/blacksmith_tutorial + name = "blacksmithing pamphlet" + default_raw_text = "Blacksmiths are the ones responsible for fine craftsmanship of all types of equipment: swords, armor, toolbelts, and more.
\n\ + If someone wants specialized equipment -- especially old stuff -- they should go to you.
\n\ + If you have a dedicated department room, it should come with the following:
\n\ + The forge heats up sheets of metal to be worked into desired shapes.
\n\ + The wall mounted A/C unit will cool down the excess heat that your forge leaks, so that you do not boil yourself alive. Remember to turn it on!
\n\ + The anvil can hold a heated, incomplete item.
\n\ + The quenching basin allows you to cool a heated item.
\n\ + The workbench is where you will finish crafting your equipment.
\n\ + The billows blow air into your forge to accelerate its heating process.
\n\ + The tongs let you safely insert a sheet of material into your forge, and safely handle hot items.
\n\ + The forging mallet is used alongside an anvil to work an item into shape.
\n\ + If you lack a dedicated department room and cannot summon a package of these items via your summon beacon, almost all of the above can be crafted using basic wood and iron (except the anvil, which requires plastitanium). Use the crafting menu (the hammer icon -- bottom right of your screen) to take a look.
\n\ +
\ + How to heat your forge:
\n\ + -1: Acquire a fuel source. You can use coal or wood for this purpose.
\n\ + -2: Insert your fuel into the forge.
\n\ + -3: Use your billows to blow air into the forge, accelerating its heating process.
\n\ + -3a: If you don't blow air into the forge then the burning fuel will heat by itself.
\n\ +
\n\ + How to make a weapon:
\n\ + -1: Pick up a sheet of iron or glass (or a different smithable material) using your tongs, then insert it into your heated forge.
\n\ + -2: Decide on what you want to craft. Then, use tongs to pull the heated item from your forge and place it on your anvil.
\n\ + -3: Hit it a whole bunch with your hammer! Pay attention to your timing; swing with the correct pace, and you'll smith it perfectly.
\n\ + -3a: Your heated metal might cool down before it's ready, if you're too slow. If this happens, pick it up with your tongs and reheat them at your forge.
\n\ + -4: Once you think it's ready, use a smithing basin to quench your metal.
\n\ + -5: Pick up the quenched weapon head and secure it onto your crafting table. Have some wood set aside to use as a base, then use your hammer at the bench to finish the weapon!
\n\ +
\n\ + How to make other equipment:
\n\ + -1: With a bare hand, click on your crafting table to check what you can craft.
\n\ + -2: Once you decide on what to craft, the table should now have that recipe selected. Examine the table (shift + click) to see what ingredients are required.
\n\ + -3: Obtain your ingredients and set them aside next to the table.
\n\ + -3a: The most common ingredients will be wooden planks (produced by botany, or ordered via cargo), leather (from botany or cargo) and smithed plates (smithed from metal via your forge).
\n\ + -4: Once all your material is placed next to the table, get your hammer ready and start working at the table to produce your equipment!
\n\ +
\n\ + Reforging/Repairing Smithed Equipment:
\n\ + -1: Insert your equipment into the forge using tongs or your bare hands.
\n\ + -2: Once heated, place it onto the smithing table and work it like you'd work an incomplete smithed item.
\n\ + -3: Once it sounds done, quench it at a basin to cool it.
\n\ +
\n\ + Reagent Imbuing:
\n\ + This is an ancient technique used to imbue most smithed equipment with properties from a chosen liquid reagent. Using reagents other than smithing oil will cause the equipment to be less effective, but depending on what chemical is imbued, the tradeoff may be worth it.
\n\ + Be warned; reagent imbuing stresses the material and will wear it down whenever its imbue effect activates!
\n\ + Normally, anything that can be imbued will be imbued upon being quenched in liquid. However, reagent imbue effects can be changed by reforging the equipment, or by manually imbuing it:
\n\ + -1: Prepare at least 60 units of chemical solution to imbue.
\n\ + -2: Transfer those chemicals into your smithed equipment.
\n\ + -3: Insert your equipment into a heated forge to set the chemicals into the material.
\n\ +
\n\ + Other notes:
\n\ + -Use your eyes! If you inspect your equipment -- whether it's your crafting stations or handheld tools -- you'll usually be able to discern something useful.
\n\ + -Vary up what you make! You'll learn/relearn your skills fastest if you make new products each time.
\n\ + -Reagent imbuing is powerful, but if you don't know what chemical to imbue, smithing oil is a good fallback; it will enhance the expected capabilities of the equipment.
\n\ + -As you create weapons and equipment, your muscle memory will build over time -- which will let you make new equipment. Check your forge and your crafting bench for new items.
\n\ + -Be mindful of the material you use; material properties will change the usefulness of what you produce. Experiment, but don't expect a pizza katana to be worth more than a laugh.
\n\ + -You can build a two-handed hammer at your forge. Make it well, and it will work faster than your Nanotrasen-assigned hammer!" + +/obj/item/choice_beacon/blacksmith + name = "smithing supply beacon" + desc = "Calls a supplypod containing all the required equipment for blacksmithing." + icon_state = "sb_delivery" + inhand_icon_state = "sb_delivery" + +/obj/item/choice_beacon/blacksmith/generate_display_names() + return list("Smithing Equipment Crate" = /obj/structure/closet/crate/large/smithing) + diff --git a/modular_zubbers/code/modules/jobs/trims/jobs.dm b/modular_zubbers/code/modules/jobs/trims/jobs.dm index b75e0700057..1030ef1e56c 100644 --- a/modular_zubbers/code/modules/jobs/trims/jobs.dm +++ b/modular_zubbers/code/modules/jobs/trims/jobs.dm @@ -11,12 +11,14 @@ ACCESS_BLACKSMITH, ) -/datum/id_trim/job/blacksmith //Place Holder. You'll probably wanna come by and set these up correctly. +/datum/id_trim/job/blacksmith assignment = "Blacksmith" - trim_state = "trim_cargotechnician" + trim_icon = 'modular_skyrat/master_files/icons/obj/card.dmi' + trim_state = "trim_blacksmith" + department_color = COLOR_CARGO_BROWN subdepartment_color = COLOR_CARGO_BROWN - sechud_icon_state = SECHUD_CARGO_TECHNICIAN + sechud_icon_state = SECHUD_BLACKSMITH minimal_access = list( ACCESS_BLACKSMITH, ACCESS_CARGO, @@ -24,6 +26,7 @@ ACCESS_MECH_MINING, ACCESS_MINERAL_STOREROOM, ACCESS_SHIPPING, + ACCESS_WEAPONS, ) extra_access = list( ACCESS_MINING, diff --git a/modular_zubbers/icons/mob/huds/hud.dmi b/modular_zubbers/icons/mob/huds/hud.dmi index 3c31a58122d..cddfbb7d939 100644 Binary files a/modular_zubbers/icons/mob/huds/hud.dmi and b/modular_zubbers/icons/mob/huds/hud.dmi differ 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