diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml new file mode 100644 index 0000000000..141fc9a7d8 --- /dev/null +++ b/.github/workflows/compile_changelogs.yml @@ -0,0 +1,36 @@ +name: Compile changelogs + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + compile: + name: "Compile changelogs" + runs-on: ubuntu-latest + steps: + - name: "Setup python" + uses: actions/setup-python@v1 + with: + python-version: '3.x' + - name: "Install deps" + run: | + python -m pip install --upgrade pip + python -m pip install pyyaml + - name: "Checkout" + uses: actions/checkout@v1 + with: + fetch-depth: 25 + - name: "Compile" + run: | + python tools/ss13_genchangelog.py html/changelog.html html/changelogs + - name: Commit + run: | + git config --local user.email "action@github.com" + git config --local user.name "Changelogs" + git pull origin master + git commit -m "Automatic changelog compile [ci skip]" -a || true + - name: "Push" + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/_maps/RandomRuins/SpaceRuins/spacehotel.dmm b/_maps/RandomRuins/SpaceRuins/spacehotel.dmm index 69b56946f2..209feb82e3 100644 --- a/_maps/RandomRuins/SpaceRuins/spacehotel.dmm +++ b/_maps/RandomRuins/SpaceRuins/spacehotel.dmm @@ -2688,7 +2688,7 @@ /obj/structure/window/reinforced{ dir = 1 }, -/obj/structure/piano{ +/obj/structure/musician/piano{ icon_state = "piano" }, /turf/open/floor/plasteel/grimy, diff --git a/_maps/RandomRuins/SpaceRuins/vaporwave.dmm b/_maps/RandomRuins/SpaceRuins/vaporwave.dmm index 75adc455b0..a1b2271d96 100644 --- a/_maps/RandomRuins/SpaceRuins/vaporwave.dmm +++ b/_maps/RandomRuins/SpaceRuins/vaporwave.dmm @@ -28,7 +28,7 @@ /turf/open/floor/holofloor/beach, /area/ruin/space/has_grav/powered/aesthetic) "i" = ( -/obj/structure/piano, +/obj/structure/musician/piano, /obj/structure/window{ dir = 8 }, diff --git a/_maps/RandomZLevels/Cabin.dmm b/_maps/RandomZLevels/Cabin.dmm index 4d07c02565..a0862213a2 100644 --- a/_maps/RandomZLevels/Cabin.dmm +++ b/_maps/RandomZLevels/Cabin.dmm @@ -713,7 +713,7 @@ /turf/open/floor/wood, /area/awaymission/cabin) "cz" = ( -/obj/structure/piano, +/obj/structure/musician/piano, /turf/open/floor/wood, /area/awaymission/cabin) "cA" = ( diff --git a/_maps/RandomZLevels/caves.dmm b/_maps/RandomZLevels/caves.dmm index e9d592c107..c84acf0131 100644 --- a/_maps/RandomZLevels/caves.dmm +++ b/_maps/RandomZLevels/caves.dmm @@ -924,8 +924,8 @@ /area/awaymission/caves/BMP_asteroid/level_two) "cK" = ( /obj/structure/sign/warning/securearea{ - desc = "A warning sign which reads 'HOLY SHIT NIGGA WHAT ARE YOU DOING'."; - name = "\improper HOLY SHIT NIGGA WHAT ARE YOU DOING" + desc = "A warning sign which reads 'HOLY SHIT DUDE WHAT ARE YOU DOING'."; + name = "\improper HOLY SHIT DUDE WHAT ARE YOU DOING" }, /turf/closed/wall, /area/awaymission/caves/BMP_asteroid/level_two) diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm index 0a08edb9b2..d85c8b12d6 100644 --- a/_maps/RandomZLevels/moonoutpost19.dmm +++ b/_maps/RandomZLevels/moonoutpost19.dmm @@ -761,7 +761,7 @@ }, /area/awaymission/moonoutpost19/syndicate) "bF" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/high_loot{ dir = 1 }, /turf/open/floor/plasteel/freezer{ @@ -4121,7 +4121,7 @@ }, /area/awaymission/moonoutpost19/research) "hP" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/high_loot{ dir = 4 }, /obj/effect/decal/cleanable/dirt, @@ -4473,7 +4473,7 @@ }, /area/awaymission/moonoutpost19/research) "iw" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/high_loot{ dir = 4 }, /obj/machinery/light/small{ @@ -4764,7 +4764,7 @@ }, /area/awaymission/moonoutpost19/arrivals) "iZ" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/high_loot{ dir = 1 }, /turf/open/floor/plasteel/freezer{ diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm index c24bf45f81..a40066df4f 100644 --- a/_maps/RandomZLevels/snowdin.dmm +++ b/_maps/RandomZLevels/snowdin.dmm @@ -5939,7 +5939,7 @@ /turf/open/floor/plasteel, /area/awaymission/snowdin/post/garage) "mD" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/high_loot{ dir = 1 }, /obj/machinery/light/small, @@ -8796,7 +8796,7 @@ /turf/open/floor/plasteel/showroomfloor, /area/awaymission/snowdin/post/cavern2) "tk" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/high_loot{ dir = 1 }, /turf/open/floor/plasteel/showroomfloor, @@ -9643,7 +9643,7 @@ /turf/open/floor/plasteel/showroomfloor, /area/awaymission/snowdin/post/cavern1) "vE" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/high_loot{ dir = 1 }, /turf/open/floor/plasteel/showroomfloor, diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index 086067a6fb..3b2ee52e24 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -1337,7 +1337,7 @@ /turf/open/floor/plasteel, /area/security/prison) "adf" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/prison{ dir = 1 }, /turf/open/floor/plasteel/freezer, @@ -11677,7 +11677,7 @@ /turf/open/floor/plating, /area/maintenance/starboard/fore) "aAs" = ( -/obj/structure/piano{ +/obj/structure/musician/piano{ icon_state = "piano" }, /turf/open/floor/plating, @@ -13056,7 +13056,7 @@ /turf/open/floor/plasteel, /area/crew_quarters/cryopod) "aDL" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/structure/mirror{ @@ -13944,7 +13944,7 @@ /turf/open/floor/plasteel/dark, /area/ai_monitored/storage/eva) "aGk" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/structure/mirror{ @@ -21467,7 +21467,7 @@ /turf/open/floor/plating, /area/maintenance/port) "aZt" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/structure/mirror{ @@ -55621,7 +55621,7 @@ }, /area/crew_quarters/theatre) "iTU" = ( -/obj/structure/piano{ +/obj/structure/musician/piano{ icon_state = "piano" }, /turf/open/floor/wood, @@ -56804,7 +56804,7 @@ /turf/open/floor/plating, /area/security/main) "myh" = ( -/obj/structure/piano, +/obj/structure/musician/piano, /obj/structure/window/reinforced, /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/wood, @@ -57483,7 +57483,7 @@ /turf/open/floor/wood, /area/crew_quarters/bar) "oIW" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /turf/open/floor/mineral/titanium/blue, @@ -60606,7 +60606,7 @@ /turf/open/floor/plasteel, /area/crew_quarters/fitness/pool) "ycd" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/structure/mirror{ diff --git a/_maps/map_files/CogStation/job_changes.dm b/_maps/map_files/CogStation/job_changes.dm new file mode 100644 index 0000000000..8b1e3e9172 --- /dev/null +++ b/_maps/map_files/CogStation/job_changes.dm @@ -0,0 +1,29 @@ +#define JOB_MODIFICATION_MAP_NAME "CogStation" + +//Medical +/datum/job/scientist/New() + ..() + MAP_JOB_CHECK + access += ACCESS_CHEMISTRY + minimal_access += ACCESS_CHEMISTRY + +//Science + +/datum/job/roboticist/New() + ..() + MAP_JOB_CHECK + access += list(ACCESS_MEDICAL, ACCESS_MORGUE) + minimal_access += list(ACCESS_MEDICAL, ACCESS_MORGUE) + +//Civilian +/datum/job/clown/New() + ..() + MAP_JOB_CHECK + access += ACCESS_MAINT_TUNNELS + minimal_access += ACCESS_MAINT_TUNNELS + +/datum/job/mime/New() + ..() + MAP_JOB_CHECK + access += ACCESS_MAINT_TUNNELS + minimal_access += ACCESS_MAINT_TUNNELS \ No newline at end of file diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 82ac8604ef..61ed3edba7 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -12048,7 +12048,7 @@ "aAm" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/vomit/old, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/newscaster{ @@ -12059,7 +12059,7 @@ /area/crew_quarters/toilet/auxiliary) "aAn" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/newscaster{ @@ -12071,7 +12071,7 @@ /area/crew_quarters/toilet/auxiliary) "aAo" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/newscaster{ @@ -19681,7 +19681,7 @@ /turf/open/floor/plasteel, /area/quartermaster/storage) "aNx" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/prison{ dir = 4 }, /obj/machinery/light/small{ @@ -26182,7 +26182,7 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/bar/atrium) "aWX" = ( -/obj/structure/piano{ +/obj/structure/musician/piano{ icon_state = "piano" }, /obj/machinery/airalarm{ @@ -59858,6 +59858,7 @@ /area/crew_quarters/heads/captain) "bXh" = ( /obj/structure/toilet{ + contents = newlist(/obj/item/toy/snappop/phoenix); dir = 4 }, /obj/machinery/light{ @@ -82141,7 +82142,7 @@ "cIa" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/vomit/old, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/newscaster{ @@ -82152,7 +82153,7 @@ /area/crew_quarters/toilet/restrooms) "cIb" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/newscaster{ @@ -82164,7 +82165,7 @@ /area/crew_quarters/toilet/restrooms) "cIc" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/newscaster{ @@ -89653,7 +89654,7 @@ }, /area/medical/storage) "cUP" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/effect/decal/cleanable/dirt, @@ -115313,7 +115314,7 @@ /obj/structure/window/reinforced{ dir = 8 }, -/obj/structure/piano, +/obj/structure/musician/piano, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/crew_quarters/theatre/abandoned) @@ -116756,7 +116757,7 @@ /turf/open/floor/plasteel, /area/maintenance/port/aft) "dPN" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/effect/decal/cleanable/dirt, diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 446fe3e519..3e12c8ec86 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -56558,7 +56558,7 @@ pixel_x = -12; pixel_y = 2 }, -/obj/structure/toilet{ +/obj/structure/toilet/secret/prison{ pixel_y = 16 }, /obj/effect/turf_decal/tile/neutral{ @@ -56908,7 +56908,7 @@ /area/hallway/primary/central) "bKT" = ( /obj/effect/turf_decal/box, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/structure/mirror{ @@ -56941,7 +56941,7 @@ /area/crew_quarters/toilet/restrooms) "bKU" = ( /obj/effect/turf_decal/box, -/obj/structure/toilet{ +//obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/structure/mirror{ @@ -59908,7 +59908,7 @@ pixel_x = -12; pixel_y = 2 }, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 16 }, /obj/effect/turf_decal/tile/neutral{ @@ -62988,7 +62988,7 @@ pixel_x = -12; pixel_y = 2 }, -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 16 }, /obj/effect/turf_decal/tile/neutral{ diff --git a/_maps/map_files/LambdaStation/lambda.dmm b/_maps/map_files/LambdaStation/lambda.dmm index 808d2f5ccb..6d1469d483 100644 --- a/_maps/map_files/LambdaStation/lambda.dmm +++ b/_maps/map_files/LambdaStation/lambda.dmm @@ -1031,7 +1031,6 @@ "acK" = ( /obj/structure/sign/warning/vacuum/external{ pixel_x = 30; - pixel_y = 0 }, /turf/open/floor/plating, /area/science/mixing) @@ -1069,7 +1068,6 @@ desc = "In Space, no one can hear you scream, especially if the microphone has been disabled."; name = "Prison Intercom"; pixel_x = 32; - pixel_y = 0; prison_radio = 1 }, /turf/open/floor/plasteel/dark, @@ -1638,7 +1636,6 @@ "aeh" = ( /obj/structure/table, /obj/machinery/microwave{ - pixel_x = 0; pixel_y = 6 }, /turf/open/floor/plasteel, @@ -1999,7 +1996,6 @@ }, /obj/machinery/status_display/ai{ pixel_x = -32; - pixel_y = 0 }, /turf/open/floor/plasteel/white, /area/science/mixing) @@ -3037,7 +3033,7 @@ /turf/open/floor/plasteel, /area/security/prison) "agR" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/prison{ dir = 8 }, /turf/open/floor/plasteel/freezer, @@ -3143,7 +3139,6 @@ /obj/item/assembly/flash/handheld, /obj/item/reagent_containers/spray/pepper, /obj/item/radio/intercom{ - pixel_x = 0; pixel_y = 28 }, /obj/machinery/light/small{ @@ -3438,7 +3433,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -3489,7 +3483,6 @@ /obj/item/radio/intercom{ desc = "In Space, no one can hear you scream, especially if the microphone has been disabled."; name = "Prison Intercom"; - pixel_x = 0; pixel_y = -32; prison_radio = 1 }, @@ -4496,7 +4489,6 @@ department = "Robotics"; departmentType = 2; name = "Robotics RC"; - pixel_x = 0; pixel_y = 31; receive_ore_updates = 1 }, @@ -4507,7 +4499,6 @@ /obj/machinery/mecha_part_fabricator, /obj/item/radio/intercom{ dir = 2; - pixel_x = 0; pixel_y = 28 }, /turf/open/floor/plasteel, @@ -4757,7 +4748,6 @@ icon_state = "sink_alt"; name = "old sink"; pixel_x = 15; - pixel_y = 0 }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3, /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1, @@ -5340,7 +5330,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -6021,7 +6010,6 @@ /obj/item/radio/intercom{ dir = 2; pixel_x = -29; - pixel_y = 0 }, /turf/open/floor/plasteel, /area/science/xenobiology) @@ -6487,7 +6475,6 @@ }, /obj/item/grenade/barrier{ pixel_x = 6; - pixel_y = 0 }, /obj/effect/turf_decal/tile/red{ dir = 1 @@ -7569,7 +7556,6 @@ idDoor = "xeno_airlock_exterior"; idSelf = "xeno_airlock_control"; name = "Access Button"; - pixel_x = 0; pixel_y = 25 }, /obj/machinery/door/firedoor, @@ -8301,7 +8287,6 @@ dir = 4; name = "Xenobiology APC"; pixel_x = 24; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/purple/filled/line{ icon_state = "trimline_fill"; @@ -9280,7 +9265,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -9496,7 +9480,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel/dark, @@ -11043,7 +11026,6 @@ "aut" = ( /obj/item/radio/intercom{ name = "Station Intercom"; - pixel_x = 0; pixel_y = 26 }, /turf/open/floor/plasteel, @@ -11345,7 +11327,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel/white, @@ -11563,7 +11544,6 @@ dir = 4; name = "Research Firing Range APC"; pixel_x = 28; - pixel_y = 0 }, /obj/item/target, /obj/item/target, @@ -11815,7 +11795,6 @@ }, /obj/machinery/status_display/ai{ pixel_x = 32; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/neutral/filled/line{ icon_state = "trimline_fill"; @@ -12295,7 +12274,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -12779,7 +12757,6 @@ /obj/item/reagent_containers/syringe, /obj/machinery/newscaster{ pixel_x = -32; - pixel_y = 0 }, /turf/open/floor/plasteel/freezer, /area/security/prison) @@ -12905,7 +12882,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -12954,7 +12930,6 @@ /obj/machinery/requests_console{ department = "Security"; name = "Security RC"; - pixel_x = 0; pixel_y = -32 }, /turf/open/floor/plasteel, @@ -13201,7 +13176,6 @@ /obj/structure/bed, /obj/machinery/flasher{ id = "insaneflash"; - pixel_x = 0; pixel_y = -26 }, /turf/open/floor/plasteel/freezer, @@ -13696,7 +13670,6 @@ areastring = "/area/crew_quarters/heads/hos"; dir = 1; name = "Head of Security's Office APC"; - pixel_x = 0; pixel_y = 25 }, /obj/structure/cable{ @@ -13712,7 +13685,6 @@ }, /obj/machinery/recharger, /obj/structure/reagent_dispensers/peppertank{ - pixel_x = 0; pixel_y = 30 }, /turf/open/floor/plasteel/dark, @@ -13779,10 +13751,8 @@ id = "hosspace"; name = "Space Shutters Control"; pixel_x = 25; - pixel_y = 0 }, /obj/machinery/status_display/evac{ - pixel_x = 0; pixel_y = 32 }, /turf/open/floor/plasteel/dark, @@ -15659,7 +15629,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer3{ @@ -15732,7 +15701,6 @@ /area/crew_quarters/heads/hor) "aBO" = ( /obj/item/storage/secure/safe{ - pixel_x = 0; pixel_y = -32 }, /obj/structure/table, @@ -16468,7 +16436,6 @@ /obj/machinery/iv_drip, /obj/structure/sign/poster/official/cleanliness{ pixel_x = 32; - pixel_y = 0 }, /turf/open/floor/plasteel/white, /area/security/brig) @@ -16626,7 +16593,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3, /obj/machinery/status_display/evac{ pixel_x = 32; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/purple/filled/line{ icon_state = "trimline_fill"; @@ -16891,7 +16857,6 @@ }, /obj/machinery/status_display/evac{ pixel_x = 32; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/neutral/filled/line{ icon_state = "trimline_fill"; @@ -18008,7 +17973,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel/white, @@ -18144,7 +18108,6 @@ }, /obj/machinery/status_display/evac{ pixel_x = 32; - pixel_y = 0 }, /turf/open/floor/plasteel/grimy, /area/hallway/primary/fore) @@ -20094,7 +20057,6 @@ /obj/machinery/requests_console{ department = "Law office"; pixel_x = 31; - pixel_y = 0 }, /turf/open/floor/plasteel/grimy, /area/lawoffice) @@ -21261,7 +21223,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -22103,7 +22064,6 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1, /obj/machinery/light_switch{ pixel_x = -28; - pixel_y = 0 }, /turf/open/floor/plasteel/grimy, /area/lawoffice) @@ -22577,7 +22537,6 @@ /obj/structure/filingcabinet/chestdrawer, /obj/structure/sign/poster/official/enlist{ pixel_x = 32; - pixel_y = 0 }, /obj/effect/turf_decal/tile/red, /obj/effect/turf_decal/tile/neutral{ @@ -22701,7 +22660,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -22727,7 +22685,6 @@ dir = 8; name = "Quartermaster's Office APC"; pixel_x = -25; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/yellow/filled/line{ icon_state = "trimline_fill"; @@ -23303,7 +23260,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -24483,7 +24439,6 @@ }, /obj/structure/sign/nanotrasen{ pixel_x = 32; - pixel_y = 0 }, /turf/open/floor/plasteel, /area/security/brig) @@ -24728,10 +24683,8 @@ /obj/machinery/computer/med_data/laptop, /obj/machinery/computer/security/telescreen/entertainment{ pixel_x = -32; - pixel_y = 0 }, /obj/structure/extinguisher_cabinet{ - pixel_x = 0; pixel_y = 29 }, /obj/machinery/light/small{ @@ -25329,7 +25282,6 @@ /obj/structure/dresser, /obj/item/radio/intercom{ dir = 2; - pixel_x = 0; pixel_y = 28 }, /turf/open/floor/carpet/blue, @@ -26523,7 +26475,6 @@ dir = 8; name = "Captain's Office APC"; pixel_x = -25; - pixel_y = 0 }, /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1{ dir = 4 @@ -26870,7 +26821,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /obj/machinery/computer/bounty{ @@ -27825,7 +27775,6 @@ /obj/machinery/power/apc/highcap/five_k{ dir = 1; name = "Hydroponics APC"; - pixel_x = 0; pixel_y = 23 }, /obj/effect/turf_decal/trimline/green/filled/line{ @@ -28516,7 +28465,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -29172,7 +29120,6 @@ frequency = 1423; listening = 0; name = "Interrogation Intercom"; - pixel_x = 0; pixel_y = -24 }, /obj/effect/turf_decal/tile/neutral{ @@ -29313,7 +29260,6 @@ }, /obj/machinery/newscaster{ pixel_x = 32; - pixel_y = 0 }, /turf/open/floor/plasteel, /area/crew_quarters/bar) @@ -30392,7 +30338,7 @@ /turf/open/floor/plasteel, /area/crew_quarters/bar/atrium) "bbF" = ( -/obj/structure/piano, +/obj/structure/musician/piano, /obj/structure/window/reinforced{ dir = 8 }, @@ -31413,7 +31359,6 @@ "bdK" = ( /obj/machinery/requests_console{ department = "EVA"; - pixel_x = 0; pixel_y = 32 }, /turf/open/floor/plasteel, @@ -31919,7 +31864,6 @@ dir = 8; name = "Council Chambers APC"; pixel_x = -25; - pixel_y = 0 }, /turf/open/floor/carpet, /area/bridge/meeting_room) @@ -32295,7 +32239,6 @@ dir = 8; name = "Cargo Security APC"; pixel_x = -25; - pixel_y = 0 }, /obj/machinery/camera{ c_tag = "Security Post - Cargo"; @@ -33401,7 +33344,6 @@ /area/crew_quarters/cafeteria) "bhD" = ( /obj/machinery/newscaster{ - pixel_x = 0; pixel_y = 30 }, /turf/open/floor/plasteel, @@ -36459,7 +36401,6 @@ dir = 8; name = "Gateway APC"; pixel_x = -25; - pixel_y = 0 }, /obj/effect/turf_decal/stripes/end{ dir = 8 @@ -37499,7 +37440,6 @@ dir = 1 }, /obj/item/radio/intercom{ - pixel_x = 0; pixel_y = -29 }, /turf/open/floor/plasteel, @@ -38166,7 +38106,6 @@ departmentType = 0; name = "Genetics Requests Console"; pixel_x = -32; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/blue/filled/line{ icon_state = "trimline_fill"; @@ -39378,7 +39317,6 @@ }, /obj/structure/chair, /obj/item/radio/intercom{ - pixel_x = 0; pixel_y = 26 }, /turf/open/floor/plasteel, @@ -40778,7 +40716,6 @@ departmentType = 2; name = "Science Requests Console"; pixel_x = -32; - pixel_y = 0; receive_ore_updates = 1 }, /obj/machinery/camera/autoname{ @@ -41468,7 +41405,6 @@ /obj/structure/table/wood, /obj/machinery/newscaster{ pixel_x = 32; - pixel_y = 0 }, /obj/item/folder, /obj/item/folder, @@ -43797,7 +43733,6 @@ dir = 8; name = "Cloning Lab APC"; pixel_x = -25; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/blue/filled/line{ icon_state = "trimline_fill"; @@ -44652,7 +44587,6 @@ dir = 4 }, /obj/machinery/computer/security/telescreen/entertainment{ - pixel_x = 0; pixel_y = 30 }, /turf/open/floor/plasteel/cult{ @@ -44943,7 +44877,6 @@ id = "cmoshutter"; name = "CMO Office Shutters"; pixel_x = -25; - pixel_y = 0; req_access_txt = "40" }, /turf/open/floor/plasteel/white, @@ -47007,7 +46940,6 @@ department = "Security"; departmentType = 5; pixel_x = -30; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/red/filled/line{ icon_state = "trimline_fill"; @@ -47084,7 +47016,6 @@ /obj/machinery/power/apc/highcap/five_k{ dir = 1; name = "Central Hall APC"; - pixel_x = 0; pixel_y = 25 }, /obj/structure/cable{ @@ -47914,7 +47845,6 @@ icon_state = "sink_alt"; name = "old sink"; pixel_x = 15; - pixel_y = 0 }, /turf/open/floor/wood, /area/maintenance/bar) @@ -50049,7 +49979,6 @@ /obj/machinery/light/small, /obj/machinery/vending/wallmed{ pixel_x = -28; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/blue/filled/line{ icon_state = "trimline_fill"; @@ -50759,7 +50688,6 @@ areastring = "/area/ai_monitored/turret_protected/aisat_interior"; dir = 1; name = "MiniSat Antechamber APC"; - pixel_x = 0; pixel_y = 29 }, /obj/effect/turf_decal/tile/blue{ @@ -52934,7 +52862,6 @@ dir = 2; name = "MiniSat Foyer APC"; pixel_x = -29; - pixel_y = 0 }, /obj/effect/turf_decal/tile/blue{ dir = 8 @@ -53002,7 +52929,6 @@ dir = 8; name = "MiniSat Exterior APC"; pixel_x = -24; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/blue/filled/line{ icon_state = "trimline_fill"; @@ -53664,7 +53590,6 @@ /obj/machinery/requests_console{ department = "Tool Storage"; departmentType = 0; - pixel_x = 0; pixel_y = 30 }, /obj/item/folder/yellow, @@ -53825,7 +53750,6 @@ }, /obj/effect/turf_decal/tile/neutral, /obj/machinery/newscaster{ - pixel_x = 0; pixel_y = 32 }, /obj/effect/turf_decal/loading_area{ @@ -55174,7 +55098,6 @@ dir = 8; name = "Tool Storage APC"; pixel_x = -25; - pixel_y = 0 }, /obj/effect/turf_decal/tile/brown{ dir = 1 @@ -55622,7 +55545,6 @@ dir = 4; name = "Fore Maintenance APC"; pixel_x = 24; - pixel_y = 0 }, /turf/open/floor/plating, /area/maintenance/fore) @@ -55929,7 +55851,6 @@ }, /obj/structure/sign/directions/evac{ dir = 4; - pixel_x = 0; pixel_y = 24 }, /turf/open/floor/plasteel, @@ -56057,7 +55978,6 @@ /obj/machinery/announcement_system, /obj/machinery/status_display/ai{ pixel_x = 31; - pixel_y = 0 }, /turf/open/floor/carpet, /area/tcommsat/computer) @@ -57109,7 +57029,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -57391,7 +57310,6 @@ }, /obj/machinery/status_display/evac{ pixel_x = 31; - pixel_y = 0 }, /turf/open/floor/carpet, /area/tcommsat/computer) @@ -57545,7 +57463,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -57577,7 +57494,6 @@ id = "atmos"; name = "Atmospherics Lockdown"; pixel_x = -38; - pixel_y = 0; req_access_txt = "24" }, /turf/open/floor/plasteel, @@ -57847,7 +57763,6 @@ freerange = 1; name = "Station Intercom (Telecomms)"; pixel_x = 30; - pixel_y = 0 }, /turf/open/floor/carpet, /area/tcommsat/computer) @@ -58657,7 +58572,6 @@ dir = 4; name = "Chief Engineer's APC"; pixel_x = 26; - pixel_y = 0 }, /obj/structure/cable{ icon_state = "0-2"; @@ -58782,7 +58696,6 @@ dir = 8; name = "Locker Room APC"; pixel_x = -28; - pixel_y = 0 }, /turf/open/floor/plasteel, /area/crew_quarters/locker) @@ -59393,7 +59306,6 @@ dir = 4; name = "Dormitories APC"; pixel_x = 24; - pixel_y = 0 }, /obj/structure/cable{ icon_state = "0-8" @@ -60076,7 +59988,7 @@ /turf/open/floor/plasteel, /area/crew_quarters/fitness/recreation) "ccw" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/machinery/light/small{ @@ -60333,7 +60245,6 @@ dir = 4; name = "Chief Medical Officer's Quarters APC"; pixel_x = 24; - pixel_y = 0 }, /obj/structure/cable{ icon_state = "0-8" @@ -60612,7 +60523,7 @@ /turf/open/floor/plating, /area/maintenance/fore) "cdE" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/effect/landmark/blobstart, @@ -60646,7 +60557,7 @@ /turf/open/floor/plasteel/freezer, /area/crew_quarters/toilet) "cdH" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/light/small{ @@ -60658,7 +60569,6 @@ name = "Lock Control"; normaldoorcontrol = 1; pixel_x = 25; - pixel_y = 0; specialfunctions = 4 }, /obj/machinery/newscaster{ @@ -61078,7 +60988,6 @@ /area/maintenance/department/crew_quarters/dorms) "ceK" = ( /obj/structure/sign/warning/radiation/rad_area{ - pixel_x = 0; pixel_y = -32 }, /turf/open/floor/plating, @@ -61126,7 +61035,6 @@ /obj/structure/bed, /obj/machinery/status_display/ai{ pixel_x = -32; - pixel_y = 0 }, /turf/open/floor/carpet/blue, /area/crew_quarters/heads/cmo/private) @@ -61599,7 +61507,6 @@ areastring = "/area/chapel/main"; dir = 1; name = "Chapel APC"; - pixel_x = 0; pixel_y = 24 }, /obj/structure/cable{ @@ -62561,7 +62468,6 @@ "cig" = ( /obj/structure/sign/warning/radiation/rad_area{ pixel_x = -32; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/yellow/filled/line{ icon_state = "trimline_fill"; @@ -62922,7 +62828,7 @@ }, /area/maintenance/aft) "ciL" = ( -/obj/structure/piano, +/obj/structure/musician/piano, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -63311,7 +63217,6 @@ /area/maintenance/aft) "cjC" = ( /obj/structure/mirror{ - pixel_x = 0; pixel_y = -28 }, /obj/item/lipstick/black, @@ -63650,7 +63555,6 @@ /obj/machinery/vending/coffee, /obj/machinery/newscaster{ pixel_x = 30; - pixel_y = 0 }, /turf/open/floor/plasteel/dark, /area/crew_quarters/fitness/recreation) @@ -63947,7 +63851,6 @@ }, /obj/machinery/newscaster{ pixel_x = -32; - pixel_y = 0 }, /turf/open/floor/plasteel, /area/crew_quarters/fitness/recreation) @@ -64090,7 +63993,6 @@ dir = 1; name = "SMES room APC"; pixel_x = -26; - pixel_y = 0 }, /obj/structure/cable{ icon_state = "0-2"; @@ -64416,7 +64318,6 @@ broadcasting = 1; frequency = 1481; name = "Confessional Intercom"; - pixel_x = 0; pixel_y = -30 }, /obj/machinery/light/small{ @@ -64437,7 +64338,6 @@ broadcasting = 1; frequency = 1481; name = "Confessional Intercom"; - pixel_x = 0; pixel_y = 30 }, /obj/machinery/light/small{ @@ -64949,7 +64849,6 @@ dir = 1; name = "Atmospherics APC"; pixel_x = -27; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/neutral/filled/line{ icon_state = "trimline_fill"; @@ -68610,7 +68509,6 @@ areastring = "/area/hydroponics/garden"; dir = 2; name = "Garden APC"; - pixel_x = 0; pixel_y = -26 }, /obj/structure/cable{ @@ -69318,7 +69216,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /obj/machinery/camera/autoname{ @@ -69369,7 +69266,6 @@ dir = 4; name = "Auxillary Base Construction APC"; pixel_x = 24; - pixel_y = 0 }, /obj/structure/cable{ icon_state = "0-2" @@ -70371,7 +70267,6 @@ }, /obj/item/radio/intercom{ pixel_x = -26; - pixel_y = 0 }, /obj/item/stack/cable_coil/white{ pixel_x = 3; @@ -72121,7 +72016,6 @@ }, /obj/item/radio/intercom{ pixel_x = 26; - pixel_y = 0 }, /turf/open/floor/plasteel/dark/corner, /area/engine/atmos) @@ -72265,7 +72159,6 @@ dir = 4; name = "Starboard Hallway APC"; pixel_x = 24; - pixel_y = 0 }, /obj/structure/cable{ icon_state = "0-8" @@ -72718,7 +72611,6 @@ name = "Auxillary Base Monitor"; network = list("auxbase"); pixel_x = 25; - pixel_y = 0 }, /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer1, /obj/structure/cable{ @@ -73580,7 +73472,6 @@ /obj/machinery/disposal/bin, /obj/structure/disposalpipe/trunk, /obj/item/radio/intercom{ - pixel_x = 0; pixel_y = 23 }, /turf/open/floor/plasteel/dark, @@ -74023,7 +73914,6 @@ light_color = "#cee5d2" }, /obj/machinery/status_display/evac{ - pixel_x = 0; pixel_y = 32 }, /turf/open/floor/plasteel/white, @@ -74593,7 +74483,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/visible, /obj/structure/fireaxecabinet{ pixel_x = -31; - pixel_y = 0 }, /obj/effect/turf_decal/trimline/neutral/filled/line{ icon_state = "trimline_fill"; @@ -75451,7 +75340,6 @@ id = "aux_base_shutters"; name = "Public Shutters Control"; pixel_x = 24; - pixel_y = 0; req_one_access_txt = "32;47;48" }, /obj/machinery/camera{ @@ -76509,7 +76397,6 @@ /obj/machinery/flasher{ id = "brigflashdoor"; pixel_x = -26; - pixel_y = 0 }, /turf/open/floor/plasteel, /area/security/brig) @@ -77128,7 +77015,6 @@ /obj/effect/turf_decal/tile/red, /obj/item/storage/secure/safe/HoS{ pixel_x = 36; - pixel_y = 0 }, /obj/machinery/status_display/ai{ pixel_y = -32 @@ -78620,7 +78506,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel, @@ -78654,7 +78539,6 @@ freerange = 1; name = "Station Intercom (Telecomms)"; pixel_x = 30; - pixel_y = 0 }, /turf/open/floor/plasteel/white, /area/science/xenobiology) @@ -78665,7 +78549,6 @@ /obj/item/radio/intercom{ dir = 1; name = "Station Intercom"; - pixel_x = 0; pixel_y = -28 }, /turf/open/floor/plasteel/dark, @@ -79513,7 +79396,6 @@ id = "ewradiation"; name = "Shutters Control"; pixel_x = 33; - pixel_y = 0; req_access_txt = "11" }, /turf/open/floor/plasteel, @@ -81824,7 +81706,6 @@ /obj/structure/bed/dogbed/ian, /obj/item/radio/intercom{ dir = 2; - pixel_x = 0; pixel_y = 28 }, /turf/open/floor/wood, @@ -83804,7 +83685,6 @@ dir = 1 }, /obj/structure/sign/warning/securearea{ - pixel_x = 0; pixel_y = 32 }, /turf/open/floor/plasteel, @@ -83822,7 +83702,6 @@ areastring = "/area/engine/storage"; dir = 2; name = "Engineering Storage APC"; - pixel_x = 0; pixel_y = -26 }, /obj/structure/cable, @@ -84043,7 +83922,7 @@ /turf/open/floor/plating, /area/maintenance/port/fore) "cXf" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/light/small{ @@ -84209,7 +84088,6 @@ dir = 4; name = "Electrical Maintenance APC"; pixel_x = 24; - pixel_y = 0 }, /obj/structure/cable{ icon_state = "0-8" @@ -84391,7 +84269,6 @@ "cXO" = ( /obj/machinery/status_display/ai{ pixel_x = -32; - pixel_y = 0 }, /obj/machinery/power/apc/highcap/ten_k{ areastring = "/area/crew_quarters/heads/chief/private"; @@ -84483,7 +84360,6 @@ /obj/machinery/camera/autoname, /obj/item/radio/intercom{ name = "Station Intercom"; - pixel_x = 0; pixel_y = 26 }, /turf/open/floor/plasteel/cafeteria, @@ -84507,7 +84383,6 @@ }, /obj/item/radio/intercom{ name = "Station Intercom"; - pixel_x = 0; pixel_y = 26 }, /turf/open/floor/plasteel/cafeteria, diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index a08aaf094f..e6eddb2777 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -1028,7 +1028,7 @@ /obj/machinery/light/small{ dir = 4 }, -/obj/structure/toilet{ +/obj/structure/toilet/secret/prison{ dir = 4 }, /turf/open/floor/plasteel/freezer, @@ -15496,7 +15496,7 @@ }, /area/maintenance/fore) "aDP" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /obj/machinery/light/small{ @@ -16890,7 +16890,7 @@ /turf/open/floor/plating, /area/maintenance/fore) "aGz" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /obj/machinery/light/small{ @@ -18173,7 +18173,7 @@ /turf/open/floor/plating, /area/maintenance/fore) "aJa" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /obj/machinery/light/small{ @@ -18200,7 +18200,7 @@ /turf/open/floor/plasteel/freezer, /area/crew_quarters/toilet/restrooms) "aJc" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/machinery/light/small{ @@ -32280,7 +32280,7 @@ /turf/open/floor/plasteel/white, /area/crew_quarters/heads/captain/private) "bkL" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 13 }, /obj/machinery/light, @@ -40361,7 +40361,7 @@ /turf/open/floor/plasteel, /area/crew_quarters/toilet/auxiliary) "bBp" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /obj/machinery/light/small{ @@ -41832,7 +41832,7 @@ /turf/open/floor/plasteel, /area/crew_quarters/toilet/auxiliary) "bEv" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /obj/machinery/light/small{ @@ -43537,7 +43537,7 @@ /turf/open/floor/plating, /area/crew_quarters/toilet/auxiliary) "bHQ" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /obj/machinery/light/small{ @@ -79574,7 +79574,7 @@ /turf/closed/wall, /area/crew_quarters/bar) "dhU" = ( -/obj/structure/piano, +/obj/structure/musician/piano, /obj/structure/window/reinforced{ dir = 8 }, diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index 2ca3c3deee..8a951881f9 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -11768,7 +11768,7 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/theatre) "atY" = ( -/obj/structure/piano{ +/obj/structure/musician/piano{ icon_state = "piano" }, /obj/machinery/light{ diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index fc5f6e9778..f1380ec2f5 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -2285,7 +2285,7 @@ /turf/open/floor/plasteel, /area/security/prison) "ahb" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/prison{ dir = 8 }, /turf/open/floor/plasteel/freezer, @@ -9106,7 +9106,7 @@ /turf/open/floor/plasteel/freezer, /area/crew_quarters/heads/captain) "avK" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /turf/open/floor/plasteel/freezer, @@ -14667,7 +14667,7 @@ /turf/open/floor/plasteel/freezer, /area/crew_quarters/toilet/restrooms) "aIl" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 8 }, /obj/machinery/light/small{ @@ -16490,7 +16490,7 @@ /turf/open/floor/plasteel/cafeteria, /area/crew_quarters/cafeteria/lunchroom) "aNc" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /obj/effect/landmark/start/assistant, @@ -43015,7 +43015,7 @@ /area/maintenance/department/engine) "bUb" = ( /obj/structure/grille/broken, -/obj/structure/piano, +/obj/structure/musician/piano, /turf/open/floor/plating, /area/maintenance/department/engine) "bUc" = ( @@ -47393,7 +47393,7 @@ /turf/open/floor/plasteel/grimy, /area/chapel/main/monastery) "cgO" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /obj/machinery/light/small{ @@ -47734,7 +47734,7 @@ /turf/open/floor/plasteel/grimy, /area/chapel/main/monastery) "ciq" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ pixel_y = 8 }, /turf/open/floor/plasteel/showroomfloor, @@ -55939,7 +55939,7 @@ /turf/open/floor/carpet, /area/chapel/office) "kgR" = ( -/obj/structure/toilet{ +/obj/structure/toilet/secret/low_loot{ dir = 4 }, /turf/open/floor/plating, @@ -58271,7 +58271,7 @@ /turf/open/floor/plating, /area/chapel/office) "plA" = ( -/obj/structure/piano, +/obj/structure/musician/piano, /turf/open/floor/plasteel/dark, /area/maintenance/department/crew_quarters/dorms) "pmB" = ( diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index daff5e51ad..c9b81cefd9 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -14420,7 +14420,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/structure/piano, +/obj/structure/musician/piano, /turf/open/indestructible/hotelwood, /area/centcom/holding) "Hk" = ( diff --git a/_maps/shuttles/emergency_bar.dmm b/_maps/shuttles/emergency_bar.dmm index 7c14d9ebd0..7bc7de9bf1 100644 --- a/_maps/shuttles/emergency_bar.dmm +++ b/_maps/shuttles/emergency_bar.dmm @@ -197,7 +197,7 @@ /turf/open/floor/plasteel, /area/shuttle/escape) "aF" = ( -/obj/structure/piano{ +/obj/structure/musician/piano{ icon_state = "piano" }, /turf/open/floor/plasteel/grimy, diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index be0a4d0f59..14c78f90d0 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -114,12 +114,17 @@ //slowdown when in softcrit #define SOFTCRIT_ADD_SLOWDOWN 6 -//Attack types for checking shields/hit reactions -#define MELEE_ATTACK 1 -#define UNARMED_ATTACK 2 -#define PROJECTILE_ATTACK 3 -#define THROWN_PROJECTILE_ATTACK 4 -#define LEAP_ATTACK 5 +/// Attack types for check_block()/run_block(). Flags, combinable. +/// Attack was melee, whether or not armed. +#define ATTACK_TYPE_MELEE (1<<0) +/// Attack was with a gun or something that should count as a gun (but not if a gun shouldn't count for a gun, crazy right?) +#define ATTACK_TYPE_PROJECTILE (1<<1) +/// Attack was unarmed.. this usually means hand to hand combat. +#define ATTACK_TYPE_UNARMED (1<<2) +/// Attack was a thrown atom hitting the victim. +#define ATTACK_TYPE_THROWN (1<<3) +/// Attack was a bodyslam/leap/tackle. See: Xenomorph leap tackles. +#define ATTACK_TYPE_TACKLE (1<<4) //attack visual effects #define ATTACK_EFFECT_PUNCH "punch" @@ -252,4 +257,53 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default. #define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs. +/// Bitflags for check_block() and handle_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS. +/// Attack was not blocked +#define BLOCK_NONE NONE +/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION! +#define BLOCK_SUCCESS (1<<1) +/// The below are for "metadata" on "how" the attack was blocked. + +/// Attack was and should be redirected according to list argument REDIRECT_METHOD (NOTE: the SHOULD here is important, as it says "the thing blocking isn't handling the reflecting for you so do it yourself"!) +#define BLOCK_SHOULD_REDIRECT (1<<2) +/// Attack was redirected (whether by us or by SHOULD_REDIRECT flagging for automatic handling) +#define BLOCK_REDIRECTED (1<<3) +/// Attack was blocked by something like a shield. +#define BLOCK_PHYSICAL_EXTERNAL (1<<4) +/// Attack was blocked by something worn on you. +#define BLOCK_PHYSICAL_INTERNAL (1<<5) +/// Attack outright missed because the target dodged. Should usually be combined with redirection passthrough or something (see martial arts) +#define BLOCK_TARGET_DODGED (1<<7) +/// Meta-flag for run_block/do_run_block : By default, BLOCK_SUCCESS tells do_run_block() to assume the attack is completely blocked and not continue the block chain. If this is present, it will continue to check other items in the chain rather than stopping. +#define BLOCK_CONTINUE_CHAIN (1<<8) + +/// For keys in associative list/block_return as we don't want to saturate our (somewhat) limited flags. +#define BLOCK_RETURN_REDIRECT_METHOD "REDIRECT_METHOD" + /// Pass through victim + #define REDIRECT_METHOD_PASSTHROUGH "passthrough" + /// Deflect at randomish angle + #define REDIRECT_METHOD_DEFLECT "deflect" + /// reverse 180 angle, basically (as opposed to "realistic" wall reflections) + #define REDIRECT_METHOD_REFLECT "reflect" + /// "do not taser the bad man with the desword" - actually aims at the firer/attacker rather than just reversing + #define REDIRECT_METHOD_RETURN_TO_SENDER "no_you" + +/// These keys are generally only applied to the list if real_attack is FALSE. Used incase we want to make "smarter" mob AI in the future or something. +/// Tells the caller how likely from 0 (none) to 100 (always) we are to reflect energy projectiles +#define BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE "reflect_projectile_chance" +/// Tells the caller how likely we are to block attacks from 0 to 100 in general +#define BLOCK_RETURN_NORMAL_BLOCK_CHANCE "normal_block_chance" +/// Tells the caller about how many hits we can soak on average before our blocking fails. +#define BLOCK_RETURN_BLOCK_CAPACITY "block_capacity" + +/// Default if the above isn't set in the list. +#define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT + +/// Block priorities +#define BLOCK_PRIORITY_HELD_ITEM 100 +#define BLOCK_PRIORITY_CLOTHING 50 +#define BLOCK_PRIORITY_WEAR_SUIT 75 +#define BLOCK_PRIORITY_UNIFORM 25 + +#define BLOCK_PRIORITY_DEFAULT BLOCK_PRIORITY_HELD_ITEM diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1ef8b9bb66..e75f832df3 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -192,6 +192,8 @@ #define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client) #define COMSIG_MOB_CLIENT_MOVE "comsig_mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc) #define COMSIG_LIVING_GUN_PROCESS_FIRE "living_gun_process_fire" //from base of /obj/item/gun/proc/process_fire(): (atom/target, params, zone_override) +// This returns flags as defined for block in __DEFINES/combat.dm! +#define COMSIG_LIVING_RUN_BLOCK "living_do_run_block" //from base of mob/living/do_run_block(): (real_attack, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone) #define COMSIG_LIVING_COMBAT_ENABLED "combatmode_enabled" //from base of mob/living/enable_combat_mode() (was_forced) #define COMSIG_LIVING_COMBAT_DISABLED "combatmode_disabled" //from base of mob/living/disable_combat_mode() (was_forced) @@ -240,6 +242,9 @@ #define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" //return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user) #define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args) #define COMSIG_ITEM_WEARERCROSSED "wearer_crossed" //called on item when crossed by something (): (/atom/movable) +// THE FOLLOWING TWO BLOCKS SHOULD RETURN BLOCK FLAGS AS DEFINED IN __DEFINES/combat.dm! +#define COMSIG_ITEM_CHECK_BLOCK "check_block" //from base of obj/item/check_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) +#define COMSIG_ITEM_RUN_BLOCK "run_block" //from base of obj/item/run_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) // /obj/item/clothing signals #define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): () diff --git a/code/__DEFINES/dynamic.dm b/code/__DEFINES/dynamic.dm index ae5b031c8b..da8a76e71e 100644 --- a/code/__DEFINES/dynamic.dm +++ b/code/__DEFINES/dynamic.dm @@ -7,6 +7,7 @@ #define WAROPS_ALWAYS_ALLOWED (1<<1) #define USE_PREF_WEIGHTS (1<<2) #define FORCE_IF_WON (1<<3) +#define USE_PREV_ROUND_WEIGHTS (1<<4) #define ONLY_RULESET (1<<0) #define HIGHLANDER_RULESET (1<<1) diff --git a/code/__DEFINES/instruments.dm b/code/__DEFINES/instruments.dm new file mode 100644 index 0000000000..3c414f87f4 --- /dev/null +++ b/code/__DEFINES/instruments.dm @@ -0,0 +1,29 @@ +#define INSTRUMENT_MIN_OCTAVE 1 +#define INSTRUMENT_MAX_OCTAVE 9 +#define INSTRUMENT_MIN_KEY 0 +#define INSTRUMENT_MAX_KEY 127 + +/// Max number of playing notes per instrument. +#define CHANNELS_PER_INSTRUMENT 128 + +/// Distance multiplier that makes us not be impacted by 3d sound as much. This is a multiplier so lower it is the closer we will pretend to be to people. +#define INSTRUMENT_DISTANCE_FALLOFF_BUFF 0.2 +/// How many tiles instruments have no falloff for +#define INSTRUMENT_DISTANCE_NO_FALLOFF 3 + +/// Maximum length a note should ever go for +#define INSTRUMENT_MAX_TOTAL_SUSTAIN (5 SECONDS) + +/// These are per decisecond. +#define INSTRUMENT_EXP_FALLOFF_MIN 1.025 //100/(1.025^50) calculated for [INSTRUMENT_MIN_SUSTAIN_DROPOFF] to be 30. +#define INSTRUMENT_EXP_FALLOFF_MAX 10 + +/// Minimum volume for when the sound is considered dead. +#define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0 + +#define SUSTAIN_LINEAR 1 +#define SUSTAIN_EXPONENTIAL 2 + +// /datum/instrument instrument_flags +#define INSTRUMENT_LEGACY (1<<0) //Legacy instrument. Implies INSTRUMENT_DO_NOT_AUTOSAMPLE +#define INSTRUMENT_DO_NOT_AUTOSAMPLE (1<<1) //Do not automatically sample diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 9efffefce5..3fb149d071 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -465,7 +465,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define EGG_LAYING_MESSAGES list("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.") // list of all null rod weapons -#define HOLY_WEAPONS /obj/item/nullrod, /obj/item/twohanded/dualsaber/hypereutactic/chaplain, /obj/item/gun/energy/laser/redtag/hitscan/chaplain, /obj/item/multitool/chaplain, /obj/item/melee/baseball_bat/chaplain +#define HOLY_WEAPONS /obj/item/nullrod, /obj/item/twohanded/dualsaber/hypereutactic/chaplain, /obj/item/gun/energy/laser/redtag/hitscan/chaplain, /obj/item/multitool/chaplain, /obj/item/clothing/gloves/fingerless/pugilist/chaplain, /obj/item/melee/baseball_bat/chaplain // Used by PDA and cartridge code to reduce repetitiveness of spritesheets #define PDAIMG(what) {""} diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 581bb2bc29..73781154c5 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -20,6 +20,7 @@ #define CHANNEL_HIGHEST_AVAILABLE 1008 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS +#define MAX_INSTRUMENT_CHANNELS (128 * 6) #define SOUND_MINIMUM_PRESSURE 10 #define FALLOFF_SOUNDS 1 diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index a7ffd1c543..9a9133c196 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -99,7 +99,7 @@ #define STATUS_EFFECT_PENIS_ENLARGEMENT /datum/status_effect/chem/penis_enlarger //More applied slowdown, just like the above. #define STATUS_EFFECT_NO_COMBAT_MODE /datum/status_effect/no_combat_mode //Wont allow combat mode and will disable it -#define STATUS_EFFECT_MESMERIZE /datum/status_effect/no_combat_mode/mesmerize //Just reskinned no_combat_mode +#define STATUS_EFFECT_MESMERIZE /datum/status_effect/mesmerize //Just reskinned no_combat_mode #define STATUS_EFFECT_ELECTROSTAFF /datum/status_effect/electrostaff //slows down victim diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index a693b26b20..5aa0301bf6 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -50,17 +50,19 @@ #define INIT_ORDER_PROFILER 100 #define INIT_ORDER_FAIL2TOPIC 99 #define INIT_ORDER_TITLE 98 -#define INIT_ORDER_GARBAGE 97 -#define INIT_ORDER_DBCORE 95 -#define INIT_ORDER_BLACKBOX 94 -#define INIT_ORDER_SERVER_MAINT 93 -#define INIT_ORDER_INPUT 85 +#define INIT_ORDER_GARBAGE 95 +#define INIT_ORDER_DBCORE 93 +#define INIT_ORDER_BLACKBOX 92 +#define INIT_ORDER_SERVER_MAINT 91 +#define INIT_ORDER_INPUT 90 +#define INIT_ORDER_SOUNDS 85 #define INIT_ORDER_VIS 80 #define INIT_ORDER_RESEARCH 75 #define INIT_ORDER_EVENTS 70 #define INIT_ORDER_JOBS 65 #define INIT_ORDER_QUIRKS 60 #define INIT_ORDER_TICKER 55 +#define INIT_ORDER_INSTRUMENTS 53 #define INIT_ORDER_MAPPING 50 #define INIT_ORDER_NETWORKS 45 #define INIT_ORDER_ATOMS 30 @@ -100,6 +102,7 @@ #define FIRE_PRIORITY_PROCESS 25 #define FIRE_PRIORITY_THROWING 25 #define FIRE_PRIORITY_SPACEDRIFT 30 +#define FIRE_PRIORITY_INSTRUMENTS 30 #define FIRE_PRIORITY_FIELDS 30 #define FIRE_PRIOTITY_SMOOTHING 35 #define FIRE_PRIORITY_NETWORKS 40 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 476d99fd9b..e70e0ba743 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -147,6 +147,8 @@ #define TRAIT_NOPULSE "nopulse" // Your heart doesn't beat. #define TRAIT_EXEMPT_HEALTH_EVENTS "exempt-health-events" #define TRAIT_NO_MIDROUND_ANTAG "no-midround-antag" //can't be turned into an antag by random events +#define TRAIT_PUGILIST "pugilist" //This guy punches people for a living +#define TRAIT_KI_VAMPIRE "ki-vampire" //when someone with this trait rolls maximum damage on a punch and stuns the target, they regain some stamina and do clone damage #define TRAIT_PASSTABLE "passtable" // mobility flag traits @@ -240,8 +242,8 @@ #define GHOSTROLE_TRAIT "ghostrole" #define APHRO_TRAIT "aphro" #define BLOODSUCKER_TRAIT "bloodsucker" -#define CLOTHING_TRAIT "clothing" //used for quirky carrygloves #define SHOES_TRAIT "shoes" //inherited from your sweet kicks +#define GLOVE_TRAIT "glove" //inherited by your cool gloves // unique trait sources, still defines #define STATUE_MUTE "statue" @@ -275,6 +277,7 @@ #define LOCKED_HELMET_TRAIT "locked-helmet" #define NINJA_SUIT_TRAIT "ninja-suit" #define ANTI_DROP_IMPLANT_TRAIT "anti-drop-implant" +#define MARTIAL_ARTIST_TRAIT "martial_artist" #define SLEEPING_CARP_TRAIT "sleeping_carp" #define RISING_BASS_TRAIT "rising_bass" #define ABDUCTOR_ANTAGONIST "abductor-antagonist" diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm index 9afae12c30..5f78aeb740 100644 --- a/code/__HELPERS/_cit_helpers.dm +++ b/code/__HELPERS/_cit_helpers.dm @@ -112,7 +112,7 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE) set desc = "Toggles seeing LocalOutOfCharacter chat" prefs.chat_toggles ^= CHAT_LOOC prefs.save_preferences() - src << "You will [(prefs.chat_toggles & CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel." + to_chat(src, "You will [(prefs.chat_toggles & CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel.") SSblackbox.record_feedback("tally", "admin_verb", 1, "TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /mob/living/carbon/proc/has_penis() diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index c95896d853..9b877e8fb0 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -116,4 +116,7 @@ GLOBAL_VAR_INIT(cmp_field, "name") if(a_sign != b_sign) return a_sign - b_sign else - return sorttext(b_name, a_name) \ No newline at end of file + return sorttext(b_name, a_name) + +/proc/cmp_item_block_priority_asc(obj/item/A, obj/item/B) + return A.block_priority - B.block_priority diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 7703b13018..7f5035347c 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -425,8 +425,7 @@ candidates -= M /proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE) - var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility)) - var/list/candidates = eligibility.get_all_ghost_role_eligible() + var/list/candidates = get_all_ghost_role_eligible() return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates) /proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null) @@ -581,4 +580,4 @@ var/area/A = C.area if(GLOB.typecache_powerfailure_safe_areas[A.type]) continue - C.energy_fail(rand(duration_min,duration_max)) \ No newline at end of file + C.energy_fail(rand(duration_min,duration_max)) diff --git a/code/__HELPERS/radiation.dm b/code/__HELPERS/radiation.dm index e082be7d1f..abdfe7e8d3 100644 --- a/code/__HELPERS/radiation.dm +++ b/code/__HELPERS/radiation.dm @@ -25,6 +25,12 @@ /proc/radiation_pulse(atom/source, intensity, range_modifier, log=FALSE, can_contaminate=TRUE) if(!SSradiation.can_fire) return + var/area/A = get_area(source) + var/atom/nested_loc = source.loc + while(nested_loc != A) + if(nested_loc.rad_flags & RAD_PROTECT_CONTENTS) + return + nested_loc = nested_loc.loc for(var/dir in GLOB.cardinals) new /datum/radiation_wave(source, dir, intensity, range_modifier, can_contaminate) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 0a4af736dd..e416396991 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -320,8 +320,10 @@ parts += "[FOURSPACES]Nobody died this shift!" if(istype(SSticker.mode, /datum/game_mode/dynamic)) var/datum/game_mode/dynamic/mode = SSticker.mode + mode.update_playercounts() parts += "[FOURSPACES]Final threat level: [mode.threat_level]" parts += "[FOURSPACES]Final threat: [mode.threat]" + parts += "[FOURSPACES]Average threat: [mode.threat_average]" parts += "[FOURSPACES]Executed rules:" for(var/datum/dynamic_ruleset/rule in mode.executed_rules) parts += "[FOURSPACES][FOURSPACES][rule.ruletype] - [rule.name]: -[rule.cost + rule.scaled_times * rule.scaling_cost] threat" diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index ec4784bd1f..02cbd6bea5 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -49,6 +49,7 @@ message_admins("[ADMIN_LOOKUPFLW(src)] might be running a modified client! (failed can_see on AI click of [A] (Turf Loc: [ADMIN_VERBOSEJMP(pixel_turf)]))") var/message = "[key_name(src)] might be running a modified client! (failed can_see on AI click of [A] (Turf Loc: [AREACOORD(pixel_turf)]))" log_admin(message) + to_chat(src, "You're experiencing a bug. Reconnect immediately to fix it. Admins have been notified.") if(REALTIMEOFDAY >= chnotify + 9000) chnotify = REALTIMEOFDAY send2irc_adminless_only("NOCHEAT", message) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index f422e073b2..f846c9c79e 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -125,7 +125,7 @@ if(!CHECK_MOBILITY(user, MOBILITY_STAND)) totitemdamage *= 0.5 //CIT CHANGES END HERE - if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration)) + if((user != src) && run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user) & BLOCK_SUCCESS) return FALSE send_item_attack_message(I, user) I.do_stagger_action(src, user) diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index baf2fbcb27..0a60c6e5d3 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -14,6 +14,7 @@ SUBSYSTEM_DEF(persistence) var/list/saved_modes = list(1,2,3) var/list/saved_dynamic_rules = list(list(),list(),list()) var/list/saved_storytellers = list("foo","bar","baz") + var/list/average_dynamic_threat = 50 var/list/saved_maps var/list/saved_trophies = list() var/list/spawned_objects = list() @@ -436,9 +437,10 @@ SUBSYSTEM_DEF(persistence) saved_storytellers[3] = saved_storytellers[2] saved_storytellers[2] = saved_storytellers[1] saved_storytellers[1] = mode.storyteller.name + average_dynamic_threat = (mode.threat_average + average_dynamic_threat) / 2 var/json_file = file("data/RecentStorytellers.json") var/list/file_data = list() - file_data["data"] = saved_storytellers + file_data["data"] = saved_storytellers + average_dynamic_threat fdel(json_file) WRITE_FILE(json_file, json_encode(file_data)) diff --git a/code/controllers/subsystem/processing/chemistry.dm b/code/controllers/subsystem/processing/chemistry.dm index da31d65fb3..64a780c1ad 100644 --- a/code/controllers/subsystem/processing/chemistry.dm +++ b/code/controllers/subsystem/processing/chemistry.dm @@ -1,5 +1,4 @@ PROCESSING_SUBSYSTEM_DEF(chemistry) wait = 5 flags = SS_KEEP_TIMING - - + name = "Chemistry" diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm new file mode 100644 index 0000000000..a4e0d7703f --- /dev/null +++ b/code/controllers/subsystem/processing/instruments.dm @@ -0,0 +1,43 @@ +PROCESSING_SUBSYSTEM_DEF(instruments) + name = "Instruments" + wait = 0.5 + init_order = INIT_ORDER_INSTRUMENTS + flags = SS_KEEP_TIMING + priority = FIRE_PRIORITY_INSTRUMENTS + var/static/list/datum/instrument/instrument_data = list() //id = datum + var/static/list/datum/song/songs = list() + var/static/musician_maxlines = 600 + var/static/musician_maxlinechars = 300 + var/static/musician_hearcheck_mindelay = 5 + var/static/max_instrument_channels = MAX_INSTRUMENT_CHANNELS + var/static/current_instrument_channels = 0 + +/datum/controller/subsystem/processing/instruments/Initialize() + initialize_instrument_data() + return ..() + +/datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S) + songs += S + +/datum/controller/subsystem/processing/instruments/proc/on_song_del(datum/song/S) + songs -= S + +/datum/controller/subsystem/processing/instruments/proc/initialize_instrument_data() + for(var/path in subtypesof(/datum/instrument)) + var/datum/instrument/I = path + if(initial(I.abstract_type) == path) + continue + I = new path + I.Initialize() + instrument_data[I.id || "[I.type]"] = I + CHECK_TICK + +/datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path) + return instrument_data["[id_or_path]"] + +/datum/controller/subsystem/processing/instruments/proc/reserve_instrument_channel(datum/instrument/I) + if(current_instrument_channels > max_instrument_channels) + return + . = SSsounds.reserve_sound_channel(I) + if(!isnull(.)) + current_instrument_channels++ diff --git a/code/controllers/subsystem/processing/status_effects.dm b/code/controllers/subsystem/processing/status_effects.dm index e8984a44cb..57b340b2ce 100644 --- a/code/controllers/subsystem/processing/status_effects.dm +++ b/code/controllers/subsystem/processing/status_effects.dm @@ -1,3 +1,4 @@ PROCESSING_SUBSYSTEM_DEF(status_effects) wait = 1 flags = SS_TICKER + name = "Status Effects" diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm new file mode 100644 index 0000000000..5e7c5e6545 --- /dev/null +++ b/code/controllers/subsystem/sounds.dm @@ -0,0 +1,91 @@ +#define DATUMLESS "NO_DATUM" + +SUBSYSTEM_DEF(sounds) + name = "Sounds" + flags = SS_NO_FIRE + init_order = INIT_ORDER_SOUNDS + var/static/using_channels_max = CHANNEL_HIGHEST_AVAILABLE //BYOND max channels + + // Hey uh these two needs to be initialized fast because the whole "things get deleted before init" thing. + /// Assoc list, "[channel]" = either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel + var/list/using_channels = list() + /// Assoc list datum = list(channel1, channel2, ...) for what channels something reserved. + var/list/using_channels_by_datum = list() + /// List of all available channels with associations set to TRUE for fast lookups/allocation. + var/list/available_channels + +/datum/controller/subsystem/sounds/Initialize() + setup_available_channels() + return ..() + +/datum/controller/subsystem/sounds/proc/setup_available_channels() + available_channels = list() + for(var/i in 1 to using_channels_max) + available_channels[num2text(i)] = TRUE + +/// Removes a channel from using list. +/datum/controller/subsystem/sounds/proc/free_sound_channel(channel) + channel = num2text(channel) + var/using = using_channels[channel] + using_channels -= channel + if(using) + using_channels_by_datum[using] -= channel + if(!length(using_channels_by_datum[using])) + using_channels_by_datum -= using + available_channels[channel] = TRUE + +/// Frees all the channels a datum is using. +/datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D) + var/list/L = using_channels_by_datum[D] + if(!L) + return + for(var/channel in L) + using_channels -= channel + available_channels[channel] = TRUE + using_channels_by_datum -= D + +/// Frees all datumless channels +/datum/controller/subsystem/sounds/proc/free_datumless_channels() + free_datum_channels(DATUMLESS) + +/// NO AUTOMATIC CLEANUP - If you use this, you better manually free it later! Returns an integer for channel. +/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless() + var/channel = random_available_channel_text() + if(!channel) //oh no.. + return FALSE + available_channels -= channel + using_channels[channel] = DATUMLESS + LAZYINITLIST(using_channels_by_datum[DATUMLESS]) + using_channels_by_datum[DATUMLESS] += channel + return text2num(channel) + +/// Reserves a channel for a datum. Automatic cleanup only when the datum is deleted. Returns an integer for channel. +/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D) + if(!D) //i don't like typechecks but someone will fuck it up + CRASH("Attempted to reserve sound channel without datum using the managed proc.") + var/channel = random_available_channel_text() + if(!channel) + return FALSE + available_channels -= channel + using_channels[channel] = D + LAZYINITLIST(using_channels_by_datum[D]) + using_channels_by_datum[D] += channel + return text2num(channel) + +/// Random available channel, returns text. +/datum/controller/subsystem/sounds/proc/random_available_channel_text() + return pick(available_channels) + +/// Random available channel, returns number +/datum/controller/subsystem/sounds/proc/random_available_channel() + return text2num(pick(available_channels)) + +/// If a channel is available +/datum/controller/subsystem/sounds/proc/is_channel_available(channel) + return available_channels[num2text(channel)] + +/// How many channels we have left. +/datum/controller/subsystem/sounds/proc/available_channels_left() + return length(available_channels) + +#undef DATUMLESS diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm index 0d854371aa..87158f021e 100644 --- a/code/controllers/subsystem/traumas.dm +++ b/code/controllers/subsystem/traumas.dm @@ -172,7 +172,7 @@ SUBSYSTEM_DEF(traumas) /obj/item/ammo_box/magazine/pistolm9mm, /obj/item/ammo_box/a357, /obj/item/ammo_box/magazine/m12g, /obj/item/ammo_box/magazine/mm195x129, /obj/item/antag_spawner/nuke_ops, /obj/mecha/combat/gygax/dark, /obj/mecha/combat/marauder/mauler, /obj/item/soap/syndie, /obj/item/gun/syringe/syndicate, /obj/item/cartridge/virus/syndicate, /obj/item/cartridge/virus/frame, /obj/item/chameleon, /obj/item/storage/box/syndie_kit/cutouts, /obj/item/clothing/suit/space/hardsuit/syndi, /obj/item/card/emag, /obj/item/storage/toolbox/syndicate, /obj/item/storage/book/bible/syndicate, /obj/item/encryptionkey/binary, /obj/item/encryptionkey/syndicate, /obj/item/aiModule/syndicate, /obj/item/clothing/shoes/magboots/syndie, /obj/item/powersink, /obj/item/sbeacondrop, /obj/item/sbeacondrop/bomb, /obj/item/syndicatedetonator, /obj/item/shield/energy, /obj/item/assault_pod, /obj/item/slimepotion/slime/sentience/nuclear, /obj/item/stack/telecrystal, /obj/item/jammer, /obj/item/codespeak_manual/unlimited, - /obj/item/toy/cards/deck/syndicate, /obj/item/storage/secure/briefcase/syndie, /obj/item/storage/fancy/cigarettes/cigpack_syndicate, /obj/item/toy/syndicateballoon, /obj/item/clothing/gloves/rapid, /obj/item/paper/fluff/ruins/thederelict/syndie_mission, /obj/item/organ/cyberimp/eyes/hud/security/syndicate, /obj/item/clothing/head/HoS/syndicate, + /obj/item/toy/cards/deck/syndicate, /obj/item/storage/secure/briefcase/syndie, /obj/item/storage/fancy/cigarettes/cigpack_syndicate, /obj/item/toy/syndicateballoon, /obj/item/clothing/gloves/fingerless/pugilist/rapid, /obj/item/paper/fluff/ruins/thederelict/syndie_mission, /obj/item/organ/cyberimp/eyes/hud/security/syndicate, /obj/item/clothing/head/HoS/syndicate, /obj/machinery/computer/pod/old/syndicate, /obj/machinery/vending/medical/syndicate_access, /obj/item/mmi/syndie, /obj/item/target/syndicate, /obj/machinery/vending/cigarette/syndicate, /obj/item/robot_module/syndicate, /obj/item/clothing/mask/gas/syndicate, /obj/machinery/power/singularity_beacon/syndicate, /obj/item/clothing/head/syndicatefake, /obj/item/radio/headset/syndicate, /obj/item/gun/ballistic/automatic/pistol/antitank/syndicate, /obj/item/pda/syndicate, /obj/item/clothing/suit/armor/vest/capcarapace/syndicate, /obj/item/gun/ballistic/automatic/flechette, /obj/item/ammo_box/magazine/flechette, /obj/item/clothing/suit/toggle/lawyer/black/syndie, /obj/item/melee/transforming/energy/sword/cx/traitor, /obj/structure/sign/poster/contraband/syndicate_pistol, /obj/structure/sign/poster/contraband/syndicate_recruitment, /obj/item/bedsheet/syndie, /obj/item/borg/upgrade/syndicate, /obj/item/tank/jetpack/oxygen/harness, /obj/item/firing_pin/implant/pindicate, /obj/item/reagent_containers/glass/bottle/traitor, /obj/item/storage/belt/military, diff --git a/code/datums/action.dm b/code/datums/action.dm index 74df2bf9a1..6c08a33f33 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -302,16 +302,6 @@ name = "Toggle Friendly Fire \[ON\]" ..() -/datum/action/item_action/synthswitch - name = "Change Synthesizer Instrument" - desc = "Change the type of instrument your synthesizer is playing as." - -/datum/action/item_action/synthswitch/Trigger() - if(istype(target, /obj/item/instrument/piano_synth)) - var/obj/item/instrument/piano_synth/synth = target - return synth.selectInstrument() - return ..() - /datum/action/item_action/vortex_recall name = "Vortex Recall" desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time.
If the beacon is still attached, will detach it." diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 8b82c1812e..5b1df6cb68 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -39,7 +39,7 @@ //title_image = ntitle_image /datum/browser/proc/add_stylesheet(name, file) - if (istype(name, /datum/asset/spritesheet)) + if(istype(name, /datum/asset/spritesheet)) var/datum/asset/spritesheet/sheet = name stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]" else diff --git a/code/datums/components/crafting/glassware.dm b/code/datums/components/crafting/glassware/glassware.dm similarity index 58% rename from code/datums/components/crafting/glassware.dm rename to code/datums/components/crafting/glassware/glassware.dm index 22d7e01f8a..2b57837c5d 100644 --- a/code/datums/components/crafting/glassware.dm +++ b/code/datums/components/crafting/glassware/glassware.dm @@ -14,26 +14,32 @@ name = "Glass working tools" desc = "A lovely belt of most the tools you will need to shape, mold, and refine glass into more advanced shapes." icon_state = "glass_tools" - tool_behaviour = TOOL_GLASS_CUT + tool_behaviour = TOOL_GLASS_CUT //Cutting takes 20 ticks /obj/item/glasswork/blowing_rod name = "Glass working blow rod" desc = "A hollow metal stick made for glass blowing." icon_state = "blowing_rods_unused" - tool_behaviour = TOOL_BLOW + tool_behaviour = TOOL_BLOW //Rods take 5 ticks -/obj/item/glasswork/glass_base +/obj/item/glasswork/glass_base //Welding takes 30 ticks name = "Glass fodder sheet" desc = "A sheet of glass set aside for glass working" icon_state = "glass_base" var/next_step = null var/rod = /obj/item/glasswork/blowing_rod -/obj/item/lens - name = "Optical lens" - desc = "Good for selling or crafting, by itself its useless" - icon = 'icons/obj/chemical.dmi' - icon_state = "glass_optics" +/obj/item/tea_plate + name = "Tea Plate" + desc = "A polished plate for a tea cup. How fancy!" + icon = 'icons/obj/glass_ware.dmi' + icon_state = "tea_plate" + +/obj/item/tea_cup + name = "Tea Cup" + desc = "A glass cup made for fake tea!" + icon = 'icons/obj/glass_ware.dmi' + icon_state = "tea_plate" //////////////////////Chem Disk///////////////////// //Two Steps // @@ -49,8 +55,9 @@ /obj/item/glasswork/glass_base/dish/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/dish_part1 name = "Half chem dish sheet" @@ -61,8 +68,9 @@ /obj/item/glasswork/glass_base/dish_part1/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + qdel(src) //////////////////////Lens////////////////////////// //Six Steps // @@ -78,8 +86,9 @@ /obj/item/glasswork/glass_base/glass_lens/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/glass_lens_part1 name = "Glass fodder sheet" @@ -90,8 +99,9 @@ /obj/item/glasswork/glass_base/glass_lens_part1/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_WELDER) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,30, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/glass_lens_part2 name = "Glass fodder sheet" @@ -102,8 +112,9 @@ /obj/item/glasswork/glass_base/glass_lens_part2/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_WELDER) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,30, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/glass_lens_part3 name = "Glass fodder sheet" @@ -114,9 +125,10 @@ /obj/item/glasswork/glass_base/glass_lens_part3/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_BLOW) - new next_step(user.loc, 1) - qdel(src) - qdel(I) + if(do_after(user,5, target = src)) + new next_step(user.loc, 1) + qdel(src) + qdel(I) /obj/item/glasswork/glass_base/glass_lens_part4 name = "Glass fodder sheet" @@ -127,29 +139,31 @@ /obj/item/glasswork/glass_base/glass_lens_part4/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - new rod(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + new rod(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/glass_lens_part5 name = "Unpolished glass lens" - desc = "A small unpolished glass lens. Could be polished with some cloth." - icon = 'icons/obj/chemical.dmi' + desc = "A small unpolished glass lens. Could be polished with some silk." + icon = 'icons/obj/glass_ware.dmi' icon_state = "glass_optics" next_step = /obj/item/glasswork/glass_base/glass_lens_part6 /obj/item/glasswork/glass_base/glass_lens_part5/attackby(obj/item/I, mob/user, params) ..() - if(istype(I, /obj/item/stack/sheet/cloth)) - new next_step(user.loc, 1) - qdel(src) + if(istype(I, /obj/item/stack/sheet/silk)) + if(do_after(user,10, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/glass_lens_part6 name = "Unrefined glass lens" desc = "A small polished glass lens. Just needs to be refined with some sandstone." - icon = 'icons/obj/chemical.dmi' + icon = 'icons/obj/glass_ware.dmi' icon_state = "glass_optics" - next_step = /obj/item/lens + next_step = /obj/item/glasswork/glass_base/lens /obj/item/glasswork/glass_base/glass_lens_part6/attackby(obj/item/I, mob/user, params) ..() @@ -171,8 +185,9 @@ /obj/item/glasswork/glass_base/spouty/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/spouty_part2 name = "Glass fodder sheet" @@ -183,8 +198,9 @@ /obj/item/glasswork/glass_base/spouty_part2/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_WELDER) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,30, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/spouty_part3 name = "Glass fodder sheet" @@ -195,9 +211,10 @@ /obj/item/glasswork/glass_base/spouty_part3/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_BLOW) - new next_step(user.loc, 1) - qdel(src) - qdel(I) + if(do_after(user,5, target = src)) + new next_step(user.loc, 1) + qdel(src) + qdel(I) /obj/item/glasswork/glass_base/spouty_part4 name = "Glass fodder sheet" @@ -208,9 +225,10 @@ /obj/item/glasswork/glass_base/spouty_part4/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - new rod(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + new rod(user.loc, 1) + qdel(src) //////////////////////Small Bulb Flask////////////// //Two Steps // @@ -226,8 +244,9 @@ /obj/item/glasswork/glass_base/flask_small/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_WELDER) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,30, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/flask_small_part1 name = "Metled glass" @@ -238,9 +257,10 @@ /obj/item/glasswork/glass_base/flask_small_part1/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_BLOW) - new next_step(user.loc, 1) - qdel(src) - qdel(I) + if(do_after(user,5, target = src)) + new next_step(user.loc, 1) + qdel(src) + qdel(I) /obj/item/glasswork/glass_base/flask_small_part2 name = "Metled glass" @@ -251,9 +271,10 @@ /obj/item/glasswork/glass_base/flask_small_part2/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - new rod(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + new rod(user.loc, 1) + qdel(src) //////////////////////Large Bulb Flask////////////// //Two Steps // @@ -269,8 +290,9 @@ /obj/item/glasswork/glass_base/flask_large/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_WELDER) - new next_step(user.loc, 1) - qdel(src) + if(do_after(user,30, target = src)) + new next_step(user.loc, 1) + qdel(src) /obj/item/glasswork/glass_base/flask_large_part1 name = "Metled glass" @@ -281,9 +303,10 @@ /obj/item/glasswork/glass_base/flask_large_part1/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_BLOW) - new next_step(user.loc, 1) - qdel(src) - qdel(I) + if(do_after(user,5, target = src)) + new next_step(user.loc, 1) + qdel(src) + qdel(I) /obj/item/glasswork/glass_base/flask_large_part2 name = "Metled glass" @@ -294,6 +317,138 @@ /obj/item/glasswork/glass_base/flask_large_part2/attackby(obj/item/I, mob/user, params) ..() if(I.tool_behaviour == TOOL_GLASS_CUT) - new next_step(user.loc, 1) - new rod(user.loc, 1) - qdel(src) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + new rod(user.loc, 1) + qdel(src) + +//////////////////////Tea Plates//////////////////// +//Three Steps // +//Sells for 1200 cr, takes 5 glass shets // +//Usefull for selling and chemical things // +//////////////////////////////////////////////////// + +/obj/item/glasswork/glass_base/tea_plate + name = "Glass fodder sheet" + desc = "A set of glass sheets set aside for glass working, this one is ideal for a tea plate, how fancy! Needs to be heated with some tools." + next_step = /obj/item/glasswork/glass_base/tea_plate1 + +/obj/item/glasswork/glass_base/tea_plate/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_WELDER) + if(do_after(user,30, target = src)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/tea_plate1 + name = "Metled glass" + desc = "A blob of metled glass, this one is ideal for a tea plate. Needs to be blown with some tools." + icon_state = "glass_base_molding" + next_step = /obj/item/glasswork/glass_base/tea_plate2 + +/obj/item/glasswork/glass_base/tea_plate1/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_BLOW) + if(do_after(user,5, target = src)) + new next_step(user.loc, 1) + qdel(src) + qdel(I) + +/obj/item/glasswork/glass_base/tea_plate2 + name = "Metled glass" + desc = "A blob of metled glass on the end of a blowing rod. Needs to be cut off with some tools." + icon_state = "blowing_rods_inuse" + next_step = /obj/item/glasswork/glass_base/tea_plate3 + +/obj/item/glasswork/glass_base/tea_plate2/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_GLASS_CUT) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + new rod(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/tea_plate3 + name = "Disk of glass" + desc = "A disk of glass that can be cant be used for much. Needs to be polished with some silk." + icon_state = "glass_base_half" + next_step = /obj/item/tea_plate + +/obj/item/glasswork/glass_base/tea_plate3/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/stack/sheet/silk)) + if(do_after(user,10, target = src)) + new next_step(user.loc, 1) + qdel(src) + +//////////////////////Tea Cup/////////////////////// +//Four Steps // +//Sells for 1800 cr, takes 6 glass shets // +//Usefull for selling and chemical things // +//////////////////////////////////////////////////// + +/obj/item/glasswork/glass_base/tea_cup + name = "Glass fodder sheet" + desc = "A set of glass sheets set aside for glass working, this one is ideal for a tea cup, how fancy! Needs to be heated with some tools." + next_step = /obj/item/glasswork/glass_base/tea_cup1 + +/obj/item/glasswork/glass_base/tea_cup/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_WELDER) + if(do_after(user,30, target = src)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/tea_cup1 + name = "Metled glass" + desc = "A blob of metled glass, this one is ideal for a tea cup. Needs to be blown with some tools." + icon_state = "glass_base_molding" + next_step = /obj/item/glasswork/glass_base/tea_cup2 + +/obj/item/glasswork/glass_base/tea_cup1/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_BLOW) + if(do_after(user,5, target = src)) + new next_step(user.loc, 1) + qdel(src) + qdel(I) + +/obj/item/glasswork/glass_base/tea_cupe2 + name = "Metled glass" + desc = "A blob of metled glass on the end of a blowing rod. Needs to be cut off with some tools." + icon_state = "blowing_rods_inuse" + next_step = /obj/item/glasswork/glass_base/tea_cup3 + +/obj/item/glasswork/glass_base/tea_cup2/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_GLASS_CUT) + if(do_after(user,20, target = src)) + new next_step(user.loc, 1) + new rod(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/tea_cup3 + name = "Disk of glass" + desc = "A bowl of glass that can be cant be used for much. Needs to be polished with some silk." + icon_state = "glass_base_half" + next_step = /obj/item/glasswork/glass_base/tea_cup4 + +/obj/item/glasswork/glass_base/cup3/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/stack/sheet/silk)) + if(do_after(user,10, target = src)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/tea_cup4 + name = "Disk of glass" + desc = "A bowl of polished glass that can be cant be used for much. Needs some more glass to make a handle." + icon_state = "glass_base_half" + next_step = /obj/item/tea_cup + +/obj/item/glasswork/glass_base/cup4/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/stack/sheet/glass)) + if(do_after(user,10, target = src)) + new next_step(user.loc, 1) + qdel(src) \ No newline at end of file diff --git a/code/datums/components/crafting/glassware/lens_crafting.dm b/code/datums/components/crafting/glassware/lens_crafting.dm new file mode 100644 index 0000000000..117df25a30 --- /dev/null +++ b/code/datums/components/crafting/glassware/lens_crafting.dm @@ -0,0 +1,98 @@ +//This file is for crafting using a lens! + +/obj/item/glasswork/glass_base/lens + name = "Optical lens" + desc = "Good for selling or crafting, by itself its useless" + icon = 'icons/obj/glass_ware.dmi' + icon_state = "glass_optics" + +//Laser pointers - 2600 +/obj/item/glasswork/glass_base/laserpointer_shell + name = "Laser pointer assembly" + desc = "Good for selling or crafting, by itself its useless. Needs a power capactor." + icon_state = "laser_case" + icon = 'icons/obj/glass_ware.dmi' + next_step = /obj/item/glasswork/glass_base/laserpointer_shell_1 + +/obj/item/glasswork/glass_base/laserpointer_shell/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/stock_parts/capacitor)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/laserpointer_shell_1 + name = "Laser pointer assembly" + desc = "Good for selling or crafting, by itself its useless. Needs a glass lens." + icon_state = "laser_wire" + icon_state = "laser_case" + next_step = /obj/item/glasswork/glass_base/laserpointer_shell_2 + +/obj/item/glasswork/glass_base/laserpointer_shell_1/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/glasswork/glass_base/lens)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/laserpointer_shell_2 + name = "Laser pointer assembly" + desc = "Good for selling or crafting, by itself its useless. Needs to be screwed together." + icon_state = "laser_wire" + icon_state = "laser_case" + next_step = /obj/item/laser_pointer/blue/handmade + +/obj/item/glasswork/glass_base/laserpointer_shell_2/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_SCREWDRIVER) + if(do_after(user,260, target = src)) + new next_step(user.loc, 1) + qdel(src) + +//NERD SHIT - 5000 + +/obj/item/glasswork/glass_base/glasses_frame + name = "Glasses Frame" + desc = "Good for crafting a pare of glasses, by itself its useless. Just add a pare of lens." + icon = 'icons/obj/glass_ware.dmi' + icon_state = "frames" + next_step = /obj/item/glasswork/glass_base/glasses_frame_1 + +/obj/item/glasswork/glass_base/glasses_frame/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/glasswork/glass_base/lens)) + if(do_after(user,60, target = src)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/glasses_frame_1 + name = "Glasses Frame" + desc = "Good for crafting a pare of glasses, by itself its useless. Just add a the other lens." + icon = 'icons/obj/glass_ware.dmi' + icon_state = "frames_1" + next_step = /obj/item/glasswork/glass_base/glasses_frame_2 + +/obj/item/glasswork/glass_base/glasses_frame_1/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/glasswork/glass_base/lens)) + if(do_after(user,60, target = src)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glass_base/glasses_frame_2 + name = "Glasses Frame" + desc = "Good for crafting a pare of glasses, by itself its useless. Just adjust the pices into the frame with a screwdriver." + icon = 'icons/obj/glass_ware.dmi' + icon_state = "frames_2" + next_step = /obj/item/glasswork/glasses + +/obj/item/glasswork/glass_base/glasses_frame_2/attackby(obj/item/I, mob/user, params) + ..() + if(I.tool_behaviour == TOOL_SCREWDRIVER) + if(do_after(user,180, target = src)) + new next_step(user.loc, 1) + qdel(src) + +/obj/item/glasswork/glasses + name = "Hand Made Glasses" + desc = "Hande made glasses that have not been polished at all making them useless. Selling them could still be worth a bit of credits." + icon = 'icons/obj/glass_ware.dmi' + icon_state = "frames_2" \ No newline at end of file diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index e73465d785..dde8961482 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -27,6 +27,8 @@ GLOBAL_LIST_EMPTY(uplinks) var/datum/ui_state/checkstate var/compact_mode = FALSE var/debug = FALSE + var/saved_player_population = 0 + var/list/filters = list() /datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate, datum/traitor_class/traitor_class) if(!isitem(parent)) @@ -47,7 +49,6 @@ GLOBAL_LIST_EMPTY(uplinks) RegisterSignal(parent, COMSIG_PEN_ROTATED, .proc/pen_rotation) GLOB.uplinks += src - var/list/filters = list() if(istype(traitor_class)) filters = traitor_class.uplink_filters starting_tc = traitor_class.TC @@ -68,6 +69,7 @@ GLOBAL_LIST_EMPTY(uplinks) if(!lockable) active = TRUE locked = FALSE + saved_player_population = GLOB.joined_player_list.len /datum/component/uplink/InheritComponent(datum/component/uplink/U) lockable |= U.lockable @@ -117,7 +119,18 @@ GLOBAL_LIST_EMPTY(uplinks) return active = TRUE if(user) + //update the saved population + var/previous_player_population = saved_player_population + saved_player_population = GLOB.joined_player_list.len + //if population has changed, update uplink items + if(saved_player_population != previous_player_population) + //make sure discounts are not rerolled + var/old_discounts = uplink_items["Discounted Gear"] + uplink_items = get_uplink_items(gamemode, FALSE, allow_restricted, filters) + if(old_discounts) + uplink_items["Discounted Gear"] = old_discounts ui_interact(user) + // an unlocked uplink blocks also opening the PDA or headset menu return COMPONENT_NO_INTERACT @@ -190,7 +203,9 @@ GLOBAL_LIST_EMPTY(uplinks) if(item in buyable_items) var/datum/uplink_item/I = buyable_items[item] - MakePurchase(usr, I) + //check to make sure people cannot buy items when the player pop is below the requirement + if(GLOB.joined_player_list.len >= I.player_minimum) + MakePurchase(usr, I) . = TRUE if("lock") active = FALSE diff --git a/code/datums/components/wearertargeting.dm b/code/datums/components/wearertargeting.dm index feaa88f934..4760757701 100644 --- a/code/datums/components/wearertargeting.dm +++ b/code/datums/components/wearertargeting.dm @@ -19,4 +19,4 @@ UnregisterSignal(equipper, signals) /datum/component/wearertargeting/proc/on_drop(datum/source, mob/user) - UnregisterSignal(user, signals) \ No newline at end of file + UnregisterSignal(user, signals) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index a856f2392d..d7ff5db26a 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -109,6 +109,8 @@ UnregisterSignal(target, signal_procs[target]) //END: ECS SHIT + SSsounds.free_datum_channels(src) + return QDEL_HINT_QUEUE #ifdef DATUMVAR_DEBUGGING_MODE diff --git a/code/datums/elements/ghost_role_eligibility.dm b/code/datums/elements/ghost_role_eligibility.dm index 81fd593d5f..4e7884efe4 100644 --- a/code/datums/elements/ghost_role_eligibility.dm +++ b/code/datums/elements/ghost_role_eligibility.dm @@ -1,67 +1,48 @@ +GLOBAL_LIST_EMPTY(ghost_eligible_mobs) + +GLOBAL_LIST_EMPTY(client_ghost_timeouts) + /datum/element/ghost_role_eligibility element_flags = ELEMENT_DETACH | ELEMENT_BESPOKE - id_arg_index = 3 - var/list/timeouts = list() - var/list/mob/eligible_mobs = list() + id_arg_index = 2 var/penalizing = FALSE var/free_ghost = FALSE -/datum/element/ghost_role_eligibility/Attach(datum/target,penalize = FALSE,free_ghosting = FALSE, penalize_on_ghost = FALSE) +/datum/element/ghost_role_eligibility/Attach(datum/target,free_ghosting = FALSE, penalize_on_ghost = FALSE) . = ..() if(!ismob(target)) return ELEMENT_INCOMPATIBLE penalizing = penalize_on_ghost free_ghost = free_ghosting var/mob/M = target - if(!(M in eligible_mobs)) - eligible_mobs += M + if(!(M in GLOB.ghost_eligible_mobs)) + GLOB.ghost_eligible_mobs += M RegisterSignal(M, COMSIG_MOB_GHOSTIZE, .proc/get_ghost_flags) - if(penalize) //penalizing them from making a ghost role / midround antag comeback right away. - var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES - var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES - if(world.time < roundstart_quit_limit) //add up the time difference to their antag rolling penalty if they quit before half a (ingame) hour even passed. - penalty += roundstart_quit_limit - world.time - if(penalty) - penalty += world.realtime - if(SSautotransfer.can_fire && SSautotransfer.maxvotes) - var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes - if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime) - penalty = CANT_REENTER_ROUND - if(!(M.ckey in timeouts)) - timeouts += M.ckey - timeouts[M.ckey] = 0 - else if(timeouts[M.ckey] == CANT_REENTER_ROUND) - return - timeouts[M.ckey] = max(timeouts[M.ckey],penalty) /datum/element/ghost_role_eligibility/Detach(mob/M) . = ..() - if(M in eligible_mobs) - eligible_mobs -= M + if(M in GLOB.ghost_eligible_mobs) + GLOB.ghost_eligible_mobs -= M UnregisterSignal(M, COMSIG_MOB_GHOSTIZE) -/datum/element/ghost_role_eligibility/proc/get_all_ghost_role_eligible(silent = FALSE) +/proc/get_all_ghost_role_eligible(silent = FALSE) var/list/candidates = list() - for(var/m in eligible_mobs) + for(var/m in GLOB.ghost_eligible_mobs) var/mob/M = m if(M.can_reenter_round(TRUE)) candidates += M return candidates /mob/proc/can_reenter_round(silent = FALSE) - var/datum/element/ghost_role_eligibility/eli = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility)) - return eli.can_reenter_round(src,silent) - -/datum/element/ghost_role_eligibility/proc/can_reenter_round(var/mob/M,silent = FALSE) - if(!(M in eligible_mobs)) + if(!(src in GLOB.ghost_eligible_mobs)) return FALSE - if(!(M.ckey in timeouts)) + if(!(ckey in GLOB.client_ghost_timeouts)) return TRUE - var/timeout = timeouts[M.ckey] + var/timeout = GLOB.client_ghost_timeouts[ckey] if(timeout != CANT_REENTER_ROUND && timeout <= world.realtime) return TRUE - if(!silent && M.client) - to_chat(M, "You are unable to reenter the round[timeout != CANT_REENTER_ROUND ? " yet. Your ghost role blacklist will expire in [DisplayTimeText(timeout - world.realtime)]" : ""].") + if(!silent && client) + to_chat(src, "You are unable to reenter the round[timeout != CANT_REENTER_ROUND ? " yet. Your ghost role blacklist will expire in [DisplayTimeText(timeout - world.realtime)]" : ""].") return FALSE /datum/element/ghost_role_eligibility/proc/get_ghost_flags() diff --git a/code/datums/elements/spellcasting.dm b/code/datums/elements/spellcasting.dm index b325fd3a3a..a917108bf1 100644 --- a/code/datums/elements/spellcasting.dm +++ b/code/datums/elements/spellcasting.dm @@ -4,6 +4,7 @@ var/cast_flags var/cast_slots var/list/users_by_item = list() + var/list/stacked_spellcasting_by_user = list() /datum/element/spellcasting/Attach(datum/target, _flags, _slots) . = ..() @@ -12,6 +13,7 @@ RegisterSignal(target, COMSIG_ITEM_DROPPED, .proc/on_drop) else if(ismob(target)) RegisterSignal(target, COMSIG_MOB_SPELL_CAST_CHECK, .proc/on_cast) + stacked_spellcasting_by_user[target]++ else return ELEMENT_INCOMPATIBLE cast_flags = _flags @@ -22,16 +24,31 @@ UnregisterSignal(target, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_MOB_SPELL_CAST_CHECK)) if(users_by_item[target]) var/mob/user = users_by_item[target] - UnregisterSignal(user, COMSIG_MOB_SPELL_CAST_CHECK) + stacked_spellcasting_by_user[user]-- + if(!stacked_spellcasting_by_user[user]) + stacked_spellcasting_by_user -= user + UnregisterSignal(user, COMSIG_MOB_SPELL_CAST_CHECK) + else if(ismob(target)) + stacked_spellcasting_by_user[target]-- + if(!stacked_spellcasting_by_user[target]) + stacked_spellcasting_by_user -= target /datum/element/spellcasting/proc/on_equip(datum/source, mob/equipper, slot) - if(slot in cast_slots) + if(!(slot in cast_slots)) + return + users_by_item[source] = equipper + if(!stacked_spellcasting_by_user[equipper]) RegisterSignal(equipper, COMSIG_MOB_SPELL_CAST_CHECK, .proc/on_cast) - users_by_item[source] = equipper + stacked_spellcasting_by_user[equipper]++ /datum/element/spellcasting/proc/on_drop(datum/source, mob/user) - UnregisterSignal(user, COMSIG_MOB_SPELL_CAST_CHECK) + if(!users_by_item[source]) + return users_by_item -= source + stacked_spellcasting_by_user[user]-- + if(!stacked_spellcasting_by_user[user]) + stacked_spellcasting_by_user -= user + UnregisterSignal(user, COMSIG_MOB_SPELL_CAST_CHECK) /datum/element/spellcasting/proc/on_cast(mob/caster, obj/effect/proc_holder/spell) return cast_flags diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index 49942976ce..bafb6fbf0e 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -73,7 +73,7 @@ var/list/atoms_cache = output_atoms var/sound/S = sound(soundfile) if(direct) - S.channel = open_sound_channel() + S.channel = SSsounds.random_available_channel() S.volume = volume for(var/i in 1 to atoms_cache.len) var/atom/thing = atoms_cache[i] diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm index 7b7975bedb..d8a888871e 100644 --- a/code/datums/martial/_martial.dm +++ b/code/datums/martial/_martial.dm @@ -35,6 +35,17 @@ current_target = new_target streak = "" +/datum/martial_art/proc/damage_roll(mob/living/carbon/human/A, mob/living/carbon/human/D) + //Here we roll for our damage to be added into the damage var in the various attack procs. This is changed depending on whether we are in combat mode, lying down, or if our target is in combat mode. + var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh) + if(!(D.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) + damage *= 1.5 + if(!CHECK_MOBILITY(A, MOBILITY_STAND)) + damage *= 0.5 + if(!(A.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) + damage *= 0.25 + return damage + /datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE) if(!istype(H) || !H.mind) return FALSE @@ -50,6 +61,7 @@ if(help_verb) H.verbs += help_verb H.mind.martial_art = src + ADD_TRAIT(H, TRAIT_PUGILIST, MARTIAL_ARTIST_TRAIT) return TRUE /datum/martial_art/proc/store(datum/martial_art/M,mob/living/carbon/human/H) @@ -68,7 +80,8 @@ else var/datum/martial_art/X = H.mind.default_martial_art X.teach(H) - + REMOVE_TRAIT(H, TRAIT_PUGILIST, MARTIAL_ARTIST_TRAIT) + /datum/martial_art/proc/on_remove(mob/living/carbon/human/H) if(help_verb) H.verbs -= help_verb diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm index 16d61c29f9..93d70c12cc 100644 --- a/code/datums/martial/boxing.dm +++ b/code/datums/martial/boxing.dm @@ -16,16 +16,13 @@ A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) var/atk_verb = pick("left hook","right hook","straight punch") - - var/damage = rand(10, 13) - var/extra_damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh) + var/extra_damage = damage_roll(A,D) if(extra_damage == A.dna.species.punchdamagelow) playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1) D.visible_message("[A] has attempted to [atk_verb] [D]!", \ "[A] has attempted to [atk_verb] [D]!", null, COMBAT_MESSAGE_RANGE) log_combat(A, D, "attempted to hit", atk_verb) return TRUE - damage += extra_damage var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) var/armor_block = D.run_armor_check(affecting, "melee") @@ -35,7 +32,7 @@ D.visible_message("[A] has [atk_verb]ed [D]!", \ "[A] has [atk_verb]ed [D]!", null, COMBAT_MESSAGE_RANGE) - D.apply_damage(damage, STAMINA, affecting, armor_block) + D.apply_damage(rand(10,13) + extra_damage, STAMINA, affecting, armor_block) log_combat(A, D, "punched (boxing) ") if(D.getStaminaLoss() > 100 && istype(D.mind?.martial_art, /datum/martial_art/boxing)) var/knockout_prob = (D.getStaminaLoss() + rand(-15,15))*0.75 diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm index 0871cf7eaa..39fac11129 100644 --- a/code/datums/martial/cqc.dm +++ b/code/datums/martial/cqc.dm @@ -42,11 +42,12 @@ /datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D) if(!can_use(A)) return FALSE + var/damage = (damage_roll(A,D) + 5) if(CHECK_MOBILITY(D, MOBILITY_STAND)) D.visible_message("[A] slams [D] into the ground!", \ "[A] slams you into the ground!") playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1) - D.apply_damage(10, BRUTE) + D.apply_damage(damage, BRUTE) D.DefaultCombatKnockdown(120) log_combat(A, D, "slammed (CQC)") return TRUE @@ -54,29 +55,33 @@ /datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D) if(!can_use(A)) return FALSE - if(CHECK_MOBILITY(D, MOBILITY_STAND)) - D.visible_message("[A] kicks [D] back!", \ - "[A] kicks you back!") - playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) - var/atom/throw_target = get_edge_target_turf(D, A.dir) - D.throw_at(throw_target, 1, 14, A) - D.apply_damage(10, BRUTE) - log_combat(A, D, "kicked (CQC)") + var/damage = damage_roll(A,D) if(!CHECK_MOBILITY(D, MOBILITY_STAND) && CHECK_MOBILITY(D, MOBILITY_USE)) log_combat(A, D, "knocked out (Head kick)(CQC)") D.visible_message("[A] kicks [D]'s head, knocking [D.p_them()] out!", \ "[A] kicks your head, knocking you out!") playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1) D.SetSleeping(300) - D.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 150) + D.apply_damage(damage + 5, BRUTE) + var/atom/throw_target = get_edge_target_turf(D, A.dir) + D.throw_at(throw_target, 1, 14, A) + D.adjustOrganLoss(ORGAN_SLOT_BRAIN, damage + 10, 150) + else + D.visible_message("[A] kicks [D]!", \ + "[A] kicks you!") + playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + D.Dizzy(damage) + D.apply_damage(damage + 15, BRUTE) + log_combat(A, D, "kicked (CQC)") return TRUE /datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D) if(!can_use(A)) return FALSE + var/damage = (damage_roll(A,D) + 55) log_combat(A, D, "pressured (CQC)") D.visible_message("[A] punches [D]'s neck!") - D.adjustStaminaLoss(60) + D.apply_damage(damage, STAMINA) playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) return TRUE @@ -85,11 +90,12 @@ return if(!can_use(A)) return FALSE + var/damage = (damage_roll(A,D) + 15) if(!D.stat) log_combat(A, D, "restrained (CQC)") D.visible_message("[A] locks [D] into a restraining position!", \ "[A] locks you into a restraining position!") - D.adjustStaminaLoss(20) + D.apply_damage(damage, STAMINA) D.Stun(100) restraining = TRUE addtimer(VARSET_CALLBACK(src, restraining, FALSE), 50, TIMER_UNIQUE) @@ -98,6 +104,7 @@ /datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D) if(!can_use(A)) return FALSE + var/damage = damage_roll(A,D) if(!D.stat) log_combat(A, D, "consecutive CQC'd (CQC)") D.visible_message("[A] strikes [D]'s abdomen, neck and back consecutively", \ @@ -106,8 +113,8 @@ var/obj/item/I = D.get_active_held_item() if(I && D.temporarilyRemoveItemFromInventory(I)) A.put_in_hands(I) - D.adjustStaminaLoss(50) - D.apply_damage(25, BRUTE) + D.apply_damage(damage + 45, STAMINA) + D.apply_damage(damage + 20, BRUTE) return TRUE /datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) @@ -135,7 +142,7 @@ log_combat(A, D, "attacked (CQC)") A.do_attack_animation(D) var/picked_hit_type = pick("CQC'd", "Big Bossed") - var/bonus_damage = 13 + var/bonus_damage = (damage_roll(A,D) + 7) if(!CHECK_MOBILITY(D, MOBILITY_STAND)) bonus_damage += 5 picked_hit_type = "stomps on" @@ -151,7 +158,7 @@ D.visible_message("[A] leg sweeps [D]!", \ "[A] leg sweeps you!") playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1) - D.apply_damage(10, BRUTE) + D.apply_damage(bonus_damage, BRUTE) D.DefaultCombatKnockdown(60) log_combat(A, D, "sweeped (CQC)") return TRUE @@ -161,6 +168,7 @@ return FALSE add_to_streak("D",D) var/obj/item/I = null + var/damage = (damage_roll(A,D)*0.5) if(check_streak(A,D)) return TRUE if(prob(65)) @@ -172,7 +180,7 @@ if(I && D.temporarilyRemoveItemFromInventory(I)) A.put_in_hands(I) D.Jitter(2) - D.apply_damage(5, BRUTE) + D.apply_damage(damage, BRUTE) else D.visible_message("[A] attempted to disarm [D]!", \ "[A] attempted to disarm [D]!") diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm index 1a244f911e..8ea1c2efca 100644 --- a/code/datums/martial/krav_maga.dm +++ b/code/datums/martial/krav_maga.dm @@ -84,64 +84,71 @@ if("neck_chop") streak = "" neck_chop(A,D) - return 1 + return TRUE if("leg_sweep") streak = "" leg_sweep(A,D) - return 1 + return TRUE if("quick_choke")//is actually lung punch streak = "" quick_choke(A,D) - return 1 - return 0 + return TRUE + return FALSE /datum/martial_art/krav_maga/proc/leg_sweep(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/obj/item/bodypart/affecting = D.get_bodypart(BODY_ZONE_CHEST) + var/armor_block = D.run_armor_check(affecting, "melee") + var/damage = damage_roll(A,D) if(!CHECK_MOBILITY(D, MOBILITY_STAND)) - return 0 + return FALSE D.visible_message("[A] leg sweeps [D]!", \ "[A] leg sweeps you!") playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1) - D.apply_damage(5, BRUTE) - D.DefaultCombatKnockdown(40, override_hardstun = 0.01, override_stamdmg = 25) + D.apply_damage(damage + 25, STAMINA, affecting, armor_block) + D.DefaultCombatKnockdown(80, override_hardstun = 1, override_stamdmg = 0) log_combat(A, D, "leg sweeped") - return 1 + return TRUE /datum/martial_art/krav_maga/proc/quick_choke(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)//is actually lung punch + var/damage = damage_roll(A,D) D.visible_message("[A] pounds [D] on the chest!", \ "[A] slams your chest! You can't breathe!") playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1) if(D.losebreath <= 10) D.losebreath = CLAMP(D.losebreath + 5, 0, 10) - D.adjustOxyLoss(10) + D.adjustOxyLoss(damage + 5) log_combat(A, D, "quickchoked") - return 1 + return TRUE /datum/martial_art/krav_maga/proc/neck_chop(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) + var/damage = (damage_roll(A,D)*0.5) D.visible_message("[A] karate chops [D]'s neck!", \ "[A] karate chops your neck, rendering you unable to speak!") playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1) - D.apply_damage(5, BRUTE) + D.apply_damage(damage, BRUTE) if(D.silent <= 10) D.silent = CLAMP(D.silent + 10, 0, 10) log_combat(A, D, "neck chopped") - return 1 + return TRUE /datum/martial_art/krav_maga/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) if(check_streak(A,D)) - return 1 + return TRUE log_combat(A, D, "grabbed (Krav Maga)") ..() /datum/martial_art/krav_maga/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/armor_block = D.run_armor_check(affecting, "melee") if(check_streak(A,D)) - return 1 + return TRUE log_combat(A, D, "punched") var/picked_hit_type = pick("punches", "kicks") - var/bonus_damage = 10 + var/bonus_damage = damage_roll(A,D) if(CHECK_MOBILITY(D, MOBILITY_STAND)) - bonus_damage += 5 + bonus_damage += 10 picked_hit_type = "stomps on" - D.apply_damage(bonus_damage, BRUTE) + D.apply_damage(bonus_damage, BRUTE, affecting, armor_block) if(picked_hit_type == "kicks" || picked_hit_type == "stomps on") A.do_attack_animation(D, ATTACK_EFFECT_KICK) playsound(get_turf(D), 'sound/effects/hit_kick.ogg', 50, 1, -1) @@ -151,24 +158,34 @@ D.visible_message("[A] [picked_hit_type] [D]!", \ "[A] [picked_hit_type] you!") log_combat(A, D, "[picked_hit_type] with [name]") - return 1 + return TRUE -/datum/martial_art/krav_maga/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - var/obj/item/I = null - if(prob(60)) - I = D.get_active_held_item() - if(I) - if(D.temporarilyRemoveItemFromInventory(I)) - A.put_in_hands(I) - D.visible_message("[A] has disarmed [D]!", \ - "[A] has disarmed [D]!") - playsound(D, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) +/datum/martial_art/krav_maga/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(check_streak(A,D)) + return TRUE + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/armor_block = D.run_armor_check(affecting, "melee") + var/damage = damage_roll(A,D) + if(D.mobility_flags & MOBILITY_STAND) + D.visible_message("[A] reprimands [D]!", \ + "You're slapped by [A]!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A) + to_chat(A, "You jab [D]!") + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + playsound(D, 'sound/effects/hit_punch.ogg', 50, TRUE, -1) + D.apply_damage(damage + 5, STAMINA, affecting, armor_block) + log_combat(A, D, "punched nonlethally") else - D.visible_message("[A] attempted to disarm [D]!", \ - "[A] attempted to disarm [D]!") - playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - log_combat(A, D, "disarmed (Krav Maga)", "[I ? " removing \the [I]" : ""]") - return 1 + D.visible_message("[A] reprimands [D]!", \ + "You're manhandled by [A]!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A) + to_chat(A, "You stomp [D]!") + A.do_attack_animation(D, ATTACK_EFFECT_KICK) + playsound(D, 'sound/effects/hit_punch.ogg', 50, TRUE, -1) + D.apply_damage(damage + 10, STAMINA, affecting, armor_block) + log_combat(A, D, "stomped nonlethally") + if(prob(D.getStaminaLoss())) + D.visible_message("[D] sputters and recoils in pain!", "You recoil in pain as you are jabbed in a nerve!") + D.drop_all_held_items() + return TRUE //Krav Maga Gloves diff --git a/code/datums/martial/mushpunch.dm b/code/datums/martial/mushpunch.dm index fe9f0d77d6..3fd0a637c2 100644 --- a/code/datums/martial/mushpunch.dm +++ b/code/datums/martial/mushpunch.dm @@ -4,6 +4,7 @@ /datum/martial_art/mushpunch/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) var/atk_verb + var/damage = (damage_roll(A,D)*3) to_chat(A, "You begin to wind up an attack...") if(!do_after(A, 25, target = D)) to_chat(A, "Your attack was interrupted!") @@ -12,7 +13,7 @@ atk_verb = pick("punches", "smashes", "ruptures", "cracks") D.visible_message("[A] [atk_verb] [D] with inhuman strength, sending [D.p_them()] flying backwards!", \ "[A] [atk_verb] you with inhuman strength, sending you flying backwards!") - D.apply_damage(rand(15,30), BRUTE) + D.apply_damage(damage, BRUTE) //KAPOW playsound(D, 'sound/effects/meteorimpact.ogg', 25, 1, -1) var/throwtarget = get_edge_target_turf(A, get_dir(A, get_step_away(D, A))) D.throw_at(throwtarget, 4, 2, A)//So stuff gets tossed around at the same time. diff --git a/code/datums/martial/plasma_fist.dm b/code/datums/martial/plasma_fist.dm index 71fb00c467..c751d3b146 100644 --- a/code/datums/martial/plasma_fist.dm +++ b/code/datums/martial/plasma_fist.dm @@ -44,11 +44,13 @@ return /datum/martial_art/plasma_fist/proc/Throwback(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/damage = (damage_roll(A,D)*3) D.visible_message("[A] has hit [D] with Plasma Punch!", \ "[A] has hit [D] with Plasma Punch!") playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A))) D.throw_at(throw_target, 200, 4,A) + D.apply_damage(damage, BRUTE) A.say("HYAH!", forced="plasma fist") log_combat(A, D, "threw back (Plasma Fist)") return diff --git a/code/datums/martial/psychotic_brawl.dm b/code/datums/martial/psychotic_brawl.dm index 75053dbbfc..06d07260ed 100644 --- a/code/datums/martial/psychotic_brawl.dm +++ b/code/datums/martial/psychotic_brawl.dm @@ -14,6 +14,7 @@ /datum/martial_art/psychotic_brawling/proc/psycho_attack(mob/living/carbon/human/A, mob/living/carbon/human/D) var/atk_verb + var/damage = damage_roll(A,D) switch(rand(1,8)) if(1) D.help_shake_act(A) @@ -44,10 +45,10 @@ D.visible_message("[A] [atk_verb] [D]!", \ "[A] [atk_verb] you!") playsound(get_turf(D), 'sound/weapons/punch1.ogg', 40, 1, -1) - D.apply_damage(rand(5,10), BRUTE, BODY_ZONE_HEAD) - A.apply_damage(rand(5,10), BRUTE, BODY_ZONE_HEAD) + D.apply_damage(damage*1.5, BRUTE, BODY_ZONE_HEAD) + A.apply_damage(damage, BRUTE, BODY_ZONE_HEAD) if(!istype(D.head,/obj/item/clothing/head/helmet/) && !istype(D.head,/obj/item/clothing/head/hardhat)) - D.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5) + D.adjustOrganLoss(ORGAN_SLOT_BRAIN, damage) A.Stun(rand(10,45)) D.DefaultCombatKnockdown(rand(5,30))//CIT CHANGE - makes stuns from martial arts always use Knockdown instead of Stun for the sake of consistency if(5,6) @@ -55,7 +56,7 @@ atk_verb = pick("punches", "kicks", "hits", "slams into") D.visible_message("[A] [atk_verb] [D] with inhuman strength, sending [D.p_them()] flying backwards!", \ "[A] [atk_verb] you with inhuman strength, sending you flying backwards!") - D.apply_damage(rand(15,30), BRUTE) + D.apply_damage(damage*2, BRUTE) playsound(get_turf(D), 'sound/effects/meteorimpact.ogg', 25, 1, -1) var/throwtarget = get_edge_target_turf(A, get_dir(A, get_step_away(D, A))) D.throw_at(throwtarget, 4, 2, A)//So stuff gets tossed around at the same time. diff --git a/code/datums/martial/rising_bass.dm b/code/datums/martial/rising_bass.dm index 8055bd54e9..1b767df52c 100644 --- a/code/datums/martial/rising_bass.dm +++ b/code/datums/martial/rising_bass.dm @@ -78,6 +78,7 @@ return TRUE /datum/martial_art/the_rising_bass/proc/sideKick(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/damage = (damage_roll(A,D)*0.5) if(CHECK_MOBILITY(D, MOBILITY_STAND)) var/dir = A.dir & (NORTH | SOUTH) ? pick(EAST, WEST) : pick(NORTH, SOUTH) var/oppdir = dir == NORTH ? SOUTH : dir == SOUTH ? NORTH : dir == EAST ? WEST : EAST @@ -87,7 +88,7 @@ D.visible_message("[A] kicks [D] in the side, sliding them over!", \ "[A] kicks you in the side, forcing you to step away!") playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - D.apply_damage(5, BRUTE, BODY_ZONE_CHEST) + D.apply_damage(damage, BRUTE, BODY_ZONE_CHEST) D.DefaultCombatKnockdown(60) var/L = !checkfordensity(H,D) ? (!checkfordensity(K,D) ? D.loc : K) : H D.forceMove(L) @@ -96,6 +97,7 @@ return TRUE /datum/martial_art/the_rising_bass/proc/shoulderFlip(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/damage = (damage_roll(A,D) + 25) if(CHECK_MOBILITY(D, MOBILITY_STAND)) var/turf/H = get_step(A, get_dir(D,A)) var/L = checkfordensity(H,D) ? H : A.loc @@ -104,8 +106,8 @@ "[A] flips you over their shoulder, slamming you into the ground!") playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) D.emote("scream") - D.apply_damage(10, BRUTE, BODY_ZONE_CHEST) - D.apply_damage(30, BRUTE, BODY_ZONE_HEAD) + D.apply_damage(damage, BRUTE, BODY_ZONE_CHEST) + D.apply_damage(damage, BRUTE, BODY_ZONE_HEAD) D.Sleeping(60) D.DefaultCombatKnockdown(300) D.forceMove(L) @@ -114,6 +116,7 @@ return FALSE /datum/martial_art/the_rising_bass/proc/repulsePunch(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/damage = damage_roll(A,D) if(CHECK_MOBILITY(D, MOBILITY_STAND) && repulsecool < world.time) A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) D.visible_message("[A] smashes [D] in the chest, throwing them away!", \ @@ -121,7 +124,7 @@ playsound(get_turf(A), 'sound/weapons/punch1.ogg', 50, 1, -1) var/atom/F = get_edge_target_turf(D, get_dir(A, get_step_away(D, A))) D.throw_at(F, 10, 1) - D.apply_damage(10, BRUTE, BODY_ZONE_CHEST) + D.apply_damage(damage, BRUTE, BODY_ZONE_CHEST) D.DefaultCombatKnockdown(90) log_combat(A, D, "repulse punched (Rising Bass)") repulsecool = world.time + 3 SECONDS @@ -129,12 +132,13 @@ return FALSE /datum/martial_art/the_rising_bass/proc/footSmash(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/damage = (damage_roll(A,D)*0.5) if(CHECK_MOBILITY(D, MOBILITY_STAND)) A.do_attack_animation(D, ATTACK_EFFECT_KICK) D.visible_message("[A] smashes their foot down on [D]'s foot!", \ "[A] smashes your foot!") playsound(get_turf(A), 'sound/weapons/punch1.ogg', 50, 1, -1) - D.apply_damage(5, BRUTE, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) + D.apply_damage(damage, BRUTE, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) D.dropItemToGround(D.get_active_held_item()) log_combat(A, D, "foot smashed (Rising Bass)") return TRUE @@ -181,7 +185,7 @@ . = ..() if(A.incapacitated(FALSE, TRUE)) //NO STUN return BULLET_ACT_HIT - if(!(A.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE + if(CHECK_ALL_MOBILITY(A, MOBILITY_USE|MOBILITY_STAND)) //NO UNABLE TO USE, NO DODGING ON THE FLOOR return BULLET_ACT_HIT if(A.dna && A.dna.check_mutation(HULK)) //NO HULK return BULLET_ACT_HIT diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index a9e1b8a729..41479c57bd 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -31,45 +31,48 @@ var/atk_verb = pick("precisely kick", "brutally chop", "cleanly hit", "viciously slam") ///this is the critical hit damage added to the attack if it rolls, it starts at 0 because it'll be changed when rolled var/crit_damage = 0 + var/damage = damage_roll(A,D) D.visible_message("[A] [atk_verb]s [D]!", \ "[A] [atk_verb]s you!", null, null, A) to_chat(A, "You [atk_verb] [D]!") if(prob(10)) - crit_damage += 20 + crit_damage += (damage*2 + 15) playsound(get_turf(D), 'sound/weapons/bite.ogg', 50, TRUE, -1) - D.visible_message("[D] sputters blood as the blow strikes them with inhuman force!", "You are struck with incredible precision by [A]!") + D.visible_message("[D] staggers as the blow strikes them with inhuman force!", "You are struck with incredible precision by [A]!") log_combat(A, D, "critcal strong punched (Sleeping Carp)")//log it here because a critical can swing for 40 force and it's important for the sake of how hard they hit else playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, TRUE, -1) log_combat(A, D, "strong punched (Sleeping Carp)")//so as to not double up on logging - D.apply_damage(20 + crit_damage, BRUTE, affecting) + D.apply_damage((damage + 15) + crit_damage, BRUTE, affecting) return ///Crashing Wave Kick: Harm Disarm combo, throws people seven tiles backwards /datum/martial_art/the_sleeping_carp/proc/launchKick(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/damage = (damage_roll(A,D) + 15) A.do_attack_animation(D, ATTACK_EFFECT_KICK) D.visible_message("[A] kicks [D] square in the chest, sending them flying!", \ "You are kicked square in the chest by [A], sending you flying!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A) playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, TRUE, -1) var/atom/throw_target = get_edge_target_turf(D, A.dir) D.throw_at(throw_target, 7, 14, A) - D.apply_damage(15, BRUTE, BODY_ZONE_CHEST) + D.apply_damage(damage, BRUTE, BODY_ZONE_CHEST) log_combat(A, D, "launchkicked (Sleeping Carp)") return ///Keelhaul: Harm Grab combo, knocks people down, deals stamina damage while they're on the floor /datum/martial_art/the_sleeping_carp/proc/dropKick(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/damage = damage_roll(A,D) A.do_attack_animation(D, ATTACK_EFFECT_KICK) playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, TRUE, -1) if((D.mobility_flags & MOBILITY_STAND)) - D.apply_damage(10, BRUTE, BODY_ZONE_HEAD) + D.apply_damage(damage, BRUTE, BODY_ZONE_HEAD) D.DefaultCombatKnockdown(50, override_hardstun = 0.01, override_stamdmg = 0) - D.adjustStaminaLoss(40) //A cit specific change form the tg port to really punish anyone who tries to stand up + D.apply_damage(damage + 35, STAMINA, BODY_ZONE_HEAD) //A cit specific change form the tg port to really punish anyone who tries to stand up D.visible_message("[A] kicks [D] in the head, sending them face first into the floor!", \ "You are kicked in the head by [A], sending you crashing to the floor!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A) - else if(!(D.mobility_flags & MOBILITY_STAND)) - D.apply_damage(5, BRUTE, BODY_ZONE_HEAD) - D.adjustStaminaLoss(40) + else + D.apply_damage(damage*0.5, BRUTE, BODY_ZONE_HEAD) + D.apply_damage(damage + 35, STAMINA, BODY_ZONE_HEAD) D.drop_all_held_items() D.visible_message("[A] kicks [D] in the head!", \ "You are kicked in the head by [A]!", "You hear a sickening sound of flesh hitting flesh!", COMBAT_MESSAGE_RANGE, A) @@ -85,6 +88,7 @@ /datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) add_to_streak("H",D) + var/damage = (damage_roll(A,D) + 5) if(check_streak(A,D)) return TRUE var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) @@ -93,7 +97,7 @@ D.visible_message("[A] [atk_verb]s [D]!", \ "[A] [atk_verb]s you!", null, null, A) to_chat(A, "You [atk_verb] [D]!") - D.apply_damage(rand(10,15), BRUTE, affecting) + D.apply_damage(damage, BRUTE, affecting) playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, TRUE, -1) log_combat(A, D, "punched (Sleeping Carp)") return TRUE @@ -110,7 +114,7 @@ . = ..() if(A.incapacitated(FALSE, TRUE)) //NO STUN return BULLET_ACT_HIT - if(!(A.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE + if(CHECK_ALL_MOBILITY(A, MOBILITY_USE|MOBILITY_STAND)) //NO UNABLE TO USE, NO DEFLECTION ON THE FLOOR return BULLET_ACT_HIT if(A.dna && A.dna.check_mutation(HULK)) //NO HULK return BULLET_ACT_HIT @@ -121,6 +125,7 @@ playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE) P.firer = A P.setAngle(rand(0, 360))//SHING + A.adjustStaminaLossBuffered (3) //Citadel change to stop infinite bullet sponging as you run away, but it is buffered! return BULLET_ACT_FORCE_PIERCE return BULLET_ACT_HIT @@ -133,7 +138,6 @@ ADD_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT) H.physiology.brute_mod *= 0.4 //brute is really not gonna cut it H.physiology.burn_mod *= 0.7 //burn is distinctly more useful against them than brute but they're still resistant - H.physiology.stamina_mod *= 0.5 //stun batons prove to be one of the few ways to fight them. They have stun resistance already, so I think doubling down too hard on this resistance is a bit much. H.physiology.stun_mod *= 0.3 //for those rare stuns H.physiology.pressure_mod *= 0.3 //go hang out with carp H.physiology.cold_mod *= 0.3 //cold mods are different to burn mods, they do stack however @@ -148,7 +152,6 @@ REMOVE_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT) H.physiology.brute_mod = initial(H.physiology.brute_mod) H.physiology.burn_mod = initial(H.physiology.burn_mod) - H.physiology.stamina_mod = initial(H.physiology.stamina_mod) H.physiology.stun_mod = initial(H.physiology.stun_mod) H.physiology.pressure_mod = initial(H.physiology.pressure_mod) //no more carpies H.physiology.cold_mod = initial(H.physiology.cold_mod) @@ -237,7 +240,7 @@ else return ..() -/obj/item/twohanded/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(wielded) - return ..() - return FALSE +/obj/item/twohanded/bostaff/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!wielded) + return BLOCK_NONE + return ..() diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index 349efdd342..f7923d029f 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -19,29 +19,29 @@ /datum/martial_art/wrestling/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) if(!can_use(A, D)) - return 0 + return FALSE switch(streak) if("drop") streak = "" drop(A,D) - return 1 + return TRUE if("strike") streak = "" strike(A,D) - return 1 + return TRUE if("kick") streak = "" kick(A,D) - return 1 + return TRUE if("throw") streak = "" throw_wrassle(A,D) - return 1 + return TRUE if("slam") streak = "" slam(A,D) - return 1 - return 0 + return TRUE + return FALSE /datum/action/slam name = "Slam (Cinch) - Slam a grappled opponent into the floor." @@ -138,7 +138,7 @@ /datum/martial_art/wrestling/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) if(check_streak(A,D)) - return 1 + return TRUE log_combat(A, D, "punched with wrestling") ..() @@ -173,11 +173,11 @@ if (get_dist(A, D) > 1) to_chat(A, "[D] is too far away!") - return 0 + return FALSE if (!isturf(A.loc) || !isturf(D.loc)) to_chat(A, "You can't throw [D] from here!") - return 0 + return FALSE A.setDir(turn(A.dir, 90)) var/turf/T = get_step(A, A.dir) @@ -186,7 +186,7 @@ D.forceMove(T) D.setDir(get_dir(D, A)) else - return 0 + return FALSE sleep(delay) @@ -195,11 +195,11 @@ if (get_dist(A, D) > 1) to_chat(A, "[D] is too far away!") - return 0 + return FALSE if (!isturf(A.loc) || !isturf(D.loc)) to_chat(A, "You can't throw [D] from here!") - return 0 + return FALSE D.forceMove(A.loc) // Maybe this will help with the wallthrowing bug. @@ -211,7 +211,7 @@ D.emote("scream") D.throw_at(T, 10, 4, A, TRUE, TRUE, callback = CALLBACK(D, /mob/living/carbon/human.proc/DefaultCombatKnockdown, 20)) log_combat(A, D, "has thrown with wrestling") - return 0 + return FALSE /datum/martial_art/wrestling/proc/FlipAnimation(mob/living/carbon/human/D) set waitfor = FALSE @@ -227,6 +227,7 @@ if(!A.pulling || A.pulling != D) to_chat(A, "You need to have [D] in a cinch!") return + var/damage = damage_roll(A,D) D.forceMove(A.loc) A.setDir(get_dir(A, D)) D.setDir(get_dir(D, A)) @@ -258,7 +259,7 @@ A.pixel_y = 0 D.pixel_x = 0 D.pixel_y = 0 - return 0 + return FALSE if (!isturf(A.loc) || !isturf(D.loc)) to_chat(A, "You can't slam [D] here!") @@ -266,7 +267,7 @@ A.pixel_y = 0 D.pixel_x = 0 D.pixel_y = 0 - return 0 + return FALSE else if (A) A.pixel_x = 0 @@ -274,7 +275,7 @@ if (D) D.pixel_x = 0 D.pixel_y = 0 - return 0 + return FALSE sleep(1) @@ -286,11 +287,11 @@ if (get_dist(A, D) > 1) to_chat(A, "[D] is too far away!") - return 0 + return FALSE if (!isturf(A.loc) || !isturf(D.loc)) to_chat(A, "You can't slam [D] here!") - return 0 + return FALSE D.forceMove(A.loc) @@ -309,11 +310,11 @@ switch(rand(1,3)) if (2) - D.adjustBruteLoss(rand(20,30)) + D.apply_damage(damage + 25, BRUTE) if (3) D.ex_act(EXPLODE_LIGHT) else - D.adjustBruteLoss(rand(10,20)) + D.apply_damage(damage + 15, BRUTE) else D.ex_act(EXPLODE_LIGHT) @@ -327,7 +328,7 @@ log_combat(A, D, "body-slammed") - return 0 + return FALSE /datum/martial_art/wrestling/proc/CheckStrikeTurf(mob/living/carbon/human/A, turf/T) if (A && (T && isturf(T) && get_dist(A, T) <= 1)) @@ -337,6 +338,7 @@ if(!D) return var/turf/T = get_turf(A) + var/damage = damage_roll(A,D) if (T && isturf(T) && D && isturf(D.loc)) for (var/i = 0, i < 4, i++) A.setDir(turn(A.dir, 90)) @@ -345,7 +347,7 @@ addtimer(CALLBACK(src, .proc/CheckStrikeTurf, A, T), 4) A.visible_message("[A] headbutts [D]!") - D.adjustBruteLoss(rand(10,20)) + D.apply_damage(damage + 15, BRUTE) playsound(A.loc, "swing_hit", 50, 1) D.Unconscious(20) log_combat(A, D, "headbutted") @@ -353,13 +355,14 @@ /datum/martial_art/wrestling/proc/kick(mob/living/carbon/human/A, mob/living/carbon/human/D) if(!D) return + var/damage = damage_roll(A,D) A.emote("scream") A.emote("flip") A.setDir(turn(A.dir, 90)) A.visible_message("[A] roundhouse-kicks [D]!") playsound(A.loc, "swing_hit", 50, 1) - D.adjustBruteLoss(rand(10,20)) + D.apply_damage(damage + 15, STAMINA) var/turf/T = get_edge_target_turf(A, get_dir(A, get_step_away(D, A))) if (T && isturf(T)) @@ -373,7 +376,8 @@ var/obj/surface = null var/turf/ST = null var/falling = 0 - + var/damage = damage_roll(A,D) + for (var/obj/O in oview(1, A)) if (O.density == 1) if (O == A) @@ -401,15 +405,15 @@ A.pixel_y = 0 if (falling == 1) A.visible_message("...and dives head-first into the ground, ouch!") - A.adjustBruteLoss(rand(10,20)) + A.apply_damage(damage + 15, BRUTE) A.DefaultCombatKnockdown(60) to_chat(A, "[D] is too far away!") - return 0 + return FALSE if (!isturf(A.loc) || !isturf(D.loc)) A.pixel_y = 0 to_chat(A, "You can't drop onto [D] from here!") - return 0 + return FALSE if(A) animate(A, transform = matrix(90, MATRIX_ROTATE), time = 1, loop = 0) @@ -427,9 +431,9 @@ if (prob(33) || D.stat) D.ex_act(EXPLODE_LIGHT) else - D.adjustBruteLoss(rand(20,30)) + D.apply_damage(damage + 25, BRUTE) else - D.adjustBruteLoss(rand(20,30)) + D.apply_damage(damage + 25, BRUTE) D.DefaultCombatKnockdown(40) @@ -442,14 +446,16 @@ return /datum/martial_art/wrestling/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + damage_roll(A,D) if(check_streak(A,D)) - return 1 + return TRUE log_combat(A, D, "wrestling-disarmed") ..() /datum/martial_art/wrestling/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + damage_roll(A,D) if(check_streak(A,D)) - return 1 + return TRUE if(!can_use(A,D)) return ..() if(A.pulling == D || A == D) // don't stun grab yoursel @@ -459,7 +465,7 @@ "[A] gets [D] in a cinch!") D.Stun(rand(60,100)) log_combat(A, D, "cinched") - return 1 + return TRUE /obj/item/storage/belt/champion/wrestling name = "Wrestling Belt" @@ -492,7 +498,7 @@ //Make sure that moves can only be used on people wearing the holodeck belt /datum/martial_art/wrestling/holodeck/can_use(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) if(!(istype(D.mind?.martial_art, /datum/martial_art/wrestling/holodeck))) - return 0 + return FALSE else return ..() diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 5a69ebe427..69b74841df 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -113,20 +113,25 @@ duration = set_duration . = ..() -/datum/status_effect/no_combat_mode/mesmerize +/datum/status_effect/mesmerize id = "Mesmerize" alert_type = /obj/screen/alert/status_effect/mesmerized -/datum/status_effect/no_combat_mode/mesmerize/on_creation(mob/living/new_owner, set_duration) +/datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration) . = ..() ADD_TRAIT(owner, TRAIT_MUTE, "mesmerize") owner.add_movespeed_modifier("[STATUS_EFFECT_MESMERIZE]_[id]", TRUE, priority = 64, override = TRUE, multiplicative_slowdown = 5, blacklisted_movetypes = FALSE? NONE : CRAWLING) -/datum/status_effect/no_combat_mode/mesmerize/on_remove() +/datum/status_effect/mesmerize/on_remove() . = ..() REMOVE_TRAIT(owner, TRAIT_MUTE, "mesmerize") owner.remove_movespeed_modifier("[STATUS_EFFECT_MESMERIZE]_[id]") +/datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration) + if(isnum(set_duration)) + duration = set_duration + . = ..() + /obj/screen/alert/status_effect/mesmerized name = "Mesmerized" desc = "You cant tear your sight from who is in front of you... their gaze is simply too enthralling.." diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 6b86f6e653..cd5c9dd6a9 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -54,12 +54,18 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic) // Current storyteller var/datum/dynamic_storyteller/storyteller = null // Threat logging vars + /// Starting threat level, for things that increase it but can bring it back down. + var/initial_threat_level = 0 /// Target threat level right now. Events and antags will try to keep the round at this level. var/threat_level = 0 /// The current antag threat. Recalculated every time a ruletype starts or ends. var/threat = 0 - /// Starting threat level, for things that increase it but can bring it back down. - var/initial_threat_level = 0 + /// Threat average over the course of the round, for endgame logs. + var/threat_average = 0 + /// Number of times threat average has been calculated, for calculating above. + var/threat_average_weight = 0 + /// Last time a threat average sample was taken. Used for weighting the rolling average. + var/last_threat_sample_time = 0 /// Things that cause a rolling threat adjustment to be displayed at roundend. var/list/threat_tallies = list() /// Running information about the threat. Can store text or datum entries. @@ -717,6 +723,16 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic) continue current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow) threat = storyteller.calculate_threat() + added_threat + if(threat_average_weight) + var/cur_sample_weight = world.time - last_threat_sample_time + threat_average = ((threat_average * threat_average_weight) + threat) / (threat_average_weight + cur_sample_weight) + threat_average_weight += cur_sample_weight + last_threat_sample_time = world.time + else + threat_average = threat + threat_average_weight++ + last_threat_sample_time = world.time + /// Removes type from the list /datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type) for(var/I in type_list) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index b746100f27..48e306b2b5 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -34,8 +34,7 @@ living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS]) living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS]) list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS]) - var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility)) - ghost_eligible = trim_list(eligibility.get_all_ghost_role_eligible()) + ghost_eligible = trim_list(get_all_ghost_role_eligible()) /datum/dynamic_ruleset/midround/proc/trim_list(list/L = list()) var/list/trimmed_list = L.Copy() diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm index 35d5a04488..c37eb9bc2a 100644 --- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm +++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm @@ -11,6 +11,7 @@ WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level. USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre. FORCE_IF_WON: If this mode won the vote, forces it + USE_PREV_ROUND_WEIGHTS: Changes its threat centre based on the average chaos of previous rounds. */ var/flags = 0 var/dead_player_weight = 1 // How much dead players matter for threat calculation @@ -92,6 +93,8 @@ Property weights are: mean += 5 if(voters) GLOB.dynamic_curve_centre += (mean/voters) + if(flags & USE_PREV_ROUND_WEIGHTS) + GLOB.dynamic_curve_centre += (50 - SSpersistence.average_dynamic_threat) / 10 GLOB.dynamic_forced_threat_level = forced_threat_level /datum/dynamic_storyteller/proc/get_midround_cooldown() @@ -230,7 +233,7 @@ Property weights are: curve_width = 1.5 weight = 2 min_players = 30 - flags = WAROPS_ALWAYS_ALLOWED + flags = WAROPS_ALWAYS_ALLOWED | USE_PREV_ROUND_WEIGHTS property_weights = list("valid" = 3, "trust" = 5) /datum/dynamic_storyteller/team/get_injection_chance(dry_run = FALSE) @@ -268,9 +271,6 @@ Property weights are: /datum/dynamic_storyteller/random/get_injection_chance() return 50 // i would do rand(0,100) but it's actually the same thing when you do the math -/datum/dynamic_storyteller/random/calculate_threat() - return 0 // what IS threat - /datum/dynamic_storyteller/random/roundstart_draft() var/list/drafted_rules = list() for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules) @@ -324,6 +324,7 @@ Property weights are: desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies. Has a buildup-climax-falling action threat curve." weight = 2 curve_width = 2 + flags = USE_PREV_ROUND_WEIGHTS property_weights = list("story_potential" = 2) @@ -336,7 +337,7 @@ Property weights are: name = "Classic" config_tag = "classic" desc = "No special antagonist weights. Good variety, but not like random. Uses your chaos preference to weight." - flags = USE_PREF_WEIGHTS + flags = USE_PREF_WEIGHTS | USE_PREV_ROUND_WEIGHTS /datum/dynamic_storyteller/suspicion name = "Intrigue" @@ -345,6 +346,7 @@ Property weights are: weight = 2 curve_width = 2 dead_player_weight = 2 + flags = USE_PREV_ROUND_WEIGHTS property_weights = list("trust" = -3) /datum/dynamic_storyteller/liteextended diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index b561224668..f78a05244e 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -55,7 +55,7 @@ /obj/item/stack/tile/fakespace/loaded = ARCADE_WEIGHT_TRICK, /obj/item/stack/tile/fakepit/loaded = ARCADE_WEIGHT_TRICK, /obj/item/restraints/handcuffs/fake = ARCADE_WEIGHT_TRICK, - /obj/item/clothing/gloves/rapid/hug = ARCADE_WEIGHT_TRICK, + /obj/item/clothing/gloves/fingerless/pugilist/rapid/hug = ARCADE_WEIGHT_TRICK, /obj/item/grenade/chem_grenade/glitter/pink = ARCADE_WEIGHT_TRICK, /obj/item/grenade/chem_grenade/glitter/blue = ARCADE_WEIGHT_TRICK, diff --git a/code/game/machinery/computer/arcade/minesweeper.dm b/code/game/machinery/computer/arcade/minesweeper.dm index 5c30489b00..370f85b4b9 100644 --- a/code/game/machinery/computer/arcade/minesweeper.dm +++ b/code/game/machinery/computer/arcade/minesweeper.dm @@ -382,18 +382,26 @@ explosion(loc, 1, 3, rand(1,5), rand(1,10)) var/list/targets = list() var/cur_y = y - round(row_limit * 0.5, 1) + var/starting_row = 1 + if(cur_y < 1) + starting_row -= cur_y - 1 + cur_y = 1 var/start_x = x - round(column_limit * 0.5, 1) - for(var/row in table) //translate the mines locations into actual turf coordinates. + var/starting_column = 1 + if(start_x < 1) + starting_column -= start_x - 1 + start_x = 1 + for(var/row in starting_row to length(table)) //translate the mines locations into actual turf coordinates. if(!locate(cur_y, start_x, z)) - continue + break var/cur_x = start_x - for(var/column in row) + for(var/column in starting_column to length(table[row])) var/coord_value = table[row][column] if(coord_value == 10 || coord_value == 0) //there is a mine in here. - var/turf/target = locate(cur_y, cur_x, z) - if(!target) - continue - targets += target + var/turf/T = locate(cur_y, cur_x, z) + if(!T) + break + targets += T cur_x++ cur_y++ var/num_explosions = 0 @@ -406,4 +414,4 @@ #undef MINESWEEPER_GAME_MAIN_MENU #undef MINESWEEPER_GAME_PLAYING #undef MINESWEEPER_GAME_LOST -#undef MINESWEEPER_GAME_WON \ No newline at end of file +#undef MINESWEEPER_GAME_WON diff --git a/code/game/machinery/telecomms/machines/broadcaster.dm b/code/game/machinery/telecomms/machines/broadcaster.dm index 6b03bcc880..d16835eee7 100644 --- a/code/game/machinery/telecomms/machines/broadcaster.dm +++ b/code/game/machinery/telecomms/machines/broadcaster.dm @@ -17,6 +17,11 @@ GLOBAL_VAR_INIT(message_delay, 0) // To make sure restarting the recentmessages idle_power_usage = 25 circuit = /obj/item/circuitboard/machine/telecomms/broadcaster +/obj/machinery/telecomms/broadcaster/RefreshParts() + idle_power_usage = 25 + for(var/obj/item/stock_parts/manipulator/P in component_parts) + idle_power_usage -= (P.rating * 1.5) //Has 2 manipulators + /obj/machinery/telecomms/broadcaster/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from) // Don't broadcast rejected signals if(!istype(signal)) diff --git a/code/game/machinery/telecomms/machines/bus.dm b/code/game/machinery/telecomms/machines/bus.dm index ce5ed31094..4732c5bddd 100644 --- a/code/game/machinery/telecomms/machines/bus.dm +++ b/code/game/machinery/telecomms/machines/bus.dm @@ -19,6 +19,11 @@ circuit = /obj/item/circuitboard/machine/telecomms/bus var/change_frequency = 0 +/obj/machinery/telecomms/bus/RefreshParts() + idle_power_usage = 50 + for(var/obj/item/stock_parts/manipulator/P in component_parts) + idle_power_usage -= (P.rating * 2) //Has 2 manipulators + /obj/machinery/telecomms/bus/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from) if(!istype(signal) || !is_freq_listening(signal)) return diff --git a/code/game/machinery/telecomms/machines/hub.dm b/code/game/machinery/telecomms/machines/hub.dm index e7e3788774..e960dab422 100644 --- a/code/game/machinery/telecomms/machines/hub.dm +++ b/code/game/machinery/telecomms/machines/hub.dm @@ -19,6 +19,11 @@ netspeed = 40 circuit = /obj/item/circuitboard/machine/telecomms/hub +/obj/machinery/telecomms/hub/RefreshParts() + idle_power_usage = 80 + for(var/obj/item/stock_parts/manipulator/P in component_parts) + idle_power_usage -= (P.rating * 5) //Has 2 manipulators + /obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) if(!is_freq_listening(signal)) return diff --git a/code/game/machinery/telecomms/machines/processor.dm b/code/game/machinery/telecomms/machines/processor.dm index a09b3ca8fb..269f5269f3 100644 --- a/code/game/machinery/telecomms/machines/processor.dm +++ b/code/game/machinery/telecomms/machines/processor.dm @@ -16,6 +16,11 @@ circuit = /obj/item/circuitboard/machine/telecomms/processor var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals +/obj/machinery/telecomms/processor/RefreshParts() + idle_power_usage = 30 + for(var/obj/item/stock_parts/manipulator/P in component_parts) + idle_power_usage -= (P.rating * 1.5) //Has 2 manipulators + /obj/machinery/telecomms/processor/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from) if(!is_freq_listening(signal)) return diff --git a/code/game/machinery/telecomms/machines/receiver.dm b/code/game/machinery/telecomms/machines/receiver.dm index 4dd3af036c..0e443bad8f 100644 --- a/code/game/machinery/telecomms/machines/receiver.dm +++ b/code/game/machinery/telecomms/machines/receiver.dm @@ -15,6 +15,11 @@ idle_power_usage = 30 circuit = /obj/item/circuitboard/machine/telecomms/receiver +/obj/machinery/telecomms/receiver/RefreshParts() + idle_power_usage = 30 + for(var/obj/item/stock_parts/manipulator/P in component_parts) + idle_power_usage -= (P.rating * 1.5) //Has 2 manipulators + /obj/machinery/telecomms/receiver/receive_signal(datum/signal/subspace/signal) if(!on || !istype(signal) || !check_receive_level(signal) || signal.transmission_method != TRANSMISSION_SUBSPACE) return diff --git a/code/game/machinery/telecomms/machines/relay.dm b/code/game/machinery/telecomms/machines/relay.dm index 65f70a4f23..10328f0788 100644 --- a/code/game/machinery/telecomms/machines/relay.dm +++ b/code/game/machinery/telecomms/machines/relay.dm @@ -19,6 +19,11 @@ var/broadcasting = 1 var/receiving = 1 +/obj/machinery/telecomms/relay/RefreshParts() + idle_power_usage = 30 + for(var/obj/item/stock_parts/manipulator/P in component_parts) + idle_power_usage -= (P.rating * 1.5) //Has 2 manipulators + /obj/machinery/telecomms/relay/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from) // Add our level and send it back var/turf/T = get_turf(src) diff --git a/code/game/machinery/telecomms/machines/server.dm b/code/game/machinery/telecomms/machines/server.dm index 46b16252c5..ad4599b4c3 100644 --- a/code/game/machinery/telecomms/machines/server.dm +++ b/code/game/machinery/telecomms/machines/server.dm @@ -19,6 +19,11 @@ /obj/machinery/telecomms/server/Initialize() . = ..() +/obj/machinery/telecomms/server/RefreshParts() + idle_power_usage = 15 + for(var/obj/item/stock_parts/manipulator/P in component_parts) + idle_power_usage -= (P.rating) //has 2 manipulators + /obj/machinery/telecomms/server/receive_information(datum/signal/subspace/vocal/signal, obj/machinery/telecomms/machine_from) // can't log non-vocal signals if(!istype(signal) || !signal.data["message"] || !is_freq_listening(signal)) diff --git a/code/game/objects/effects/decals/vgdecals.dm b/code/game/objects/effects/decals/vgdecals.dm new file mode 100644 index 0000000000..93db0075c8 --- /dev/null +++ b/code/game/objects/effects/decals/vgdecals.dm @@ -0,0 +1,119 @@ +// Full credit goes to VG station for these assets. https://github.com/vgstation-coders/vgstation13 +// All items in this .dm and the associated .dmi were made by VG station and all credit should go to them. + +// -<| IMPORTANT MAPPER NOTE |>- +// Change the 'color' variable on any white sprite to simply recolour it! + +/obj/effect/turf_decal/vg_decals + icon = 'icons/turf/vgstation_decals.dmi' + icon_state = "no" + +// NUMBERS START + +/obj/effect/turf_decal/vg_decals/numbers + icon_state = "no" + +/obj/effect/turf_decal/vg_decals/numbers/one + icon_state = "1" + +/obj/effect/turf_decal/vg_decals/numbers/two + icon_state = "2" + +/obj/effect/turf_decal/vg_decals/numbers/three + icon_state = "3" + +/obj/effect/turf_decal/vg_decals/numbers/four + icon_state = "4" + +/obj/effect/turf_decal/vg_decals/numbers/five + icon_state = "5" + +/obj/effect/turf_decal/vg_decals/numbers/six + icon_state = "6" + +/obj/effect/turf_decal/vg_decals/numbers/seven + icon_state = "7" + +/obj/effect/turf_decal/vg_decals/numbers/eight + icon_state = "8" + +/obj/effect/turf_decal/vg_decals/numbers/nine + icon_state = "9" + +/obj/effect/turf_decal/vg_decals/numbers/zero + icon_state = "0" + +// NUMBERS END + +// ATMOS START + +/obj/effect/turf_decal/vg_decals/atmos + icon_state = "no" + +/obj/effect/turf_decal/vg_decals/atmos/oxygen + icon_state = "oxygen" + +/obj/effect/turf_decal/vg_decals/atmos/carbon_dioxide + icon_state = "carbon_dioxide" + +/obj/effect/turf_decal/vg_decals/atmos/nitrogen + icon_state = "nitrogen" + +/obj/effect/turf_decal/vg_decals/atmos/air + icon_state = "air" + +/obj/effect/turf_decal/vg_decals/atmos/nitrous_oxide + icon_state = "nitrous_oxide" + +/obj/effect/turf_decal/vg_decals/atmos/plasma + icon_state = "plasma" + +/obj/effect/turf_decal/vg_decals/atmos/mix + icon_state = "mix" + +// ATMOS END + +// DEPARTMENT START + +/obj/effect/turf_decal/vg_decals/department/hop + icon_state = "hop" + +/obj/effect/turf_decal/vg_decals/department/bar + icon_state = "bar" + +/obj/effect/turf_decal/vg_decals/department/cargo + icon_state = "cargo" + +/obj/effect/turf_decal/vg_decals/department/med + icon_state = "med" + +/obj/effect/turf_decal/vg_decals/department/sci + icon_state = "sci" + +/obj/effect/turf_decal/vg_decals/department/sec + icon_state = "sec" + +/obj/effect/turf_decal/vg_decals/department/mining + icon_state = "mine" + +/obj/effect/turf_decal/vg_decals/department/zoo + icon_state = "zoo" + +// DEPARTMENT END + +// MISC START + +/obj/effect/turf_decal/vg_decals/no + icon = 'icons/turf/vgstation_decals.dmi' + icon_state = "no" + +/obj/effect/turf_decal/vg_decals/radiation_huge + icon_state = "radiation_huge" + +/obj/effect/turf_decal/vg_decals/radiation + icon_state = "radiation" + +/obj/effect/turf_decal/vg_decals/radiation_custom + icon_state = "radiation-w" + +// MISC END diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 71c394a404..d79fba7172 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -45,7 +45,6 @@ /obj/effect/spawner/lootdrop/armory_contraband name = "armory contraband gun spawner" lootdoubles = FALSE - loot = list( /obj/item/gun/ballistic/automatic/pistol = 8, /obj/item/gun/ballistic/shotgun/automatic/combat = 5, @@ -129,7 +128,6 @@ loot = typesof(/obj/item/flashlight/glowstick) . = ..() - /obj/effect/spawner/lootdrop/gloves name = "random gloves" desc = "These gloves are supposed to be a random color..." @@ -150,7 +148,6 @@ /obj/effect/spawner/lootdrop/crate_spawner name = "lootcrate spawner" //USE PROMO CODE "SELLOUT" FOR 20% OFF! lootdoubles = FALSE - loot = list( /obj/structure/closet/crate/secure/loot = 20, "" = 80 @@ -386,3 +383,141 @@ /obj/structure/reagent_dispensers/keg/aphro = 2, /obj/structure/reagent_dispensers/keg/aphro/strong = 2, /obj/structure/reagent_dispensers/keg/gargle = 1) + +/obj/effect/spawner/lootdrop/coin + lootcount = 1 + loot = list( + /obj/item/coin/silver = 30, + /obj/item/coin/iron = 30, + /obj/item/coin/gold = 10, + /obj/item/coin/diamond = 10, + /obj/item/coin/plasma = 10, + /obj/item/coin/uranium = 10, + ) + +/obj/effect/spawner/lootdrop/cig_packs + lootcount = 1 + loot = list( + /obj/item/storage/fancy/cigarettes = 20, + /obj/item/storage/fancy/cigarettes/dromedaryco = 10, + /obj/item/storage/fancy/cigarettes/cigpack_robust = 5, + /obj/item/storage/fancy/cigarettes/cigpack_robustgold = 5, + /obj/item/storage/fancy/cigarettes/cigpack_carp = 15, + /obj/item/storage/fancy/cigarettes/cigpack_syndicate = 2, + /obj/item/storage/fancy/cigarettes/cigpack_midori = 10, + /obj/item/storage/fancy/cigarettes/cigpack_shadyjims = 5, + /obj/item/storage/fancy/cigarettes/cigpack_xeno = 3, + /obj/item/storage/fancy/cigarettes/cigpack_cannabis = 10, + /obj/item/storage/fancy/cigarettes/cigpack_mindbreaker = 10, + /obj/item/storage/fancy/rollingpapers = 10 + ) + +/obj/effect/spawner/lootdrop/cigars_cases + lootcount = 1 + loot = list( + /obj/item/storage/fancy/cigarettes/cigars = 50, + /obj/item/storage/fancy/cigarettes/cigars/cohiba = 25, + /obj/item/storage/fancy/cigarettes/cigars/havana = 25, + ) + +/obj/effect/spawner/lootdrop/space_cash + lootcount = 1 + loot = list( + /obj/item/stack/spacecash/c1 = 1, + /obj/item/stack/spacecash/c10 = 9, + /obj/item/stack/spacecash/c20 = 10, + /obj/item/stack/spacecash/c50 = 15, + /obj/item/stack/spacecash/c100 = 25, + /obj/item/stack/spacecash/c200 = 20, + /obj/item/stack/spacecash/c500 = 19, + /obj/item/stack/spacecash/c1000 = 1, + ) + +/obj/effect/spawner/lootdrop/druggie_pill + lootcount = 1 + loot = list( + /obj/item/reagent_containers/pill/stimulant = 1, + /obj/item/reagent_containers/pill/zoom = 9, + /obj/item/reagent_containers/pill/happy = 10, + /obj/item/reagent_containers/pill/lsd = 15, + /obj/item/reagent_containers/pill/aranesp = 25, + /obj/item/reagent_containers/pill/psicodine = 20, + /obj/item/reagent_containers/pill/mannitol = 19, + /obj/item/reagent_containers/pill/happiness = 1, + ) + +/obj/effect/spawner/lootdrop/low_loot_toilet + name = "random low toilet spawner" + lootcount = 1 +//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that +//This is ment for "low" loot that anyone could fine in a toilet, for better gear use high loot toilet + loot = list("" = 30, + /obj/item/lighter = 2, + /obj/item/tape/random = 1, + /obj/item/poster/random_contraband = 1, + /obj/item/clothing/glasses/sunglasses/blindfold = 4, + /obj/item/clothing/glasses/sunglasses = 1, + /obj/item/toy/plush/random = 5, + /obj/effect/spawner/lootdrop/gloves = 5, + /obj/effect/spawner/lootdrop/glowstick = 5, + /obj/effect/spawner/lootdrop/coin = 3, + /obj/effect/spawner/lootdrop/cig_packs = 10, + /obj/effect/spawner/lootdrop/cigars_cases = 2, + /obj/effect/spawner/lootdrop/space_cash = 5, + /obj/item/reagent_containers/food/snacks/grown/cannabis = 5, + /obj/item/storage/pill_bottle/dice = 5, + /obj/item/toy/cards/deck = 5, + /obj/effect/spawner/lootdrop/druggie_pill = 5 + ) + +/obj/effect/spawner/lootdrop/prison_loot_toilet + name = "random prison toilet spawner" + lootcount = 1 +//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that +//This is ment for "prison" loot that is rather rare and ment for "prisoners if they get a crowbar to fine, or sec. + loot = list("" = 10, + /obj/item/lighter = 5, + /obj/item/poster/random_contraband = 5, + /obj/item/clothing/glasses/sunglasses = 5, + /obj/effect/spawner/lootdrop/coin = 5, + /obj/effect/spawner/lootdrop/cig_packs = 10, + /obj/effect/spawner/lootdrop/cigars_cases = 5, + /obj/item/reagent_containers/food/snacks/grown/cannabis = 5, + /obj/item/storage/pill_bottle/dice = 5, + /obj/item/toy/cards/deck = 5, + /obj/effect/spawner/lootdrop/druggie_pill = 5, + /obj/item/kitchen/knife = 5, + /obj/item/screwdriver = 5, + /obj/item/crowbar/red = 0.5, //Dont you need a crowbar to open this? + /obj/item/stack/medical/bruise_pack = 3, + /obj/item/reagent_containers/food/drinks/bottle/vodka = 2, + /obj/item/radio = 5, + /obj/item/flashlight = 4.5, + /obj/item/clothing/mask/breath = 2, + /obj/item/tank/internals/emergency_oxygen = 3, + /obj/item/storage/box/mre/menu4/safe = 3, + /obj/item/grenade/smokebomb = 2 + ) + +/obj/effect/spawner/lootdrop/high_loot_toilet + name = "random high toilet spawner" + lootcount = 1 +//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that +//The items inside are always going to be something usefull, illegal and likely traitorous. + loot = list( + /obj/item/clothing/glasses/sunglasses = 5, + /obj/effect/spawner/lootdrop/coin = 5, + /obj/effect/spawner/lootdrop/space_cash = 5, + /obj/effect/spawner/lootdrop/druggie_pill = 5, + /obj/item/storage/fancy/cigarettes/cigpack_syndicate = 5, + /obj/item/suppressor = 5, + /obj/item/toy/cards/deck/syndicate = 5, + /obj/item/clothing/under/syndicate = 5, + /obj/item/clothing/mask/gas/syndicate = 5, + /obj/item/grenade/smokebomb = 10, + /obj/item/gun/ballistic/automatic/toy/pistol = 5, + /obj/item/firing_pin = 5, + /obj/item/grenade/empgrenade = 15, + /obj/item/clothing/gloves/combat = 10, + /obj/item/clothing/shoes/sneakers/noslip = 10 + ) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 222b82d134..8b4567a754 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -93,8 +93,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/tool_behaviour = NONE var/toolspeed = 1 - var/block_chance = 0 - var/hit_reaction_chance = 0 //If you want to have something unrelated to blocking/armour piercing etc. Maybe not needed, but trying to think ahead/allow more freedom var/reach = 1 //In tiles, how far this weapon can reach; 1 for adjacent, which is default //The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. @@ -365,13 +363,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) // afterattack() and attack() prototypes moved to _onclick/item_attack.dm for consistency -/obj/item/proc/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, args) - if(prob(final_block_chance)) - owner.visible_message("[owner] blocks [attack_text] with [src]!") - return 1 - return 0 - /obj/item/proc/talk_into(mob/M, input, channel, spans, datum/language/language) return ITALICS | REDUCE_RANGE @@ -464,9 +455,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) /obj/item/proc/ui_action_click(mob/user, actiontype) attack_self(user) -/obj/item/proc/IsReflect(var/def_zone) //This proc determines if and at what% an object will reflect energy projectiles if it's in l_hand,r_hand or wear_suit - return 0 - /obj/item/proc/eyestab(mob/living/carbon/M, mob/living/carbon/user) if(HAS_TRAIT(user, TRAIT_PACIFISM)) to_chat(user, "You don't want to harm [M]!") diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 5b7c70b1b6..2303278f5c 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -706,11 +706,11 @@ CIGARETTE PACKETS ARE IN FANCY.DM name = "\improper E-Cigarette" desc = "A classy and highly sophisticated electronic cigarette, for classy and dignified gentlemen. A warning label reads \"Warning: Do not fill with flammable materials.\""//<<< i'd vape to that. icon = 'icons/obj/clothing/masks.dmi' - icon_state = null - item_state = null + icon_state = "black_vape" + item_state = "black_vape" w_class = WEIGHT_CLASS_TINY var/chem_volume = 100 - var/vapetime = FALSE //this so it won't puff out clouds every tick + var/vapetime = FALSE //this so it won't puff out clouds every tick var/screw = FALSE // kinky var/super = FALSE //for the fattest vapes dude. @@ -723,11 +723,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM . = ..() create_reagents(chem_volume, NO_REACT, NO_REAGENTS_VALUE) // so it doesn't react until you light it reagents.add_reagent(/datum/reagent/drug/nicotine, 50) - if(!icon_state) - if(!param_color) - param_color = pick("red","blue","black","white","green","purple","yellow","orange") - icon_state = "[param_color]_vape" - item_state = "[param_color]_vape" + if(!param_color) + param_color = pick("red","blue","black","white","green","purple","yellow","orange") + icon_state = "[param_color]_vape" + item_state = "[param_color]_vape" /obj/item/clothing/mask/vape/attackby(obj/item/O, mob/user, params) if(O.tool_behaviour == TOOL_SCREWDRIVER) @@ -1067,4 +1066,4 @@ CIGARETTE PACKETS ARE IN FANCY.DM name = "coconut bong" icon_off = "coconut_bong" icon_on = "coconut_bong_lit" - desc = "A water bong used for smoking dried plants. This one's made out of a coconut and some bamboo." \ No newline at end of file + desc = "A water bong used for smoking dried plants. This one's made out of a coconut and some bamboo." diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index a3cfdb9177..9c94348fc2 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -18,7 +18,6 @@ var/recharge_locked = FALSE var/obj/item/stock_parts/micro_laser/diode //used for upgrading! - /obj/item/laser_pointer/red pointer_icon_state = "red_laser" /obj/item/laser_pointer/green @@ -28,6 +27,9 @@ /obj/item/laser_pointer/purple pointer_icon_state = "purple_laser" +/obj/item/laser_pointer/blue/handmade + diode = null + /obj/item/laser_pointer/New() ..() diode = new(src) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 0e109cd400..634ca4d637 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -310,11 +310,11 @@ SLIME SCANNER //GENERAL HANDLER if(!damage_message) if(O.organ_flags & ORGAN_FAILING) - damage_message += " End Stage [O.name] failure detected." + damage_message += " Chronic [O.name] failure detected." else if(O.damage > O.high_threshold) - damage_message += " Chronic [O.name] failure detected." + damage_message += " Acute [O.name] failure detected." else if(O.damage > O.low_threshold && advanced) - damage_message += " Acute [O.name] failure detected." + damage_message += " Minor [O.name] failure detected." if(temp_message || damage_message) msg += "\t[uppertext(O.name)]: [damage_message] [temp_message]\n" diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm index b0882b6e5d..e2140bd0fd 100644 --- a/code/game/objects/items/flamethrower.dm +++ b/code/game/objects/items/flamethrower.dm @@ -234,15 +234,16 @@ /obj/item/flamethrower/full/tank create_with_tank = TRUE -/obj/item/flamethrower/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - var/obj/item/projectile/P = hitby - if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15)) - owner.visible_message("\The [attack_text] hits the fueltank on [owner]'s [name], rupturing it! What a shot!") - var/target_turf = get_turf(owner) - igniter.ignite_turf(src,target_turf, release_amount = 100) - qdel(ptank) - return 1 //It hit the flamethrower, not them - +/obj/item/flamethrower/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(attack_type & ATTACK_TYPE_PROJECTILE) + var/obj/item/projectile/P = object + if(istype(P) && (P.damage_type != STAMINA) && damage && !P.nodamage && prob(15)) + owner.visible_message("\The [attack_text] hits the fueltank on [owner]'s [name], rupturing it! What a shot!") + var/target_turf = get_turf(owner) + igniter.ignite_turf(src,target_turf, release_amount = 100) + qdel(ptank) + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return ..() /obj/item/assembly/igniter/proc/flamethrower_process(turf/open/location) location.hotspot_expose(700,2) diff --git a/code/game/objects/items/grenades/grenade.dm b/code/game/objects/items/grenades/grenade.dm index 77b4b33a8c..8a41ee4601 100644 --- a/code/game/objects/items/grenades/grenade.dm +++ b/code/game/objects/items/grenades/grenade.dm @@ -115,12 +115,14 @@ /obj/item/grenade/attack_paw(mob/user) return attack_hand(user) -/obj/item/grenade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - var/obj/item/projectile/P = hitby - if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15)) - owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!") - prime() - return TRUE //It hit the grenade, not them +/obj/item/grenade/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(attack_type & ATTACK_TYPE_PROJECTILE) + var/obj/item/projectile/P = object + if(damage && !P.nodamage && (P.damage_type != STAMINA) && prob(15)) + owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!") + prime() + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return ..() /obj/item/proc/grenade_prime_react(obj/item/grenade/nade) return diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index b8f15ec295..84a9193eaf 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -328,9 +328,9 @@ hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") -/obj/item/nullrod/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight +/obj/item/nullrod/claymore/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(attack_type & ATTACK_TYPE_PROJECTILE) // Don't bring a sword to a gunfight + return NONE return ..() /obj/item/nullrod/claymore/darkblade diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm index 5f3524d1c3..502930bf65 100644 --- a/code/game/objects/items/kitchen.dm +++ b/code/game/objects/items/kitchen.dm @@ -142,6 +142,7 @@ name = "plastic knife" desc = "A plastic knife. Rather harmless to anything." force = 1 + throwforce = 1 bayonet = FALSE /obj/item/kitchen/knife/combat/cyborg diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index aba07c9120..766da28d51 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -113,10 +113,10 @@ else RemoveElement(/datum/element/sword_point) -/obj/item/melee/transforming/energy/sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(active) - return ..() - return 0 +/obj/item/melee/transforming/energy/sword/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!active) + return NONE + return ..() /obj/item/melee/transforming/energy/sword/cyborg item_color = "red" @@ -148,8 +148,8 @@ tool_behaviour = TOOL_SAW toolspeed = 0.7 -/obj/item/melee/transforming/energy/sword/cyborg/saw/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - return 0 +/obj/item/melee/transforming/energy/sword/cyborg/saw/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + return NONE /obj/item/melee/transforming/energy/sword/saber var/list/possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index cbbd50b573..011837d48b 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -2,13 +2,12 @@ item_flags = NEEDS_PERMIT /obj/item/melee/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user) - if(target.check_block()) + if(target.check_martial_melee_block()) target.visible_message("[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!", "You block the attack!") user.Stun(40) return TRUE - /obj/item/melee/chainofcommand name = "chain of command" desc = "A tool used by great men to placate the frothing masses." @@ -75,9 +74,9 @@ AddComponent(/datum/component/butchering, 30, 95, 5) //fast and effective, but as a sword, it might damage the results. AddElement(/datum/element/sword_point) -/obj/item/melee/sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight +/obj/item/melee/sabre/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(attack_type & ATTACK_TYPE_PROJECTILE) // Don't bring a sword to a gunfight. + return BLOCK_NONE return ..() /obj/item/melee/sabre/on_exit_storage(datum/component/storage/S) @@ -165,8 +164,8 @@ . = ..() AddComponent(/datum/component/butchering, 20, 65, 0) -/obj/item/melee/rapier/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) +/obj/item/melee/rapier/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(attack_type == ATTACK_TYPE_PROJECTILE) final_block_chance = 0 return ..() @@ -305,7 +304,7 @@ return else if(cooldown_check < world.time) - if(target.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) + if(target.run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user) & BLOCK_SUCCESS) playsound(target, 'sound/weapons/genhit.ogg', 50, 1) return if(ishuman(target)) diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index 498990fcf3..a564d7f9f9 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -434,7 +434,15 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths()) can_random_spawn = FALSE /obj/item/toy/plush/random/Initialize() - var/newtype = prob(CONFIG_GET(number/snowflake_plushie_prob))? /obj/item/toy/plush/random_snowflake : pick(GLOB.valid_plushie_paths) + var/newtype + var/list/snowflake_list = CONFIG_GET(keyed_list/snowflake_plushies) + + /// If there are no snowflake plushies we'll default to base plush, so we grab from the valid list + if (snowflake_list.len) + newtype = prob(CONFIG_GET(number/snowflake_plushie_prob)) ? /obj/item/toy/plush/random_snowflake : pick(GLOB.valid_plushie_paths) + else + newtype = pick(GLOB.valid_plushie_paths) + new newtype(loc) return INITIALIZE_HINT_QDEL diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index 0ca2fbd386..318753e4ad 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -11,7 +11,7 @@ var/charge_cost = 30 /obj/item/borg/stun/attack(mob/living/M, mob/living/user) - if(M.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK)) + if(M.run_block(src, 0, "[M]'s [name]", ATTACK_TYPE_MELEE, 0, user, ran_zone(user.zone_selected)) & BLOCK_SUCCESS) playsound(M, 'sound/weapons/genhit.ogg', 50, 1) return FALSE if(iscyborg(user)) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 07f4cb4e40..37dfc417d5 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -5,7 +5,7 @@ armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) var/transparent = FALSE // makes beam projectiles pass through the shield -/obj/item/shield/proc/on_shield_block(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK) +/obj/item/shield/proc/on_shield_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance) return TRUE /obj/item/shield/riot @@ -26,16 +26,18 @@ transparent = TRUE max_integrity = 75 -/obj/item/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(transparent && (hitby.pass_flags & PASSGLASS)) - return FALSE - if(attack_type == THROWN_PROJECTILE_ATTACK) +/obj/item/shield/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(ismovableatom(object)) + var/atom/movable/AM = object + if(transparent && (AM.pass_flags & PASSGLASS)) + return BLOCK_NONE + if(attack_type & ATTACK_TYPE_THROWN) final_block_chance += 30 - if(attack_type == LEAP_ATTACK) + if(attack_type & ATTACK_TYPE_TACKLE) final_block_chance = 100 . = ..() - if(.) - on_shield_block(owner, hitby, attack_text, damage, attack_type) + if(. & BLOCK_SUCCESS) + on_shield_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) /obj/item/shield/riot/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/melee/baton)) @@ -69,10 +71,10 @@ playsound(owner, 'sound/effects/glassbr3.ogg', 100) new /obj/item/shard((get_turf(src))) -/obj/item/shield/riot/on_shield_block(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK) +/obj/item/shield/riot/on_shield_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(obj_integrity <= damage) var/turf/T = get_turf(owner) - T.visible_message("[hitby] destroys [src]!") + T.visible_message("[attack_text] destroys [src]!") shatter(owner) qdel(src) return FALSE @@ -139,11 +141,11 @@ . = ..() icon_state = "[base_icon_state]0" -/obj/item/shield/energy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - return 0 - -/obj/item/shield/energy/IsReflect() - return (active) +/obj/item/shield/energy/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object)) + block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT + return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT + return ..() /obj/item/shield/energy/attack_self(mob/living/carbon/human/user) if(clumsy_check && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) @@ -182,10 +184,10 @@ w_class = WEIGHT_CLASS_NORMAL var/active = 0 -/obj/item/shield/riot/tele/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(active) - return ..() - return 0 +/obj/item/shield/riot/tele/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!active) + return BLOCK_NONE + return ..() /obj/item/shield/riot/tele/attack_self(mob/living/user) active = !active @@ -249,7 +251,7 @@ transparent = FALSE item_flags = SLOWS_WHILE_IN_HAND -/obj/item/shield/riot/implant/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) +/obj/item/shield/riot/implant/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(attack_type & ATTACK_TYPE_PROJECTILE) final_block_chance = 60 //Massive shield return ..() diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 75db89f616..c8ee6b05c7 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -22,6 +22,8 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \ new/datum/stack_recipe("spout flask", /obj/item/glasswork/glass_base/spouty, 20), \ new/datum/stack_recipe("small bulb flask", /obj/item/glasswork/glass_base/flask_small, 5), \ new/datum/stack_recipe("large bottle flask", /obj/item/glasswork/glass_base/flask_large, 15), \ + new/datum/stack_recipe("tea cup", /obj/item/glasswork/glass_base/tea_plate, 5), \ + new/datum/stack_recipe("tea plate", /obj/item/glasswork/glass_base/tea_cup, 5), \ )), \ )) @@ -137,7 +139,7 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \ return ..() /obj/item/stack/sheet/plasmaglass/on_solar_construction(obj/machinery/power/solar/S) - S.obj_integrity *= 1.2 + S.max_integrity *= 1.2 S.efficiency *= 1.2 /* @@ -170,7 +172,7 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ ..() /obj/item/stack/sheet/rglass/on_solar_construction(obj/machinery/power/solar/S) - S.obj_integrity *= 2 + S.max_integrity *= 2 /obj/item/stack/sheet/rglass/cyborg custom_materials = null @@ -218,7 +220,7 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \ . += GLOB.prglass_recipes /obj/item/stack/sheet/plasmarglass/on_solar_construction(obj/machinery/power/solar/S) - S.obj_integrity *= 2.2 + S.max_integrity *= 2.2 S.efficiency *= 1.2 GLOBAL_LIST_INIT(titaniumglass_recipes, list( @@ -242,7 +244,7 @@ GLOBAL_LIST_INIT(titaniumglass_recipes, list( . += GLOB.titaniumglass_recipes /obj/item/stack/sheet/titaniumglass/on_solar_construction(obj/machinery/power/solar/S) - S.obj_integrity *= 2.5 + S.max_integrity *= 2.5 S.efficiency *= 1.5 GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( @@ -270,7 +272,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( . += GLOB.plastitaniumglass_recipes /obj/item/stack/sheet/titaniumglass/on_solar_construction(obj/machinery/power/solar/S) - S.obj_integrity *= 2 + S.max_integrity *= 2 S.efficiency *= 2 /obj/item/shard diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 51f5349f81..45dc1b65ea 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -368,6 +368,7 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \ new/datum/stack_recipe("towel", /obj/item/reagent_containers/rag/towel, 3), \ new/datum/stack_recipe("bedsheet", /obj/item/bedsheet, 3), \ new/datum/stack_recipe("empty sandbag", /obj/item/emptysandbag, 4), \ + new/datum/stack_recipe("padded floor tile", /obj/item/stack/tile/padded, 1, 4, 20), \ null, \ new/datum/stack_recipe("fingerless gloves", /obj/item/clothing/gloves/fingerless, 1),\ new/datum/stack_recipe("white gloves", /obj/item/clothing/gloves/color/white, 1),\ @@ -768,6 +769,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list( new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \ new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \ new /datum/stack_recipe("shower curtain", /obj/structure/curtain, 10, time = 10, one_per_turf = 1, on_floor = 1), \ + new /datum/stack_recipe("laser pointer case", /obj/item/glasswork/glass_base/laserpointer_shell, 30), \ new /datum/stack_recipe("wet floor sign", /obj/item/caution, 2))) /obj/item/stack/sheet/plastic diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 2e537d3eff..ff81b4340a 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -142,6 +142,14 @@ turf_type = /turf/open/floor/wood resistance_flags = FLAMMABLE +//Cloth Floors + +/obj/item/stack/tile/padded + name = "padded floor tile" + desc = "These are soft and cushy, they'd make good pillows. They look very comfortable, although what they're used for is discomforting." + icon_state = "tile_padded" + turf_type = /turf/open/floor/padded + //Basalt /obj/item/stack/tile/basalt name = "basalt tile" diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 0a29562d6b..4893e92d77 100755 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -804,3 +804,4 @@ attack_verb = list("bashed", "slashes", "prods", "pokes") fitting_swords = list(/obj/item/melee/rapier) starting_sword = /obj/item/melee/rapier + diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 037ae51e30..874c858b3f 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -404,6 +404,12 @@ new /obj/item/gun/ballistic/revolver(src) new /obj/item/ammo_box/a357(src) +/obj/item/storage/box/syndie_kit/pistol + +/obj/item/storage/box/syndie_kit/pistol/PopulateContents() + new /obj/item/gun/ballistic/automatic/pistol(src) + new /obj/item/ammo_box/magazine/m10mm(src) + /obj/item/storage/box/syndie_kit/contract_kit name = "contractor kit" desc = "Supplied to Syndicate contractors in active mission areas." @@ -494,4 +500,4 @@ new item1(src) // Create three, non repeat items from the list. new item2(src) new item3(src) - new /obj/item/paper/contractor_guide(src) //Paper guide \ No newline at end of file + new /obj/item/paper/contractor_guide(src) //Paper guide diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index 64a0026424..0fc47f649a 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -170,7 +170,7 @@ return disarming || (user.a_intent != INTENT_HARM) /obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE) - if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that + if(L.run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user) & BLOCK_SUCCESS) //No message; check_shields() handles that playsound(L, 'sound/weapons/genhit.ogg', 50, 1) return FALSE var/stunpwr = stamforce diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index ba51fa3d65..ef213c233a 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -454,11 +454,8 @@ total_mass_on = TOTAL_MASS_TOY_SWORD sharpness = IS_BLUNT -/obj/item/twohanded/dualsaber/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - return FALSE - -/obj/item/twohanded/dualsaber/toy/IsReflect()//Stops Toy Dualsabers from reflecting energy projectiles - return FALSE +/obj/item/twohanded/dualsaber/toy/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + return BLOCK_NONE /obj/item/twohanded/dualsaber/hypereutactic/toy name = "\improper DX Hyper-Euplastic LightSword" @@ -474,11 +471,8 @@ slowdown_wielded = 0 sharpness = IS_BLUNT -/obj/item/twohanded/dualsaber/hypereutactic/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - return FALSE - -/obj/item/twohanded/dualsaber/hypereutactic/toy/IsReflect()//Stops it from reflecting energy projectiles - return FALSE +/obj/item/twohanded/dualsaber/hypereutactic/toy/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + return BLOCK_NONE /obj/item/twohanded/dualsaber/hypereutactic/toy/rainbow name = "\improper Hyper-Euclidean Reciprocating Trigonometric Zweihander" diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index b188250fff..4bc8bdb36d 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -289,6 +289,8 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) resistance_flags = FIRE_PROOF var/hacked = FALSE + /// Can this reflect all energy projectiles? + var/can_reflect = TRUE var/brightness_on = 6 //TWICE AS BRIGHT AS A REGULAR ESWORD var/list/possible_colors = list("red", "blue", "green", "purple") var/list/rainbow_colors = list(LIGHT_COLOR_RED, LIGHT_COLOR_GREEN, LIGHT_COLOR_LIGHT_CYAN, LIGHT_COLOR_LAVENDER) @@ -373,10 +375,13 @@ else user.adjustStaminaLoss(25) -/obj/item/twohanded/dualsaber/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(wielded) - return ..() - return 0 +/obj/item/twohanded/dualsaber/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!wielded) + return NONE + if(can_reflect && is_energy_reflectable_projectile(object) && (attack_type & ATTACK_TYPE_PROJECTILE)) + block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER //no you + return BLOCK_SHOULD_REDIRECT | BLOCK_SUCCESS | BLOCK_REDIRECTED + return ..() /obj/item/twohanded/dualsaber/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up if(wielded) @@ -419,10 +424,6 @@ /obj/item/twohanded/dualsaber/proc/rainbow_process() light_color = pick(rainbow_colors) -/obj/item/twohanded/dualsaber/IsReflect() - if(wielded) - return 1 - /obj/item/twohanded/dualsaber/ignition_effect(atom/A, mob/user) // same as /obj/item/melee/transforming/energy, mostly if(!wielded) @@ -560,15 +561,13 @@ block_chance = 50 armour_penetration = 0 var/chaplain_spawnable = TRUE + can_reflect = FALSE obj_flags = UNIQUE_RENAME /obj/item/twohanded/dualsaber/hypereutactic/chaplain/ComponentInitialize() . = ..() AddComponent(/datum/component/anti_magic, TRUE, TRUE, FALSE, null, null, FALSE) -/obj/item/twohanded/dualsaber/hypereutactic/chaplain/IsReflect() - return FALSE - //spears /obj/item/twohanded/spear icon_state = "spearglass0" @@ -752,12 +751,16 @@ armour_penetration = 100 force_on = 30 -/obj/item/twohanded/required/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) +/obj/item/twohanded/required/chainsaw/doomslayer/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + block_return[BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE] = 100 + return ..() + +/obj/item/twohanded/required/chainsaw/doomslayer/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(attack_type & ATTACK_TYPE_PROJECTILE) owner.visible_message("Ranged attacks just make [owner] angrier!") playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1) - return 1 - return 0 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return ..() //GREY TIDE /obj/item/twohanded/spear/grey_tide @@ -897,19 +900,20 @@ AddComponent(/datum/component/butchering, 20, 105) AddElement(/datum/element/sword_point) -/obj/item/twohanded/vibro_weapon/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/twohanded/vibro_weapon/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(wielded) final_block_chance *= 2 - if(wielded || attack_type != PROJECTILE_ATTACK) + if(wielded || !(attack_type & ATTACK_TYPE_PROJECTILE)) if(prob(final_block_chance)) - if(attack_type == PROJECTILE_ATTACK) + if(attack_type & ATTACK_TYPE_PROJECTILE) owner.visible_message("[owner] deflects [attack_text] with [src]!") playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1) - return 1 + block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT + return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL else owner.visible_message("[owner] parries [attack_text] with [src]!") - return 1 - return 0 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return NONE /obj/item/twohanded/vibro_weapon/update_icon_state() icon_state = "hfrequency[wielded]" @@ -1055,11 +1059,9 @@ var/mob/living/silicon/robot/R = loc . = R.get_cell() -/obj/item/twohanded/electrostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!on) - return FALSE - if((attack_type == PROJECTILE_ATTACK) && !can_block_projectiles) - return FALSE +/obj/item/twohanded/electrostaff/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!on || (!can_block_projectiles && (attack_type & ATTACK_TYPE_PROJECTILE))) + return BLOCK_NONE return ..() /obj/item/twohanded/electrostaff/proc/min_hitcost() @@ -1180,7 +1182,7 @@ if(iscyborg(target)) ..() return - if(target.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that + if(target.run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user) & BLOCK_SUCCESS) //No message; run_block() handles that playsound(target, 'sound/weapons/genhit.ogg', 50, 1) return FALSE if(user.a_intent != INTENT_HARM) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 7f40e3d698..8496a8a216 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -84,6 +84,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 name = "purified longsword" desc = "A hastily-purified longsword. While not as holy as it could be, it's still a formidable weapon against those who would rather see you dead." force = 25 + block_chance = 0 /obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim." @@ -155,8 +156,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 return to_chat(user, "[src] thrums and points to the [dir2text(get_dir(user, closest_victim))].") -/obj/item/claymore/highlander/IsReflect() - return 1 //YOU THINK YOUR PUNY LASERS CAN STOP ME? +/obj/item/claymore/highlander/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object)) + return BLOCK_SUCCESS | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL | BLOCK_REDIRECTED + return ..() /obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE notches++ @@ -607,14 +610,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 force = 12 throwforce = 15 -/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers - var/picksound = rand(1,2) - var/turf = get_turf(src) - if(picksound == 1) - playsound(turf, 'sound/weapons/effects/batreflect1.ogg', 50, 1) - if(picksound == 2) - playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1) - return 1 +/obj/item/melee/baseball_bat/ablative/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + //some day this will reflect thrown items instead of lasers + if(is_energy_reflectable_projectile(object) && (attack_type == ATTACK_TYPE_PROJECTILE)) + var/turf = get_turf(src) + playsound(turf, pick('sound/weapons/effects/batreflect1.ogg', 'sound/weapons/effects/batreflect2.ogg'), 50, 1) + return BLOCK_SUCCESS | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL | BLOCK_REDIRECTED + return ..() /obj/item/melee/baseball_bat/ablative/syndi name = "syndicate major league bat" diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 06c1d96711..c593451c24 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -317,7 +317,7 @@ throwforce = 10 throw_range = 3 hitsound = 'sound/items/trayhit1.ogg' - hit_reaction_chance = 50 + block_chance = 50 custom_materials = list(/datum/material/iron = 2000) var/break_chance = 5 //Likely hood of smashing the chair. var/obj/structure/chair/origin_type = /obj/structure/chair @@ -362,18 +362,17 @@ new stack_type(get_turf(loc)) qdel(src) -/obj/item/chair/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == UNARMED_ATTACK && prob(hit_reaction_chance)) - owner.visible_message("[owner] fends off [attack_text] with [src]!") - return 1 - return 0 +/obj/item/chair/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!(attack_type & ATTACK_TYPE_UNARMED)) + return NONE + return ..() /obj/item/chair/afterattack(atom/target, mob/living/carbon/user, proximity) . = ..() if(!proximity) return if(prob(break_chance)) - user.visible_message("[user] smashes \the [src] to pieces against \the [target]") + user.visible_message("[user] smashes [src] to pieces against [target]") if(iscarbon(target)) var/mob/living/carbon/C = target if(C.health < C.maxHealth*0.5) diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm deleted file mode 100644 index bba51035fe..0000000000 --- a/code/game/objects/structures/musician.dm +++ /dev/null @@ -1,383 +0,0 @@ - -#define MUSICIAN_HEARCHECK_MINDELAY 4 -#define MUSIC_MAXLINES 600 -#define MUSIC_MAXLINECHARS 150 - -/datum/song - var/name = "Untitled" - var/list/lines = new() - var/tempo = 5 // delay between notes - - var/playing = 0 // if we're playing - var/help = 0 // if help is open - var/edit = 1 // if we're in editing mode - var/repeat = 0 // number of times remaining to repeat - var/max_repeats = 10 // maximum times we can repeat - - var/instrumentDir = "piano" // the folder with the sounds - var/instrumentExt = "ogg" // the file extension - var/obj/instrumentObj = null // the associated obj playing the sound - var/last_hearcheck = 0 - var/list/hearing_mobs - -/datum/song/New(dir, obj, ext = "ogg") - tempo = sanitize_tempo(tempo) - instrumentDir = dir - instrumentObj = obj - instrumentExt = ext - -/datum/song/Destroy() - instrumentObj = null - return ..() - -// note is a number from 1-7 for A-G -// acc is either "b", "n", or "#" -// oct is 1-8 (or 9 for C) -/datum/song/proc/playnote(note, acc as text, oct) - // handle accidental -> B<>C of E<>F - if(acc == "b" && (note == 3 || note == 6)) // C or F - if(note == 3) - oct-- - note-- - acc = "n" - else if(acc == "#" && (note == 2 || note == 5)) // B or E - if(note == 2) - oct++ - note++ - acc = "n" - else if(acc == "#" && (note == 7)) //G# - note = 1 - acc = "b" - else if(acc == "#") // mass convert all sharps to flats, octave jump already handled - acc = "b" - note++ - - // check octave, C is allowed to go to 9 - if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) - return - - // now generate name - var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]" - soundfile = file(soundfile) - // make sure the note exists - if(!fexists(soundfile)) - return - // and play - var/turf/source = get_turf(instrumentObj) - if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) - LAZYCLEARLIST(hearing_mobs) - for(var/mob/M in get_hearers_in_view(15, source)) - if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS)) - continue - LAZYADD(hearing_mobs, M) - last_hearcheck = world.time - - var/sound/music_played = sound(soundfile) - for(var/i in hearing_mobs) - var/mob/M = i - M.playsound_local(source, null, 100, falloff = 5, S = music_played) - -/datum/song/proc/updateDialog(mob/user) - instrumentObj.updateDialog() // assumes it's an object in world, override if otherwise - -/datum/song/proc/shouldStopPlaying(mob/user) - if(instrumentObj) - if(!user.canUseTopic(instrumentObj, TRUE, FALSE, FALSE, FALSE)) - return TRUE - return !instrumentObj.anchored // add special cases to stop in subclasses - else - return TRUE - -/datum/song/proc/playsong(mob/user) - while(repeat >= 0) - var/cur_oct[7] - var/cur_acc[7] - for(var/i = 1 to 7) - cur_oct[i] = 3 - cur_acc[i] = "n" - - for(var/line in lines) - for(var/beat in splittext(lowertext(line), ",")) - var/list/notes = splittext(beat, "/") - for(var/note in splittext(notes[1], "-")) - if(!playing || shouldStopPlaying(user))//If the instrument is playing, or special case - playing = FALSE - hearing_mobs = null - return - if(!length(note)) - continue - var/cur_note = text2ascii(note) - 96 - if(cur_note < 1 || cur_note > 7) - continue - var/notelen = length(note) - var/ni = "" - for(var/i = length(note[1]) + 1, i <= notelen, i += length(ni)) - ni = note[i] - if(!text2num(ni)) - if(ni == "#" || ni == "b" || ni == "n") - cur_acc[cur_note] = ni - else if(ni == "s") - cur_acc[cur_note] = "#" // so shift is never required - else - cur_oct[cur_note] = text2num(ni) - if(user.dizziness > 0 && prob(user.dizziness / 2)) - cur_note = CLAMP(cur_note + rand(round(-user.dizziness / 10), round(user.dizziness / 10)), 1, 7) - if(user.dizziness > 0 && prob(user.dizziness / 5)) - if(prob(30)) - cur_acc[cur_note] = "#" - else if(prob(42)) - cur_acc[cur_note] = "b" - else if(prob(75)) - cur_acc[cur_note] = "n" - playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note]) - if(notes.len >= 2 && text2num(notes[2])) - sleep(sanitize_tempo(tempo / text2num(notes[2]))) - else - sleep(tempo) - repeat-- - hearing_mobs = null - playing = FALSE - repeat = 0 - updateDialog(user) - -/datum/song/proc/interact(mob/user) - var/dat = "" - - if(lines.len > 0) - dat += "

Playback

" - if(!playing) - dat += "Play Stop

" - dat += "Repeat Song: " - dat += repeat > 0 ? "--" : "--" - dat += " [repeat] times " - dat += repeat < max_repeats ? "++" : "++" - dat += "
" - else - dat += "Play Stop
" - dat += "Repeats left: [repeat]
" - if(!edit) - dat += "
Show Editor
" - else - dat += "

Editing

" - dat += "Hide Editor" - dat += " Start a New Song" - dat += " Import a Song

" - var/bpm = round(600 / tempo) - dat += "Tempo: - [bpm] BPM +

" - var/linecount = 0 - for(var/line in lines) - linecount += 1 - dat += "Line [linecount]: Edit X [line]
" - dat += "Add Line

" - if(help) - dat += "Hide Help
" - dat += {" - Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).
- Every note in a chord will play together, with chord timed by the tempo.
-
- Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
- By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.
- Example: C,D,E,F,G,A,B will play a C major scale.
- After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3
- Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B
- A pause may be denoted by an empty chord: C,E,,C,G
- To make a chord be a different time, end it with /x, where the chord length will be length
- defined by tempo / x: C,G/2,E/4
- Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4 -
- Lines may be up to [MUSIC_MAXLINECHARS] characters.
- A song may only contain up to [MUSIC_MAXLINES] lines.
- "} - else - dat += "Show Help
" - - var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state)) - popup.open() - -/datum/song/proc/ParseSong(text) - set waitfor = FALSE - //split into lines - lines = splittext(text, "\n") - if(lines.len) - var/bpm_string = "BPM: " - if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1)) - var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default - tempo = sanitize_tempo(600 / round(divisor, 1)) - lines.Cut(1, 2) - else - tempo = sanitize_tempo(5) // default 120 BPM - if(lines.len > MUSIC_MAXLINES) - to_chat(usr, "Too many lines!") - lines.Cut(MUSIC_MAXLINES + 1) - var/linenum = 1 - for(var/l in lines) - if(length_char(l) > MUSIC_MAXLINECHARS) - to_chat(usr, "Line [linenum] too long!") - lines.Remove(l) - else - linenum++ - updateDialog(usr) // make sure updates when complete - -/datum/song/Topic(href, href_list) - if(!usr.canUseTopic(instrumentObj, TRUE, FALSE, FALSE, FALSE)) - usr << browse(null, "window=instrument") - usr.unset_machine() - return - - instrumentObj.add_fingerprint(usr) - - if(href_list["newsong"]) - lines = new() - tempo = sanitize_tempo(5) // default 120 BPM - name = "" - - else if(href_list["import"]) - var/t = "" - do - t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) - if(!in_range(instrumentObj, usr)) - return - - if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS) - var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") - if(cont == "no") - break - while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS) - ParseSong(t) - - else if(href_list["help"]) - help = text2num(href_list["help"]) - 1 - - else if(href_list["edit"]) - edit = text2num(href_list["edit"]) - 1 - - if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. - if(playing) - return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. - repeat += round(text2num(href_list["repeat"])) - if(repeat < 0) - repeat = 0 - if(repeat > max_repeats) - repeat = max_repeats - - else if(href_list["tempo"]) - tempo = sanitize_tempo(tempo + text2num(href_list["tempo"])) - - else if(href_list["play"]) - playing = TRUE - spawn() - playsong(usr) - - else if(href_list["newline"]) - var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null) - if(!newline || !in_range(instrumentObj, usr)) - return - if(lines.len > MUSIC_MAXLINES) - return - if(length(newline) > MUSIC_MAXLINECHARS) - newline = copytext(newline, 1, MUSIC_MAXLINECHARS) - lines.Add(newline) - - else if(href_list["deleteline"]) - var/num = round(text2num(href_list["deleteline"])) - if(num > lines.len || num < 1) - return - lines.Cut(num, num+1) - - else if(href_list["modifyline"]) - var/num = round(text2num(href_list["modifyline"]),1) - var/content = stripped_input(usr, "Enter your line: ", instrumentObj.name, lines[num], MUSIC_MAXLINECHARS) - if(!content || !in_range(instrumentObj, usr)) - return - if(num > lines.len || num < 1) - return - lines[num] = content - - else if(href_list["stop"]) - playing = FALSE - hearing_mobs = null - - updateDialog(usr) - return - -/datum/song/proc/sanitize_tempo(new_tempo) - new_tempo = abs(new_tempo) - return max(round(new_tempo, world.tick_lag), world.tick_lag) - -// subclass for handheld instruments, like violin -/datum/song/handheld - -/datum/song/handheld/updateDialog(mob/user) - instrumentObj.interact(user) - -/datum/song/handheld/shouldStopPlaying() - if(instrumentObj) - return !isliving(instrumentObj.loc) - else - return TRUE - - -////////////////////////////////////////////////////////////////////////// - - -/obj/structure/piano - name = "space minimoog" - icon = 'icons/obj/musician.dmi' - icon_state = "minimoog" - anchored = TRUE - density = TRUE - var/datum/song/song - -/obj/structure/piano/unanchored - anchored = FALSE - -/obj/structure/piano/New() - ..() - song = new("piano", src) - - if(prob(50) && icon_state == initial(icon_state)) - name = "space minimoog" - desc = "This is a minimoog, like a space piano, but more spacey!" - icon_state = "minimoog" - else - name = "space piano" - desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." - icon_state = "piano" - -/obj/structure/piano/Destroy() - qdel(song) - song = null - return ..() - -/obj/structure/piano/Initialize(mapload) - . = ..() - if(mapload) - song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded - -/obj/structure/piano/attack_hand(mob/user) - . = ..() - if(.) - return - interact(user) - -/obj/structure/piano/attack_paw(mob/user) - return attack_hand(user) - -/obj/structure/piano/interact(mob/user) - ui_interact(user) - -/obj/structure/piano/ui_interact(mob/user) - if(!user || !anchored) - return - - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return 1 - user.set_machine(src) - song.interact(user) - -/obj/structure/piano/wrench_act(mob/living/user, obj/item/I) - default_unfasten_wrench(user, I, 40) - return TRUE diff --git a/code/game/objects/structures/signs/signs_departments.dm b/code/game/objects/structures/signs/signs_departments.dm index 12932bbab6..b4fd823e76 100644 --- a/code/game/objects/structures/signs/signs_departments.dm +++ b/code/game/objects/structures/signs/signs_departments.dm @@ -60,6 +60,11 @@ desc = "A sign labelling a restroom." icon_state = "restroom" +/obj/structure/sign/departments/showers + name ="\improper SHOWER" + desc = "A sign labelling showers." + icon_state = "showerroom" + /obj/structure/sign/departments/medbay name = "\improper MEDBAY" desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." diff --git a/code/game/objects/structures/signs/signs_maps.dm b/code/game/objects/structures/signs/signs_maps.dm index 1e3bfd6d36..ee29ef7443 100644 --- a/code/game/objects/structures/signs/signs_maps.dm +++ b/code/game/objects/structures/signs/signs_maps.dm @@ -55,3 +55,18 @@ name = "cafe" desc = "A direction sign, pointing out which way the Cafe is." icon_state = "direction_cafe" + +obj/structure/sign/directions/rooms + name = "room" + desc = "Room numbers, helps others find you!" + icon_state = "roomnum" + +obj/structure/sign/directions/dorms + name = "dorm" + desc = "Dorm numbers, help others find you, or you find others." + icon_state = "dormnum" + +obj/structure/sign/directions/cells + name = "room" + desc = "So the less fortunate amongst us know where they'll be staying." + icon_state = "cellnum" diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 5f806e7fc4..892d85cd6e 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -12,13 +12,11 @@ var/w_items = 0 //the combined w_class of all the items in the cistern var/mob/living/swirlie = null //the mob being given a swirlie - /obj/structure/toilet/Initialize() . = ..() open = round(rand(0, 1)) update_icon() - /obj/structure/toilet/attack_hand(mob/living/user) . = ..() if(.) @@ -71,11 +69,9 @@ open = !open update_icon() - /obj/structure/toilet/update_icon_state() icon_state = "toilet[open][cistern]" - /obj/structure/toilet/attackby(obj/item/I, mob/living/user, params) if(istype(I, /obj/item/crowbar)) to_chat(user, "You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]...") @@ -116,12 +112,17 @@ . = ..() if (secret_type) secret = new secret_type(src) - secret.desc += " It's a secret!" - w_items += secret.w_class + secret.desc += "" //In case you want to add something to the item that spawns contents += secret +/obj/structure/toilet/secret/low_loot + secret_type = /obj/effect/spawner/lootdrop/low_loot_toilet +/obj/structure/toilet/secret/high_loot + secret_type = /obj/effect/spawner/lootdrop/high_loot_toilet +/obj/structure/toilet/secret/prison + secret_type = /obj/effect/spawner/lootdrop/prison_loot_toilet /obj/structure/urinal name = "urinal" @@ -194,7 +195,6 @@ exposed = !exposed return TRUE - /obj/item/reagent_containers/food/urinalcake name = "urinal cake" desc = "The noble urinal cake, protecting the station's pipes from the station's pee. Do not eat." @@ -278,7 +278,6 @@ add_hiddenprint(user) return TRUE - /obj/machinery/shower/update_overlays() . = ..() if(on) @@ -315,7 +314,6 @@ else if(isobj(AM)) wash_obj(AM) - /obj/machinery/shower/proc/wash_obj(obj/O) . = SEND_SIGNAL(O, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK) . = O.clean_blood() @@ -325,7 +323,6 @@ I.acid_level = 0 I.extinguish() - /obj/machinery/shower/proc/wash_turf() if(isturf(loc)) var/turf/tile = loc @@ -336,7 +333,6 @@ if(is_cleanable(E)) qdel(E) - /obj/machinery/shower/proc/wash_mob(mob/living/L) SEND_SIGNAL(L, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK) L.wash_cream() @@ -667,11 +663,9 @@ /obj/structure/sink/puddle/deconstruct(disassembled = TRUE) qdel(src) - //Shower Curtains// //Defines used are pre-existing in layers.dm// - /obj/structure/curtain name = "curtain" desc = "Contains less than 1% mercury." @@ -727,7 +721,6 @@ return TRUE - /obj/structure/curtain/attack_hand(mob/user) . = ..() if(.) diff --git a/code/game/sound.dm b/code/game/sound.dm index c285026d5b..577b058178 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -8,7 +8,7 @@ return //allocate a channel if necessary now so its the same for everyone - channel = channel || open_sound_channel() + channel = channel || SSsounds.random_available_channel() // Looping through the player list has the added bonus of working for mobs inside containers var/sound/S = sound(get_sfx(soundin)) @@ -26,10 +26,10 @@ if(get_dist(M, turf_source) <= maxdistance) M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, soundenvwet, soundenvdry) -/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, envwet = -10000, envdry = 0, manual_x, manual_y) +/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, envwet = -10000, envdry = 0, manual_x, manual_y, distance_multiplier = 1) if(audiovisual_redirect) var/turf/T = get_turf(src) - audiovisual_redirect.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, 0, -1000, turf_source.x - T.x, turf_source.y - T.y) + audiovisual_redirect.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, 0, -1000, turf_source.x - T.x, turf_source.y - T.y, distance_multiplier) if(!client || !can_hear()) return @@ -37,7 +37,7 @@ S = sound(get_sfx(soundin)) S.wait = 0 //No queue - S.channel = channel || open_sound_channel() + S.channel = channel || SSsounds.random_available_channel() S.volume = vol S.environment = 7 @@ -55,6 +55,8 @@ if(!manual_x && !manual_y) distance = get_dist(T, turf_source) + distance *= distance_multiplier + S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. if(pressure_affected) @@ -86,13 +88,13 @@ dx = turf_source.x - T.x else dx = manual_x - S.x = dx + S.x = dx * distance_multiplier var/dz = 0 // Hearing from infront/behind if(!manual_x) dz = turf_source.y - T.y else dz = manual_y - S.z = dz + S.z = dz * distance_multiplier // The y value is for above your head, but there is no ceiling in 2d spessmens. S.y = 1 S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) @@ -107,15 +109,14 @@ var/mob/M = m M.playsound_local(M, null, volume, vary, frequency, falloff, channel, pressure_affected, S) -/proc/open_sound_channel() - var/static/next_channel = 1 //loop through the available 1024 - (the ones we reserve) channels and pray that its not still being used - . = ++next_channel - if(next_channel > CHANNEL_HIGHEST_AVAILABLE) - next_channel = 1 - /mob/proc/stop_sound_channel(chan) SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = chan)) +/mob/proc/set_sound_channel_volume(channel, volume) + var/sound/S = sound(null, FALSE, FALSE, channel, volume) + S.status = SOUND_UPDATE + SEND_SOUND(src, S) + /client/proc/playtitlemusic(vol = 85) set waitfor = FALSE UNTIL(SSticker.login_music) //wait for SSticker init to set the login music diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index f75772a230..f6e70f8ef5 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -260,3 +260,10 @@ icon = 'icons/obj/clockwork_objects.dmi' icon_state = "clockwork_floor" floor_tile = /obj/item/stack/tile/bronze + +/turf/open/floor/padded + name = "padded floor" + desc = "Keeps crazy people from hurting themselves. It's soft, plush, and very nice to get shoved agaisnt." + icon = 'icons/turf/floors.dmi' + icon_state = "floor_padded" + floor_tile = /obj/item/stack/tile/padded diff --git a/code/game/turfs/simulated/floor/plasteel_floor.dm b/code/game/turfs/simulated/floor/plasteel_floor.dm index a3a92e7898..af48160ef1 100644 --- a/code/game/turfs/simulated/floor/plasteel_floor.dm +++ b/code/game/turfs/simulated/floor/plasteel_floor.dm @@ -68,6 +68,9 @@ /turf/open/floor/plasteel/chapel icon_state = "chapel" +/turf/open/floor/plasteel/chapel_floor + icon_state = "chapel_alt" + /turf/open/floor/plasteel/showroomfloor icon_state = "showroomfloor" diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index d86211f216..62c2dc900c 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1732,9 +1732,8 @@ var/mob/M = locate(href_list["makeeligible"]) if(!ismob(M)) to_chat(usr, "this can only be used on instances of type /mob.") - var/datum/element/ghost_role_eligibility/eli = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility)) - if(M.ckey in eli.timeouts) - eli.timeouts -= M.ckey + if(M.ckey in GLOB.client_ghost_timeouts) + GLOB.client_ghost_timeouts -= M.ckey else if(href_list["sendtoprison"]) if(!check_rights(R_ADMIN)) diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm index 39cdf8a5fa..a9f97942a1 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm @@ -92,10 +92,8 @@ M.cut_overlays() M.regenerate_icons() -/obj/item/clothing/suit/armor/abductor/vest/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - DeactivateStealth() - -/obj/item/clothing/suit/armor/abductor/vest/IsReflect() +/obj/item/clothing/suit/armor/abductor/vest/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + . = ..() DeactivateStealth() /obj/item/clothing/suit/armor/abductor/vest/ui_action_click() @@ -493,7 +491,7 @@ user.do_attack_animation(L) - if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) + if(L.run_block(src, 0, "[user]'s [src]", ATTACK_TYPE_MELEE, 0, user, check_zone(user.zone_selected)) & BLOCK_SUCCESS) playsound(L, 'sound/weapons/genhit.ogg', 50, TRUE) return FALSE diff --git a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm index 3be5eb02e9..814ad2abd7 100644 --- a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm +++ b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm @@ -64,7 +64,7 @@ to_chat(owner, "Your victim's eyes are glazed over. They cannot perceive you.") return FALSE // Check: Target See Me? (behind wall) - if(!(target in view(target_range, get_turf(owner)))) + if(!(target in viewers(target_range, get_turf(owner)))) // Sub-Check: GET CLOSER //if (!(owner in range(target_range, get_turf(target))) // if (display_error) @@ -90,48 +90,56 @@ /datum/action/bloodsucker/targeted/mesmerize/proc/ContinueTarget(atom/A) var/mob/living/carbon/target = A - var/mob/living/user = owner + var/mob/living/L = owner - var/cancontinue=CheckCanTarget(target) + var/cancontinue = CheckCanTarget(target) if(!cancontinue) success = FALSE target.remove_status_effect(STATUS_EFFECT_MESMERIZE) - user.remove_status_effect(STATUS_EFFECT_MESMERIZE) + L.remove_status_effect(STATUS_EFFECT_MESMERIZE) DeactivatePower() DeactivateRangedAbility() StartCooldown() - to_chat(user, "[target] has escaped your gaze!") + to_chat(L, "[target] has escaped your gaze!") UnregisterSignal(target, COMSIG_MOVABLE_MOVED) /datum/action/bloodsucker/targeted/mesmerize/FireTargetedPower(atom/A) // set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done. var/mob/living/carbon/target = A - var/mob/living/user = owner + var/mob/living/L = owner + L.face_atom(A) + if(!istype(target)) + return + success = TRUE + var/power_time = 138 + level_current * 12 + target.apply_status_effect(STATUS_EFFECT_MESMERIZE, 30) + L.apply_status_effect(STATUS_EFFECT_MESMERIZE, 30) + RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/ContinueTarget) + // 5 second windup + addtimer(CALLBACK(src, .proc/apply_effects, L, target, power_time), 6 SECONDS) + ADD_TRAIT(target, TRAIT_COMBAT_MODE_LOCKED, src) + ADD_TRAIT(L, TRAIT_COMBAT_MODE_LOCKED, src) - if(istype(target)) - success = TRUE - var/power_time = 138 + level_current * 12 - target.apply_status_effect(STATUS_EFFECT_MESMERIZE, 30) - user.apply_status_effect(STATUS_EFFECT_MESMERIZE, 30) - - RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/ContinueTarget) +/datum/action/bloodsucker/targeted/mesmerize/proc/apply_effects(aggressor, victim, power_time) + var/mob/living/carbon/target = victim + var/mob/living/L = aggressor + if(!success) + return + PowerActivatedSuccessfully() // blood & cooldown only altered if power activated successfully - less "fuck you"-y + target.apply_status_effect(STATUS_EFFECT_MESMERIZE, power_time) + REMOVE_TRAIT(L, TRAIT_COMBAT_MODE_LOCKED, src) + target.face_atom(L) + target.Stun(power_time) + to_chat(L, "[target] is fixed in place by your hypnotic gaze.") + target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // target.changeNext_move(power_time) // check click.dm + target.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze. + spawn(power_time) + if(istype(target) && success) + target.notransform = FALSE + REMOVE_TRAIT(target, TRAIT_COMBAT_MODE_LOCKED, src) + if(istype(L) && target.stat == CONSCIOUS && (target in view(10, get_turf(L)))) // They Woke Up! (Notice if within view) + to_chat(L, "[target] has snapped out of their trance.") - // 3 second windup - sleep(30) - if(success) - PowerActivatedSuccessfully() // blood & cooldown only altered if power activated successfully - less "fuck you"-y - target.face_atom(user) - target.apply_status_effect(STATUS_EFFECT_MESMERIZE, power_time) // pretty much purely cosmetic - target.Stun(power_time) - to_chat(user, "[target] is fixed in place by your hypnotic gaze.") - target.next_move = world.time + power_time // <--- Use direct change instead. We want an unmodified delay to their next move // target.changeNext_move(power_time) // check click.dm - target.notransform = TRUE // <--- Fuck it. We tried using next_move, but they could STILL resist. We're just doing a hard freeze. - spawn(power_time) - if(istype(target) && success) - target.notransform = FALSE - // They Woke Up! (Notice if within view) - if(istype(user) && target.stat == CONSCIOUS && (target in view(10, get_turf(user))) ) - to_chat(user, "[target] has snapped out of their trance.") /datum/action/bloodsucker/targeted/mesmerize/ContinueActive(mob/living/user, mob/living/target) return ..() && CheckCanUse() && CheckCanTarget(target) diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 49c34f4d1f..8514df4bd6 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -441,23 +441,23 @@ var/remaining_uses //Set by the changeling ability. -/obj/item/shield/changeling/Initialize() +/obj/item/shield/changeling/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) if(ismob(loc)) loc.visible_message("The end of [loc.name]\'s hand inflates rapidly, forming a huge shield-like mass!", "We inflate our hand into a strong shield.", "You hear organic matter ripping and tearing!") -/obj/item/shield/changeling/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(remaining_uses < 1) +/obj/item/shield/changeling/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + block_return[BLOCK_RETURN_BLOCK_CAPACITY] = (block_return[BLOCK_RETURN_BLOCK_CAPACITY] || 0) + remaining_uses + return ..() + +/obj/item/shield/changeling/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + . = ..() + if(--remaining_uses < 1) if(ishuman(loc)) var/mob/living/carbon/human/H = loc H.visible_message("With a sickening crunch, [H] reforms [H.p_their()] shield into an arm!", "We assimilate our shield into our body", "[owner] deflects [attack_text] with [src]!") playsound(src, pick('sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg', 'sound/weapons/effects/ric3.ogg', 'sound/weapons/effects/ric4.ogg', 'sound/weapons/effects/ric5.ogg'), 100, 1) - return TRUE + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT else playsound(src, 'sound/weapons/parry.ogg', 75, 1) owner.visible_message("[owner] parries [attack_text] with [src]!") - return TRUE - return FALSE + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return BLOCK_NONE /obj/item/twohanded/required/cult_bastard/afterattack(atom/target, mob/user, proximity, click_parameters) . = ..() @@ -423,7 +419,13 @@ user.adjustBruteLoss(25) user.dropItemToGround(src, TRUE) -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(current_charges) + block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100 + block_return[BLOCK_RETURN_BLOCK_CAPACITY] = (block_return[BLOCK_RETURN_BLOCK_CAPACITY] || 0) + current_charges + return ..() + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(current_charges) owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") current_charges-- @@ -431,8 +433,8 @@ if(!current_charges) owner.visible_message("The runed shield around [owner] suddenly disappears!") owner.update_inv_wear_suit() - return 1 - return 0 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return BLOCK_NONE /obj/item/clothing/suit/hooded/cultrobes/cult_shield/worn_overlays(isinhands, icon_file, style_flags = NONE) . = list() @@ -734,19 +736,19 @@ playsound(T, 'sound/effects/glassbr3.ogg', 100) qdel(src) -/obj/item/twohanded/cult_spear/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/twohanded/cult_spear/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(wielded) final_block_chance *= 2 if(prob(final_block_chance)) - if(attack_type == PROJECTILE_ATTACK) + if(attack_type & ATTACK_TYPE_PROJECTILE) owner.visible_message("[owner] deflects [attack_text] with [src]!") playsound(src, pick('sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg', 'sound/weapons/effects/ric3.ogg', 'sound/weapons/effects/ric4.ogg', 'sound/weapons/effects/ric5.ogg'), 100, 1) - return TRUE + return BLOCK_SUCCESS | BLOCK_SHOULD_REDIRECT | BLOCK_REDIRECTED | BLOCK_PHYSICAL_EXTERNAL else playsound(src, 'sound/weapons/parry.ogg', 100, 1) owner.visible_message("[owner] parries [attack_text] with [src]!") - return TRUE - return FALSE + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return BLOCK_NONE /datum/action/innate/cult/spear name = "Bloody Bond" @@ -945,10 +947,18 @@ hitsound = 'sound/weapons/smash.ogg' var/illusions = 2 -/obj/item/shield/mirror/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/shield/mirror/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + block_return[BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE] = max(block_return[BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE] || null, final_block_chance) + return ..() + +/obj/item/shield/mirror/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(iscultist(owner)) - if(istype(hitby, /obj/item/projectile)) - var/obj/item/projectile/P = hitby + if(istype(object, /obj/item/projectile) && (attack_type == ATTACK_TYPE_PROJECTILE)) + if(is_energy_reflectable_projectile(object)) + if(prob(final_block_chance)) + return BLOCK_SUCCESS | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL | BLOCK_REDIRECTED + return BLOCK_NONE //To avoid reflection chance double-dipping with block chance + var/obj/item/projectile/P = object if(P.damage >= 30) var/turf/T = get_turf(owner) T.visible_message("The sheer force from [P] shatters the mirror shield!") @@ -956,11 +966,9 @@ playsound(T, 'sound/effects/glassbr3.ogg', 100) owner.DefaultCombatKnockdown(25) qdel(src) - return FALSE - if(P.is_reflectable) - return FALSE //To avoid reflection chance double-dipping with block chance + return BLOCK_NONE . = ..() - if(.) + if(. & BLOCK_SUCCESS) playsound(src, 'sound/weapons/parry.ogg', 100, 1) if(illusions > 0) illusions-- @@ -975,7 +983,7 @@ E.Copy_Parent(owner, 70, 10) E.GiveTarget(owner) E.Goto(owner, owner.movement_delay(), E.minimum_distance) - return TRUE + return else if(prob(50)) var/mob/living/simple_animal/hostile/illusion/H = new(owner.loc) @@ -984,7 +992,7 @@ H.GiveTarget(owner) H.move_to_delay = owner.movement_delay() to_chat(owner, "[src] betrays you!") - return FALSE + return BLOCK_NONE /obj/item/shield/mirror/proc/readd() illusions++ @@ -992,11 +1000,6 @@ var/mob/living/holder = loc to_chat(holder, "The shield's illusions are back at full strength!") -/obj/item/shield/mirror/IsReflect() - if(prob(block_chance)) - return TRUE - return FALSE - /obj/item/shield/mirror/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) var/turf/T = get_turf(hit_atom) var/datum/thrownthing/D = throwingdatum diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm index 2c91e18bd8..b4f896fa08 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm @@ -414,11 +414,11 @@ if(safety) to_chat(usr, "The safety is still on.") return + if(!timing && nuclear_cooldown > world.time) + to_chat(usr, "[src]'s timer protocols are currently on cooldown, please stand by.") + return timing = !timing if(timing) - if(nuclear_cooldown > world.time) - to_chat(usr, "[src]'s timer protocols are currently on cooldown, please stand by.") - return previous_level = get_security_level() detonation_timer = world.time + (timer_set * 10) for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 338d135b4c..f7dc7ad6d1 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -38,8 +38,8 @@ weights[C] = weight var/choice = pickweightAllowZero(weights) if(!choice) - choice = GLOB.traitor_classes[TRAITOR_HUMAN] - set_traitor_kind(pickweightAllowZero(weights)) + choice = GLOB.traitor_classes[TRAITOR_HUMAN] // it's an "easter egg" + set_traitor_kind(choice) traitor_kind.weight *= 0.8 // less likely this round SSticker.mode.traitors += owner owner.special_role = special_role diff --git a/code/modules/antagonists/traitor/syndicate_contract.dm b/code/modules/antagonists/traitor/syndicate_contract.dm index d62792042f..daa9127591 100644 --- a/code/modules/antagonists/traitor/syndicate_contract.dm +++ b/code/modules/antagonists/traitor/syndicate_contract.dm @@ -89,7 +89,7 @@ /datum/syndicate_contract/proc/handleVictimExperience(var/mob/living/M) // They're off to holding - handle the return timer and give some text about what's going on. addtimer(CALLBACK(src, .proc/returnVictim, M), 4 MINUTES) // Ship 'em back - dead or alive... 4 minutes wait. if(M.stat != DEAD) //Even if they weren't the target, we're still treating them the same. - M.reagents.add_reagent(/datum/reagent/medicine/omnizine, 20) // Heal them up - gets them out of crit/soft crit. + M.reagents.add_reagent(/datum/reagent/medicine/regen_jelly, 20) // Heal them up - gets them out of crit/soft crit. -- now 100% toxinlover friendly!! M.flash_act() M.confused += 10 M.blur_eyes(5) diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm index 13ea317b9b..0fbc5cf71c 100644 --- a/code/modules/assembly/flash.dm +++ b/code/modules/assembly/flash.dm @@ -312,7 +312,7 @@ if(holder) holder.update_icon() -/obj/item/assembly/flash/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/assembly/flash/shield/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) activate() return ..() diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm index 19f7bff965..5c34ece723 100644 --- a/code/modules/atmospherics/gasmixtures/gas_types.dm +++ b/code/modules/atmospherics/gasmixtures/gas_types.dm @@ -179,7 +179,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/g /datum/gas/miasma id = "miasma" - specific_heat = 0.00001 + specific_heat = 20 fusion_power = 50 name = "Miasma" gas_overlay = "miasma" diff --git a/code/modules/cargo/exports/gear.dm b/code/modules/cargo/exports/gear.dm index 10b3d4f707..4db7c68efd 100644 --- a/code/modules/cargo/exports/gear.dm +++ b/code/modules/cargo/exports/gear.dm @@ -309,7 +309,7 @@ /datum/export/gear/combatgloves cost = 80 unit_name = "combat gloves" - export_types = list(/obj/item/clothing/gloves/combat, /obj/item/clothing/gloves/rapid, /obj/item/clothing/gloves/krav_maga) + export_types = list(/obj/item/clothing/gloves/combat, /obj/item/clothing/gloves/fingerless/pugilist/rapid, /obj/item/clothing/gloves/krav_maga) include_subtypes = TRUE /datum/export/gear/bonegloves diff --git a/code/modules/cargo/exports/parts.dm b/code/modules/cargo/exports/parts.dm index e4efaf0810..660d79cae4 100644 --- a/code/modules/cargo/exports/parts.dm +++ b/code/modules/cargo/exports/parts.dm @@ -112,7 +112,7 @@ /datum/export/glasswork_lens cost = 1800 unit_name = "small glass lens" - export_types = list(/obj/item/lens) + export_types = list(/obj/item/glasswork/glass_base/lens) /datum/export/glasswork_spouty cost = 1200 @@ -131,3 +131,27 @@ unit_name = "large flask" export_types = list(/obj/item/reagent_containers/glass/beaker/flask/large) include_subtypes = FALSE + +/datum/export/glasswork_teaplate + cost = 1200 + unit_name = "tea gear" + export_types = list(/obj/item/tea_plate) + include_subtypes = FALSE + +/datum/export/glasswork_teacup + cost = 1800 + unit_name = "tea gear" + export_types = list(/obj/item/tea_cup) + include_subtypes = FALSE + +/datum/export/glasswork_laserpointer + cost = 2600 + unit_name = "hand made laserpointer" + export_types = list(/obj/item/laser_pointer/blue/handmade) + include_subtypes = FALSE + +/datum/export/glasswork_glasses + cost = 5000 + unit_name = "hand made glasses" + export_types = list(/obj/item/glasswork/glasses) + include_subtypes = FALSE diff --git a/code/modules/cargo/exports/weapons.dm b/code/modules/cargo/exports/weapons.dm index 836dc044b7..2342603bde 100644 --- a/code/modules/cargo/exports/weapons.dm +++ b/code/modules/cargo/exports/weapons.dm @@ -287,7 +287,7 @@ /datum/export/weapon/gloves cost = 90 unit_name = "star struck gloves" - export_types = list(/obj/item/clothing/gloves/rapid) + export_types = list(/obj/item/clothing/gloves/fingerless/pugilist/rapid) /datum/export/weapon/l6 cost = 500 diff --git a/code/modules/cargo/packs/misc.dm b/code/modules/cargo/packs/misc.dm index 273acb10e2..14828729cd 100644 --- a/code/modules/cargo/packs/misc.dm +++ b/code/modules/cargo/packs/misc.dm @@ -152,7 +152,7 @@ /obj/item/instrument/trombone, /obj/item/instrument/recorder, /obj/item/instrument/harmonica, - /obj/structure/piano/unanchored) + /obj/structure/musician/piano/unanchored) crate_type = /obj/structure/closet/crate/wooden /datum/supply_pack/misc/casinocrate diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 7bafefcf53..6f62701590 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -529,7 +529,7 @@ GLOBAL_LIST_EMPTY(asset_datums) "pill21" = 'icons/UI_Icons/Pills/pill21.png', "pill22" = 'icons/UI_Icons/Pills/pill22.png', ) - + /datum/asset/simple/IRV assets = list( "jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js', @@ -704,9 +704,43 @@ GLOBAL_LIST_EMPTY(asset_datums) Insert(initial(D.id), I) return ..() +/datum/asset/spritesheet/vending + name = "vending" + +/datum/asset/spritesheet/vending/register() + for(var/k in GLOB.vending_products) + var/atom/item = k + if(!ispath(item, /atom)) + continue + + var/icon_file = initial(item.icon) + var/icon_state = initial(item.icon_state) + var/icon/I + + var/icon_states_list = icon_states(icon_file) + if(icon_state in icon_states_list) + I = icon(icon_file, icon_state, SOUTH) + var/c = initial(item.color) + if(!isnull(c) && c != "#FFFFFF") + I.Blend(c, ICON_MULTIPLY) + else + var/icon_states_string + for(var/an_icon_state in icon_states_list) + if(!icon_states_string) + icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])" + else + icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])" + stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]") + I = icon('icons/turf/floors.dmi', "", SOUTH) + + var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-") + + Insert(imgid, I) + return ..() + /datum/asset/simple/genetics assets = list( "dna_discovered.gif" = 'html/dna_discovered.gif', "dna_undiscovered.gif" = 'html/dna_undiscovered.gif', "dna_extra.gif" = 'html/dna_extra.gif' -) \ No newline at end of file +) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 7e2d73190a..780eda532b 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -3,6 +3,7 @@ resistance_flags = FLAMMABLE max_integrity = 200 integrity_failure = 0.4 + block_priority = BLOCK_PRIORITY_CLOTHING var/damaged_clothes = 0 //similar to machine's BROKEN stat and structure's broken var var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index cf3541fd89..e2af067e8c 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -207,11 +207,11 @@ /obj/item/clothing/gloves/color/latex/equipped(mob/user, slot) ..() if(slot == SLOT_GLOVES) - ADD_TRAIT(user, carrytrait, CLOTHING_TRAIT) + ADD_TRAIT(user, carrytrait, GLOVE_TRAIT) /obj/item/clothing/gloves/color/latex/dropped(mob/user) ..() - REMOVE_TRAIT(user, carrytrait, CLOTHING_TRAIT) + REMOVE_TRAIT(user, carrytrait, GLOVE_TRAIT) /obj/item/clothing/gloves/color/latex/nitrile name = "nitrile gloves" diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index 9f4b89f607..3d7315b88d 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -12,6 +12,135 @@ min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT strip_mod = 0.9 +/obj/item/clothing/gloves/fingerless/pugilist + name = "armwraps" + desc = "A series of armwraps. Makes you pretty keen to start punching people." + icon_state = "armwraps" + item_state = "armwraps" + body_parts_covered = ARMS + cold_protection = ARMS + strip_delay = 300 //you can't just yank them off + ///Extra damage through the punch. + var/enhancement = 0 //it's a +0 to your punches because it isn't magical + ///Main trait added by the gloves to the user on wear. + var/inherited_trait = TRAIT_NOGUNS //what are you, dishonoroable? + ///Secondary trait added by the gloves to the user on wear. + var/secondary_trait = TRAIT_FEARLESS //what are you, a coward? + +/obj/item/clothing/gloves/fingerless/pugilist/equipped(mob/user, slot) + . = ..() + if(slot == SLOT_GLOVES) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + ADD_TRAIT(H, TRAIT_PUGILIST, GLOVE_TRAIT) + ADD_TRAIT(H, inherited_trait, GLOVE_TRAIT) + ADD_TRAIT(H, secondary_trait, GLOVE_TRAIT) + H.dna.species.punchdamagehigh += enhancement + H.dna.species.punchdamagelow += enhancement + +/obj/item/clothing/gloves/fingerless/pugilist/dropped(mob/user) + + REMOVE_TRAIT(user, TRAIT_PUGILIST, GLOVE_TRAIT) + REMOVE_TRAIT(user, inherited_trait, GLOVE_TRAIT) + REMOVE_TRAIT(user, secondary_trait, GLOVE_TRAIT) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.dna.species.punchdamagehigh = initial(H.dna.species.punchdamagehigh) + H.dna.species.punchdamagelow = initial(H.dna.species.punchdamagelow) + return ..() + +/obj/item/clothing/gloves/fingerless/pugilist/chaplain + name = "armwraps of unyielding resolve" + desc = "A series of armwraps, soaked in holy water. Makes you pretty keen to smite evil magic users." + resistance_flags = FIRE_PROOF | ACID_PROOF + enhancement = 1 //It is not magic that makes you punch harder, but force of will. Trust me. + secondary_trait = TRAIT_ANTIMAGIC + var/chaplain_spawnable = TRUE + +/obj/item/clothing/gloves/fingerless/pugilist/chaplain/Initialize() + . = ..() + AddComponent(/datum/component/anti_magic, TRUE, TRUE, FALSE, null, null, FALSE) + +/obj/item/clothing/gloves/fingerless/pugilist/magic + name = "armwraps of mighty fists" + desc = "A series of armwraps. Makes you pretty keen to go adventuring and punch dragons." + resistance_flags = FIRE_PROOF | ACID_PROOF //magic items are harder to damage with energy this is a dnd joke okay? + enhancement = 1 //They're +1! + +/obj/item/clothing/gloves/fingerless/pugilist/hungryghost + name = "armwraps of the hungry ghost" + desc = "A series of blackened, bloodstained armwraps stitched with strange geometric symbols. Makes you pretty keen to commit horrible acts against the living through bloody carnage." + icon_state = "narsiearmwraps" + item_state = "narsiearmwraps" + resistance_flags = FIRE_PROOF | ACID_PROOF + armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 35, "rad" = 0, "fire" = 50, "acid" = 50) + enhancement = 3 + secondary_trait = TRAIT_KI_VAMPIRE + +/obj/item/clothing/gloves/fingerless/pugilist/brassmountain + name = "armbands of the brass mountain" + desc = "A series of scolding hot brass armbands. Makes you pretty keen to bring the light to the unenlightened through unmitigated violence." + icon_state = "ratvararmwraps" + item_state = "ratvararmwraps" + resistance_flags = FIRE_PROOF | ACID_PROOF + armor = list("melee" = 10, "bullet" = 0, "laser" = -10, "energy" = 0, "bomb" = 0, "bio" = 35, "rad" = 0, "fire" = 50, "acid" = 50) + enhancement = 4 //The artifice of Ratvar is unmatched except when it is. + secondary_trait = TRAIT_STRONG_GRABBER + +/obj/item/clothing/gloves/fingerless/pugilist/rapid + name = "Bands of the North Star" + desc = "The armbands of a deadly martial artist. Makes you pretty keen to put an end to evil in an extremely violent manner." + icon_state = "rapid" + item_state = "rapid" + enhancement = 10 //omae wa mou shindeiru + var/warcry = "AT" + secondary_trait = TRAIT_NOSOFTCRIT //basically extra health + +/obj/item/clothing/gloves/fingerless/pugilist/rapid/Initialize() + . = ..() + ADD_TRAIT(src, TRAIT_NODROP, GLOVE_TRAIT) + +/obj/item/clothing/gloves/fingerless/pugilist/rapid/Touch(atom/target, proximity = TRUE) + if(!isliving(target)) + return + + var/mob/living/M = loc + M.changeNext_move(CLICK_CD_RAPID) + if(warcry) + M.say("[warcry]", ignore_spam = TRUE, forced = TRUE) + + return FALSE + +/obj/item/clothing/gloves/fingerless/pugilist/rapid/AltClick(mob/user) + var/input = stripped_input(user,"What do you want your battlecry to be? Max length of 6 characters.", ,"", 7) + if(input) + warcry = input + +/obj/item/clothing/gloves/fingerless/pugilist/rapid/hug + name = "Hugs of the North Star" + desc = "The armbands of a humble friend. Makes you pretty keen to go let everyone know how much you appreciate them!" + warcry = "owo" //Shouldn't ever come into play + enhancement = 0 + secondary_trait = TRAIT_PACIFISM //You are only here to hug and be friends! + +/obj/item/clothing/gloves/fingerless/pugilist/rapid/hug/Touch(mob/target, proximity = TRUE) + if(!isliving(target)) + return + + var/mob/living/M = loc + + if(M.a_intent != INTENT_HELP) + return FALSE + if(target.stat != CONSCIOUS) //Can't hug people who are dying/dead + return FALSE + else + M.changeNext_move(CLICK_CD_RAPID) + + return FALSE + +/obj/item/clothing/gloves/fingerless/pugilist/rapid/hug/AltClick(mob/user) + return FALSE + /obj/item/clothing/gloves/botanic_leather name = "botanist's leather gloves" desc = "These leather gloves protect against thorns, barbs, prickles, spikes and other harmful objects of floral origin. They're also quite warm." @@ -59,54 +188,6 @@ resistance_flags = NONE armor = list("melee" = 15, "bullet" = 35, "laser" = 35, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0) -/obj/item/clothing/gloves/rapid - name = "Gloves of the North Star" - desc = "Just looking at these fills you with an urge to beat the shit out of people. Violently." - icon_state = "rapid" - item_state = "rapid" - transfer_prints = TRUE - var/warcry = "AT" - -/obj/item/clothing/gloves/rapid/Touch(mob/living/target,proximity = TRUE) - if(!istype(target)) - return - - var/mob/living/M = loc - M.changeNext_move(CLICK_CD_RAPID) - M.adjustStaminaLoss(-3.5) // used to be -2 with some comment about stamina buffer management but *shrug -hatterhat - if(warcry) - M.say("[warcry]", ignore_spam = TRUE, forced = "north star warcry") - - .= FALSE - - -/obj/item/clothing/gloves/rapid/attack_self(mob/user) - var/input = stripped_input(user,"What do you want your battlecry to be? Max length of 6 characters.", ,"", 7) - if(input) - warcry = input - -/obj/item/clothing/gloves/rapid/hug - name = "Hugs of the North Star" - desc = "Just looking at these fills you with an urge to hug the shit out of people. In a very friendly manner." - warcry = "owo" //Shouldn't ever come into play - -/obj/item/clothing/gloves/rapid/hug/Touch(mob/living/target,proximity = TRUE) - if(!istype(target)) - return - - var/mob/living/M = loc - - if(M.a_intent == INTENT_HELP) - if(target.health >= 0 && !HAS_TRAIT(target, TRAIT_FAKEDEATH)) //Can't hug people who are dying/dead - if(target.on_fire || target.lying) //No spamming extinguishing, helping them up, or other non-hugging/patting help interactions - return - else - M.changeNext_move(CLICK_CD_RAPID) - . = FALSE - -/obj/item/clothing/gloves/rapid/hug/attack_self(mob/user) - return FALSE - /obj/item/clothing/gloves/thief name = "black gloves" desc = "Gloves made with completely frictionless, insulated cloth, easier to steal from people with." @@ -117,4 +198,4 @@ strip_delay = 80 transfer_prints = FALSE strip_mod = 5 - strip_silence = TRUE \ No newline at end of file + strip_silence = TRUE diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 0b58ea234c..d5632a1e4c 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -187,14 +187,15 @@ /obj/item/clothing/head/wig name = "wig" desc = "A bunch of hair without a head attached." - icon_state = "" - item_state = "pwig" + icon = 'icons/mob/human_face.dmi' // default icon for all hairs + icon_state = "hair_vlong" flags_inv = HIDEHAIR + color = "#000" var/hair_style = "Very Long Hair" - var/hair_color = "#000" /obj/item/clothing/head/wig/Initialize(mapload) . = ..() + icon_state = "" //Shitty hack that i dont know if it is even neccesary to deal with the vendor stack exception update_icon() /obj/item/clothing/head/wig/update_icon_state() @@ -202,14 +203,9 @@ if(!S) icon = 'icons/obj/clothing/hats.dmi' icon_state = "pwig" - -/obj/item/clothing/head/wig/update_overlays() - . = ..() - var/datum/sprite_accessory/S = GLOB.hair_styles_list[hair_style] - if(S) - var/mutable_appearance/M = mutable_appearance(S.icon, S.icon_state, color = hair_color) - M.appearance_flags |= RESET_COLOR - . += M + else + icon = S.icon + icon_state = S.icon_state /obj/item/clothing/head/wig/worn_overlays(isinhands = FALSE, icon_file, style_flags = NONE) . = list() @@ -219,12 +215,12 @@ return var/mutable_appearance/M = mutable_appearance(S.icon, S.icon_state,layer = -HAIR_LAYER) M.appearance_flags |= RESET_COLOR - M.color = hair_color + M.color = color . += M /obj/item/clothing/head/wig/random/Initialize(mapload) hair_style = pick(GLOB.hair_styles_list - "Bald") //Don't want invisible wig - hair_color = "#[random_short_color()]" + color = "#[random_short_color()]" . = ..() /obj/item/clothing/head/bronze diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 1f23569a9c..26767395ed 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -762,7 +762,13 @@ if(!allowed) allowed = GLOB.advanced_hardsuit_allowed -/obj/item/clothing/suit/space/hardsuit/shielded/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/clothing/suit/space/hardsuit/shielded/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(current_charges > 0) + block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100 + block_return[BLOCK_RETURN_BLOCK_CAPACITY] = (block_return[BLOCK_RETURN_BLOCK_CAPACITY] || 0) + current_charges + return ..() + +/obj/item/clothing/suit/space/hardsuit/shielded/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) recharge_cooldown = world.time + recharge_delay if(current_charges > 0) var/datum/effect_system/spark_spread/s = new @@ -776,8 +782,8 @@ owner.visible_message("[owner]'s shield overloads!") shield_state = "broken" owner.update_inv_wear_suit() - return 1 - return 0 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return BLOCK_NONE /obj/item/clothing/suit/space/hardsuit/shielded/Destroy() STOP_PROCESSING(SSobj, src) diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index 8c39427c49..b01fe5eb8e 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -456,7 +456,8 @@ Contains: armor = list("melee" = 5, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 0, "acid" = 0) strip_delay = 65 -/obj/item/clothing/suit/space/fragile/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/clothing/suit/space/fragile/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + . = ..() if(!torn && prob(50) && damage >= 5) to_chat(owner, "[src] tears from the damage, breaking the air-tight seal!") clothing_flags &= ~STOPSPRESSUREDAMAGE diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index 1b2080feb1..e01ec2dde8 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -1,6 +1,7 @@ /obj/item/clothing/suit icon = 'icons/obj/clothing/suits.dmi' name = "suit" + block_priority = BLOCK_PRIORITY_WEAR_SUIT var/fire_resist = T0C+100 allowed = list(/obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 7384fbde21..a19dfaa889 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -173,12 +173,13 @@ armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF var/hit_reflect_chance = 40 + var/list/protected_zones = list(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN) -/obj/item/clothing/suit/armor/laserproof/IsReflect(def_zone) - if(!(def_zone in list(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN))) //If not shot where ablative is covering you, you don't get the reflection bonus! - return 0 - if (prob(hit_reflect_chance)) - return 1 +/obj/item/clothing/suit/armor/laserproof/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(def_zone in protected_zones) + if(prob(hit_reflect_chance)) + return BLOCK_SHOULD_REDIRECT | BLOCK_REDIRECTED | BLOCK_SUCCESS | BLOCK_PHYSICAL_INTERNAL + return ..() /obj/item/clothing/suit/armor/vest/det_suit name = "detective's armor vest" diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm index 87e7098ebd..c8f54f8bda 100644 --- a/code/modules/clothing/suits/reactive_armour.dm +++ b/code/modules/clothing/suits/reactive_armour.dm @@ -36,8 +36,8 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) actions_types = list(/datum/action/item_action/toggle) resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - hit_reaction_chance = 50 // Only on the chest yet blocks all attacks? rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE + var/hit_reaction_chance = 50 /obj/item/clothing/suit/armor/reactive/attack_self(mob/user) active = !(active) @@ -62,6 +62,14 @@ item_state = "reactiveoff" reactivearmor_cooldown = world.time + 200 +/obj/item/clothing/suit/armor/reactive/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!active) + return BLOCK_NONE + return block_action(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + +/obj/item/clothing/suit/armor/reactive/proc/block_action(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + return BLOCK_NONE + //When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) /obj/item/clothing/suit/armor/reactive/teleport name = "reactive teleport armor" @@ -71,18 +79,15 @@ var/rad_amount_before = 120 reactivearmor_cooldown_duration = 100 -/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - . = FALSE - if(!active) - return +/obj/item/clothing/suit/armor/reactive/teleport/block_action(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(prob(hit_reaction_chance)) var/mob/living/carbon/human/H = owner if(world.time < reactivearmor_cooldown) owner.visible_message("The reactive teleport system is still recharging! It fails to teleport [H]!") return owner.visible_message("The reactive teleport system flings [H] clear of [attack_text], shutting itself off in the process!") - playsound(get_turf(owner),'sound/magic/blink.ogg', 100, 1) - var/list/turfs = new/list() + playsound(get_turf(owner), 'sound/magic/blink.ogg', 100, 1) + var/list/turfs = new var/turf/old = get_turf(src) for(var/turf/T in orange(tele_range, H)) if(T.density) @@ -101,7 +106,9 @@ radiation_pulse(old, rad_amount_before) radiation_pulse(src, rad_amount) reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration - return TRUE + block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_PASSTHROUGH + return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT | BLOCK_TARGET_DODGED + return BLOCK_NONE //Fire @@ -109,9 +116,7 @@ name = "reactive incendiary armor" desc = "An experimental suit of armor with a reactive sensor array rigged to a flame emitter. For the stylish pyromaniac." -/obj/item/clothing/suit/armor/reactive/fire/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 +/obj/item/clothing/suit/armor/reactive/fire/block_action(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(prob(hit_reaction_chance)) if(world.time < reactivearmor_cooldown) owner.visible_message("The reactive incendiary armor on [owner] activates, but fails to send out flames as it is still recharging its flame jets!") @@ -124,8 +129,8 @@ C.IgniteMob() owner.fire_stacks = -20 reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration - return 1 - return 0 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_INTERNAL + return BLOCK_NONE //Stealth @@ -134,9 +139,7 @@ reactivearmor_cooldown_duration = 65 desc = "An experimental suit of armor that renders the wearer invisible on detection of imminent harm, and creates a decoy that runs away from the owner. You can't fight what you can't see." -/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 +/obj/item/clothing/suit/armor/reactive/stealth/block_action(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(prob(hit_reaction_chance)) if(world.time < reactivearmor_cooldown) owner.visible_message("The reactive stealth system on [owner] activates, but is still recharging its holographic emitters!") @@ -150,7 +153,8 @@ spawn(40) owner.alpha = initial(owner.alpha) reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration - return 1 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_INTERNAL + return BLOCK_NONE //Tesla @@ -175,9 +179,7 @@ if(slot_flags & slotdefine2slotbit(slot)) //Was equipped to a valid slot for this item? user.flags_1 |= TESLA_IGNORE_1 -/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return FALSE +/obj/item/clothing/suit/armor/reactive/tesla/block_action(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(prob(hit_reaction_chance)) if(world.time < reactivearmor_cooldown) var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread @@ -196,7 +198,8 @@ M.adjustFireLoss(legacy_dmg) playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1) reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration - return TRUE + return BLOCK_SUCCESS | BLOCK_PHYSICAL_INTERNAL + return NONE //Repulse @@ -205,9 +208,7 @@ reactivearmor_cooldown_duration = 20 desc = "An experimental suit of armor that violently throws back attackers." -/obj/item/clothing/suit/armor/reactive/repulse/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 +/obj/item/clothing/suit/armor/reactive/repulse/block_action(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(prob(hit_reaction_chance)) if(world.time < reactivearmor_cooldown) owner.visible_message("The repulse generator is still recharging!") @@ -234,7 +235,8 @@ AM.throw_at(throwtarget,10,1) safety-- reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration - return 1 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_INTERNAL + return BLOCK_NONE /obj/item/clothing/suit/armor/reactive/table name = "reactive table armor" @@ -242,9 +244,7 @@ desc = "If you can't beat the memes, embrace them." var/tele_range = 10 -/obj/item/clothing/suit/armor/reactive/table/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 +/obj/item/clothing/suit/armor/reactive/table/block_action(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(prob(hit_reaction_chance)) var/mob/living/carbon/human/H = owner if(world.time < reactivearmor_cooldown) @@ -270,8 +270,8 @@ H.forceMove(picked) new /obj/structure/table(get_turf(owner)) reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration - return 1 - return 0 + return BLOCK_SUCCESS | BLOCK_PHYSICAL_INTERNAL + return BLOCK_NONE /obj/item/clothing/suit/armor/reactive/table/emp_act() return diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index cc91391393..45aee9085c 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -3,6 +3,7 @@ name = "under" body_parts_covered = CHEST|GROIN|LEGS|ARMS permeability_coefficient = 0.9 + block_priority = BLOCK_PRIORITY_UNIFORM slot_flags = ITEM_SLOT_ICLOTHING armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) var/fitted = FEMALE_UNIFORM_FULL // For use in alternate clothing styles for women diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index 6b40e08af9..f612138984 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -79,9 +79,11 @@ name = "ancient jumpsuit" desc = "A terribly ragged and frayed grey jumpsuit. It looks like it hasn't been washed in over a decade." -/obj/item/clothing/under/color/grey/glorf/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - owner.forcesay(GLOB.hit_appends) - return 0 +/obj/item/clothing/under/color/grey/glorf/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + . = ..() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + H.forcesay(GLOB.hit_appends) /obj/item/clothing/under/color/blue name = "blue jumpsuit" diff --git a/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm b/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm index 74181b05eb..42902bf7a7 100644 --- a/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm +++ b/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm @@ -12,7 +12,6 @@ item_state = "explorer_envirosuit" item_color = "explorer_envirosuit" - /obj/item/clothing/under/plasmaman/chef name = "chef's plasma envirosuit" desc = "A white plasmaman envirosuit designed for cullinary practices. One might question why a member of a species that doesn't need to eat would become a chef." @@ -69,6 +68,8 @@ item_state = "captain_envirosuit" item_color = "captain_envirosuit" armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 95, "acid" = 95) + sensor_mode = SENSOR_COORDS + random_sensor = FALSE /obj/item/clothing/under/plasmaman/mime name = "mime envirosuit" @@ -97,4 +98,4 @@ H.visible_message("[H]'s suit spews out a tonne of space lube!","Your suit spews out a tonne of space lube!") H.ExtinguishMob() new /obj/effect/particle_effect/foam(loc) //Truely terrifying. - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/clothing/under/jobs/Plasmaman/security.dm b/code/modules/clothing/under/jobs/Plasmaman/security.dm index ff756e09fd..6ef9eb9e53 100644 --- a/code/modules/clothing/under/jobs/Plasmaman/security.dm +++ b/code/modules/clothing/under/jobs/Plasmaman/security.dm @@ -5,6 +5,8 @@ item_state = "security_envirosuit" item_color = "security_envirosuit" armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 0, "fire" = 95, "acid" = 95) + sensor_mode = SENSOR_COORDS + random_sensor = FALSE /obj/item/clothing/under/plasmaman/security/warden name = "warden plasma envirosuit" diff --git a/code/modules/clothing/under/jobs/civilian/curator.dm b/code/modules/clothing/under/jobs/civilian/curator.dm index c02cc4b710..741407d71d 100644 --- a/code/modules/clothing/under/jobs/civilian/curator.dm +++ b/code/modules/clothing/under/jobs/civilian/curator.dm @@ -13,7 +13,6 @@ item_state = "red_suit" item_color = "red_suit_skirt" body_parts_covered = CHEST|GROIN|ARMS - can_adjust = FALSE fitted = FEMALE_UNIFORM_TOP /obj/item/clothing/under/rank/civilian/curator/treasure_hunter @@ -37,5 +36,4 @@ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - can_adjust = FALSE resistance_flags = NONE diff --git a/code/modules/clothing/under/jobs/rnd.dm b/code/modules/clothing/under/jobs/rnd.dm index 142cb8d8b1..5af6ce26bf 100644 --- a/code/modules/clothing/under/jobs/rnd.dm +++ b/code/modules/clothing/under/jobs/rnd.dm @@ -14,7 +14,6 @@ item_state = "lb_suit" item_color = "director_skirt" body_parts_covered = CHEST|GROIN|ARMS - can_adjust = FALSE fitted = FEMALE_UNIFORM_TOP /obj/item/clothing/under/rank/rnd/research_director/alt diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index 21e088f634..e91125c152 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -8,6 +8,11 @@ /* * Security */ +/obj/item/clothing/under/rank/security + strip_delay = 50 + alt_covers_chest = TRUE + sensor_mode = SENSOR_COORDS + random_sensor = FALSE /obj/item/clothing/under/rank/security/officer name = "security jumpsuit" @@ -16,10 +21,6 @@ item_state = "r_suit" item_color = "rsecurity" armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - strip_delay = 50 - alt_covers_chest = TRUE - sensor_mode = SENSOR_COORDS - random_sensor = FALSE /obj/item/clothing/under/rank/security/officer/grey name = "grey security jumpsuit" @@ -73,10 +74,6 @@ item_state = "r_suit" item_color = "rwarden" armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - strip_delay = 50 - alt_covers_chest = TRUE - sensor_mode = 3 - random_sensor = FALSE /obj/item/clothing/under/rank/security/warden/grey name = "grey security suit" @@ -114,10 +111,6 @@ item_state = "det" item_color = "detective" armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - strip_delay = 50 - alt_covers_chest = TRUE - sensor_mode = 3 - random_sensor = FALSE /obj/item/clothing/under/rank/security/detective/skirt name = "detective's suitskirt" @@ -135,7 +128,6 @@ icon_state = "greydet" item_state = "greydet" item_color = "greydet" - alt_covers_chest = TRUE /obj/item/clothing/under/rank/security/detective/grey/skirt name = "noir suitskirt" @@ -159,9 +151,6 @@ item_color = "rhos" armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) strip_delay = 60 - alt_covers_chest = TRUE - sensor_mode = 3 - random_sensor = FALSE /obj/item/clothing/under/rank/security/head_of_security/skirt name = "head of security's jumpskirt" @@ -203,7 +192,6 @@ icon_state = "hosblueclothes" item_state = "hosblueclothes" item_color = "hosblueclothes" - alt_covers_chest = TRUE /obj/item/clothing/under/rank/security/head_of_security/parade name = "head of security's parade uniform" @@ -220,4 +208,3 @@ item_state = "r_suit" item_color = "hos_parade_fem" fitted = FEMALE_UNIFORM_TOP - can_adjust = FALSE diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm index 884769ebd4..b2e016635f 100644 --- a/code/modules/holodeck/items.dm +++ b/code/modules/holodeck/items.dm @@ -34,10 +34,10 @@ . = ..() item_color = "red" -/obj/item/holo/esword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(active) +/obj/item/holo/esword/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!active) return ..() - return 0 + return ..() /obj/item/holo/esword/attack(target as mob, mob/user as mob) ..() diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm index 80792538d6..eba7a3d3c4 100644 --- a/code/modules/hydroponics/grown/berries.dm +++ b/code/modules/hydroponics/grown/berries.dm @@ -90,7 +90,7 @@ lifespan = 30 endurance = 25 mutatelist = list() - genes = list(/datum/plant_gene/trait/glow/white, /datum/plant_gene/trait/noreact, /datum/plant_gene/trait/repeated_harvest) + genes = list(/datum/plant_gene/trait/glow/white, /datum/plant_gene/trait/repeated_harvest) reagents_add = list(/datum/reagent/uranium = 0.25, /datum/reagent/iodine = 0.2, /datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.1) rarity = 20 diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index 44abb4939b..f356ed7b68 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -492,9 +492,6 @@ return var/turf/T = get_turf(src) reagents.chem_temp = 1000 - //Disable seperated contents when the grenade primes - if (seed.get_gene(/datum/plant_gene/trait/noreact)) - DISABLE_BITFIELD(reagents.reagents_holder_flags, NO_REACT) reagents.handle_reactions() log_game("Coconut bomb detonation at [AREACOORD(T)], location [loc]") qdel(src) diff --git a/code/modules/hydroponics/grown/tobacco.dm b/code/modules/hydroponics/grown/tobacco.dm index 1e2d25d825..db929d45fb 100644 --- a/code/modules/hydroponics/grown/tobacco.dm +++ b/code/modules/hydroponics/grown/tobacco.dm @@ -34,6 +34,7 @@ mutatelist = list() reagents_add = list(/datum/reagent/medicine/salbutamol = 0.05, /datum/reagent/drug/nicotine = 0.08, /datum/reagent/consumable/nutriment = 0.03) rarity = 20 + genes = list(/datum/plant_gene/trait/smoke) //get it? because you smoke tobacco? i'm hilarious. /obj/item/reagent_containers/food/snacks/grown/tobacco/space seed = /obj/item/seeds/tobacco/space diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index bf8aa888cc..ef2eff707f 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -216,6 +216,10 @@ name = "Liquid Contents" examine_line = "It has a lot of liquid contents inside." +/datum/plant_gene/trait/squash/on_slip(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/C) + // Squash the plant on slip. + G.squash(C) + /datum/plant_gene/trait/slip // Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery. // Applies other trait effects (teleporting, etc) to the target by on_slip. @@ -364,20 +368,6 @@ new /obj/effect/decal/cleanable/molten_object(T) //Leave a pile of goo behind for dramatic effect... qdel(G) - -/datum/plant_gene/trait/noreact - // Makes plant reagents not react until squashed. - name = "Separated Chemicals" - -/datum/plant_gene/trait/noreact/on_new(obj/item/reagent_containers/food/snacks/grown/G, newloc) - ..() - ENABLE_BITFIELD(G.reagents.reagents_holder_flags, NO_REACT) - -/datum/plant_gene/trait/noreact/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target) - DISABLE_BITFIELD(G.reagents.reagents_holder_flags, NO_REACT) - G.reagents.handle_reactions() - - /datum/plant_gene/trait/maxchem // 2x to max reagents volume. name = "Densified Chemicals" diff --git a/code/modules/instruments/instrument_data/_instrument_data.dm b/code/modules/instruments/instrument_data/_instrument_data.dm new file mode 100644 index 0000000000..447bcdffb1 --- /dev/null +++ b/code/modules/instruments/instrument_data/_instrument_data.dm @@ -0,0 +1,107 @@ +/proc/path_to_instrument_ids(path) + if(!ispath(path)) + path = text2path(path) + if(!ispath(path)) + return + if(!ispath(path, /datum/instrument)) + return + . = list() + for(var/i in typesof(path)) + var/datum/instrument/I = i + var/init_id = initial(I.id) + if(init_id) + . |= init_id + +/// Get all non admin_only instruments. +/proc/get_allowed_instrument_ids() + . = list() + for(var/id in SSinstruments.instrument_data) + var/datum/instrument/I = SSinstruments.instrument_data[id] + if(!I.admin_only) + . += I.id + +/datum/instrument + /// Name of the instrument + var/name = "Generic instrument" + /// Uniquely identifies this instrument so runtime changes are possible as opposed to paths. If this is unset, things will use path instead. + var/id + /// Category + var/category = "Unsorted" + /// Used for categorization subtypes + var/abstract_type = /datum/instrument + /// Write here however many samples, follow this syntax: "%note num%"='%sample file%' eg. "27"='synthesizer/e2.ogg'. Key must never be lower than 0 and higher than 127 + var/list/real_samples + /// assoc list key = /datum/instrument_key. do not fill this yourself! + var/list/samples + /// See __DEFINES/flags/instruments.dm + var/instrument_flags = NONE + /// For legacy instruments, the path to our notes + var/legacy_instrument_path + /// For legacy instruments, our file extension + var/legacy_instrument_ext + /// What songs are using us + var/list/datum/song/songs_using = list() + /// Don't touch this + var/static/HIGHEST_KEY = 127 + /// Don't touch this x2 + var/static/LOWEST_KEY = 0 + /// Oh no - For truly troll instruments. + var/admin_only = FALSE + /// Volume multiplier. Synthesized instruments are quite loud and I don't like to cut off potential detail via editing. (someone correct me if this isn't a thing) + var/volume_multiplier = 1/3 + +/datum/instrument/New() + if(isnull(id)) + id = "[type]" + +/datum/instrument/proc/Initialize() + if(CHECK_BITFIELD(instrument_flags, INSTRUMENT_LEGACY | INSTRUMENT_DO_NOT_AUTOSAMPLE)) + return + calculate_samples() + +/datum/instrument/proc/ready() + if(CHECK_BITFIELD(instrument_flags, INSTRUMENT_LEGACY)) + return legacy_instrument_path && legacy_instrument_ext + else if(CHECK_BITFIELD(instrument_flags, INSTRUMENT_DO_NOT_AUTOSAMPLE)) + return length(samples) + return (length(samples) >= 128) + +/datum/instrument/Destroy() + SSinstruments.instrument_data -= id + for(var/i in songs_using) + var/datum/song/S = i + S.set_instrument(null) + real_samples = null + samples = null + songs_using = null + return ..() + +/datum/instrument/proc/calculate_samples() + if(!length(real_samples)) + CRASH("No real samples defined for [id] [type] on calculate_samples() call.") + var/list/real_keys = list() + samples = list() + for(var/key in real_samples) + real_keys += text2num(key) + sortTim(real_keys, /proc/cmp_numeric_asc, associative = FALSE) + + for(var/i in 1 to (length(real_keys) - 1)) + var/from_key = real_keys[i] + var/to_key = real_keys[i+1] + var/sample1 = real_samples[num2text(from_key)] + var/sample2 = real_samples[num2text(to_key)] + var/pivot = FLOOR((from_key + to_key) / 2, 1) //original code was a round but I replaced it because that's effectively a floor, thanks Baystation! who knows what was intended. + for(var/key in from_key to pivot) + samples[num2text(key)] = new /datum/instrument_key(sample1, key, key - from_key) + for(var/key in (pivot + 1) to to_key) + samples[num2text(key)] = new /datum/instrument_key(sample2, key, key - to_key) + + // Fill in 0 to first key and last key to 127 + var/first_key = real_keys[1] + var/last_key = real_keys[length(real_keys)] + var/first_sample = real_samples[num2text(first_key)] + var/last_sample = real_samples[num2text(last_key)] + for(var/key in LOWEST_KEY to (first_key - 1)) + samples[num2text(key)] = new /datum/instrument_key(first_sample, key, key - first_key) + for(var/key in last_key to HIGHEST_KEY) + samples[num2text(key)] = new /datum/instrument_key(last_sample, key, key - last_key) diff --git a/code/modules/instruments/instrument_data/_instrument_key.dm b/code/modules/instruments/instrument_data/_instrument_key.dm new file mode 100644 index 0000000000..012270ae11 --- /dev/null +++ b/code/modules/instruments/instrument_data/_instrument_key.dm @@ -0,0 +1,20 @@ +/datum/instrument_key + var/key //1 to 127 + var/sample //file + var/frequency //frequency generated + var/deviation //deviation up/down towards pivot from sample (??) + +/datum/instrument_key/New(sample = src.sample, key = src.key, deviation = src.deviation, frequency = src.frequency) + src.sample = sample + src.key = key + src.deviation = deviation + src.frequency = frequency + if(!frequency && deviation) + calculate() + +/datum/instrument_key/proc/calculate() + if(!deviation) + CRASH("Invalid calculate call: No deviation or sample in instrument_key") + #define KEY_TWELTH (1/12) + frequency = 2 ** (KEY_TWELTH * deviation) + #undef KEY_TWELTH diff --git a/code/modules/instruments/instrument_data/brass.dm b/code/modules/instruments/instrument_data/brass.dm new file mode 100644 index 0000000000..7f8f103831 --- /dev/null +++ b/code/modules/instruments/instrument_data/brass.dm @@ -0,0 +1,26 @@ +/datum/instrument/brass + name = "Generic brass instrument" + category = "Brass" + abstract_type = /datum/instrument/brass + +/datum/instrument/brass/crisis_section + name = "Crisis Brass Section" + id = "crbrass" + real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg', + "48"='sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg', + "60"='sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg', + "72"='sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg') + +/datum/instrument/brass/crisis_trombone + name = "Crisis Trombone" + id = "crtrombone" + real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_trombone/c2.ogg', + "48"='sound/instruments/synthesis_samples/brass/crisis_trombone/c3.ogg', + "60"='sound/instruments/synthesis_samples/brass/crisis_trombone/c4.ogg', + "72"='sound/instruments/synthesis_samples/brass/crisis_trombone/c5.ogg') + +/datum/instrument/brass/crisis_trumpet + name = "Crisis Trumpet" + id = "crtrumpet" + real_samples = list("60"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c4.ogg', + "72"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c5.ogg') diff --git a/code/modules/instruments/instrument_data/chromatic_percussion.dm b/code/modules/instruments/instrument_data/chromatic_percussion.dm new file mode 100644 index 0000000000..cafa9e31ed --- /dev/null +++ b/code/modules/instruments/instrument_data/chromatic_percussion.dm @@ -0,0 +1,31 @@ +/datum/instrument/chromatic + name = "Generic chromatic percussion instrument" + category = "Chromatic percussion" + abstract_type = /datum/instrument/chromatic + +/datum/instrument/chromatic/vibraphone1 + name = "Crisis Vibraphone" + id = "crvibr" + real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg', + "48"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg', + "60"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg', + "72"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg') + +/datum/instrument/chromatic/musicbox1 + name = "SGM Music Box" + id = "sgmmbox" + real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg', + "48"='sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg', + "60"='sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg', + "72"='sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg') + +/datum/instrument/chromatic/fluid_celeste + name = "FluidR3 Celeste" + id = "r3celeste" + real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c2.ogg', + "48"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c3.ogg', + "60"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c4.ogg', + "72"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c5.ogg', + "84"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c6.ogg', + "96"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c7.ogg', + "108"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c8.ogg') diff --git a/code/modules/instruments/instrument_data/fun.dm b/code/modules/instruments/instrument_data/fun.dm new file mode 100644 index 0000000000..44d78b8ccd --- /dev/null +++ b/code/modules/instruments/instrument_data/fun.dm @@ -0,0 +1,25 @@ +/datum/instrument/fun + name = "Generic Fun Instrument" + category = "Fun" + abstract_type = /datum/instrument/fun + +/datum/instrument/fun/honk + name = "!!HONK!!" + id = "honk" + real_samples = list("74"='sound/items/bikehorn.ogg') // Cluwne Heaven + +/datum/instrument/fun/signal + name = "Ping" + id = "ping" + real_samples = list("79"='sound/machines/ping.ogg') + +/datum/instrument/fun/chime + name = "Chime" + id = "chime" + real_samples = list("79"='sound/machines/chime.ogg') + +/datum/instrument/fun/nya + name = "NYA NYA NYA" + id = "nyanyanya" + real_samples = list("79" = 'modular_citadel/sound/voice/nya.ogg') + admin_only = TRUE diff --git a/code/modules/instruments/instrument_data/guitar.dm b/code/modules/instruments/instrument_data/guitar.dm new file mode 100644 index 0000000000..be7cfbe467 --- /dev/null +++ b/code/modules/instruments/instrument_data/guitar.dm @@ -0,0 +1,36 @@ +/datum/instrument/guitar + name = "Generic guitar-like instrument" + category = "Guitar" + abstract_type = /datum/instrument/guitar + +/datum/instrument/guitar/steel_crisis + name = "Crisis Steel String Guitar" + id = "csteelgt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg') + +/datum/instrument/guitar/nylon_crisis + name = "Crisis Nylon String Guitar" + id = "cnylongt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg') + +/datum/instrument/guitar/clean_crisis + name = "Crisis Clean Guitar" + id = "ccleangt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_clean/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_clean/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_clean/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_clean/c5.ogg') + +/datum/instrument/guitar/muted_crisis + name = "Crisis Muted Guitar" + id = "cmutedgt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_muted/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_muted/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_muted/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_muted/c5.ogg') diff --git a/code/modules/instruments/instrument_data/hardcoded.dm b/code/modules/instruments/instrument_data/hardcoded.dm new file mode 100644 index 0000000000..757fea08a8 --- /dev/null +++ b/code/modules/instruments/instrument_data/hardcoded.dm @@ -0,0 +1,86 @@ +//THESE ARE HARDCODED INSTRUMENT SAMPLES. +//SONGS WILL BE AUTOMATICALLY SWITCHED TO LEGACY MODE IF THEY USE THIS KIND OF INSTRUMENT! +//I'd prefer these stayed. They sound different from the mechanical synthesis of synthed instruments, and I quite like them that way. It's not legacy, it's hardcoded, old style. - kevinz000 +/datum/instrument/hardcoded + abstract_type = /datum/instrument/hardcoded + category = "Non-Synthesized" + instrument_flags = INSTRUMENT_LEGACY + volume_multiplier = 1 //not as loud as synth'd + +/datum/instrument/hardcoded/accordian + name = "Accordian" + id = "accordian" + legacy_instrument_ext = "mid" + legacy_instrument_path = "accordian" + +/datum/instrument/hardcoded/bikehorn + name = "Bike Horn" + id = "bikehorn" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "bikehorn" + +/datum/instrument/hardcoded/eguitar + name = "Electric Guitar" + id = "eguitar" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "eguitar" + +/datum/instrument/hardcoded/glockenspiel + name = "Glockenspiel" + id = "glockenspiel" + legacy_instrument_ext = "mid" + legacy_instrument_path = "glockenspiel" + +/datum/instrument/hardcoded/guitar + name = "Guitar" + id = "guitar" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "guitar" + +/datum/instrument/hardcoded/harmonica + name = "Harmonica" + id = "harmonica" + legacy_instrument_ext = "mid" + legacy_instrument_path = "harmonica" + +/datum/instrument/hardcoded/piano + name = "Piano" + id = "piano" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "piano" + +/datum/instrument/hardcoded/recorder + name = "Recorder" + id = "recorder" + legacy_instrument_ext = "mid" + legacy_instrument_path = "recorder" + +/datum/instrument/hardcoded/saxophone + name = "Saxophone" + id = "saxophone" + legacy_instrument_ext = "mid" + legacy_instrument_path = "saxophone" + +/datum/instrument/hardcoded/trombone + name = "Trombone" + id = "trombone" + legacy_instrument_ext = "mid" + legacy_instrument_path = "trombone" + +/datum/instrument/hardcoded/violin + name = "Violin" + id = "violin" + legacy_instrument_ext = "mid" + legacy_instrument_path = "violin" + +/datum/instrument/hardcoded/xylophone + name = "Xylophone" + id = "xylophone" + legacy_instrument_ext = "mid" + legacy_instrument_path = "xylophone" + +/datum/instrument/hardcoded/banjo + name = "Banjo" + id = "banjo" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "banjo" diff --git a/code/modules/instruments/instrument_data/organ.dm b/code/modules/instruments/instrument_data/organ.dm new file mode 100644 index 0000000000..25da740998 --- /dev/null +++ b/code/modules/instruments/instrument_data/organ.dm @@ -0,0 +1,43 @@ +/datum/instrument/organ + name = "Generic organ" + category = "Organ" + abstract_type = /datum/instrument/organ + +/datum/instrument/organ/crisis_church + name = "Crisis Church Organ" + id = "crichugan" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg') + +/datum/instrument/organ/crisis_hammond + name = "Crisis Hammond Organ" + id = "crihamgan" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg') + +/datum/instrument/organ/crisis_accordian + name = "Crisis Accordian" + id = "crack" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg') + +/datum/instrument/organ/crisis_harmonica + name = "Crisis Harmonica" + id = "crharmony" + real_samples = list("48"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg') + +/datum/instrument/organ/crisis_tango_accordian + name = "Crisis Tango Accordian" + id = "crtango" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg') diff --git a/code/modules/instruments/instrument_data/piano.dm b/code/modules/instruments/instrument_data/piano.dm new file mode 100644 index 0000000000..fdd2f6e938 --- /dev/null +++ b/code/modules/instruments/instrument_data/piano.dm @@ -0,0 +1,56 @@ +/datum/instrument/piano + name = "Generic piano" + category = "Piano" + abstract_type = /datum/instrument/piano + +/datum/instrument/piano/fluid_piano + name = "FluidR3 Grand Piano" + id = "r3grand" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg') + +/datum/instrument/piano/fluid_harpsichord + name = "FluidR3 Harpsichord" + id = "r3harpsi" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c8.ogg') + +/datum/instrument/piano/crisis_harpsichord + name = "Crisis Harpsichord" + id = "crharpsi" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg') + +/datum/instrument/piano/crisis_grandpiano_uni + name = "Crisis Grand Piano One" + id = "crgrand1" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg') + +/datum/instrument/piano/crisis_brightpiano_uni + name = "Crisis Bright Piano One" + id = "crbright1" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg') diff --git a/code/modules/instruments/instrument_data/synth_tones.dm b/code/modules/instruments/instrument_data/synth_tones.dm new file mode 100644 index 0000000000..9ad9250f40 --- /dev/null +++ b/code/modules/instruments/instrument_data/synth_tones.dm @@ -0,0 +1,19 @@ +/datum/instrument/tones + name = "Ideal tone" + category = "Tones" + abstract_type = /datum/instrument/tones + +/datum/instrument/tones/square_wave + name = "Ideal square wave" + id = "square" + real_samples = list("81"='sound/instruments/synthesis_samples/tones/Square.ogg') + +/datum/instrument/tones/sine_wave + name = "Ideal sine wave" + id = "sine" + real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sine.ogg') + +/datum/instrument/tones/saw_wave + name = "Ideal sawtooth wave" + id = "saw" + real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sawtooth.ogg') diff --git a/code/game/objects/items/devices/instruments.dm b/code/modules/instruments/instruments/item.dm similarity index 78% rename from code/game/objects/items/devices/instruments.dm rename to code/modules/instruments/instruments/item.dm index 8dedc2cb86..fa3e75e431 100644 --- a/code/game/objects/items/devices/instruments.dm +++ b/code/modules/instruments/instruments/item.dm @@ -1,83 +1,80 @@ //copy pasta of the space piano, don't hurt me -Pete /obj/item/instrument name = "generic instrument" - resistance_flags = FLAMMABLE force = 10 max_integrity = 100 + resistance_flags = FLAMMABLE icon = 'icons/obj/musician.dmi' lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi' var/datum/song/handheld/song - var/instrumentId = "generic" - var/instrumentExt = "mid" - var/tune_time = 0 - -/obj/item/instrument/Initialize() - . = ..() - song = new(instrumentId, src, instrumentExt) - -/obj/item/instrument/Destroy() - if (tune_time) - STOP_PROCESSING(SSobj, src) - qdel(song) - song = null - return ..() - -/obj/item/instrument/suicide_act(mob/user) - user.visible_message("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!") - return (BRUTELOSS) + var/list/allowed_instrument_ids + var/tune_time_left = 0 /obj/item/instrument/Initialize(mapload) . = ..() - if(mapload) - song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded + song = new(src, allowed_instrument_ids) + allowed_instrument_ids = null //We don't need this clogging memory after it's used. -/obj/item/instrument/attack_self(mob/user) - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return 1 - interact(user) +/obj/item/instrument/Destroy() + QDEL_NULL(song) + if(tune_time_left) + STOP_PROCESSING(SSprocessing, src) + return ..() -/obj/item/instrument/interact(mob/user) - ui_interact(user) +/obj/item/instrument/proc/should_stop_playing(mob/user) + return !user.CanReach(src) || !user.canUseTopic(src, FALSE, TRUE, FALSE, FALSE) -/obj/item/instrument/ui_interact(mob/user) - if(!user) - return - - if(!isliving(user) || user.stat || user.restrained() || user.lying) - return - - user.set_machine(src) - song.interact(user) - -/obj/item/instrument/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/musicaltuner)) - var/mob/living/carbon/human/H = user - if (HAS_TRAIT(H, TRAIT_MUSICIAN)) - if (!tune_time) - H.visible_message("[H] tunes the [src] to perfection!", "You tune the [src] to perfection!") - tune_time = 300 - START_PROCESSING(SSobj, src) - else - to_chat(H, "[src] is already well tuned!") - else - to_chat(H, "You have no idea how to use this.") - -/obj/item/instrument/process() - if (tune_time) +/obj/item/instrument/process(wait) + if(is_tuned()) if (song.playing) for (var/mob/living/M in song.hearing_mobs) M.dizziness = max(0,M.dizziness-2) M.jitteriness = max(0,M.jitteriness-2) M.confused = max(M.confused-1) SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "goodmusic", /datum/mood_event/goodmusic) - tune_time-- + tune_time_left -= wait else - if (!tune_time) - if (song.playing) - loc.visible_message("[src] starts sounding a little off...") - STOP_PROCESSING(SSobj, src) + tune_time_left = 0 + if (song.playing) + loc.visible_message("[src] starts sounding a little off...") + STOP_PROCESSING(SSprocessing, src) + +/obj/item/instrument/suicide_act(mob/user) + user.visible_message("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!") + return (BRUTELOSS) + +/obj/item/instrument/attack_self(mob/user) + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return TRUE + interact(user) + +/obj/item/instrument/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/musicaltuner)) + var/mob/living/carbon/human/H = user + if (HAS_TRAIT(H, TRAIT_MUSICIAN)) + if (!is_tuned()) + H.visible_message("[H] tunes the [src] to perfection!", "You tune the [src] to perfection!") + tune_time_left = 600 SECONDS + START_PROCESSING(SSprocessing, src) + else + to_chat(H, "[src] is already well tuned!") + else + to_chat(H, "You have no idea how to use this.") + +/obj/item/instrument/proc/is_tuned() + return tune_time_left > 0 + +/obj/item/instrument/interact(mob/user) + ui_interact(user) + +/obj/item/instrument/ui_interact(mob/living/user) + if(!isliving(user) || user.stat || user.restrained()) + return + + user.set_machine(src) + song.ui_interact(user) /obj/item/instrument/violin name = "space violin" @@ -85,7 +82,7 @@ icon_state = "violin" item_state = "violin" hitsound = "swing_hit" - instrumentId = "violin" + allowed_instrument_ids = "violin" /obj/item/instrument/violin/golden name = "golden violin" @@ -99,40 +96,20 @@ desc = "An advanced electronic synthesizer that can be used as various instruments." icon_state = "synth" item_state = "synth" - instrumentId = "piano" - instrumentExt = "ogg" - var/static/list/insTypes = list("accordion" = "mid", "bikehorn" = "ogg", "glockenspiel" = "mid", "banjo" = "ogg", "guitar" = "ogg", "harmonica" = "mid", "piano" = "ogg", "recorder" = "mid", "saxophone" = "mid", "trombone" = "mid", "violin" = "mid", "xylophone" = "mid") //No eguitar you ear-rapey fuckers. - actions_types = list(/datum/action/item_action/synthswitch) + allowed_instrument_ids = "piano" -/obj/item/instrument/piano_synth/proc/changeInstrument(name = "piano") - song.instrumentDir = name - song.instrumentExt = insTypes[name] - -/obj/item/instrument/piano_synth/proc/selectInstrument() // Moved here so it can be used by the action and PAI software panel without copypasta - var/chosen = input("Choose the type of instrument you want to use", "Instrument Selection", song.instrumentDir) as null|anything in insTypes - if(!insTypes[chosen]) - return - return changeInstrument(chosen) - -/obj/item/instrument/banjo - name = "banjo" - desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings." - icon_state = "banjo" - item_state = "banjo" - instrumentExt = "ogg" - attack_verb = list("scruggs-styled", "hum-diggitied", "shin-digged", "clawhammered") - hitsound = 'sound/weapons/banjoslap.ogg' - instrumentId = "banjo" +/obj/item/instrument/piano_synth/Initialize() + . = ..() + song.allowed_instrument_ids = get_allowed_instrument_ids() /obj/item/instrument/guitar name = "guitar" desc = "It's made of wood and has bronze strings." icon_state = "guitar" item_state = "guitar" - instrumentExt = "ogg" attack_verb = list("played metal on", "serenaded", "crashed", "smashed") hitsound = 'sound/weapons/stringsmash.ogg' - instrumentId = "guitar" + allowed_instrument_ids = "guitar" /obj/item/instrument/eguitar name = "electric guitar" @@ -142,29 +119,28 @@ force = 12 attack_verb = list("played metal on", "shredded", "crashed", "smashed") hitsound = 'sound/weapons/stringsmash.ogg' - instrumentId = "eguitar" - instrumentExt = "ogg" + allowed_instrument_ids = "eguitar" /obj/item/instrument/glockenspiel name = "glockenspiel" desc = "Smooth metal bars perfect for any marching band." icon_state = "glockenspiel" item_state = "glockenspiel" - instrumentId = "glockenspiel" + allowed_instrument_ids = "glockenspiel" /obj/item/instrument/accordion name = "accordion" desc = "Pun-Pun not included." icon_state = "accordion" item_state = "accordion" - instrumentId = "accordion" + allowed_instrument_ids = "accordion" /obj/item/instrument/trumpet name = "trumpet" desc = "To announce the arrival of the king!" icon_state = "trumpet" item_state = "trombone" - instrumentId = "trombone" + allowed_instrument_ids = "trombone" /obj/item/instrument/trumpet/spectral name = "spectral trumpet" @@ -172,7 +148,6 @@ icon_state = "trumpet" item_state = "trombone" force = 0 - instrumentId = "trombone" attack_verb = list("played","jazzed","trumpeted","mourned","dooted","spooked") /obj/item/instrument/trumpet/spectral/Initialize() @@ -188,14 +163,13 @@ desc = "This soothing sound will be sure to leave your audience in tears." icon_state = "saxophone" item_state = "saxophone" - instrumentId = "saxophone" + allowed_instrument_ids = "saxophone" /obj/item/instrument/saxophone/spectral name = "spectral saxophone" desc = "This spooky sound will be sure to leave mortals in bones." icon_state = "saxophone" item_state = "saxophone" - instrumentId = "saxophone" force = 0 attack_verb = list("played","jazzed","saxxed","mourned","dooted","spooked") @@ -204,7 +178,7 @@ AddComponent(/datum/component/spooky) /obj/item/instrument/saxophone/spectral/attack(mob/living/carbon/C, mob/user) - playsound (loc, 'sound/instruments/saxophone/En4.mid', 100,1,-1) + playsound(loc, 'sound/instruments/saxophone/En4.mid', 100,1,-1) ..() /obj/item/instrument/trombone @@ -212,12 +186,11 @@ desc = "How can any pool table ever hope to compete?" icon_state = "trombone" item_state = "trombone" - instrumentId = "trombone" + allowed_instrument_ids = "trombone" /obj/item/instrument/trombone/spectral name = "spectral trombone" desc = "A skeleton's favorite instrument. Apply directly on the mortals." - instrumentId = "trombone" icon_state = "trombone" item_state = "trombone" force = 0 @@ -237,14 +210,14 @@ force = 5 icon_state = "recorder" item_state = "recorder" - instrumentId = "recorder" + allowed_instrument_ids = "recorder" /obj/item/instrument/harmonica name = "harmonica" desc = "For when you get a bad case of the space blues." icon_state = "harmonica" item_state = "harmonica" - instrumentId = "harmonica" + allowed_instrument_ids = "harmonica" slot_flags = ITEM_SLOT_MASK force = 5 w_class = WEIGHT_CLASS_SMALL @@ -271,14 +244,22 @@ lefthand_file = 'icons/mob/inhands/equipment/horns_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/horns_righthand.dmi' attack_verb = list("beautifully honks") - instrumentId = "bikehorn" - instrumentExt = "ogg" + allowed_instrument_ids = "bikehorn" w_class = WEIGHT_CLASS_TINY force = 0 throw_speed = 3 throw_range = 15 hitsound = 'sound/items/bikehorn.ogg' +/obj/item/instrument/banjo + name = "banjo" + desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings." + icon_state = "banjo" + item_state = "banjo" + attack_verb = list("scruggs-styled", "hum-diggitied", "shin-digged", "clawhammered") + hitsound = 'sound/weapons/banjoslap.ogg' + allowed_instrument_ids = "banjo" + /obj/item/musicaltuner name = "musical tuner" desc = "A device for tuning musical instruments both manual and electronic alike." diff --git a/code/modules/instruments/instruments/stationary.dm b/code/modules/instruments/instruments/stationary.dm new file mode 100644 index 0000000000..f236927c38 --- /dev/null +++ b/code/modules/instruments/instruments/stationary.dm @@ -0,0 +1,52 @@ +/obj/structure/musician + name = "Not A Piano" + desc = "Something broke, contact coderbus." + interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT | INTERACT_ATOM_REQUIRES_DEXTERITY + var/can_play_unanchored = FALSE + var/list/allowed_instrument_ids + var/datum/song/song + +/obj/structure/musician/Initialize(mapload) + . = ..() + song = new(src, allowed_instrument_ids) + allowed_instrument_ids = null + +/obj/structure/musician/Destroy() + QDEL_NULL(song) + return ..() + +/obj/structure/musician/proc/should_stop_playing(mob/user) + if(!(anchored || can_play_unanchored)) + return TRUE + if(!user) + return FALSE + return !user.canUseTopic(src, FALSE, TRUE, FALSE, FALSE) //can play with TK and while resting because fun. + +/obj/structure/musician/ui_interact(mob/user) + . = ..() + song.ui_interact(user) + +/obj/structure/musician/wrench_act(mob/living/user, obj/item/I) + default_unfasten_wrench(user, I, 40) + return TRUE + +/obj/structure/musician/piano + name = "space minimoog" + icon = 'icons/obj/musician.dmi' + icon_state = "minimoog" + anchored = TRUE + density = TRUE + +/obj/structure/musician/piano/unanchored + anchored = FALSE + +/obj/structure/musician/piano/Initialize(mapload) + . = ..() + if(prob(50) && icon_state == initial(icon_state)) + name = "space minimoog" + desc = "This is a minimoog, like a space piano, but more spacey!" + icon_state = "minimoog" + else + name = "space piano" + desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." + icon_state = "piano" diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm new file mode 100644 index 0000000000..67feb14457 --- /dev/null +++ b/code/modules/instruments/songs/_song.dm @@ -0,0 +1,301 @@ +#define MUSICIAN_HEARCHECK_MINDELAY 4 +#define MUSIC_MAXLINES 1000 +#define MUSIC_MAXLINECHARS 300 + +/datum/song + /// Name of the song + var/name = "Untitled" + + /// The atom we're attached to/playing from + var/atom/parent + + /// Our song lines + var/list/lines + + /// delay between notes in deciseconds + var/tempo = 5 + + /// Are we currently playing? + var/playing = FALSE + + /// Are we currently editing? + var/editing = TRUE + /// Is the help screen open? + var/help = FALSE + + /// Repeats left + var/repeat = 0 + /// Maximum times we can repeat + var/max_repeats = 10 + + /// Our volume + var/volume = 75 + /// Max volume + var/max_volume = 75 + /// Min volume - This is so someone doesn't decide it's funny to set it to 0 and play invisible songs. + var/min_volume = 1 + + /// What instruments our built in picker can use. The picker won't show unless this is longer than one. + var/list/allowed_instrument_ids = list("r3grand") + + //////////// Cached instrument variables ///////////// + /// Instrument we are currently using + var/datum/instrument/using_instrument + /// Cached legacy ext for legacy instruments + var/cached_legacy_ext + /// Cached legacy dir for legacy instruments + var/cached_legacy_dir + /// Cached list of samples, referenced directly from the instrument for synthesized instruments + var/list/cached_samples + /// Are we operating in legacy mode (so if the instrument is a legacy instrument) + var/legacy = FALSE + ////////////////////////////////////////////////////// + + /////////////////// Playing variables //////////////// + /** + * Only used in synthesized playback - The chords we compiled. Non assoc list of lists: + * list(list(key1, key2, key3..., tempo_divisor), list(key1, key2..., tempo_divisor), ...) + * tempo_divisor always exists + * if key1 (and so if there's no keys) doesn't exist it's a rest + * Compilation happens when we start playing and is cleared after we finish playing. + */ + var/list/compiled_chords + /// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1. + var/list/channels_playing = list() + /// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments. + var/list/channels_idle = list() + ////////////////////////////////////////////////////// + + /// Last world.time we checked for who can hear us + var/last_hearcheck = 0 + /// The list of mobs that can hear us + var/list/hearing_mobs + /// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop) + var/debug_mode = FALSE + /// Last time we processed decay + var/last_process_decay + /// Max sound channels to occupy + var/max_sound_channels = CHANNELS_PER_INSTRUMENT + /// Current channels, so we can save a length() call. + var/using_sound_channels = 0 + /// Last channel to play. text. + var/last_channel_played + /// Should we not decay our last played note? + var/full_sustain_held_note = TRUE + + /////////////////////// DO NOT TOUCH THESE /////////////////// + var/octave_min = INSTRUMENT_MIN_OCTAVE + var/octave_max = INSTRUMENT_MAX_OCTAVE + var/key_min = INSTRUMENT_MIN_KEY + var/key_max = INSTRUMENT_MAX_KEY + var/static/list/note_offset_lookup = list(9, 11, 0, 2, 4, 5, 7) + var/static/list/accent_lookup = list("b" = -1, "s" = 1, "#" = 1, "n" = 0) + ////////////////////////////////////////////////////////////// + + ///////////// !!FUN!! - Only works in synthesized mode! ///////////////// + /// Note numbers to shift. + var/note_shift = 0 + var/note_shift_min = -100 + var/note_shift_max = 100 + var/can_noteshift = TRUE + /// The kind of sustain we're using + var/sustain_mode = SUSTAIN_LINEAR + /// When a note is considered dead if it is below this in volume + var/sustain_dropoff_volume = 0 + /// Total duration of linear sustain for 100 volume note to get to SUSTAIN_DROPOFF + var/sustain_linear_duration = 5 + /// Exponential sustain dropoff rate per decisecond + var/sustain_exponential_dropoff = 1.4 + ////////// DO NOT DIRECTLY SET THESE! + /// Do not directly set, use update_sustain() + var/cached_linear_dropoff = 10 + /// Do not directly set, use update_sustain() + var/cached_exponential_dropoff = 1.045 + ///////////////////////////////////////////////////////////////////////// + +/datum/song/New(atom/parent, list/instrument_ids) + SSinstruments.on_song_new(src) + lines = list() + tempo = sanitize_tempo(tempo) + src.parent = parent + if(instrument_ids) + allowed_instrument_ids = islist(instrument_ids)? instrument_ids : list(instrument_ids) + if(length(allowed_instrument_ids)) + set_instrument(allowed_instrument_ids[1]) + hearing_mobs = list() + volume = clamp(volume, min_volume, max_volume) + update_sustain() + +/datum/song/Destroy() + stop_playing() + SSinstruments.on_song_del(src) + lines = null + using_instrument = null + allowed_instrument_ids = null + parent = null + return ..() + +/datum/song/proc/do_hearcheck() + last_hearcheck = world.time + var/list/old = hearing_mobs.Copy() + hearing_mobs.len = 0 + var/turf/source = get_turf(parent) + for(var/mob/M in get_hearers_in_view(15, source)) + if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) + continue + hearing_mobs[M] = get_dist(M, source) + var/list/exited = old - hearing_mobs + for(var/i in exited) + terminate_sound_mob(i) + +/// I can either be a datum, id, or path (if the instrument has no id). +/datum/song/proc/set_instrument(datum/instrument/I) + if(using_instrument) + using_instrument.songs_using -= src + using_instrument = null + cached_samples = null + cached_legacy_ext = null + cached_legacy_dir = null + legacy = null + if(istext(I) || ispath(I)) + I = SSinstruments.instrument_data[I] + if(istype(I)) + using_instrument = I + I.songs_using += src + var/instrument_legacy = CHECK_BITFIELD(I.instrument_flags, INSTRUMENT_LEGACY) + if(instrument_legacy) + cached_legacy_ext = I.legacy_instrument_ext + cached_legacy_dir = I.legacy_instrument_path + legacy = TRUE + else + cached_samples = I.samples + legacy = FALSE + +/// THIS IS A BLOCKING CALL. +/datum/song/proc/start_playing(mob/user) + if(playing) + return + if(!using_instrument?.ready()) + to_chat(user, "An error has occured with [src]. Please reset the instrument.") + return + playing = TRUE + updateDialog() + //we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak. + //wrap the rest of the stuff to ensure stop_playing() is called. + last_process_decay = world.time + START_PROCESSING(SSinstruments, src) + . = do_play_lines(user) + stop_playing() + +/datum/song/proc/stop_playing() + if(!playing) + return + playing = FALSE + if(!debug_mode) + compiled_chords = null + STOP_PROCESSING(SSinstruments, src) + terminate_all_sounds(TRUE) + hearing_mobs.len = 0 + updateDialog() + +/// THIS IS A BLOCKING CALL. +/datum/song/proc/do_play_lines(user) + if(!playing) + return + do_hearcheck() + if(legacy) + do_play_lines_legacy(user) + else + do_play_lines_synthesized(user) + +/datum/song/proc/should_stop_playing(mob/user) + return QDELETED(parent) || !using_instrument || !playing + +/datum/song/proc/sanitize_tempo(new_tempo) + new_tempo = abs(new_tempo) + return CLAMP(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS) + +/datum/song/proc/get_bpm() + return 600 / tempo + +/datum/song/proc/set_bpm(bpm) + tempo = sanitize_tempo(600 / bpm) + +/// Updates the window for our user. Override in subtypes. +/datum/song/proc/updateDialog(mob/user) + ui_interact(user) + +/datum/song/process(wait) + if(!playing) + return PROCESS_KILL + var/delay = world.time - last_process_decay + process_decay(delay) + last_process_decay = world.time + +/datum/song/proc/update_sustain() + // Exponential is easy + cached_exponential_dropoff = sustain_exponential_dropoff + // Linear, not so much, since it's a target duration from 100 volume rather than an exponential rate. + var/target_duration = sustain_linear_duration + var/volume_diff = max(0, 100 - sustain_dropoff_volume) + var/volume_decrease_per_decisecond = volume_diff / target_duration + cached_linear_dropoff = volume_decrease_per_decisecond + +/datum/song/proc/set_volume(volume) + src.volume = CLAMP(volume, max(0, min_volume), min(100, max_volume)) + update_sustain() + updateDialog() + +/datum/song/proc/set_dropoff_volume(volume) + sustain_dropoff_volume = CLAMP(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100) + update_sustain() + updateDialog() + +/datum/song/proc/set_exponential_drop_rate(drop) + sustain_exponential_dropoff = CLAMP(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX) + update_sustain() + updateDialog() + +/datum/song/proc/set_linear_falloff_duration(duration) + sustain_linear_duration = CLAMP(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN) + update_sustain() + updateDialog() + +/datum/song/vv_edit_var(var_name, var_value) + . = ..() + if(.) + switch(var_name) + if(NAMEOF(src, volume)) + set_volume(var_value) + if(NAMEOF(src, sustain_dropoff_volume)) + set_dropoff_volume(var_value) + if(NAMEOF(src, sustain_exponential_dropoff)) + set_exponential_drop_rate(var_value) + if(NAMEOF(src, sustain_linear_duration)) + set_linear_falloff_duration(var_value) + +// subtype for handheld instruments, like violin +/datum/song/handheld + +/datum/song/handheld/updateDialog(mob/user) + parent.ui_interact(user || usr) + +/datum/song/handheld/should_stop_playing(mob/user) + . = ..() + if(.) + return TRUE + var/obj/item/instrument/I = parent + return I.should_stop_playing(user) + +// subtype for stationary structures, like pianos +/datum/song/stationary + +/datum/song/stationary/updateDialog(mob/user) + parent.ui_interact(user || usr) + +/datum/song/stationary/should_stop_playing(mob/user) + . = ..() + if(.) + return TRUE + var/obj/structure/musician/M = parent + return M.should_stop_playing(user) diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm new file mode 100644 index 0000000000..873ff0e1a7 --- /dev/null +++ b/code/modules/instruments/songs/editor.dm @@ -0,0 +1,246 @@ +/datum/song/proc/instrument_status_ui() + . = list() + . += "
" + . += "Current instrument: " + if(!using_instrument) + . += "No instrument loaded!
" + else + . += "[using_instrument.name]
" + . += "Playback Settings:
" + if(can_noteshift) + . += "Note Shift/Note Transpose: [note_shift] keys / [round(note_shift / 12, 0.01)] octaves
" + var/smt + var/modetext = "" + switch(sustain_mode) + if(SUSTAIN_LINEAR) + smt = "Linear" + modetext = "Linear Sustain Duration: [sustain_linear_duration / 10] seconds
" + if(SUSTAIN_EXPONENTIAL) + smt = "Exponential" + modetext = "Exponential Falloff Factor: [sustain_exponential_dropoff]% per decisecond
" + . += "Sustain Mode: [smt]
" + . += modetext + . += using_instrument?.ready()? "Status: Ready
" : "Status: !Instrument Definition Error!
" + . += "Instrument Type: [legacy? "Legacy" : "Synthesized"]
" + . += "Volume: [volume]
" + . += "Volume Dropoff Threshold: [sustain_dropoff_volume]
" + . += "Sustain indefinitely last held note: [full_sustain_held_note? "Enabled" : "Disabled"].
" + . += "
" + +/datum/song/ui_interact(mob/user) + var/list/dat = list() + + dat += instrument_status_ui() + + if(lines.len > 0) + dat += "

Playback

" + if(!playing) + dat += "Play Stop

" + dat += "Repeat Song: " + dat += repeat > 0 ? "--" : "--" + dat += " [repeat] times " + dat += repeat < max_repeats ? "++" : "++" + dat += "
" + else + dat += "Play Stop
" + dat += "Repeats left: [repeat]
" + if(!editing) + dat += "
Show Editor
" + else + dat += "

Editing

" + dat += "Hide Editor" + dat += " Start a New Song" + dat += " Import a Song

" + var/bpm = round(600 / tempo) + dat += "Tempo: - [bpm] BPM +

" + var/linecount = 0 + for(var/line in lines) + linecount += 1 + dat += "Line [linecount]: Edit X [line]
" + dat += "Add Line

" + if(help) + dat += "Hide Help
" + dat += {" + Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).
+ Every note in a chord will play together, with chord timed by the tempo.
+
+ Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
+ By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.
+ Example: C,D,E,F,G,A,B will play a C major scale.
+ After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3
+ Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B
+ A pause may be denoted by an empty chord: C,E,,C,G
+ To make a chord be a different time, end it with /x, where the chord length will be length
+ defined by tempo / x: C,G/2,E/4
+ Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4 +
+ Lines may be up to [MUSIC_MAXLINECHARS] characters.
+ A song may only contain up to [MUSIC_MAXLINES] lines.
+ "} + else + dat += "Show Help
" + + var/datum/browser/popup = new(user, "instrument", parent?.name || "instrument", 700, 500) + popup.set_content(dat.Join("")) + popup.set_title_image(user.browse_rsc_icon(parent.icon, parent.icon_state)) + popup.open() + +/datum/song/proc/ParseSong(text) + set waitfor = FALSE + //split into lines + lines = splittext(text, "\n") + if(lines.len) + var/bpm_string = "BPM: " + if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1)) + var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default + tempo = sanitize_tempo(600 / round(divisor, 1)) + lines.Cut(1, 2) + else + tempo = sanitize_tempo(5) // default 120 BPM + if(lines.len > MUSIC_MAXLINES) + to_chat(usr, "Too many lines!") + lines.Cut(MUSIC_MAXLINES + 1) + var/linenum = 1 + for(var/l in lines) + if(length_char(l) > MUSIC_MAXLINECHARS) + to_chat(usr, "Line [linenum] too long!") + lines.Remove(l) + else + linenum++ + updateDialog(usr) // make sure updates when complete + +/datum/song/Topic(href, href_list) + if(!usr.canUseTopic(parent, TRUE, FALSE, FALSE, FALSE)) + usr << browse(null, "window=instrument") + usr.unset_machine() + return + + parent.add_fingerprint(usr) + + if(href_list["newsong"]) + lines = new() + tempo = sanitize_tempo(5) // default 120 BPM + name = "" + + else if(href_list["import"]) + var/t = "" + do + t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) + if(!in_range(parent, usr)) + return + + if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") + if(cont == "no") + break + while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + ParseSong(t) + + else if(href_list["help"]) + help = text2num(href_list["help"]) - 1 + + else if(href_list["edit"]) + editing = text2num(href_list["edit"]) - 1 + + if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. + if(playing) + return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. + repeat += round(text2num(href_list["repeat"])) + if(repeat < 0) + repeat = 0 + if(repeat > max_repeats) + repeat = max_repeats + + else if(href_list["tempo"]) + tempo = sanitize_tempo(tempo + text2num(href_list["tempo"])) + + else if(href_list["play"]) + INVOKE_ASYNC(src, .proc/start_playing, usr) + + else if(href_list["newline"]) + var/newline = html_encode(input("Enter your line: ", parent.name) as text|null) + if(!newline || !in_range(parent, usr)) + return + if(lines.len > MUSIC_MAXLINES) + return + if(length(newline) > MUSIC_MAXLINECHARS) + newline = copytext(newline, 1, MUSIC_MAXLINECHARS) + lines.Add(newline) + + else if(href_list["deleteline"]) + var/num = round(text2num(href_list["deleteline"])) + if(num > lines.len || num < 1) + return + lines.Cut(num, num+1) + + else if(href_list["modifyline"]) + var/num = round(text2num(href_list["modifyline"]),1) + var/content = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS) + if(!content || !in_range(parent, usr)) + return + if(num > lines.len || num < 1) + return + lines[num] = content + + else if(href_list["stop"]) + stop_playing() + + else if(href_list["setlinearfalloff"]) + var/amount = input(usr, "Set linear sustain duration in seconds", "Linear Sustain Duration") as null|num + if(!isnull(amount)) + set_linear_falloff_duration(round(amount * 10, world.tick_lag)) + + else if(href_list["setexpfalloff"]) + var/amount = input(usr, "Set exponential sustain factor", "Exponential sustain factor") as null|num + if(!isnull(amount)) + set_exponential_drop_rate(round(amount, 0.00001)) + + else if(href_list["setvolume"]) + var/amount = input(usr, "Set volume", "Volume") as null|num + if(!isnull(amount)) + set_volume(round(amount, 1)) + + else if(href_list["setdropoffvolume"]) + var/amount = input(usr, "Set dropoff threshold", "Dropoff Threshold Volume") as null|num + if(!isnull(amount)) + set_dropoff_volume(round(amount, 0.01)) + + else if(href_list["switchinstrument"]) + if(!length(allowed_instrument_ids)) + return + else if(length(allowed_instrument_ids) == 1) + set_instrument(allowed_instrument_ids[1]) + return + var/list/categories = list() + for(var/i in allowed_instrument_ids) + var/datum/instrument/I = SSinstruments.get_instrument(i) + if(I) + LAZYSET(categories[I.category || "ERROR CATEGORY"], I.name, I.id) + var/cat = input(usr, "Select Category", "Instrument Category") as null|anything in categories + if(!cat) + return + var/list/instruments = categories[cat] + var/choice = input(usr, "Select Instrument", "Instrument Selection") as null|anything in instruments + if(!choice) + return + choice = instruments[choice] //get id + if(choice) + set_instrument(choice) + + else if(href_list["setnoteshift"]) + var/amount = input(usr, "Set note shift", "Note Shift") as null|num + if(!isnull(amount)) + note_shift = CLAMP(amount, note_shift_min, note_shift_max) + + else if(href_list["setsustainmode"]) + var/choice = input(usr, "Choose a sustain mode", "Sustain Mode") as null|anything in list("Linear", "Exponential") + switch(choice) + if("Linear") + sustain_mode = SUSTAIN_LINEAR + if("Exponential") + sustain_mode = SUSTAIN_EXPONENTIAL + + else if(href_list["togglesustainhold"]) + full_sustain_held_note = !full_sustain_held_note + + updateDialog() diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm new file mode 100644 index 0000000000..fa64656ebc --- /dev/null +++ b/code/modules/instruments/songs/play_legacy.dm @@ -0,0 +1,82 @@ +/// Playing legacy instruments - None of the "advanced" like sound reservations and decay are invoked. +/datum/song/proc/do_play_lines_legacy(mob/user) + while(repeat >= 0) + var/cur_oct[7] + var/cur_acc[7] + for(var/i = 1 to 7) + cur_oct[i] = 3 + cur_acc[i] = "n" + + for(var/line in lines) + for(var/beat in splittext(lowertext(line), ",")) + if(should_stop_playing(user)) + return + var/list/notes = splittext(beat, "/") + if(length(notes)) //because some jack-butts are going to do ,,,, to symbolize 3 rests instead of something reasonable like ,/1. + for(var/note in splittext(notes[1], "-")) + if(length(note) == 0) + continue + var/cur_note = text2ascii(note) - 96 + if(cur_note < 1 || cur_note > 7) + continue + for(var/i=2 to length(note)) + var/ni = copytext(note,i,i+1) + if(!text2num(ni)) + if(ni == "#" || ni == "b" || ni == "n") + cur_acc[cur_note] = ni + else if(ni == "s") + cur_acc[cur_note] = "#" // so shift is never required + else + cur_oct[cur_note] = text2num(ni) + playnote_legacy(cur_note, cur_acc[cur_note], cur_oct[cur_note]) + if(notes.len >= 2 && text2num(notes[2])) + sleep(sanitize_tempo(tempo / text2num(notes[2]))) + else + sleep(tempo) + if(should_stop_playing(user)) + return + repeat-- + updateDialog() + repeat = 0 + +// note is a number from 1-7 for A-G +// acc is either "b", "n", or "#" +// oct is 1-8 (or 9 for C) +/datum/song/proc/playnote_legacy(note, acc as text, oct) + // handle accidental -> B<>C of E<>F + if(acc == "b" && (note == 3 || note == 6)) // C or F + if(note == 3) + oct-- + note-- + acc = "n" + else if(acc == "#" && (note == 2 || note == 5)) // B or E + if(note == 2) + oct++ + note++ + acc = "n" + else if(acc == "#" && (note == 7)) //G# + note = 1 + acc = "b" + else if(acc == "#") // mass convert all sharps to flats, octave jump already handled + acc = "b" + note++ + + // check octave, C is allowed to go to 9 + if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) + return + + // now generate name + var/soundfile = "sound/instruments/[cached_legacy_dir]/[ascii2text(note+64)][acc][oct].[cached_legacy_ext]" + soundfile = file(soundfile) + // make sure the note exists + if(!fexists(soundfile)) + return + // and play + var/turf/source = get_turf(parent) + if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) + do_hearcheck() + var/sound/music_played = sound(soundfile) + for(var/i in hearing_mobs) + var/mob/M = i + M.playsound_local(source, null, volume * using_instrument.volume_multiplier, falloff = 5, S = music_played) + // Could do environment and echo later but not for now diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm new file mode 100644 index 0000000000..2573da324a --- /dev/null +++ b/code/modules/instruments/songs/play_synthesized.dm @@ -0,0 +1,135 @@ +/datum/song/proc/do_play_lines_synthesized(mob/user) + compile_lines() + while(repeat >= 0) + if(should_stop_playing(user)) + return + var/warned = FALSE + for(var/_chord in compiled_chords) + if(should_stop_playing(user)) + return + var/list/chord = _chord + var/tempodiv = chord[chord.len] + for(var/i in 1 to chord.len - 1) + var/key = chord[i] + if(!playkey_synth(key)) + if(!warned) + warned = TRUE + to_chat(user, "Your instrument has ran out of channels. You might be playing your song too fast or be setting sustain to too high of a value. This warning will be suppressed for the rest of this cycle.") + sleep(sanitize_tempo(tempo / (tempodiv || 1))) + repeat-- + updateDialog() + repeat = 0 + +/// C-Db2-A-A4/2,A-B#4-C/3,/4,A,A-B-C as an example +/datum/song/proc/compile_lines() + if(!length(src.lines)) + return + var/list/lines = src.lines //cache for hyepr speed! + compiled_chords = list() + var/list/octaves = list(3, 3, 3, 3, 3, 3, 3) + var/list/accents = list("n", "n", "n", "n", "n", "n", "n") + for(var/line in lines) + var/list/chords = splittext(lowertext(line), ",") + for(var/chord in chords) + var/list/compiled_chord = list() + var/tempodiv = 1 + var/list/notes_tempodiv = splittext(chord, "/") + var/len = length(notes_tempodiv) + if(len >= 2) + tempodiv = text2num(notes_tempodiv[2]) + if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that. + var/list/notes = splittext(notes_tempodiv[1], "-") + for(var/note in notes) + if(length(note) == 0) + continue + // 1-7, A-G + var/key = text2ascii(note) - 96 + if((key < 1) || (key > 7)) + continue + for(var/i in 2 to length(note)) + var/oct_acc = copytext(note, i, i + 1) + var/num = text2num(oct_acc) + if(!num) //it's an accidental + accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks. + else //octave + octaves[key] = CLAMP(num, octave_min, octave_max) + compiled_chord += CLAMP((note_offset_lookup[key] + octaves[key] * 12 + accent_lookup[accents[key]]), key_min, key_max) + compiled_chord += tempodiv //this goes last + if(length(compiled_chord)) + compiled_chords[++compiled_chords.len] = compiled_chord + CHECK_TICK + return compiled_chords + +/datum/song/proc/playkey_synth(key) + if(can_noteshift) + key = clamp(key + note_shift, key_min, key_max) + if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) + do_hearcheck() + var/datum/instrument_key/K = using_instrument.samples[num2text(key)] //See how fucking easy it is to make a number text? You don't need a complicated 9 line proc! + //Should probably add channel limiters here at some point but I don't care right now. + var/channel = pop_channel() + if(isnull(channel)) + return FALSE + . = TRUE + var/sound/copy = sound(K.sample) + var/volume = src.volume * using_instrument.volume_multiplier + copy.frequency = K.frequency + copy.volume = volume + var/channel_text = num2text(channel) + channels_playing[channel_text] = 100 + last_channel_played = channel_text + for(var/i in hearing_mobs) + var/mob/M = i + M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF) + // Could do environment and echo later but not for now + +/datum/song/proc/terminate_all_sounds(clear_channels = TRUE) + for(var/i in hearing_mobs) + terminate_sound_mob(i) + if(clear_channels) + channels_playing.len = 0 + channels_idle.len = 0 + SSinstruments.current_instrument_channels -= using_sound_channels + using_sound_channels = 0 + SSsounds.free_datum_channels(src) + +/datum/song/proc/terminate_sound_mob(mob/M) + for(var/channel in channels_playing) + M.stop_sound_channel(text2num(channel)) + +/datum/song/proc/pop_channel() + if(length(channels_idle)) //just pop one off of here if we have one available + . = text2num(channels_idle[1]) + channels_idle.Cut(1,2) + return + if(using_sound_channels >= max_sound_channels) + return + . = SSinstruments.reserve_instrument_channel(src) + if(!isnull(.)) + using_sound_channels++ + +/datum/song/proc/process_decay(wait_ds) + var/linear_dropoff = cached_linear_dropoff * wait_ds + var/exponential_dropoff = cached_exponential_dropoff ** wait_ds + for(var/channel in channels_playing) + if(full_sustain_held_note && (channel == last_channel_played)) + continue + var/current_volume = channels_playing[channel] + switch(sustain_mode) + if(SUSTAIN_LINEAR) + current_volume -= linear_dropoff + if(SUSTAIN_EXPONENTIAL) + current_volume /= exponential_dropoff + channels_playing[channel] = current_volume + var/dead = current_volume <= sustain_dropoff_volume + var/channelnumber = text2num(channel) + if(dead) + channels_playing -= channel + channels_idle += channel + for(var/i in hearing_mobs) + var/mob/M = i + M.stop_sound_channel(channelnumber) + else + for(var/i in hearing_mobs) + var/mob/M = i + M.set_sound_channel_volume(channelnumber, (current_volume * 0.01) * volume * using_instrument.volume_multiplier) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index a95c0e24ed..56e1a04b95 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -835,9 +835,9 @@ force = CLAMP((ghost_counter * 4), 0, 75) user.visible_message("[user] strikes with the force of [ghost_counter] vengeful spirits!") - ..() + return ..() -/obj/item/melee/ghost_sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) +/obj/item/melee/ghost_sword/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) var/ghost_counter = ghost_check() final_block_chance += CLAMP((ghost_counter * 5), 0, 75) owner.visible_message("[owner] is protected by a ring of [ghost_counter] ghosts!") diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 3023b93856..b2d02d8fa2 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -276,7 +276,23 @@ Works together with spawning an observer, noted above. if (!(client.prefs.chat_toggles & CHAT_OOC)) client.prefs.chat_toggles ^= CHAT_OOC transfer_ckey(ghost, FALSE) - ghost.AddElement(/datum/element/ghost_role_eligibility,penalize) // technically already run earlier, but this adds the penalty + if(penalize) + var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES + var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES + if(world.time < roundstart_quit_limit) //add up the time difference to their antag rolling penalty if they quit before half a (ingame) hour even passed. + penalty += roundstart_quit_limit - world.time + if(penalty) + penalty += world.realtime + if(SSautotransfer.can_fire && SSautotransfer.maxvotes) + var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes + if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime) + penalty = CANT_REENTER_ROUND + if(!(ckey in GLOB.client_ghost_timeouts)) + GLOB.client_ghost_timeouts += ckey + GLOB.client_ghost_timeouts[ckey] = 0 + else if(GLOB.client_ghost_timeouts[ckey] == CANT_REENTER_ROUND) + return + GLOB.client_ghost_timeouts[ckey] = max(GLOB.client_ghost_timeouts[ckey],penalty) // needs to be done AFTER the ckey transfer, too return ghost diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index e0647159a5..faea8e9bbc 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -63,13 +63,13 @@ if(hit_atom) if(isliving(hit_atom)) var/mob/living/L = hit_atom - if(!L.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK)) + if(L.run_block(src, 0, "the [name]", ATTACK_TYPE_TACKLE, 0, src) & BLOCK_SUCCESS) + DefaultCombatKnockdown(40, 1, 1) + else L.visible_message("[src] pounces on [L]!", "[src] pounces on you!") L.DefaultCombatKnockdown(100) sleep(2)//Runtime prevention (infinite bump() calls on hulks) step_towards(src,L) - else - DefaultCombatKnockdown(40, 1, 1) toggle_leap(0) else if(hit_atom.density && !hit_atom.CanPass(src)) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 836560ca99..9c4f0316b7 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -9,7 +9,7 @@ if(istype(src.glasses, /obj/item/clothing/glasses)) //glasses var/obj/item/clothing/glasses/GFP = src.glasses number += GFP.flash_protect - + if(istype(src.wear_mask, /obj/item/clothing/mask)) //mask var/obj/item/clothing/mask/MFP = src.wear_mask number += MFP.flash_protect @@ -86,13 +86,10 @@ if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) totitemdamage *= 1.5 //CIT CHANGES END HERE - if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration)) + var/impacting_zone = (user == src)? check_zone(user.zone_selected) : ran_zone(user.zone_selected) + if((user != src) && (run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone) & BLOCK_SUCCESS)) return FALSE - var/obj/item/bodypart/affecting - if(user == src) - affecting = get_bodypart(check_zone(user.zone_selected)) //we're self-mutilating! yay! - else - affecting = get_bodypart(ran_zone(user.zone_selected)) + var/obj/item/bodypart/affecting = get_bodypart(impacting_zone) if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest) affecting = bodyparts[1] SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) @@ -278,6 +275,8 @@ if(health >= 0 && !(HAS_TRAIT(src, TRAIT_FAKEDEATH))) var/friendly_check = FALSE + if(run_block(M, 0, M.name, ATTACK_TYPE_UNARMED)) + return if(lying) if(buckled) to_chat(M, "You need to unbuckle [src] first to do that!") diff --git a/code/modules/mob/living/carbon/human/human_block.dm b/code/modules/mob/living/carbon/human/human_block.dm new file mode 100644 index 0000000000..b1a5153ce5 --- /dev/null +++ b/code/modules/mob/living/carbon/human/human_block.dm @@ -0,0 +1,8 @@ +/mob/living/carbon/human/get_blocking_items() + . = ..() + if(wear_suit) + . += wear_suit + if(w_uniform) + . += w_uniform + if(wear_neck) + . += wear_neck diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index f4227e390e..324420a7dd 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -52,36 +52,12 @@ return martial_art_result return ..() -/mob/living/carbon/human/check_reflect(def_zone) - if(wear_suit?.IsReflect(def_zone)) - return TRUE - return ..() - -/mob/living/carbon/human/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) - . = ..() - if(.) - return - var/block_chance_modifier = round(damage / -3) - if(wear_suit) - var/final_block_chance = wear_suit.block_chance - (CLAMP((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier - if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return TRUE - if(w_uniform) - var/final_block_chance = w_uniform.block_chance - (CLAMP((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier - if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return TRUE - if(wear_neck) - var/final_block_chance = wear_neck.block_chance - (CLAMP((armour_penetration-wear_neck.armour_penetration)/2,0,100)) + block_chance_modifier - if(wear_neck.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return TRUE - return FALSE - /mob/living/carbon/human/can_embed(obj/item/I) if(I.get_sharpness() || is_pointed(I) || is_type_in_typecache(I, GLOB.can_embed_types)) return TRUE return FALSE -/mob/living/carbon/human/proc/check_block() +/mob/living/carbon/human/proc/check_martial_melee_block() if(mind) if(mind.martial_art && prob(mind.martial_art.block_chance) && mind.martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE)) return TRUE diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 34c65982dc..d541ff1066 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1437,7 +1437,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) to_chat(user, "You do not breathe, so you cannot perform CPR.") /datum/species/proc/grab(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) - if(target.check_block()) + if(target.check_martial_melee_block()) target.visible_message("[target] blocks [user]'s grab attempt!") return 0 if(attacker_style && attacker_style.grab_act(user,target)) @@ -1453,9 +1453,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(IS_STAMCRIT(user)) //CITADEL CHANGE - makes it impossible to punch while in stamina softcrit to_chat(user, "You're too exhausted.") //CITADEL CHANGE - ditto return FALSE //CITADEL CHANGE - ditto - if(target.check_block()) + if(target.check_martial_melee_block()) target.visible_message("[target] blocks [user]'s attack!") return FALSE + + if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes punching cause staminaloss but funny martial artist types get a discount + user.adjustStaminaLossBuffered(1.5) + else + user.adjustStaminaLossBuffered(3.5) + if(attacker_style && attacker_style.harm_act(user,target)) return TRUE else @@ -1474,8 +1480,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) else user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) - user.adjustStaminaLossBuffered(3.5) //CITADEL CHANGE - makes punching cause staminaloss - var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh) var/puncherstam = user.getStaminaLoss() var/puncherbrute = user.getBruteLoss() @@ -1534,13 +1538,24 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if((target.stat != DEAD) && damage >= user.dna.species.punchstunthreshold) if((punchedstam > 50) && prob(punchedstam*0.5)) //If our punch victim has been hit above the threshold, and they have more than 50 stamina damage, roll for stun, probability of 1% per 2 stamina damage + target.visible_message("[user] knocks [target] down!", \ "You're knocked down by [user]!", "You hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, user) to_chat(user, "You knock [target] down!") + var/knockdown_duration = 40 + (punchedstam + (punchedbrute*0.5))*0.8 - armor_block target.DefaultCombatKnockdown(knockdown_duration) target.forcesay(GLOB.hit_appends) log_combat(user, target, "got a stun punch with their previous punch") + + if(HAS_TRAIT(user, TRAIT_KI_VAMPIRE) && !HAS_TRAIT(target, TRAIT_NOBREATH) && (punchedbrute < 100)) //If we're a ki vampire we also sap them of lifeforce, but only if they're not too beat up. Also living organics only. + user.adjustBruteLoss(-5) + user.adjustFireLoss(-5) + user.adjustStaminaLoss(-20) + + target.adjustCloneLoss(10) + target.adjustBruteLoss(10) + else if(!(target.mobility_flags & MOBILITY_STAND)) target.forcesay(GLOB.hit_appends) @@ -1557,6 +1572,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/aim_for_groin = user.zone_selected == "groin" var/target_aiming_for_groin = target.zone_selected == "groin" + if(target.check_martial_melee_block()) //END EDIT + target.visible_message("[target] blocks [user]'s disarm attempt!") + return FALSE if(IS_STAMCRIT(user)) to_chat(user, "You're too exhausted!") return FALSE @@ -1685,9 +1703,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) /datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) // Allows you to put in item-specific reactions based on species if(user != H) - if(H.check_shields(I, I.force, "the [I.name]", MELEE_ATTACK, I.armour_penetration)) + if(H.run_block(I, I.force, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone) & BLOCK_SUCCESS) return 0 - if(H.check_block()) + if(H.check_martial_melee_block()) H.visible_message("[H] blocks [I]!") return 0 @@ -1803,7 +1821,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) return TRUE if(M.mind) attacker_style = M.mind.martial_art - if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK)) + if((M != H) && M.a_intent != INTENT_HELP && (H.run_block(M, 0, "[M]", ATTACK_TYPE_UNARMED, 0, M, M.zone_selected) & BLOCK_SUCCESS)) log_combat(M, H, "attempted to touch") H.visible_message("[M] attempted to touch [H]!") return TRUE @@ -1841,7 +1859,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(IS_STAMCRIT(user)) to_chat(user, "You're too exhausted.") return FALSE - if(target.check_block()) + if(target.check_martial_melee_block()) target.visible_message("[target] blocks [user]'s shoving attempt!") return FALSE if(attacker_style && attacker_style.disarm_act(user,target)) diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 446e726256..4ee069afe8 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -11,7 +11,7 @@ siemens_coeff = 0 punchdamagelow = 5 punchdamagehigh = 14 - punchstunthreshold = 11 //about 40% chance to stun + punchstunthreshold = 10 no_equip = list(SLOT_WEAR_MASK, SLOT_WEAR_SUIT, SLOT_GLOVES, SLOT_SHOES, SLOT_W_UNIFORM, SLOT_S_STORE) nojumpsuit = 1 sexes = 1 @@ -168,7 +168,7 @@ name = "Silver Golem" id = "silver golem" fixed_mut_color = "ddd" - punchstunthreshold = 9 //60% chance, from 40% + punchstunthreshold = 9 meat = /obj/item/stack/ore/silver info_text = "As a Silver Golem, your attacks have a higher chance of stunning. Being made of silver, your body is immune to most types of magic." prefix = "Silver" @@ -190,7 +190,7 @@ stunmod = 0.4 punchdamagelow = 12 punchdamagehigh = 21 - punchstunthreshold = 18 //still 40% stun chance + punchstunthreshold = 18 speedmod = 4 //pretty fucking slow meat = /obj/item/stack/ore/iron info_text = "As a Plasteel Golem, you are slower, but harder to stun, and hit very hard when punching." diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm index 3c17ef9066..cb8fdbf101 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -12,9 +12,9 @@ inherent_traits = list(TRAIT_NOBREATH) speedmod = 1.5 //faster than golems but not by much - punchdamagelow = 6 - punchdamagehigh = 14 - punchstunthreshold = 14 //about 44% chance to stun + punchdamagelow = 2 + punchdamagehigh = 12 //still better than humans + punchstunthreshold = 10 no_equip = list(SLOT_WEAR_MASK, SLOT_WEAR_SUIT, SLOT_GLOVES, SLOT_SHOES, SLOT_W_UNIFORM) diff --git a/code/modules/mob/living/carbon/human/species_types/synths.dm b/code/modules/mob/living/carbon/human/species_types/synths.dm index 1700927a24..6c42e622fe 100644 --- a/code/modules/mob/living/carbon/human/species_types/synths.dm +++ b/code/modules/mob/living/carbon/human/species_types/synths.dm @@ -23,7 +23,7 @@ armor = 25 punchdamagelow = 10 punchdamagehigh = 19 - punchstunthreshold = 14 //about 50% chance to stun + punchstunthreshold = 14 disguise_fail_health = 50 /datum/species/synth/on_species_gain(mob/living/carbon/human/H, datum/species/old_species) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index ebb5a3dd31..7c6d0e8cdc 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -377,9 +377,15 @@ var/turf/open/miasma_turf = deceasedturf - var/list/cached_gases = miasma_turf.air.gases + var/datum/gas_mixture/stank = new - cached_gases[/datum/gas/miasma] += 0.1 + stank.gases[/datum/gas/miasma] = 0.1 + + stank.temperature = BODYTEMP_NORMAL + + miasma_turf.assume_air(stank) + + miasma_turf.air_update_turf() /mob/living/carbon/proc/handle_blood() return diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm new file mode 100644 index 0000000000..d9e3309e47 --- /dev/null +++ b/code/modules/mob/living/living_block.dm @@ -0,0 +1,98 @@ +// This file has a weird name, but it's for anything related to the checks for shields, blocking, dodging, and similar "stop this attack before it actually impacts the target" as opposed to "defend once it has hit". + +/* +/// Bitflags for check_block() and handle_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS. +/// Attack was not blocked +#define BLOCK_NONE NONE +/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION! +#define BLOCK_SUCCESS (1<<1) + +/// The below are for "metadata" on "how" the attack was blocked. + +/// Attack was and should be reflected (NOTE: the SHOULD here is important, as it says "the thing blocking isn't handling the reflecting for you so do it yourself"!) +#define BLOCK_SHOULD_REFLECT (1<<2) +/// Attack was manually redirected (including reflected) by any means by the defender. For when YOU are handling the reflection, rather than the thing hitting you. (see sleeping carp) +#define BLOCK_REDIRECTED (1<<3) +/// Attack was blocked by something like a shield. +#define BLOCK_PHYSICAL_EXTERNAL (1<<4) +/// Attack was blocked by something worn on you. +#define BLOCK_PHYSICAL_INTERNAL (1<<5) +/// Attack should pass through. Like SHOULD_REFLECT but for.. well, passing through harmlessly. +#define BLOCK_SHOULD_PASSTHROUGH (1<<6) +/// Attack outright missed because the target dodged. Should usually be combined with SHOULD_PASSTHROUGH or something (see martial arts) +#define BLOCK_TARGET_DODGED (1<<7) +*/ + +///Check whether or not we can block, without "triggering" a block. Basically run checks without effects like depleting shields. Wrapper for do_run_block(). The arguments on that means the same as for this. +/mob/living/proc/check_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) + return do_run_block(FALSE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list) + +/// Runs a block "sequence", effectively checking and then doing effects if necessary. Wrapper for do_run_block(). The arguments on that means the same as for this. +/mob/living/proc/run_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list) + return do_run_block(TRUE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list) + +/** The actual proc for block checks. DO NOT USE THIS DIRECTLY UNLESS YOU HAVE VERY GOOD REASON TO. To reduce copypaste for differences between handling for real attacks and virtual checks. + * Automatically checks all held items for /obj/item/proc/run_block() with the same parameters. + * @params + * real_attack - If this attack is real. This one is quirky; If it's real, run_block is called. If it's not, check_block is called and none of the regular checks happen, and this is effectively only useful + * for populating return_list with blocking metadata. + * object - Whatever /atom is actually hitting us, in essence. For example, projectile if gun, item if melee, structure/whatever if it's a thrown, etc. + * damage - The nominal damage this would do if it was to hit. Obviously doesn't take into effect explosions/magic/similar things.. unless you implement it to raise the value. + * attack_text - The text that this attack should show, in the context of something like "[src] blocks [attack_text]!" + * attack_type - See __DEFINES/combat.dm - Attack types, to distinguish between, for example, someone throwing an item at us vs bashing us with it. + * armour_penetration - 0-100 value of how effectively armor penetrating the attack should be. + * attacker - Set to the mob attacking IF KNOWN. Do not expect this to always be set! + * def_zone - The zone this'll impact. + * return_list - If something wants to grab things from what items/whatever put into list/block_return on obj/item/run_block and the comsig, pass in a list so you can grab anything put in it after block runs. + */ +/mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) + // Component signal block runs have highest priority.. for now. + . = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) + if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) + return + var/list/obj/item/tocheck = get_blocking_items() + sortTim(tocheck, /proc/cmp_item_block_priority_asc) + // i don't like this + var/block_chance_modifier = round(damage / -3) + if(real_attack) + for(var/obj/item/I in tocheck) + // i don't like this too + var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example + var/results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + . |= results + if((results & BLOCK_SUCCESS) && !(results & BLOCK_CONTINUE_CHAIN)) + break + else + for(var/obj/item/I in tocheck) + // i don't like this too + var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example + I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + +/// Gets an unsortedlist of objects to run block checks on. +/mob/living/proc/get_blocking_items() + . = list() + for(var/obj/item/I in held_items) + // this is a bad check but i am not removing it until a better catchall is made + if(istype(I, /obj/item/clothing)) + continue + . |= I + +/obj/item + /// The 0% to 100% chance for the default implementation of random block rolls. + var/block_chance = 0 + /// Block priority, higher means we check this higher in the "chain". + var/block_priority = BLOCK_PRIORITY_DEFAULT + +/// Runs block and returns flag for do_run_block to process. +/obj/item/proc/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + SEND_SIGNAL(src, COMSIG_ITEM_RUN_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + if(prob(final_block_chance)) + owner.visible_message("[owner] blocks [attack_text] with [src]!") + return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL + return BLOCK_NONE + +/// Returns block information using list/block_return. Used for check_block() on mobs. +/obj/item/proc/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + SEND_SIGNAL(src, COMSIG_ITEM_CHECK_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/existing = block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] + block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = max(existing || 0, final_block_chance) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 9980d2b830..7d220739d5 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -36,48 +36,44 @@ /mob/living/proc/on_hit(obj/item/projectile/P) return BULLET_ACT_HIT -/mob/living/proc/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) - var/block_chance_modifier = round(damage / -3) - for(var/obj/item/I in held_items) - if(!istype(I, /obj/item/clothing)) - var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example - if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return TRUE - return FALSE - -/mob/living/proc/check_reflect(def_zone) //Reflection checks for anything in your hands, based on the reflection chance of the object(s) - for(var/obj/item/I in held_items) - if(I.IsReflect(def_zone)) - return TRUE - return FALSE - -/mob/living/proc/reflect_bullet_check(obj/item/projectile/P, def_zone) - if(P.is_reflectable && check_reflect(def_zone)) // Checks if you've passed a reflection% check - visible_message("The [P.name] gets reflected by [src]!", \ - "The [P.name] gets reflected by [src]!") - // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(src) - // redirect the projectile - P.original = locate(new_x, new_y, P.z) - P.starting = curloc - P.firer = src - P.yo = new_y - curloc.y - P.xo = new_x - curloc.x - var/new_angle_s = P.Angle + rand(120,240) - while(new_angle_s > 180) // Translate to regular projectile degrees - new_angle_s -= 360 - P.setAngle(new_angle_s) - return TRUE - return FALSE +/mob/living/proc/handle_projectile_attack_redirection(obj/item/projectile/P, redirection_mode, silent = FALSE) + P.ignore_source_check = TRUE + switch(redirection_mode) + if(REDIRECT_METHOD_DEFLECT) + P.setAngle(SIMPLIFY_DEGREES(P.Angle + rand(120, 240))) + if(!silent) + visible_message("[P] gets deflected by [src]!", \ + "[P] gets deflected by [src]!") + if(REDIRECT_METHOD_REFLECT) + P.setAngle(SIMPLIFY_DEGREES(P.Angle + 180)) + if(!silent) + visible_message("[P] gets reflected by [src]!", \ + "[P] gets reflected by [src]!") + if(REDIRECT_METHOD_PASSTHROUGH) + if(!silent) + visible_message("[P] passes through [src]!", \ + "[P] passes through [src]!") + return + if(REDIRECT_METHOD_RETURN_TO_SENDER) + if(!silent) + visible_message("[src] deflects [P] back at their attacker!", \ + "[src] deflects [P] back at their attacker!") + if(P.firer) + P.setAngle(Get_Angle(src, P.firer)) + else + P.setAngle(SIMPLIFY_DEGREES(P.Angle + 180)) + else + CRASH("Invalid rediretion mode [redirection_mode]") /mob/living/bullet_act(obj/item/projectile/P, def_zone) if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself - if(reflect_bullet_check(P, def_zone)) - return BULLET_ACT_FORCE_PIERCE // complete projectile permutation - if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) + var/list/returnlist = list() + var/returned = run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist) + if(returned & BLOCK_SHOULD_REDIRECT) + handle_projectile_attack_redirection(P, returnlist[BLOCK_RETURN_REDIRECT_METHOD]) + if(returned & BLOCK_REDIRECTED) + return BULLET_ACT_FORCE_PIERCE + if(returned & BLOCK_SUCCESS) P.on_hit(src, 100, def_zone) return BULLET_ACT_BLOCK var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null) @@ -108,12 +104,14 @@ return FALSE /mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) + // Throwingdatum can be null if someone had an accident() while slipping with an item in hand. var/obj/item/I var/throwpower = 30 if(isitem(AM)) I = AM throwpower = I.throwforce - if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK)) + var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest + if(run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone) & BLOCK_SUCCESS) hitpush = FALSE skipcatch = TRUE blocked = TRUE @@ -124,10 +122,9 @@ if(I) if(!skipcatch && isturf(I.loc) && catch_item(I)) return TRUE - var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest var/dtype = BRUTE var/volume = I.get_volume_by_throwforce_and_or_w_class() - SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, zone) + SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, impacting_zone) dtype = I.damtype if (I.throwforce > 0) //If the weapon's throwforce is greater than zero... @@ -145,8 +142,8 @@ if(!blocked) visible_message("[src] has been hit by [I].", \ "[src] has been hit by [I].") - var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration) - apply_damage(I.throwforce, dtype, zone, armor) + var/armor = run_armor_check(impacting_zone, "melee", "Your armor has protected your [parse_zone(impacting_zone)].", "Your armor has softened hit to your [parse_zone(impacting_zone)].",I.armour_penetration) + apply_damage(I.throwforce, dtype, impacting_zone, armor) if(I.thrownby) log_combat(I.thrownby, src, "threw and hit", I) else @@ -272,7 +269,7 @@ /mob/living/attack_hand(mob/user) ..() //Ignoring parent return value here. SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user) - if((user != src) && user.a_intent != INTENT_HELP && check_shields(user, 0, user.name, attack_type = UNARMED_ATTACK)) + if((user != src) && user.a_intent != INTENT_HELP && (run_block(user, 0, user.name, ATTACK_TYPE_UNARMED | ATTACK_TYPE_MELEE, null, user, check_zone(user.zone_selected)) & BLOCK_SUCCESS)) log_combat(user, src, "attempted to touch") visible_message("[user] attempted to touch [src]!") return TRUE @@ -283,7 +280,7 @@ to_chat(user, "You don't want to hurt [src]!") return TRUE var/hulk_verb = pick("smash","pummel") - if(user != src && check_shields(user, 15, "the [hulk_verb]ing")) + if(user != src && (run_block(user, 15, "the [hulk_verb]ing", ATTACK_TYPE_MELEE, null, user, check_zone(user.zone_selected)) & BLOCK_SUCCESS)) return TRUE ..() return FALSE @@ -298,14 +295,14 @@ M.Feedstop() return // can't attack while eating! - if(HAS_TRAIT(src, TRAIT_PACIFISM)) + if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") return FALSE var/damage = rand(5, 35) if(M.is_adult) damage = rand(20, 40) - if(check_shields(M, damage, "the [M.name]")) + if(run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS) return FALSE if (stat != DEAD) @@ -324,7 +321,7 @@ if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") return FALSE - if(check_shields(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", MELEE_ATTACK, M.armour_penetration)) + if(run_block(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS) return FALSE if(M.attack_sound) playsound(loc, M.attack_sound, 50, 1, 1) @@ -334,17 +331,15 @@ log_combat(M, src, "attacked") return TRUE - /mob/living/attack_paw(mob/living/carbon/monkey/M) if (M.a_intent == INTENT_HARM) if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") return FALSE - if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH)) to_chat(M, "You can't bite with your mouth covered!") return FALSE - if(check_shields(M, 0, "the [M.name]")) + if(run_block(M, 0, "the [M.name]", ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS) return FALSE M.do_attack_animation(src, ATTACK_EFFECT_BITE) if (prob(75)) @@ -368,7 +363,7 @@ if(HAS_TRAIT(L, TRAIT_PACIFISM)) to_chat(L, "You don't want to hurt anyone!") return FALSE - if(L != src && check_shields(L, rand(1, 3), "the [L.name]")) + if(L != src && (run_block(L, rand(1, 3), "the [L.name]", ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED, L, check_zone(L.zone_selected)) & BLOCK_SUCCESS)) return FALSE L.do_attack_animation(src) if(prob(90)) @@ -382,7 +377,7 @@ "[L.name] has attempted to bite [src]!", null, COMBAT_MESSAGE_RANGE) /mob/living/attack_alien(mob/living/carbon/alien/humanoid/M) - if((M != src) && M.a_intent != INTENT_HELP && check_shields(M, 0, "the [M.name]")) + if((M != src) && M.a_intent != INTENT_HELP && (run_block(M, 0, "the [M.name]", ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS)) visible_message("[M] attempted to touch [src]!") return FALSE switch(M.a_intent) diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 810dd778de..892cc3f13b 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -283,9 +283,7 @@ T.visible_message("A port on [src] opens to reveal [cable], which promptly falls to the floor.", "You hear the soft click of something light and hard falling to the ground.") if("loudness") if(subscreen == 1) // Open Instrument - internal_instrument.interact(src) - if(subscreen == 2) // Change Instrument type - internal_instrument.selectInstrument() + internal_instrument.ui_interact(src) //updateUsrDialog() We only need to account for the single mob this is intended for, and he will *always* be able to call this window paiInterface() // So we'll just call the update directly rather than doing some default checks diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index a93bc8662e..9b3db8af62 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -112,11 +112,15 @@ /mob/living/silicon/bullet_act(obj/item/projectile/P, def_zone) if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself - if(reflect_bullet_check(P, def_zone)) - return -1 // complete projectile permutation - if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) + var/list/returnlist = list() + var/returned = run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist) + if(returned & BLOCK_SHOULD_REDIRECT) + handle_projectile_attack_redirection(P, returnlist[BLOCK_RETURN_REDIRECT_METHOD]) + if(returned & BLOCK_REDIRECTED) + return BULLET_ACT_FORCE_PIERCE + if(returned & BLOCK_SUCCESS) P.on_hit(src, 100, def_zone) - return 2 + return BULLET_ACT_BLOCK if((P.damage_type == BRUTE || P.damage_type == BURN)) adjustBruteLoss(P.damage) if(prob(P.damage*1.5)) diff --git a/code/modules/mob/living/simple_animal/guardian/types/charger.dm b/code/modules/mob/living/simple_animal/guardian/types/charger.dm index 237ab9d919..1ece6dde37 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/charger.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/charger.dm @@ -54,7 +54,7 @@ var/blocked = FALSE if(hasmatchingsummoner(hit_atom)) //if the summoner matches don't hurt them blocked = TRUE - if(L.check_shields(src, 90, "[name]", attack_type = THROWN_PROJECTILE_ATTACK)) + if(L.run_block(src, 90, "[name]", ATTACK_TYPE_TACKLE, 0, src) & BLOCK_SUCCESS) blocked = TRUE if(!blocked) L.drop_all_held_items() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm index 95fafbabfd..884e3bb989 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/herald.dm @@ -40,27 +40,27 @@ deathsound = 'sound/magic/demon_dies.ogg' deathmessage = "begins to shudder as it becomes transparent..." loot_drop = /obj/item/clothing/neck/cloak/herald_cloak - + can_talk = 1 attack_action_types = list(/datum/action/innate/elite_attack/herald_trishot, /datum/action/innate/elite_attack/herald_directionalshot, /datum/action/innate/elite_attack/herald_teleshot, /datum/action/innate/elite_attack/herald_mirror) - + var/mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/my_mirror = null var/is_mirror = FALSE - + /mob/living/simple_animal/hostile/asteroid/elite/herald/death() . = ..() if(!is_mirror) addtimer(CALLBACK(src, .proc/become_ghost), 8) if(my_mirror != null) qdel(my_mirror) - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/become_ghost() icon_state = "herald_ghost" - + /mob/living/simple_animal/hostile/asteroid/elite/herald/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) . = ..() if(.) @@ -71,25 +71,25 @@ button_icon_state = "herald_trishot" chosen_message = "You are now firing three shots in your chosen direction." chosen_attack_num = HERALD_TRISHOT - + /datum/action/innate/elite_attack/herald_directionalshot name = "Circular Shot" button_icon_state = "herald_directionalshot" chosen_message = "You are firing projectiles in all directions." chosen_attack_num = HERALD_DIRECTIONALSHOT - + /datum/action/innate/elite_attack/herald_teleshot name = "Teleport Shot" button_icon_state = "herald_teleshot" chosen_message = "You will now fire a shot which teleports you where it lands." chosen_attack_num = HERALD_TELESHOT - + /datum/action/innate/elite_attack/herald_mirror name = "Summon Mirror" button_icon_state = "herald_mirror" chosen_message = "You will spawn a mirror which duplicates your attacks." chosen_attack_num = HERALD_MIRROR - + /mob/living/simple_animal/hostile/asteroid/elite/herald/OpenFire() if(client) switch(chosen_attack) @@ -124,7 +124,7 @@ my_mirror.herald_teleshot(target) if(HERALD_MIRROR) herald_mirror() - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/shoot_projectile(turf/marker, set_angle, var/is_teleshot) var/turf/startloc = get_turf(src) var/obj/item/projectile/herald/H = null @@ -137,7 +137,7 @@ if(target) H.original = target H.fire(set_angle) - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_trishot(target) ranged_cooldown = world.time + 30 playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) @@ -151,17 +151,17 @@ addtimer(CALLBACK(src, .proc/shoot_projectile, target_turf, angle_to_target, FALSE), 10) addtimer(CALLBACK(src, .proc/shoot_projectile, target_turf, angle_to_target, FALSE), 12) addtimer(CALLBACK(src, .proc/shoot_projectile, target_turf, angle_to_target, FALSE), 14) - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_circleshot() var/static/list/directional_shot_angles = list(0, 45, 90, 135, 180, 225, 270, 315) for(var/i in directional_shot_angles) shoot_projectile(get_turf(src), i, FALSE) - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/unenrage() if(stat == DEAD || is_mirror) return icon_state = "herald" - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_directionalshot() ranged_cooldown = world.time + 50 if(!is_mirror) @@ -172,14 +172,14 @@ playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) addtimer(CALLBACK(src, .proc/herald_circleshot), 15) addtimer(CALLBACK(src, .proc/unenrage), 20) - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_teleshot(target) ranged_cooldown = world.time + 30 playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) var/target_turf = get_turf(target) var/angle_to_target = Get_Angle(src, target_turf) shoot_projectile(target_turf, angle_to_target, TRUE) - + /mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_mirror() ranged_cooldown = world.time + 40 playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) @@ -190,7 +190,7 @@ my_mirror = new_mirror my_mirror.my_master = src my_mirror.faction = faction.Copy() - + /mob/living/simple_animal/hostile/asteroid/elite/herald/mirror name = "herald's mirror" desc = "This fiendish work of magic copies the herald's attacks. Seems logical to smash it." @@ -203,16 +203,16 @@ del_on_death = TRUE is_mirror = TRUE var/mob/living/simple_animal/hostile/asteroid/elite/herald/my_master = null - + /mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/Initialize() ..() toggle_ai(AI_OFF) - + /mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/Destroy() if(my_master != null) my_master.my_mirror = null . = ..() - + /obj/item/projectile/herald name ="death bolt" icon_state= "chronobolt" @@ -222,7 +222,7 @@ eyeblur = 0 damage_type = BRUTE pass_flags = PASSTABLE - + /obj/item/projectile/herald/teleshot name ="golden bolt" damage = 0 @@ -239,11 +239,11 @@ var/mob/living/F = firer if(F != null && istype(F, /mob/living/simple_animal/hostile/asteroid/elite) && F.faction_check_mob(L)) L.heal_overall_damage(damage) - + /obj/item/projectile/herald/teleshot/on_hit(atom/target, blocked = FALSE) . = ..() firer.forceMove(get_turf(src)) - + //Herald's loot: Cloak of the Prophet /obj/item/clothing/neck/cloak/herald_cloak @@ -252,13 +252,13 @@ icon = 'icons/obj/lavaland/elite_trophies.dmi' icon_state = "herald_cloak" body_parts_covered = CHEST|GROIN|ARMS - hit_reaction_chance = 10 - + var/hit_reaction_chance = 10 + /obj/item/clothing/neck/cloak/herald_cloak/proc/reactionshot(mob/living/carbon/owner) var/static/list/directional_shot_angles = list(0, 45, 90, 135, 180, 225, 270, 315) for(var/i in directional_shot_angles) shoot_projectile(get_turf(owner), i, owner) - + /obj/item/clothing/neck/cloak/herald_cloak/proc/shoot_projectile(turf/marker, set_angle, mob/living/carbon/owner) var/turf/startloc = get_turf(owner) var/obj/item/projectile/herald/H = null @@ -266,12 +266,11 @@ H.preparePixelProjectile(marker, startloc) H.firer = owner H.fire(set_angle) - -/obj/item/clothing/neck/cloak/herald_cloak/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + +/obj/item/clothing/neck/cloak/herald_cloak/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) . = ..() if(rand(1,100) > hit_reaction_chance) return owner.visible_message("[owner]'s [src] emits a loud noise as [owner] is struck!") - var/static/list/directional_shot_angles = list(0, 45, 90, 135, 180, 225, 270, 315) playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE) addtimer(CALLBACK(src, .proc/reactionshot, owner), 10) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm index d83a38ae24..c9011c5f5f 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm @@ -12,7 +12,7 @@ * - Charges at the target after a telegraph, throwing them across the arena should it connect. * - Legionnaire's head detaches, attacking as it's own entity. Has abilities of it's own later into the fight. Once dead, regenerates after a brief period. If the skill is used while the head is off, it will be killed. * - Leaves a pile of bones at your location. Upon using this skill again, you'll swap locations with the bone pile. - * - Spews a cloud of smoke from it's maw, wherever said maw is. + * - Spews a cloud of smoke from it's maw, wherever said maw is. * A unique fight incorporating the head mechanic of legion into a whole new beast. Combatants will need to make sure the tag-team of head and body don't lure them into a deadly trap. */ @@ -45,35 +45,35 @@ /datum/action/innate/elite_attack/head_detach, /datum/action/innate/elite_attack/bonfire_teleport, /datum/action/innate/elite_attack/spew_smoke) - + var/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/myhead = null var/obj/structure/legionnaire_bonfire/mypile = null var/has_head = TRUE - + /datum/action/innate/elite_attack/legionnaire_charge name = "Legionnaire Charge" button_icon_state = "legionnaire_charge" chosen_message = "You will attempt to grab your opponent and throw them." chosen_attack_num = LEGIONNAIRE_CHARGE - + /datum/action/innate/elite_attack/head_detach name = "Release Head" button_icon_state = "head_detach" chosen_message = "You will now detach your head or kill it if it is already released." chosen_attack_num = HEAD_DETACH - + /datum/action/innate/elite_attack/bonfire_teleport name = "Bonfire Teleport" button_icon_state = "bonfire_teleport" chosen_message = "You will leave a bonfire. Second use will let you swap positions with it indefintiely. Using this move on the same tile as your active bonfire removes it." chosen_attack_num = BONFIRE_TELEPORT - + /datum/action/innate/elite_attack/spew_smoke name = "Spew Smoke" button_icon_state = "spew_smoke" chosen_message = "Your head will spew smoke in an area, wherever it may be." chosen_attack_num = SPEW_SMOKE - + /mob/living/simple_animal/hostile/asteroid/elite/legionnaire/OpenFire() if(client) switch(chosen_attack) @@ -96,7 +96,7 @@ bonfire_teleport() if(SPEW_SMOKE) spew_smoke() - + /mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/legionnaire_charge(target) ranged_cooldown = world.time + 50 var/dir_to_target = get_dir(get_turf(src), get_turf(target)) @@ -107,7 +107,7 @@ playsound(src,'sound/magic/demon_attack1.ogg', 200, 1) visible_message("[src] prepares to charge!") addtimer(CALLBACK(src, .proc/legionnaire_charge_2, dir_to_target, 0), 5) - + /mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/legionnaire_charge_2(var/move_dir, var/times_ran) if(times_ran >= 4) return @@ -136,7 +136,7 @@ L.Stun(20) //substituting this for the Paralyze from the line above, because we don't have tg paralysis stuff L.adjustBruteLoss(50) addtimer(CALLBACK(src, .proc/legionnaire_charge_2, move_dir, (times_ran + 1)), 2) - + /mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/head_detach(target) ranged_cooldown = world.time + 10 if(myhead != null) @@ -160,11 +160,11 @@ else if(health < maxHealth * 0.5) myhead.melee_damage_lower = 20 myhead.melee_damage_upper = 20 - + /mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/onHeadDeath() myhead = null addtimer(CALLBACK(src, .proc/regain_head), 50) - + /mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/regain_head() has_head = TRUE if(stat == DEAD) @@ -196,7 +196,7 @@ forceMove(pileturf) visible_message("[src] forms from the bonfire!") mypile.forceMove(legionturf) - + /mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/spew_smoke() ranged_cooldown = world.time + 60 var/turf/T = null @@ -213,7 +213,7 @@ var/datum/effect_system/smoke_spread/smoke = new smoke.set_up(2, T) smoke.start() - + //The legionnaire's head. Basically the same as any legion head, but we have to tell our creator when we die so they can generate another head. /mob/living/simple_animal/hostile/asteroid/elite/legionnairehead name = "legionnaire head" @@ -239,12 +239,12 @@ faction = list() ranged = FALSE var/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/body = null - + /mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/death() . = ..() if(body) body.onHeadDeath() - + //The legionnaire's bonfire, which can be swapped positions with. Also sets flammable living beings on fire when they walk over it. /obj/structure/legionnaire_bonfire name = "bone pile" @@ -258,20 +258,20 @@ light_range = 4 light_color = LIGHT_COLOR_RED var/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/myowner = null - - + + /obj/structure/legionnaire_bonfire/Entered(atom/movable/mover, turf/target) if(isliving(mover)) var/mob/living/L = mover L.adjust_fire_stacks(3) L.IgniteMob() . = ..() - + /obj/structure/legionnaire_bonfire/Destroy() if(myowner != null) myowner.mypile = null . = ..() - + //The visual effect which appears in front of legionnaire when he goes to charge. /obj/effect/temp_visual/dragon_swoop/legionnaire duration = 10 @@ -280,7 +280,7 @@ /obj/effect/temp_visual/dragon_swoop/legionnaire/Initialize() . = ..() transform *= 0.33 - + // Legionnaire's loot: Legionnaire Spine /obj/item/crusher_trophy/legionnaire_spine diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm index b41ca15948..89d9919981 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm @@ -285,9 +285,10 @@ if("Miner") mob_species = pickweight(list(/datum/species/human = 70, /datum/species/lizard = 26, /datum/species/fly = 2, /datum/species/plasmaman = 2)) if(mob_species == /datum/species/plasmaman) - uniform = /obj/item/clothing/under/plasmaman - head = /obj/item/clothing/head/helmet/space/plasmaman + uniform = /obj/item/clothing/under/plasmaman/mining + head = /obj/item/clothing/head/helmet/space/plasmaman/mining belt = /obj/item/tank/internals/plasmaman/belt + mask = /obj/item/clothing/mask/gas/explorer else uniform = /obj/item/clothing/under/rank/cargo/miner/lavaland if (prob(4)) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 5cdeaecd37..1375d88948 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -7,6 +7,8 @@ /mob/living/proc/DefaultCombatKnockdown(amount, updating = TRUE, ignore_canknockdown = FALSE, override_hardstun, override_stamdmg) if(!iscarbon(src)) return Paralyze(amount, updating, ignore_canknockdown) + if(!ignore_canknockdown && !(status_flags & CANKNOCKDOWN)) + return FALSE if(istype(buckled, /obj/vehicle/ridden)) buckled.unbuckle_mob(src) var/drop_items = amount > 80 //80 is cutoff for old item dropping behavior diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 93f82991fd..49848824e4 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -556,23 +556,18 @@ By design, d1 is the smallest direction and d2 is the highest new_cable.update_icon() /obj/item/stack/cable_coil/attack_self(mob/user) - if(!check_cable_amount(user)) + if(!use(15)) + to_chat(user, "You dont have enough cable coil to make restraints out of them") return to_chat(user, "You start making some cable restraints.") - if(do_after(user, 30, TRUE, user) && check_cable_amount(user)) - amount -= 15 - var/obj/item/restraints/handcuffs/cable/result = new(get_turf(user)) - user.put_in_hands(result) - result.color = color - to_chat(user, "You make some restraints out of cable") + if(!do_after(user, 30, TRUE, user, TRUE)) + to_chat(user, "You fail to make cable restraints, you need to stand still while doing so.") + give(15) return - to_chat(user, "You fail to make cable restraints, you need to stand still while doing so.") - -/obj/item/stack/cable_coil/proc/check_cable_amount(user) - if(amount < 15) //We dont care about cyborgs here, so we dont use get_amount() - to_chat(user, "You dont have enough cable coil to make restraints out of them") - return FALSE - return TRUE + var/obj/item/restraints/handcuffs/cable/result = new(get_turf(user)) + user.put_in_hands(result) + result.color = color + to_chat(user, "You make some restraints out of cable") //add cables to the stack /obj/item/stack/cable_coil/proc/give(extra) @@ -854,4 +849,4 @@ By design, d1 is the smallest direction and d2 is the highest . = ..() var/list/cable_colors = GLOB.cable_colors color = pick(cable_colors) - \ No newline at end of file + diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 6c5f97557d..73ea3ccd59 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -185,7 +185,7 @@ if(sunfrac <= 0) return - var/sgen = SOLAR_GEN_RATE * sunfrac + var/sgen = SOLAR_GEN_RATE * sunfrac * efficiency add_avail(sgen) if(control) control.gen += sgen @@ -253,6 +253,7 @@ var/obj/item/stack/sheet/G = S.change_stack(null, 2) if(G) glass_type = G + G.moveToNullspace() playsound(src.loc, 'sound/machines/click.ogg', 50, 1) user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") if(tracker) diff --git a/code/modules/projectiles/ammunition/ballistic/pistol.dm b/code/modules/projectiles/ammunition/ballistic/pistol.dm index 13a284e970..58a5bfb497 100644 --- a/code/modules/projectiles/ammunition/ballistic/pistol.dm +++ b/code/modules/projectiles/ammunition/ballistic/pistol.dm @@ -21,6 +21,11 @@ desc = "A 10mm incendiary bullet casing." projectile_type = /obj/item/projectile/bullet/incendiary/c10mm +/obj/item/ammo_casing/c10mm/soporific + name = ".10mm soporific bullet casing" + desc = "A 10mm soporific bullet casing." + projectile_type = /obj/item/projectile/bullet/c10mm/soporific + // 9mm (Stechkin APS) /obj/item/ammo_casing/c9mm diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index fbbca99ec2..81ae026138 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -52,6 +52,29 @@ ammo_type = /obj/item/ammo_casing/c10mm max_ammo = 20 +/obj/item/ammo_box/c10mm/fire + name = "ammo box (10mm Incendiary)" + icon_state = "10mmbox" + ammo_type = /obj/item/ammo_casing/c10mm/fire + max_ammo = 20 + +/obj/item/ammo_box/c10mm/hp + name = "ammo box (10mm Hollow Point)" + icon_state = "10mmbox" + ammo_type = /obj/item/ammo_casing/c10mm/hp + max_ammo = 20 + +/obj/item/ammo_box/c10mm/ap + name = "ammo box (10mm Armour Piercing)" + icon_state = "10mmbox" + ammo_type = /obj/item/ammo_casing/c10mm/ap + max_ammo = 20 + +/obj/item/ammo_box/c10mm/soporific + name = "ammo box (10mm Soporific)" + ammo_type = /obj/item/ammo_casing/c10mm/soporific + max_ammo = 20 + /obj/item/ammo_box/c45 name = "ammo box (.45)" icon_state = "45box" diff --git a/code/modules/projectiles/boxes_magazines/external/pistol.dm b/code/modules/projectiles/boxes_magazines/external/pistol.dm index dbde572db1..63b0483875 100644 --- a/code/modules/projectiles/boxes_magazines/external/pistol.dm +++ b/code/modules/projectiles/boxes_magazines/external/pistol.dm @@ -25,6 +25,13 @@ desc= "A gun magazine. Loaded with rounds which penetrate armour, but are less effective against normal targets." ammo_type = /obj/item/ammo_casing/c10mm/ap +/obj/item/ammo_box/magazine/m10mm/soporific + name = "pistol magazine (10mm soporific)" + icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi' + icon_state = "9x19pS" + desc = "A gun magazine. Loaded with rounds which inject the target with a variety of illegal substances to induce sleep in the target." + ammo_type = /obj/item/ammo_casing/c10mm/soporific + /obj/item/ammo_box/magazine/m45 name = "handgun magazine (.45)" icon_state = "45-8" diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index 68f212ba30..e15c49b891 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -21,6 +21,38 @@ var/obj/item/suppressor/S = new(src) install_suppressor(S) +//(reskinnable stetchkin) +/obj/item/gun/ballistic/automatic/pistol/modular + name = "modular pistol" + desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors." + icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi' + icon_state = "cde" + can_unsuppress = TRUE + obj_flags = UNIQUE_RENAME + unique_reskin = list("Default" = "cde", + "N-99" = "n99", + "Stealth" = "stealthpistol", + "HKVP-78" = "vp78", + "Luger" = "p08b", + "Mk.58" = "secguncomp", + "PX4 Storm" = "px4" + ) + +/obj/item/gun/ballistic/automatic/pistol/modular/update_icon() + ..() + if(current_skin) + icon_state = "[unique_reskin[current_skin]][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + else + icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + if(magazine && suppressed) + cut_overlays() + add_overlay("[unique_reskin[current_skin]]-magazine-sup") //Yes, this means the default iconstate can't have a magazine overlay + else if (magazine) + cut_overlays() + add_overlay("[unique_reskin[current_skin]]-magazine") + else + cut_overlays() + /obj/item/gun/ballistic/automatic/pistol/m1911 name = "\improper M1911" desc = "A classic .45 handgun with a small magazine capacity." @@ -92,3 +124,34 @@ to_chat(user, "..and falls into view. Whew, that was a close one.") user.dropItemToGround(src) +////////////Anti Tank Pistol//////////// + +/obj/item/gun/ballistic/automatic/pistol/antitank + name = "Anti Tank Pistol" + desc = "A massively impractical and silly monstrosity of a pistol that fires .50 calliber rounds. The recoil is likely to dislocate your wrist." + icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi' + icon_state = "atp" + item_state = "pistol" + recoil = 4 + mag_type = /obj/item/ammo_box/magazine/sniper_rounds + fire_delay = 50 + burst_size = 1 + can_suppress = 0 + w_class = WEIGHT_CLASS_NORMAL + actions_types = list() + fire_sound = 'sound/weapons/blastcannon.ogg' + spread = 20 //damn thing has no rifling. + +/obj/item/gun/ballistic/automatic/pistol/antitank/update_icon() + ..() + if(magazine) + cut_overlays() + add_overlay("atp-mag") + else + cut_overlays() + icon_state = "[initial(icon_state)][chambered ? "" : "-e"]" + +/obj/item/gun/ballistic/automatic/pistol/antitank/syndicate + name = "Syndicate Anti Tank Pistol" + desc = "A massively impractical and silly monstrosity of a pistol that fires .50 calliber rounds. The recoil is likely to dislocate a variety of joints without proper bracing." + pin = /obj/item/firing_pin/implant/pindicate diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 36a9bba295..e0c84e7047 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -65,9 +65,6 @@ update_icon() /obj/item/gun/energy/Destroy() - //no need to delete them, since contents are already deleted in atom/movable/Destroy(). - cell = null - ammo_type = null STOP_PROCESSING(SSobj, src) return ..() diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 14b2ff0c1a..c993c0013a 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -90,9 +90,10 @@ . = ..() AddComponent(/datum/component/butchering, 15, 125, 0, hitsound) -/obj/item/gun/magic/staff/spellblade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) - final_block_chance = 0 +/obj/item/gun/magic/staff/spellblade/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + // Do not block projectiles. + if(attack_type & ATTACK_TYPE_PROJECTILE) + return BLOCK_NONE return ..() /obj/item/gun/magic/staff/locker diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 9b60f96d16..6094a7aa46 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -20,7 +20,7 @@ var/atom/movable/firer = null//Who shot it var/atom/fired_from = null // the atom that the projectile was fired from (gun, turret) var/suppressed = FALSE //Attack message var/suppressed = FALSE //Attack message - var/candink = FALSE //Can this projectile play the dink sound when hitting the head? var/yo = null + var/candink = FALSE //Can this projectile play the dink sound when hitting the head? var/yo = null var/xo = null var/atom/original = null // the original target clicked @@ -695,3 +695,9 @@ /obj/item/projectile/experience_pressure_difference() return + +/////// MISC HELPERS //////// +/// Is this atom reflectable with ""standardized"" reflection methods like you know eshields and deswords and similar +/proc/is_energy_reflectable_projectile(atom/A) + var/obj/item/projectile/P = A + return istype(P) && P.is_reflectable diff --git a/code/modules/projectiles/projectile/bullets/pistol.dm b/code/modules/projectiles/projectile/bullets/pistol.dm index b68c3847e2..319640a4d5 100644 --- a/code/modules/projectiles/projectile/bullets/pistol.dm +++ b/code/modules/projectiles/projectile/bullets/pistol.dm @@ -34,3 +34,17 @@ name = "10mm incendiary bullet" damage = 15 fire_stacks = 2 + +/obj/item/projectile/bullet/c10mm/soporific + name ="10mm soporific bullet" + nodamage = TRUE + +/obj/item/projectile/bullet/c10mm/soporific/on_hit(atom/target, blocked = FALSE) + . = ..() + if((blocked != 100) && isliving(target)) + var/mob/living/L = target + L.blur_eyes(6) + if(L.getStaminaLoss() >= 60) + L.Sleeping(300) + else + L.adjustStaminaLoss(25) \ No newline at end of file diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index bf090302c8..e7229b5abf 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -467,9 +467,9 @@ if(H.physiology) H.physiology.stamina_mod *= 0.5 if(H.dna && H.dna.species) - H.dna.species.punchdamagehigh *= 4 - H.dna.species.punchdamagelow *= 4 - H.dna.species.punchstunthreshold *= 2 + H.dna.species.punchdamagehigh += 4 + H.dna.species.punchdamagelow += 4 + H.dna.species.punchstunthreshold -= 2 /datum/reagent/drug/skooma/on_mob_end_metabolize(mob/living/L) . = ..() @@ -480,9 +480,9 @@ if(H.physiology) H.physiology.stamina_mod *= 2 if(H.dna && H.dna.species) - H.dna.species.punchdamagehigh *= 0.25 - H.dna.species.punchdamagelow *= 0.25 - H.dna.species.punchstunthreshold *= 0.5 + H.dna.species.punchdamagehigh -= 4 + H.dna.species.punchdamagelow -= 4 + H.dna.species.punchstunthreshold += 2 /datum/reagent/drug/skooma/on_mob_life(mob/living/carbon/M) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1*REM) diff --git a/code/modules/research/xenobiology/crossbreeding/_clothing.dm b/code/modules/research/xenobiology/crossbreeding/_clothing.dm index b8bdffbabf..795a57b82c 100644 --- a/code/modules/research/xenobiology/crossbreeding/_clothing.dm +++ b/code/modules/research/xenobiology/crossbreeding/_clothing.dm @@ -137,8 +137,7 @@ Slimecrossing Armor var/hit_reflect_chance = 10 // Citadel Change: because 40% chance of bouncing lasers back into peoples faces isn't good. armor = list("melee" = 70, "bullet" = 70, "laser" = 40, "energy" = 40, "bomb" = 80, "bio" = 80, "rad" = 80, "fire" = 70, "acid" = 90) //Citadel Change to avoid immortal Xenobiologists. -/obj/item/clothing/suit/armor/heavy/adamantine/IsReflect(def_zone) - if(def_zone in list(BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) && prob(hit_reflect_chance)) - return TRUE - else - return FALSE +/obj/item/clothing/suit/armor/heavy/adamantine/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(is_energy_reflectable_projectile(object) && prob(hit_reflect_chance)) + return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_INTERNAL + return ..() diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 1799a61406..03488fa8a9 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -436,7 +436,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th if(magic_flags & SPELL_SKIP_ALL_REQS) return TRUE - if(player_lock && (!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list))) + if(player_lock ? (!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list)) : !(src in user.mob_spell_list)) if(!silent) to_chat(user, "You shouldn't have this spell! Something's wrong.") return FALSE @@ -483,7 +483,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th to_chat(L, "You can't get the words out!") return FALSE - if(!(magic_flags & SPELL_SKIP_MOBTYPE) && ((mobs_whitelist && !mobs_whitelist[user]) || (mobs_blacklist && mobs_blacklist[user]))) + if(!(magic_flags & SPELL_SKIP_MOBTYPE) && ((mobs_whitelist && !mobs_whitelist[user.type]) || (mobs_blacklist && mobs_blacklist[user.type]))) if(!silent) to_chat(user, "This spell can't be casted in this current form!") return FALSE diff --git a/code/modules/uplink/uplink_items/uplink_ammo.dm b/code/modules/uplink/uplink_items/uplink_ammo.dm index 15bb47af91..4a7e1f146e 100644 --- a/code/modules/uplink/uplink_items/uplink_ammo.dm +++ b/code/modules/uplink/uplink_items/uplink_ammo.dm @@ -15,6 +15,12 @@ cost = 1 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) +/datum/uplink_item/ammo/pistol/box + name = "Ammo Box - 10mm" + desc = "An additional box of 10mm ammo. The box has 20 cases inside, does not come with a magazine." + item = /obj/item/ammo_box/c10mm + illegal_tech = FALSE + /datum/uplink_item/ammo/pistolap name = "10mm Armour Piercing Magazine" desc = "An additional 8-round 10mm magazine; compatible with the Stechkin Pistol. \ @@ -23,6 +29,12 @@ cost = 2 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) +/datum/uplink_item/ammo/pistolap/box + name = "Ammo Box - 10mm Armour Piercing" + desc = "An additional box of 10mm Armour Piercing ammo. The box has 20 cases inside, does not come with a magazine." + item = /obj/item/ammo_box/c10mm/ap + illegal_tech = FALSE + /datum/uplink_item/ammo/pistolhp name = "10mm Hollow Point Magazine" desc = "An additional 8-round 10mm magazine; compatible with the Stechkin Pistol. \ @@ -31,6 +43,12 @@ cost = 3 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) +/datum/uplink_item/ammo/pistolhp/box + name = "Ammo Box - 10mm Hollow Point" + desc = "An additional box of 10mm Hollow Point ammo. The box has 20 cases inside, does not come with a magazine." + item = /obj/item/ammo_box/c10mm/hp + illegal_tech = FALSE + /datum/uplink_item/ammo/pistolfire name = "10mm Incendiary Magazine" desc = "An additional 8-round 10mm magazine; compatible with the Stechkin Pistol. \ @@ -39,6 +57,12 @@ cost = 2 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) +/datum/uplink_item/ammo/pistolfire/box + name = "Ammo Box - 10mm Incendiary" + desc = "An additional box of 10mm Incendiary ammo. The box has 20 cases inside, does not come with a magazine." + item = /obj/item/ammo_box/magazine/m10mm/fire + illegal_tech = FALSE + /datum/uplink_item/ammo/pistolzzz name = "10mm Soporific Magazine" desc = "An additional 8-round 10mm magazine; compatible with the Stechkin Pistol. Loaded with soporific rounds that put the target to sleep. \ @@ -46,6 +70,12 @@ item = /obj/item/ammo_box/magazine/m10mm/soporific cost = 2 +/datum/uplink_item/ammo/pistolzzz/box + name = "Ammo Box - 10mm Soporific" + desc = "An additional box of 10mm Soporific ammo. The box has 20 cases inside, does not come with a magazine." + item = /obj/item/ammo_box/c10mm/soporific + illegal_tech = FALSE + /datum/uplink_item/ammo/shotgun cost = 2 include_modes = list(/datum/game_mode/nuclear) @@ -212,6 +242,12 @@ cost = 2 include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/ammo/pistolaps + name = "Ammo Box - 9mm" + desc = "An additional box of 9mm ammo. The box has 30 cases inside, does not come with a magazine." + item = /obj/item/ammo_box/c9mm + illegal_tech = FALSE + /datum/uplink_item/ammo/flechetteap name = "Armor Piercing Flechette Magazine" desc = "An additional 40-round flechette magazine; compatible with the Flechette Launcer. \ diff --git a/code/modules/uplink/uplink_items/uplink_dangerous.dm b/code/modules/uplink/uplink_items/uplink_dangerous.dm index 819ba51623..c798aa5cd9 100644 --- a/code/modules/uplink/uplink_items/uplink_dangerous.dm +++ b/code/modules/uplink/uplink_items/uplink_dangerous.dm @@ -9,9 +9,9 @@ /datum/uplink_item/dangerous/pistol name = "Stechkin Pistol" - desc = "A small, easily concealable handgun that uses 10mm auto rounds in 8-round magazines and is compatible \ + desc = "A sleek box containing a small, easily concealable handgun that uses 10mm auto rounds in 8-round magazines. The handgun is compatible \ with suppressors." - item = /obj/item/gun/ballistic/automatic/pistol + item = /obj/item/storage/box/syndie_kit/pistol cost = 7 exclude_modes = list(/datum/game_mode/nuclear/clown_ops) @@ -174,10 +174,13 @@ include_modes = list(/datum/game_mode/nuclear) /datum/uplink_item/dangerous/rapid - name = "Gloves of the North Star" - desc = "These gloves let the user punch people very fast. Does not improve weapon attack speed or the meaty fists of a hulk." - item = /obj/item/clothing/gloves/rapid - cost = 8 + name = "Bands of the North Star" + desc = "These armbands let the user punch people very fast and with the lethality of a legendary martial artist. \ + Does not improve weapon attack speed or the meaty fists of a hulk, but you will be unmatched in martial power. \ + Combines with all martial arts, but the user will be unable to bring themselves to use guns, nor remove the armbands." + item = /obj/item/clothing/gloves/fingerless/pugilist/rapid + cost = 30 + include_modes = list(/datum/game_mode/nuclear) /datum/uplink_item/dangerous/guardian name = "Holoparasites" diff --git a/code/modules/uplink/uplink_items/uplink_devices.dm b/code/modules/uplink/uplink_items/uplink_devices.dm index 06fd9aab43..1a7345a3ae 100644 --- a/code/modules/uplink/uplink_items/uplink_devices.dm +++ b/code/modules/uplink/uplink_items/uplink_devices.dm @@ -185,8 +185,9 @@ /datum/uplink_item/device_tools/stimpack name = "Stimpack" desc = "Stimpacks, the tool of many great heroes, make you nearly immune to stuns and knockdowns for about \ - 5 minutes after injection." - item = /obj/item/reagent_containers/syringe/stimulants + 5 minutes after fully injecting yourself. Can inject yourself, or others, 5 times and through hardsuits. \ + Each injection will gives around a minute of stimulants." + item = /obj/item/reagent_containers/hypospray/medipen/stimulants cost = 5 surplus = 90 diff --git a/code/modules/uplink/uplink_items/uplink_stealth.dm b/code/modules/uplink/uplink_items/uplink_stealth.dm index 80aba02e2e..b4933b30ba 100644 --- a/code/modules/uplink/uplink_items/uplink_stealth.dm +++ b/code/modules/uplink/uplink_items/uplink_stealth.dm @@ -51,6 +51,7 @@ gain skin as hard as steel and swat bullets from the air, but you also refuse to use dishonorable ranged weaponry." item = /obj/item/book/granter/martial/carp cost = 17 + player_minimum = 30 surplus = 0 exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) @@ -60,6 +61,7 @@ and dodging all ranged weapon fire, but you will refuse to use dishonorable ranged weaponry." item = /obj/item/book/granter/martial/bass cost = 18 + player_minimum = 30 surplus = 0 exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 8a62c19e16..924fd506ff 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -16,12 +16,26 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY CANISTER CHARGES in vending_items.dm */ +#define MAX_VENDING_INPUT_AMOUNT 30 +/** + * # vending record datum + * + * A datum that represents a product that is vendable + */ /datum/data/vending_product name = "generic" + ///Typepath of the product that is created when this record "sells" var/product_path = null + ///How many of this product we currently have var/amount = 0 + ///How many we can store at maximum var/max_amount = 0 +/** + * # vending machines + * + * Captalism in the year 2525, everything in a vending machine, even love + */ /obj/machinery/vending name = "\improper Vendomat" desc = "A generic vending machine." @@ -36,43 +50,117 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C integrity_failure = 0.33 armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70) circuit = /obj/item/circuitboard/machine/vendor - var/active = 1 //No sales pitches if off! - var/vend_ready = 1 //Are we ready to vend?? Is it time?? + light_power = 0.5 + light_range = MINIMUM_USEFUL_LIGHT_RANGE + /// Is the machine active (No sales pitches if off)! + var/active = TRUE + ///Are we ready to vend?? Is it time?? + var/vend_ready = TRUE + ///Next world time to send a purchase message + var/purchase_message_cooldown + ///Last mob to shop with us - // To be filled out at compile time - var/list/products = list() //For each, use the following pattern: - var/list/contraband = list() //list(/type/path = amount, /type/path2 = amount2) - var/list/premium = list() //No specified amount = only one in stock + /** + * List of products this machine sells + * + * form should be list(/type/path = amount, /type/path2 = amount2) + */ + var/list/products = list() + + /** + * List of products this machine sells when you hack it + * + * form should be list(/type/path = amount, /type/path2 = amount2) + */ + var/list/contraband = list() + + /** + * List of premium products this machine sells + * + * form should be list(/type/path, /type/path2) as there is only ever one in stock + */ + var/list/premium = list() + + ///String of slogans separated by semicolons, optional + var/product_slogans = "" + ///String of small ad messages in the vending screen - random chance + var/product_ads = "" - var/product_slogans = "" //String of slogans separated by semicolons, optional - var/product_ads = "" //String of small ad messages in the vending screen - random chance var/list/product_records = list() var/list/hidden_records = list() var/list/coin_records = list() var/list/slogan_list = list() - var/list/small_ads = list() //Small ad messages in the vending screen - random chance of popping up whenever you open it - var/vend_reply //Thank you for shopping! - var/last_reply = 0 - var/last_slogan = 0 //When did we last pitch? - var/slogan_delay = 6000 //How long until we can pitch again? - var/icon_vend //Icon_state when vending! - var/icon_deny //Icon_state when vending! - var/seconds_electrified = 0 //Shock customers like an airlock. - var/shoot_inventory = 0 //Fire items at customers! We're broken! - var/shoot_inventory_chance = 2 - var/shut_up = 0 //Stop spouting those godawful pitches! - var/extended_inventory = 0 //can we access the hidden inventory? - var/scan_id = 1 - var/obj/item/coin/coin - var/obj/item/stack/spacecash/bill - - var/static/vending_cache = list() //used for storing the icons of items being vended - + ///Small ad messages in the vending screen - random chance of popping up whenever you open it + var/list/small_ads = list() var/dish_quants = list() //used by the snack machine's custom compartment to count dishes. + ///Message sent post vend (Thank you for shopping!) + var/vend_reply + ///Last world tick we sent a vent reply + var/last_reply + ///Last world tick we sent a slogan message out + var/last_slogan + var/last_shopper + ///How many ticks until we can send another + var/slogan_delay = 6000 + ///Icon when vending an item to the user + var/icon_vend + ///Icon to flash when user is denied a vend + var/icon_deny + ///World ticks the machine is electified for + var/seconds_electrified = MACHINE_NOT_ELECTRIFIED + ///When this is TRUE, we fire items at customers! We're broken! + var/shoot_inventory + ///How likely this is to happen (prob 100) + var/shoot_inventory_chance = 2 + //Stop spouting those godawful pitches! + var/shut_up + ///can we access the hidden inventory? + var/extended_inventory + ///Are we checking the users ID + var/scan_id = TRUE + ///Coins that we accept? + var/obj/item/coin/coin + ///Default price of items if not overridden + /** + * Is this item on station or not + * + * if it doesn't originate from off-station during mapload, everything is free + */ + var/onstation = TRUE //if it doesn't originate from off-station during mapload, everything is free + ///A variable to change on a per instance basis on the map that allows the instance to force cost and ID requirements + var/onstation_override = FALSE //change this on the object on the map to override the onstation check. DO NOT APPLY THIS GLOBALLY. - var/obj/item/vending_refill/refill_canister = null //The type of refill canisters used by this machine. + ///ID's that can load this vending machine wtih refills + var/list/canload_access_list -/obj/machinery/vending/Initialize() + + var/list/vending_machine_input = list() + ///Display header on the input view + var/input_display_header = "Custom Vendor" + + //The type of refill canisters used by this machine. + var/obj/item/vending_refill/refill_canister = null + + /// how many items have been inserted in a vendor + var/loaded_items + + ///Name of lighting mask for the vending machine + var/light_mask + +/obj/item/circuitboard + ///determines if the circuit board originated from a vendor off station or not. + var/onstation = TRUE + +/** + * Initialize the vending machine + * + * Builds the vending machine inventory, sets up slogans and other such misc work + * + * This also sets the onstation var to: + * * FALSE - if the machine was maploaded on a zlevel that doesn't pass the is_station_level check + * * TRUE - all other cases + */ +/obj/machinery/vending/Initialize(mapload) var/build_inv = FALSE if(!refill_canister) circuit = null @@ -94,9 +182,11 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/machinery/vending/Destroy() QDEL_NULL(wires) QDEL_NULL(coin) - QDEL_NULL(bill) return ..() +/obj/machinery/vending/can_speak() + return !shut_up + /obj/machinery/vending/RefreshParts() //Better would be to make constructable child if(!component_parts) return @@ -118,10 +208,31 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C else ..() -/obj/machinery/vending/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1)) - stat |= BROKEN +/obj/machinery/vending/update_icon_state() + if(stat & BROKEN) icon_state = "[initial(icon_state)]-broken" + set_light(0) + else if(powered()) + icon_state = initial(icon_state) + set_light(1.4) + else + icon_state = "[initial(icon_state)]-off" + set_light(0) + + +/obj/machinery/vending/update_overlays() + . = ..() + if(!light_mask) + return + + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) + if(!(stat & BROKEN) && powered()) + SSvis_overlays.add_vis_overlay(src, icon, light_mask, EMISSIVE_LAYER, EMISSIVE_PLANE) + +/obj/machinery/vending/obj_break(damage_flag) + . = ..() + if(!.) + return var/dump_amount = 0 var/found_anything = TRUE @@ -146,6 +257,16 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if (dump_amount >= 16) return +GLOBAL_LIST_EMPTY(vending_products) +/** + * Build the inventory of the vending machine from it's product and record lists + * + * This builds up a full set of /datum/data/vending_products from the product list of the vending machine type + * Arguments: + * * productlist - the list of products that need to be converted + * * recordlist - the list containing /datum/data/vending_product datums + * * startempty - should we set vending_product record amount from the product list (so it's prefilled at roundstart) + */ /obj/machinery/vending/proc/build_inventory(list/productlist, list/recordlist, start_empty = FALSE) for(var/typepath in productlist) var/amount = productlist[typepath] @@ -154,13 +275,21 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/atom/temp = typepath var/datum/data/vending_product/R = new /datum/data/vending_product() + GLOB.vending_products[typepath] = 1 R.name = initial(temp.name) R.product_path = typepath if(!start_empty) R.amount = amount R.max_amount = amount recordlist += R - +/** + * Refill a vending machine from a refill canister + * + * This takes the products from the refill canister and then fills the products,contraband and premium product categories + * + * Arguments: + * * canister - the vending canister we are refilling from + */ /obj/machinery/vending/proc/restock(obj/item/vending_refill/canister) if (!canister.products) canister.products = products.Copy() @@ -172,7 +301,13 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C . += refill_inventory(canister.products, product_records) . += refill_inventory(canister.contraband, hidden_records) . += refill_inventory(canister.premium, coin_records) - +/** + * Refill our inventory from the passed in product list into the record list + * + * Arguments: + * * productlist - list of types -> amount + * * recordlist - existing record datums + */ /obj/machinery/vending/proc/refill_inventory(list/productlist, list/recordlist) . = 0 for(var/R in recordlist) @@ -182,7 +317,11 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C productlist[record.product_path] -= diff record.amount += diff . += diff - +/** + * Set up a refill canister that matches this machines products + * + * This is used when the machine is deconstructed, so the items aren't "lost" + */ /obj/machinery/vending/proc/update_canister() if (!component_parts) return @@ -190,12 +329,14 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/obj/item/vending_refill/R = locate() in component_parts if (!R) CRASH("Constructible vending machine did not have a refill canister") - return R.products = unbuild_inventory(product_records) R.contraband = unbuild_inventory(hidden_records) R.premium = unbuild_inventory(coin_records) +/** + * Given a record list, go through and and return a list of type -> amount + */ /obj/machinery/vending/proc/unbuild_inventory(list/recordlist) . = list() for(var/R in recordlist) @@ -209,11 +350,14 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C return TRUE /obj/machinery/vending/wrench_act(mob/living/user, obj/item/I) + ..() if(panel_open) default_unfasten_wrench(user, I, time = 60) return TRUE /obj/machinery/vending/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE if(anchored) default_deconstruction_screwdriver(user, icon_state, icon_state, I) cut_overlays() @@ -232,9 +376,6 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(coin) to_chat(user, "[src] already has [coin] inserted") return - if(bill) - to_chat(user, "[src] already has [bill] inserted") - return if(!premium.len) to_chat(user, "[src] doesn't have a coin slot.") return @@ -243,41 +384,87 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C coin = I to_chat(user, "You insert [I] into [src].") return - else if(istype(I, /obj/item/stack/spacecash)) - if(coin) - to_chat(user, "[src] already has [coin] inserted") - return - if(bill) - to_chat(user, "[src] already has [bill] inserted") - return - var/obj/item/stack/S = I - if(!premium.len) - to_chat(user, "[src] doesn't have a bill slot.") - return - S.use(1) - bill = new S.type(src, 1) - to_chat(user, "You insert [I] into [src].") - return else if(refill_canister && istype(I, refill_canister)) if (!panel_open) - to_chat(user, "You should probably unscrew the service panel first.") + to_chat(user, "You should probably unscrew the service panel first!") else if (stat & (BROKEN|NOPOWER)) to_chat(user, "[src] does not respond.") else //if the panel is open we attempt to refill the machine var/obj/item/vending_refill/canister = I if(canister.get_part_rating() == 0) - to_chat(user, "[canister] is empty!") + to_chat(user, "[canister] is empty!") else // instantiate canister if needed var/transferred = restock(canister) if(transferred) to_chat(user, "You loaded [transferred] items in [src].") else - to_chat(user, "There's nothing to restock!") + to_chat(user, "There's nothing to restock!") return + + if(compartmentLoadAccessCheck(user) && user.a_intent != INTENT_HARM) + if(canLoadItem(I)) + loadingAttempt(I,user) + updateUsrDialog() //can't put this on the proc above because we spam it below + + if(istype(I, /obj/item/storage/bag)) //trays USUALLY + var/obj/item/storage/T = I + var/loaded = 0 + var/denied_items = 0 + for(var/obj/item/the_item in T.contents) + if(contents.len >= MAX_VENDING_INPUT_AMOUNT) // no more than 30 item can fit inside, legacy from snack vending although not sure why it exists + to_chat(user, "[src]'s compartment is full.") + break + if(canLoadItem(the_item) && loadingAttempt(the_item,user)) + SEND_SIGNAL(T, COMSIG_TRY_STORAGE_TAKE, the_item, src, TRUE) + loaded++ + else + denied_items++ + if(denied_items) + to_chat(user, "[src] refuses some items!") + if(loaded) + to_chat(user, "You insert [loaded] dishes into [src]'s compartment.") + updateUsrDialog() else - return ..() + . = ..() + +/obj/machinery/vending/proc/loadingAttempt(obj/item/I, mob/user) + . = TRUE + if(!user.transferItemToLoc(I, src)) + return FALSE + if(vending_machine_input[format_text(I.name)]) + vending_machine_input[format_text(I.name)]++ + else + vending_machine_input[format_text(I.name)] = 1 + to_chat(user, "You insert [I] into [src]'s input compartment.") + loaded_items++ + +/** + * Is the passed in user allowed to load this vending machines compartments + * + * Arguments: + * * user - mob that is doing the loading of the vending machine + */ +/obj/machinery/vending/proc/compartmentLoadAccessCheck(mob/user) + if(!canload_access_list) + return TRUE + else + var/do_you_have_access = FALSE + var/req_access_txt_holder = req_access_txt + for(var/i in canload_access_list) + req_access_txt = i + if(!allowed(user) && !(obj_flags & EMAGGED) && scan_id) + continue + else + do_you_have_access = TRUE + break //you passed don't bother looping anymore + req_access_txt = req_access_txt_holder // revert to normal (before the proc ran) + if(do_you_have_access) + return TRUE + else + to_chat(user, "[src]'s input compartment blinks red: Access denied.") + return FALSE /obj/machinery/vending/exchange_parts(mob/user, obj/item/storage/part_replacer/W) if(!istype(W)) @@ -297,7 +484,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C else display_parts(user) if(moved) - to_chat(user, "[moved] items restocked.") + to_chat(user, "[moved] items restocked.") W.play_rped_sound() return TRUE @@ -319,188 +506,144 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C return return ..() -/obj/machinery/vending/ui_interact(mob/user) - var/dat = "" +/obj/machinery/vending/ui_base_html(html) + var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/vending) + . = replacetext(html, "", assets.css_tag()) - dat += "

Select an item

" - dat += "
" - if(!product_records.len) - dat += "No product loaded!" - else - var/list/display_records = product_records - if(extended_inventory) - display_records = product_records + hidden_records - if(coin || bill) - display_records = product_records + coin_records - if((coin || bill) && extended_inventory) - display_records = product_records + hidden_records + coin_records - dat += "" - for (var/datum/data/vending_product/R in display_records) - dat += "" - dat += "" - if(R.amount > 0) - dat += "" - else - dat += "" - dat += "" - dat += "
[sanitize(R.name)][R.amount] VendVend
" - dat += "
" - if(premium.len > 0) - dat += "Change Return: " - if (coin || bill) - dat += "[(coin ? coin : "")][(bill ? bill : "")]  Remove" - else - dat += "No money  Remove" - if(istype(src, /obj/machinery/vending/snack)) - dat += "

Chef's Food Selection

" - dat += "
" - for (var/O in dish_quants) - if(dish_quants[O] > 0) - var/N = dish_quants[O] - dat += "Dispense " - dat += "[capitalize(O)]: [N]
" - dat += "
" +/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending) + assets.send(user) + ui = new(user, src, ui_key, "vending", name, 450, 600, master_ui, state) + ui.open() - var/datum/browser/popup = new(user, "vending", (name)) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() +/obj/machinery/vending/ui_static_data(mob/user) + . = list() + .["product_records"] = list() + for (var/datum/data/vending_product/R in product_records) + var/list/data = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + max_amount = R.max_amount, + ref = REF(R) + ) + .["product_records"] += list(data) + .["coin_records"] = list() + for (var/datum/data/vending_product/R in coin_records) + var/list/data = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + max_amount = R.max_amount, + ref = REF(R), + premium = TRUE + ) + .["coin_records"] += list(data) + .["hidden_records"] = list() + for (var/datum/data/vending_product/R in hidden_records) + var/list/data = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + max_amount = R.max_amount, + ref = REF(R), + premium = TRUE + ) + .["hidden_records"] += list(data) -/obj/machinery/vending/proc/GetIconForProduct(datum/data/vending_product/P) - if(vending_cache[P.product_path]) - return vending_cache[P.product_path] - var/product = new P.product_path() - vending_cache[P.product_path] = icon2base64(getFlatIcon(product, no_anim = TRUE)) - qdel(product) - return vending_cache[P.product_path] +/obj/machinery/vending/ui_data(mob/user) + . = list() + .["stock"] = list() + for(var/datum/data/vending_product/R in product_records + coin_records + hidden_records) + .["stock"][R.name] = R.amount + .["extended_inventory"] = extended_inventory + .["coin"] = coin -/obj/machinery/vending/Topic(href, href_list) - if(..()) +/obj/machinery/vending/ui_act(action, params) + . = ..() + if(.) return - - if(href_list["remove_coin"]) - if(!(coin || bill)) - to_chat(usr, "There is no money in this machine.") - return - if(coin) - usr.put_in_hands(coin) - to_chat(usr, "You remove [coin] from [src].") - coin = null - if(bill) - usr.put_in_hands(bill) - to_chat(usr, "You remove [bill] from [src].") - bill = null - - - usr.set_machine(src) - - if((href_list["dispense"]) && (vend_ready)) - var/N = href_list["dispense"] - if(dish_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. - return - vend_ready = 0 - use_power(5) - - dish_quants[N] = max(dish_quants[N] - 1, 0) - for(var/obj/O in contents) - if(O.name == N) - O.forceMove(drop_location()) - break - vend_ready = 1 - updateUsrDialog() - return - - if((href_list["vend"]) && (vend_ready)) - if(panel_open) - to_chat(usr, "The vending machine cannot dispense products while its service panel is open!") - return - - if((!allowed(usr)) && !(obj_flags & EMAGGED) && scan_id) //For SECURE VENDING MACHINES YEAH - to_chat(usr, "Access denied." ) - flick(icon_deny,src) - return - - vend_ready = 0 //One thing at a time!! - - var/datum/data/vending_product/R = locate(href_list["vend"]) - if(!R || !istype(R) || !R.product_path) - vend_ready = 1 - return - - if(R in hidden_records) - if(!extended_inventory) - vend_ready = 1 + switch(action) + if("vend") + . = TRUE + if(!vend_ready) return - else if(R in coin_records) - if(!(coin || bill)) - to_chat(usr, "You need to insert money to get this item!") - vend_ready = 1 + if(panel_open) + to_chat(usr, "The vending machine cannot dispense products while its service panel is open!") return - if(coin && coin.string_attached) - if(prob(50)) - if(usr.CanReach(src)) - if(usr.put_in_hands(coin)) - to_chat(usr, "You successfully pull [coin] out before [src] could swallow it.") - coin = null - else - to_chat(usr, "You couldn't pull [coin] out because your hands are full!") - QDEL_NULL(coin) - else + if((!allowed(usr)) && !(obj_flags & EMAGGED) && scan_id) //For SECURE VENDING MACHINES YEAH + to_chat(usr, "Access denied." ) + flick(icon_deny,src) + return + vend_ready = FALSE //One thing at a time!! + var/datum/data/vending_product/R = locate(params["ref"]) + if(!R || !istype(R) || !R.product_path) + vend_ready = TRUE + return + if(R in hidden_records) + if(!extended_inventory) + vend_ready = TRUE + return + else if(R in coin_records) + if(!(coin)) + to_chat(usr, "You need to a coin to get this item!") + vend_ready = TRUE + return + if(coin && coin.string_attached) + if(!prob(50)) + to_chat(usr, "You weren't able to pull [coin] out fast enough, the machine ate it, string and all!") + QDEL_NULL(coin) + return + if(!usr.CanReach(src)) to_chat(usr, "You successfully pull [coin] out of [src] to the floor.") coin = null - else - to_chat(usr, "You weren't able to pull [coin] out fast enough, the machine ate it, string and all!") - QDEL_NULL(coin) - else + if(!usr.put_in_hands(coin)) + to_chat(usr, "You couldn't pull [coin] out because your hands are full!") + QDEL_NULL(coin) + to_chat(usr, "You successfully pull [coin] out before [src] could swallow it.") + coin = null QDEL_NULL(coin) - QDEL_NULL(bill) - - else if (!(R in product_records)) - vend_ready = 1 - message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!") - return - - if (R.amount <= 0) - to_chat(usr, "Sold out.") - vend_ready = 1 - return - else - R.amount-- - - if(((last_reply + 200) <= world.time) && vend_reply) - speak(vend_reply) - last_reply = world.time - - use_power(5) - if(icon_vend) //Show the vending animation if needed - flick(icon_vend,src) - var/vended = new R.product_path(get_turf(src)) - if(usr.CanReach(src)) - if(usr.put_in_hands(vended)) + else if(!(R in product_records)) + vend_ready = TRUE + message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!") + return + if(R.amount <= 0) + to_chat(usr, "Sold out.") + vend_ready = TRUE + return + else + R.amount-- + if(((last_reply + 200) <= world.time) && vend_reply) + speak(vend_reply) + last_reply = world.time + use_power(5) + if(icon_vend) //Show the vending animation if needed + flick(icon_vend,src) + var/vended = new R.product_path(get_turf(src)) + if(usr.CanReach(src) && usr.put_in_hands(vended)) to_chat(usr, "You take [R.name] out of the slot.") else to_chat(usr, "[capitalize(R.name)] falls onto the floor!") - else - to_chat(usr, "[capitalize(R.name)] falls onto the floor!") - SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]")) - vend_ready = 1 - updateUsrDialog() + + SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]")) + vend_ready = TRUE + return + if("takeoutcoin") + usr.put_in_hands(coin) + to_chat(usr, "You remove [coin] from [src].") + coin = null return - - else if(href_list["togglevoice"] && panel_open) + + else if("togglevoice" && panel_open) shut_up = !shut_up - - updateUsrDialog() - - + /obj/machinery/vending/process() if(stat & (BROKEN|NOPOWER)) - return + return PROCESS_KILL if(!active) return - if(seconds_electrified > 0) + if(seconds_electrified > MACHINE_NOT_ELECTRIFIED) seconds_electrified-- //Pitch to the people! Really sell it! @@ -511,7 +654,14 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(shoot_inventory && prob(shoot_inventory_chance)) throw_item() - +/** + * Speak the given message verbally + * + * Checks if the machine is powered and the message exists + * + * Arguments: + * * message - the message to speak + */ /obj/machinery/vending/proc/speak(message) if(stat & (BROKEN|NOPOWER)) return @@ -521,47 +671,61 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C say(message) /obj/machinery/vending/power_change() - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - else - if(powered()) - icon_state = initial(icon_state) - stat &= ~NOPOWER - else - icon_state = "[initial(icon_state)]-off" - stat |= NOPOWER + . = ..() + if(powered()) + START_PROCESSING(SSmachines, src) //Somebody cut an important wire and now we're following a new definition of "pitch." +/** + * Throw an item from our internal inventory out in front of us + * + * This is called when we are hacked, it selects a random product from the records that has an amount > 0 + * This item is then created and tossed out in front of us with a visible message + */ /obj/machinery/vending/proc/throw_item() var/obj/throw_item = null - var/mob/living/target = locate() in view(7,src) + var/mob/living/target = locate() in view(7, src) if(!target) - return 0 - + return FALSE for(var/datum/data/vending_product/R in shuffle(product_records)) if(R.amount <= 0) //Try to use a record that actually has something to dump. continue var/dump_path = R.product_path if(!dump_path) continue - R.amount-- throw_item = new dump_path(loc) break if(!throw_item) - return 0 + return FALSE pre_throw(throw_item) throw_item.throw_at(target, 16, 3) visible_message("[src] launches [throw_item] at [target]!") - return 1 - + return TRUE +/** + * A callback called before an item is tossed out + * + * Override this if you need to do any special case handling + * + * Arguments: + * * I - obj/item being thrown + */ /obj/machinery/vending/proc/pre_throw(obj/item/I) return - -/obj/machinery/vending/proc/shock(mob/user, prb) - if(stat & (BROKEN|NOPOWER)) // unpowered, no shock +/** + * Shock the passed in user + * + * This checks we have power and that the passed in prob is passed, then generates some sparks + * and calls electrocute_mob on the user + * + * Arguments: + * * user - the user to shock + * * prb - probability the shock happens + */ +/obj/machinery/vending/proc/shock(mob/living/user, prb) + if(!istype(user) || stat & (BROKEN|NOPOWER)) // unpowered, no shock return FALSE if(!prob(prb)) return FALSE @@ -571,6 +735,15 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C return TRUE else return FALSE +/** + * Are we able to load the item passed in + * + * Arguments: + * * I - the item being loaded + * * user - the user doing the loading + */ +/obj/machinery/vending/proc/canLoadItem(obj/item/I, mob/user) + return FALSE /obj/machinery/vending/onTransitZ() return diff --git a/icons/mob/hands.dmi b/icons/mob/hands.dmi index e7068a1854..74aabf6021 100644 Binary files a/icons/mob/hands.dmi and b/icons/mob/hands.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index 76094938e8..aea15e2e39 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi index e937842275..d53f53c029 100644 Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi index 3d382d25b3..7ed9244ea4 100644 Binary files a/icons/obj/decals.dmi and b/icons/obj/decals.dmi differ diff --git a/icons/obj/glass_ware.dmi b/icons/obj/glass_ware.dmi new file mode 100644 index 0000000000..7b720836cc Binary files /dev/null and b/icons/obj/glass_ware.dmi differ diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi index ba8111bb00..e54f55ed46 100644 Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ diff --git a/icons/obj/tiles.dmi b/icons/obj/tiles.dmi index 2de24e2499..09568ebea1 100644 Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi index 87d5e88cc9..c8675209c4 100644 Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ diff --git a/icons/turf/vgstation_decals.dmi b/icons/turf/vgstation_decals.dmi new file mode 100644 index 0000000000..4f9d52c0cd Binary files /dev/null and b/icons/turf/vgstation_decals.dmi differ diff --git a/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm b/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm index 6d080ee898..0a3bdb727d 100644 --- a/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm +++ b/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm @@ -424,15 +424,15 @@ SLEEPER CODE IS IN game/objects/items/devices/dogborg_sleeper.dm ! if(hit_atom) if(isliving(hit_atom)) var/mob/living/L = hit_atom - if(!L.check_shields(0, "the [name]", src, attack_type = LEAP_ATTACK)) + if(L.run_block(0, "the [name]", src, ATTACK_TYPE_TACKLE, 0, src) & BLOCK_SUCCESS) + DefaultCombatKnockdown(15, 1, 1) + else L.visible_message("[src] pounces on [L]!", "[src] pounces on you!") L.DefaultCombatKnockdown(iscarbon(L) ? 60 : 45, override_stamdmg = CLAMP(pounce_stamloss, 0, pounce_stamloss_cap-L.getStaminaLoss())) // Temporary. If someone could rework how dogborg pounces work to accomodate for combat changes, that'd be nice. playsound(src, 'sound/weapons/Egloves.ogg', 50, 1) sleep(2)//Runtime prevention (infinite bump() calls on hulks) step_towards(src,L) log_combat(src, L, "borg pounced") - else - DefaultCombatKnockdown(15, 1, 1) pounce_cooldown = !pounce_cooldown spawn(pounce_cooldown_time) //3s by default diff --git a/modular_citadel/code/modules/projectiles/guns/ballistic/handguns.dm b/modular_citadel/code/modules/projectiles/guns/ballistic/handguns.dm index 53463bcfe5..20917c4ba5 100644 --- a/modular_citadel/code/modules/projectiles/guns/ballistic/handguns.dm +++ b/modular_citadel/code/modules/projectiles/guns/ballistic/handguns.dm @@ -1,34 +1,3 @@ -////////////Anti Tank Pistol//////////// - -/obj/item/gun/ballistic/automatic/pistol/antitank - name = "Anti Tank Pistol" - desc = "A massively impractical and silly monstrosity of a pistol that fires .50 calliber rounds. The recoil is likely to dislocate your wrist." - icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi' - icon_state = "atp" - item_state = "pistol" - recoil = 4 - mag_type = /obj/item/ammo_box/magazine/sniper_rounds - fire_delay = 50 - burst_size = 1 - can_suppress = 0 - w_class = WEIGHT_CLASS_NORMAL - actions_types = list() - fire_sound = 'sound/weapons/blastcannon.ogg' - spread = 20 //damn thing has no rifling. - -/obj/item/gun/ballistic/automatic/pistol/antitank/update_icon() - ..() - if(magazine) - cut_overlays() - add_overlay("atp-mag") - else - cut_overlays() - icon_state = "[initial(icon_state)][chambered ? "" : "-e"]" - -/obj/item/gun/ballistic/automatic/pistol/antitank/syndicate - name = "Syndicate Anti Tank Pistol" - desc = "A massively impractical and silly monstrosity of a pistol that fires .50 calliber rounds. The recoil is likely to dislocate a variety of joints without proper bracing." - pin = /obj/item/firing_pin/implant/pindicate ///foam stealth pistol/// @@ -56,72 +25,6 @@ cut_overlays() icon_state = "[initial(icon_state)][chambered ? "" : "-e"]" -//////10mm soporific bullets////// - -/obj/item/projectile/bullet/c10mm/soporific - name ="10mm soporific bullet" - nodamage = TRUE - -/obj/item/projectile/bullet/c10mm/soporific/on_hit(atom/target, blocked = FALSE) - . = ..() - if((blocked != 100) && isliving(target)) - var/mob/living/L = target - L.blur_eyes(6) - if(L.getStaminaLoss() >= 60) - L.Sleeping(300) - else - L.adjustStaminaLoss(25) - -/obj/item/ammo_casing/c10mm/soporific - name = ".10mm soporific bullet casing" - desc = "A 10mm soporific bullet casing." - projectile_type = /obj/item/projectile/bullet/c10mm/soporific - -/obj/item/ammo_box/magazine/m10mm/soporific - name = "pistol magazine (10mm soporific)" - icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi' - icon_state = "9x19pS" - desc = "A gun magazine. Loaded with rounds which inject the target with a variety of illegal substances to induce sleep in the target." - ammo_type = /obj/item/ammo_casing/c10mm/soporific - -/obj/item/ammo_box/c10mm/soporific - name = "ammo box (10mm soporific)" - ammo_type = /obj/item/ammo_casing/c10mm/soporific - max_ammo = 24 - -//////modular pistol////// (reskinnable stetchkins) - -/obj/item/gun/ballistic/automatic/pistol/modular - name = "modular pistol" - desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors." - icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi' - icon_state = "cde" - can_unsuppress = TRUE - obj_flags = UNIQUE_RENAME - unique_reskin = list("Default" = "cde", - "NT-99" = "n99", - "Stealth" = "stealthpistol", - "HKVP-78" = "vp78", - "Luger" = "p08b", - "Mk.58" = "secguncomp", - "PX4 Storm" = "px4" - ) - -/obj/item/gun/ballistic/automatic/pistol/modular/update_icon() - ..() - if(current_skin) - icon_state = "[unique_reskin[current_skin]][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - else - icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - if(magazine && suppressed) - cut_overlays() - add_overlay("[unique_reskin[current_skin]]-magazine-sup") //Yes, this means the default iconstate can't have a magazine overlay - else if (magazine) - cut_overlays() - add_overlay("[unique_reskin[current_skin]]-magazine") - else - cut_overlays() - /////////RAYGUN MEMES///////// /obj/item/projectile/beam/lasertag/ray //the projectile, compatible with regular laser tag armor diff --git a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm index 41eccb7054..d2a063fd4e 100644 --- a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm +++ b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm @@ -95,6 +95,7 @@ taste_description = "strawberry roofies" taste_mult = 2 //Hide the roofies in stronger flavors color = "#FFADFF"//PINK, rgb(255, 173, 255) + can_synth = FALSE /datum/reagent/drug/aphrodisiac/on_mob_life(mob/living/M) if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO)) @@ -120,6 +121,7 @@ color = "#FF2BFF"//dark pink addiction_threshold = 20 overdose_threshold = 20 + can_synth = FALSE /datum/reagent/drug/aphrodisiacplus/on_mob_life(mob/living/M) if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO)) @@ -174,6 +176,7 @@ taste_mult = 2 color = "#D9D9D9"//rgb(217, 217, 217) reagent_state = SOLID + can_synth = FALSE /datum/reagent/drug/anaphrodisiac/on_mob_life(mob/living/M) if(M && M.client?.prefs.arousable && prob(16)) @@ -191,6 +194,7 @@ color = "#D9D9D9"//rgb(217, 217, 217) reagent_state = SOLID overdose_threshold = 20 + can_synth = FALSE /datum/reagent/drug/anaphrodisiacplus/on_mob_life(mob/living/M) if(M && M.client?.prefs.arousable) diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg new file mode 100644 index 0000000000..9808431b25 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg new file mode 100644 index 0000000000..4118cff81c Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg new file mode 100644 index 0000000000..ae9e1361d2 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg new file mode 100644 index 0000000000..f19ddd9300 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg new file mode 100644 index 0000000000..d2c872d5a3 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg new file mode 100644 index 0000000000..e1e3fc8635 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg new file mode 100644 index 0000000000..42c4aa755d Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg new file mode 100644 index 0000000000..762d2868d0 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg new file mode 100644 index 0000000000..baf71063af Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg new file mode 100644 index 0000000000..92b4a11018 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg new file mode 100644 index 0000000000..5a50a5091c Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg new file mode 100644 index 0000000000..f086938260 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg new file mode 100644 index 0000000000..1246bd5341 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg new file mode 100644 index 0000000000..73680ce627 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg new file mode 100644 index 0000000000..72841fb189 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg new file mode 100644 index 0000000000..47797e4c00 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg new file mode 100644 index 0000000000..ea34703160 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg new file mode 100644 index 0000000000..4b96dd3dd3 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg new file mode 100644 index 0000000000..25daf5372e Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg new file mode 100644 index 0000000000..9b989404e7 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg new file mode 100644 index 0000000000..7fa99d9278 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg new file mode 100644 index 0000000000..64578a8fe1 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg new file mode 100644 index 0000000000..d97230ce1f Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg new file mode 100644 index 0000000000..eb6f80654a Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg new file mode 100644 index 0000000000..8f25ee2747 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg new file mode 100644 index 0000000000..17bea8122c Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg new file mode 100644 index 0000000000..6dc0d8ad38 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg new file mode 100644 index 0000000000..fbbcacb372 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg new file mode 100644 index 0000000000..f9bb3f6e92 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg new file mode 100644 index 0000000000..777a38664f Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg new file mode 100644 index 0000000000..c14aa5ec93 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg new file mode 100644 index 0000000000..b2a457b496 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg new file mode 100644 index 0000000000..c4f5aac8bf Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg new file mode 100644 index 0000000000..6a8115f004 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg new file mode 100644 index 0000000000..15ec741bb6 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg new file mode 100644 index 0000000000..df4ef3fb79 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg new file mode 100644 index 0000000000..961f4a6b90 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg new file mode 100644 index 0000000000..06ccf6ae51 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg new file mode 100644 index 0000000000..14756fb249 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg new file mode 100644 index 0000000000..544b324786 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg new file mode 100644 index 0000000000..df2dc8def5 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg new file mode 100644 index 0000000000..ba20e3f4b6 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg new file mode 100644 index 0000000000..fcc8715e04 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg new file mode 100644 index 0000000000..aa21d7e230 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg new file mode 100644 index 0000000000..ac45bc6be4 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg new file mode 100644 index 0000000000..71b9280e86 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg new file mode 100644 index 0000000000..676a24669f Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg new file mode 100644 index 0000000000..24e3732d7e Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg new file mode 100644 index 0000000000..72dc2ce764 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg new file mode 100644 index 0000000000..853b7591b4 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg new file mode 100644 index 0000000000..205e190379 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg new file mode 100644 index 0000000000..e90c28f14c Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg new file mode 100644 index 0000000000..a56d3cc3f6 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg new file mode 100644 index 0000000000..14c5f35eef Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg new file mode 100644 index 0000000000..e06e733eb2 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg new file mode 100644 index 0000000000..3d21e92d53 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg new file mode 100644 index 0000000000..be51b6dcd4 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg new file mode 100644 index 0000000000..7426d246a0 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg new file mode 100644 index 0000000000..af7b66cf19 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg new file mode 100644 index 0000000000..58ff4ff216 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg new file mode 100644 index 0000000000..4b7feb77a4 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg new file mode 100644 index 0000000000..5e7eefa79a Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg new file mode 100644 index 0000000000..13e49d4f3a Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg new file mode 100644 index 0000000000..6ef00f0700 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg new file mode 100644 index 0000000000..a868249ca8 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg new file mode 100644 index 0000000000..2d08d98dd9 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg new file mode 100644 index 0000000000..50e3cb76a7 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg new file mode 100644 index 0000000000..0b87714166 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg new file mode 100644 index 0000000000..31fd625098 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg new file mode 100644 index 0000000000..0ed309dbb7 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg new file mode 100644 index 0000000000..7fd820d373 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg new file mode 100644 index 0000000000..b4ed66e27c Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg new file mode 100644 index 0000000000..13a5f7d873 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg new file mode 100644 index 0000000000..0169d52c21 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg new file mode 100644 index 0000000000..16d49cb15e Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg new file mode 100644 index 0000000000..83e906cd1a Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg new file mode 100644 index 0000000000..33f766e4aa Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg new file mode 100644 index 0000000000..6ce06dda56 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg new file mode 100644 index 0000000000..3941e7af03 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg new file mode 100644 index 0000000000..4f79ee9987 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg new file mode 100644 index 0000000000..1d773c62e7 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg new file mode 100644 index 0000000000..938a557e04 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg new file mode 100644 index 0000000000..c65f06ce3e Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg new file mode 100644 index 0000000000..87d4943509 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg new file mode 100644 index 0000000000..5800b2d0ff Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg new file mode 100644 index 0000000000..9ce5eee76e Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg new file mode 100644 index 0000000000..4644327476 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg new file mode 100644 index 0000000000..ab6d8a2a92 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg new file mode 100644 index 0000000000..0041ea2e13 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg new file mode 100644 index 0000000000..5d836b9f7c Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg new file mode 100644 index 0000000000..1981572cf2 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg new file mode 100644 index 0000000000..35e5b147bc Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg differ diff --git a/sound/instruments/synthesis_samples/tones/Sawtooth.ogg b/sound/instruments/synthesis_samples/tones/Sawtooth.ogg new file mode 100644 index 0000000000..e46f5eaced Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Sawtooth.ogg differ diff --git a/sound/instruments/synthesis_samples/tones/Sine.ogg b/sound/instruments/synthesis_samples/tones/Sine.ogg new file mode 100644 index 0000000000..e0b14869e9 Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Sine.ogg differ diff --git a/sound/instruments/synthesis_samples/tones/Square.ogg b/sound/instruments/synthesis_samples/tones/Square.ogg new file mode 100644 index 0000000000..817531bcca Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Square.ogg differ diff --git a/sound/machines/machine_vend.ogg b/sound/machines/machine_vend.ogg new file mode 100644 index 0000000000..92867a1f3d Binary files /dev/null and b/sound/machines/machine_vend.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 8187a4db16..2f9323c5d2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -51,6 +51,7 @@ #include "code\__DEFINES\food.dm" #include "code\__DEFINES\footsteps.dm" #include "code\__DEFINES\hud.dm" +#include "code\__DEFINES\instruments.dm" #include "code\__DEFINES\integrated_electronics.dm" #include "code\__DEFINES\interaction_flags.dm" #include "code\__DEFINES\inventory.dm" @@ -290,6 +291,7 @@ #include "code\controllers\subsystem\research.dm" #include "code\controllers\subsystem\server_maint.dm" #include "code\controllers\subsystem\shuttle.dm" +#include "code\controllers\subsystem\sounds.dm" #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\stickyban.dm" #include "code\controllers\subsystem\sun.dm" @@ -307,6 +309,7 @@ #include "code\controllers\subsystem\processing\circuit.dm" #include "code\controllers\subsystem\processing\fastprocess.dm" #include "code\controllers\subsystem\processing\fields.dm" +#include "code\controllers\subsystem\processing\instruments.dm" #include "code\controllers\subsystem\processing\nanites.dm" #include "code\controllers\subsystem\processing\networks.dm" #include "code\controllers\subsystem\processing\obj.dm" @@ -419,9 +422,10 @@ #include "code\datums\components\wearertargeting.dm" #include "code\datums\components\wet_floor.dm" #include "code\datums\components\crafting\craft.dm" -#include "code\datums\components\crafting\glassware.dm" #include "code\datums\components\crafting\guncrafting.dm" #include "code\datums\components\crafting\recipes.dm" +#include "code\datums\components\crafting\glassware\glassware.dm" +#include "code\datums\components\crafting\glassware\lens_crafting.dm" #include "code\datums\components\crafting\recipes\recipes_clothing.dm" #include "code\datums\components\crafting\recipes\recipes_misc.dm" #include "code\datums\components\crafting\recipes\recipes_primal.dm" @@ -847,6 +851,7 @@ #include "code\game\objects\effects\decals\decal.dm" #include "code\game\objects\effects\decals\misc.dm" #include "code\game\objects\effects\decals\remains.dm" +#include "code\game\objects\effects\decals\vgdecals.dm" #include "code\game\objects\effects\decals\cleanable\aliens.dm" #include "code\game\objects\effects\decals\cleanable\gibs.dm" #include "code\game\objects\effects\decals\cleanable\humans.dm" @@ -968,7 +973,6 @@ #include "code\game\objects\items\devices\geiger_counter.dm" #include "code\game\objects\items\devices\glue.dm" #include "code\game\objects\items\devices\gps.dm" -#include "code\game\objects\items\devices\instruments.dm" #include "code\game\objects\items\devices\laserpointer.dm" #include "code\game\objects\items\devices\lightreplacer.dm" #include "code\game\objects\items\devices\megaphone.dm" @@ -1114,7 +1118,6 @@ #include "code\game\objects\structures\mirror.dm" #include "code\game\objects\structures\mop_bucket.dm" #include "code\game\objects\structures\morgue.dm" -#include "code\game\objects\structures\musician.dm" #include "code\game\objects\structures\noticeboard.dm" #include "code\game\objects\structures\petrified_statue.dm" #include "code\game\objects\structures\plasticflaps.dm" @@ -1986,6 +1989,22 @@ #include "code\modules\hydroponics\grown\tobacco.dm" #include "code\modules\hydroponics\grown\tomato.dm" #include "code\modules\hydroponics\grown\towercap.dm" +#include "code\modules\instruments\instrument_data\_instrument_data.dm" +#include "code\modules\instruments\instrument_data\_instrument_key.dm" +#include "code\modules\instruments\instrument_data\brass.dm" +#include "code\modules\instruments\instrument_data\chromatic_percussion.dm" +#include "code\modules\instruments\instrument_data\fun.dm" +#include "code\modules\instruments\instrument_data\guitar.dm" +#include "code\modules\instruments\instrument_data\hardcoded.dm" +#include "code\modules\instruments\instrument_data\organ.dm" +#include "code\modules\instruments\instrument_data\piano.dm" +#include "code\modules\instruments\instrument_data\synth_tones.dm" +#include "code\modules\instruments\instruments\item.dm" +#include "code\modules\instruments\instruments\stationary.dm" +#include "code\modules\instruments\songs\_song.dm" +#include "code\modules\instruments\songs\editor.dm" +#include "code\modules\instruments\songs\play_legacy.dm" +#include "code\modules\instruments\songs\play_synthesized.dm" #include "code\modules\integrated_electronics\_defines.dm" #include "code\modules\integrated_electronics\core\analyzer.dm" #include "code\modules\integrated_electronics\core\assemblies.dm" @@ -2216,6 +2235,7 @@ #include "code\modules\mob\living\emote.dm" #include "code\modules\mob\living\life.dm" #include "code\modules\mob\living\living.dm" +#include "code\modules\mob\living\living_block.dm" #include "code\modules\mob\living\living_combat.dm" #include "code\modules\mob\living\living_defense.dm" #include "code\modules\mob\living\living_defines.dm" @@ -2293,6 +2313,7 @@ #include "code\modules\mob\living\carbon\human\emote.dm" #include "code\modules\mob\living\carbon\human\examine.dm" #include "code\modules\mob\living\carbon\human\human.dm" +#include "code\modules\mob\living\carbon\human\human_block.dm" #include "code\modules\mob\living\carbon\human\human_defense.dm" #include "code\modules\mob\living\carbon\human\human_defines.dm" #include "code\modules\mob\living\carbon\human\human_helpers.dm" diff --git a/tgui-next/packages/tgui/interfaces/Vending.js b/tgui-next/packages/tgui/interfaces/Vending.js index e394c2d241..b6ffde015d 100644 --- a/tgui-next/packages/tgui/interfaces/Vending.js +++ b/tgui-next/packages/tgui/interfaces/Vending.js @@ -9,54 +9,40 @@ export const Vending = props => { const { ref } = config; let inventory; let custom = false; - if (data.vending_machine_input) { - inventory = data.vending_machine_input; - custom = true; + const onstation = true; + if (data.extended_inventory && data.coin) { + inventory = [ + ...data.product_records, + ...data.hidden_records, + ...data.coin_records, + ]; } else if (data.extended_inventory) { inventory = [ ...data.product_records, - ...data.coin_records, ...data.hidden_records, ]; + } else if (data.coin) { + inventory = [ + ...data.product_records, + ...data.coin_records, + ]; } else { inventory = [ ...data.product_records, - ...data.coin_records, ]; } return ( - {data.onstation && ( -
- {data.user && ( - - Welcome, {data.user.name}, - {' '} - {data.user.job || "Unemployed"}! -
- Your balance is {data.user.cash} credits. -
- ) || ( - - No registered ID card!
- Please contact your local HoP! -
- )} + {data.coin && ( +
+
)}
{inventory.map((product => { - const free = ( - !data.onstation - || product.price === 0 - || ( - !product.premium - && data.department - && data.user - && data.department === data.user.department - ) - ); return ( @@ -91,23 +77,13 @@ export const Vending = props => { {custom && (