diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index ed2403fcfa..ebdda21c5e 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -1300,28 +1300,19 @@ /turf/closed/wall, /area/asteroid/nearstation) "abU" = ( +/turf/closed/wall/r_wall, +/area/crew_quarters/heads/hos) +"abV" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/preopen{ id = "detectivewindows"; - name = "Detective Privacy Blast door" + name = "HoS Privacy Blast door" + }, +/obj/structure/cable/white{ + icon_state = "0-2" }, /turf/open/floor/plating, -/area/security/detectives_office) -"abV" = ( -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/machinery/shower{ - pixel_y = 16 - }, -/obj/structure/curtain, -/obj/machinery/door/window/brigdoor/southleft{ - name = "Shower" - }, -/obj/item/soap/deluxe, -/obj/machinery/atmospherics/components/unary/vent_pump/on, -/turf/open/floor/plasteel/white, -/area/security/detectives_office) +/area/crew_quarters/heads/hos) "abW" = ( /obj/structure/closet/firecloset, /obj/machinery/light/small{ @@ -1734,26 +1725,54 @@ /turf/open/floor/plating, /area/asteroid/nearstation) "acG" = ( -/obj/structure/door_assembly/door_assembly_mhatch, +/obj/structure/bed, +/obj/machinery/atmospherics/components/unary/vent_pump/on, +/obj/item/bedsheet/hos, +/obj/effect/landmark/start/head_of_security, +/obj/structure/cable/white{ + icon_state = "1-2" + }, +/turf/open/floor/wood, +/area/crew_quarters/heads/hos) +"acH" = ( +/obj/effect/decal/cleanable/cobweb/cobweb2, +/turf/open/floor/plating, +/area/asteroid/nearstation) +"acI" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/shower{ + pixel_y = 16 + }, +/obj/structure/curtain, +/obj/machinery/door/window/brigdoor/southleft{ + name = "Shower" + }, +/obj/item/soap/deluxe, +/obj/machinery/atmospherics/components/unary/vent_pump/on, +/turf/open/floor/plasteel/white, +/area/crew_quarters/heads/hos) +"acJ" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 }, /obj/effect/turf_decal/stripes/line{ dir = 4 }, +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Maintenance Hatch"; + req_access_txt = "12" + }, /turf/open/floor/plasteel, /area/asteroid/nearstation) -"acH" = ( -/obj/effect/decal/cleanable/cobweb/cobweb2, -/turf/open/floor/plating, -/area/asteroid/nearstation) -"acI" = ( +"acK" = ( /obj/structure/dresser, /obj/structure/sign/warning/vacuum{ pixel_y = 32 }, /obj/machinery/camera{ - c_tag = "Detective's Office - Quarters" + c_tag = "Head of Security's Office - Quarters" }, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -1766,15 +1785,17 @@ dir = 8 }, /turf/open/floor/plasteel/dark, -/area/security/detectives_office) -"acJ" = ( -/obj/structure/bed, -/obj/machinery/atmospherics/components/unary/vent_pump/on, -/obj/effect/landmark/start/detective, -/obj/item/bedsheet/brown, +/area/crew_quarters/heads/hos) +"acL" = ( +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 8 + }, +/obj/structure/cable/white{ + icon_state = "1-2" + }, /turf/open/floor/wood, -/area/security/detectives_office) -"acK" = ( +/area/crew_quarters/heads/hos) +"acM" = ( /obj/structure/closet/crate/bin, /obj/machinery/light{ dir = 4 @@ -1784,25 +1805,7 @@ }, /obj/machinery/atmospherics/components/unary/vent_scrubber/on, /turf/open/floor/wood, -/area/security/detectives_office) -"acL" = ( -/obj/machinery/status_display, -/turf/closed/wall, -/area/security/detectives_office) -"acM" = ( -/obj/structure/toilet{ - dir = 4 - }, -/obj/machinery/newscaster{ - pixel_x = 32 - }, -/obj/machinery/light{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/effect/landmark/start/detective, -/turf/open/floor/plasteel/white, -/area/security/detectives_office) +/area/crew_quarters/heads/hos) "acN" = ( /obj/effect/landmark/blobstart, /turf/open/floor/plating, @@ -2234,7 +2237,34 @@ /turf/closed/wall, /area/asteroid/nearstation) "adA" = ( -/obj/structure/closet/secure_closet/detective, +/obj/machinery/status_display, +/turf/closed/wall, +/area/crew_quarters/heads/hos) +"adB" = ( +/obj/structure/toilet{ + dir = 4 + }, +/obj/machinery/newscaster{ + pixel_x = 32 + }, +/obj/machinery/light{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/landmark/start/head_of_security, +/turf/open/floor/plasteel/white, +/area/crew_quarters/heads/hos) +"adC" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/structure/sign/nanotrasen{ + pixel_x = -32 + }, +/obj/structure/cable/white{ + icon_state = "0-4" + }, +/turf/open/floor/plating, +/area/crew_quarters/heads/hos) +"adD" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -2245,43 +2275,31 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/structure/closet/secure_closet/hos, /turf/open/floor/plasteel/dark, -/area/security/detectives_office) -"adB" = ( -/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ - dir = 8 - }, -/turf/open/floor/wood, -/area/security/detectives_office) -"adC" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/wood, -/area/security/detectives_office) -"adD" = ( -/obj/machinery/door/airlock/silver{ - name = "Bathroom" - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/turf/open/floor/plasteel/white, -/area/security/detectives_office) +/area/crew_quarters/heads/hos) "adE" = ( -/obj/structure/mirror{ - pixel_x = 26 +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 }, -/obj/structure/sink{ - dir = 4; - pixel_x = 12 +/obj/structure/cable/white{ + icon_state = "1-2" }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 9 +/obj/structure/cable/white{ + icon_state = "2-4" }, -/turf/open/floor/plasteel/white, -/area/security/detectives_office) +/obj/structure/cable/white{ + icon_state = "2-8" + }, +/obj/machinery/door/airlock/command{ + name = "Head of Security's Office"; + req_access_txt = "58" + }, +/turf/open/floor/plasteel, +/area/crew_quarters/heads/hos) "adF" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on, /obj/effect/landmark/xeno_spawn, @@ -2666,45 +2684,47 @@ /turf/open/floor/plasteel, /area/quartermaster/qm) "aeo" = ( -/obj/structure/table, -/obj/item/extinguisher/mini, -/obj/item/tank/internals/emergency_oxygen/engi, -/obj/item/clothing/mask/breath, -/obj/effect/turf_decal/stripes/line{ - dir = 9 +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 }, -/turf/open/floor/plasteel, -/area/asteroid/nearstation) +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/turf/open/floor/wood, +/area/crew_quarters/heads/hos) "aep" = ( /turf/open/floor/plating{ icon_state = "platingdmg2" }, /area/asteroid/nearstation) "aeq" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/structure/sign/nanotrasen{ - pixel_x = -32 +/obj/machinery/door/airlock/silver{ + name = "Bathroom" }, -/turf/open/floor/plating, -/area/security/detectives_office) +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/crew_quarters/heads/hos) "aer" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/security{ - name = "Detective's Office"; - req_access_txt = "4" +/obj/structure/mirror{ + pixel_x = 26 }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 +/obj/structure/sink{ + dir = 4; + pixel_x = 12 }, -/turf/open/floor/plasteel, -/area/security/detectives_office) +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 9 + }, +/turf/open/floor/plasteel/white, +/area/crew_quarters/heads/hos) "aes" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/structure/cable/white{ + icon_state = "0-8" + }, /turf/open/floor/plating, -/area/security/detectives_office) +/area/crew_quarters/heads/hos) "aet" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/machinery/power/apc{ @@ -3168,13 +3188,13 @@ /turf/open/floor/plasteel, /area/quartermaster/storage) "afb" = ( -/obj/structure/door_assembly/door_assembly_mhatch, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 }, -/turf/open/floor/plating, -/area/asteroid/nearstation) +/obj/machinery/suit_storage_unit/hos, +/turf/open/floor/plasteel/dark, +/area/crew_quarters/heads/hos) "afc" = ( /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/tile/neutral{ @@ -3193,71 +3213,60 @@ /turf/open/floor/plasteel, /area/quartermaster/storage) "afd" = ( -/obj/structure/table/wood, -/obj/item/taperecorder, -/obj/item/restraints/handcuffs, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/turf/open/floor/plasteel/dark, -/area/security/detectives_office) -"afe" = ( /obj/machinery/atmospherics/pipe/manifold/supply/hidden{ dir = 8 }, /obj/structure/cable/white{ icon_state = "2-4" }, +/obj/structure/cable/white{ + icon_state = "1-2" + }, /turf/open/floor/wood, -/area/security/detectives_office) -"aff" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ +/area/crew_quarters/heads/hos) +"afe" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Security Maintenance"; + req_access_txt = "63" + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ dir = 4 }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/obj/structure/cable/white{ - icon_state = "4-8" - }, -/turf/open/floor/wood, -/area/security/detectives_office) +/turf/open/floor/plasteel, +/area/security/brig) +"aff" = ( +/turf/closed/wall, +/area/crew_quarters/heads/hos) "afg" = ( -/obj/machinery/newscaster{ - pixel_y = 32 +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 }, +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Security Maintenance"; + req_access_txt = "63" + }, +/turf/open/floor/plasteel, +/area/security/brig) +"afh" = ( +/obj/structure/barricade/wooden, +/turf/open/floor/plating, +/area/asteroid/nearstation) +"afi" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 8 }, /obj/structure/cable/white{ icon_state = "4-8" }, -/turf/open/floor/wood, -/area/security/detectives_office) -"afh" = ( -/obj/machinery/power/apc/highcap/five_k{ - areastring = "/area/security/detectives_office"; - dir = 1; - name = "Detective's Office APC"; - pixel_y = 25 - }, -/obj/structure/cable/white{ - icon_state = "0-8" +/obj/machinery/newscaster/security_unit{ + pixel_y = 32 }, /turf/open/floor/wood, -/area/security/detectives_office) -"afi" = ( -/obj/machinery/door/airlock/maintenance_hatch{ - name = "Detective's Office Maintenance"; - req_access_txt = "4" - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/turf/open/floor/plating, -/area/maintenance/fore) +/area/crew_quarters/heads/hos) "afj" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/structure/cable/white{ @@ -3692,10 +3701,11 @@ /turf/open/floor/plating, /area/asteroid/nearstation) "afN" = ( -/obj/effect/decal/cleanable/blood/old, -/obj/effect/turf_decal/sand/plating, -/turf/open/floor/plating, -/area/asteroid/nearstation) +/obj/structure/table/wood, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/item/taperecorder, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) "afO" = ( /obj/effect/turf_decal/tile/brown{ dir = 1 @@ -3709,60 +3719,71 @@ /turf/open/floor/plasteel, /area/quartermaster/storage) "afP" = ( -/obj/structure/table/wood, -/obj/item/book/manual/wiki/security_space_law, -/obj/item/camera/detective, -/obj/item/radio/intercom{ - name = "Station Intercom"; - pixel_x = -26 +/obj/effect/turf_decal/stripes/line{ + dir = 5 }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ +/obj/item/poster/random_contraband{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/poster/random_contraband{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/item/poster/random_contraband, +/turf/open/floor/plasteel, +/area/asteroid/nearstation) +"afQ" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, -/turf/open/floor/plasteel/dark, -/area/security/detectives_office) -"afQ" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/structure/cable/white{ - icon_state = "1-2" - }, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) -"afR" = ( -/obj/structure/table/wood, -/obj/item/clothing/mask/cigarette/cigar/cohiba{ - pixel_x = 3 - }, -/obj/item/clothing/mask/cigarette/cigar/havana{ - pixel_x = -3 - }, -/obj/item/clothing/mask/cigarette/cigar, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/obj/item/clothing/glasses/sunglasses, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/turf/open/floor/wood, +/area/crew_quarters/heads/hos) +"afR" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Head of Security's Office Maintenance"; + req_access_txt = "4" + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/plating, +/area/maintenance/fore) "afS" = ( -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) +/obj/machinery/power/apc/highcap/five_k{ + areastring = "/area/crew_quarters/heads/hos"; + dir = 1; + name = "Head of Security's Office APC"; + pixel_y = 25 + }, +/obj/structure/cable/white{ + icon_state = "0-8" + }, +/turf/open/floor/wood, +/area/crew_quarters/heads/hos) "afT" = ( -/obj/machinery/computer/med_data{ +/obj/effect/turf_decal/stripes/line{ dir = 8 }, -/obj/machinery/requests_console{ - department = "Detective's Office"; - name = "Detective RC"; - pixel_x = 30 +/obj/effect/turf_decal/stripes/line{ + dir = 4 }, -/obj/machinery/camera{ - c_tag = "Detective's Office - Desk"; - dir = 8 +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Security Maintenance"; + req_access_txt = "63" }, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) +/turf/open/floor/plasteel, +/area/security/brig) "afU" = ( -/turf/closed/wall/r_wall, -/area/security/detectives_office) +/turf/open/floor/plasteel/dark, +/area/security/brig) "afV" = ( /obj/structure/cable/white{ icon_state = "2-4" @@ -4230,75 +4251,80 @@ /turf/closed/wall/r_wall, /area/security/brig) "agG" = ( -/obj/effect/decal/cleanable/oil, +/obj/structure/reagent_dispensers/peppertank{ + pixel_y = 32 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) +"agH" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 6 + }, +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) +"agI" = ( +/obj/structure/table/wood, +/obj/item/paper_bin, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/structure/cable/white{ + icon_state = "2-8" + }, +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/obj/item/pen/fountain, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) +"agJ" = ( +/obj/machinery/camera{ + c_tag = "Head of Security's Office - Desk"; + dir = 8 + }, +/obj/machinery/requests_console{ + announcementConsole = 1; + department = "Head of Security's Desk"; + departmentType = 5; + name = "Head of Security RC"; + pixel_x = 30; + pixel_y = 0 + }, +/obj/machinery/computer/security/hos{ + icon_state = "computer"; + dir = 8 + }, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) +"agK" = ( +/obj/effect/turf_decal/sand/plating, +/obj/item/stack/ore/iron, /turf/open/floor/plating, /area/asteroid/nearstation) -"agH" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 5 - }, -/turf/open/floor/plasteel, -/area/asteroid/nearstation) -"agI" = ( -/obj/structure/rack, -/obj/item/storage/briefcase{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/storage/secure/briefcase, -/obj/machinery/airalarm/unlocked{ - dir = 4; - pixel_x = -23 - }, -/obj/machinery/light{ - dir = 8 +"agL" = ( +/obj/structure/table/wood, +/obj/item/book/manual/wiki/security_space_law, +/obj/item/camera/detective, +/obj/item/radio/intercom{ + name = "Station Intercom"; + pixel_x = -26 }, /obj/effect/turf_decal/tile/neutral, /obj/effect/turf_decal/tile/neutral{ dir = 4 }, /turf/open/floor/plasteel/dark, -/area/security/detectives_office) -"agJ" = ( -/obj/machinery/holopad, +/area/crew_quarters/heads/hos) +"agM" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/structure/cable/white{ icon_state = "1-2" }, /turf/open/floor/plasteel/grimy, -/area/security/detectives_office) -"agK" = ( -/obj/structure/table/wood, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ - dir = 8 - }, -/obj/machinery/computer/security/wooden_tv{ - pixel_x = 2 - }, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) -"agL" = ( -/obj/structure/chair/office/dark{ - dir = 8 - }, -/obj/effect/landmark/start/detective, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) -"agM" = ( -/obj/machinery/computer/secure_data{ - dir = 8 - }, -/obj/machinery/light{ - dir = 4 - }, -/obj/machinery/status_display{ - pixel_x = 32 - }, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) +/area/crew_quarters/heads/hos) "agN" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -4771,81 +4797,70 @@ /turf/closed/wall/r_wall, /area/asteroid/nearstation) "ahv" = ( -/obj/machinery/suit_storage_unit/security, -/obj/structure/extinguisher_cabinet{ - pixel_x = -26 +/obj/machinery/door/firedoor, +/obj/structure/cable/white{ + icon_state = "2-8" }, -/obj/structure/sign/nanotrasen{ - pixel_y = 32 +/obj/structure/cable/white{ + icon_state = "2-4" }, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 4 +/obj/machinery/door/airlock/security/glass{ + name = "Security E.V.A. Storage"; + req_access_txt = "63" }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ - dir = 6 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/security/brig) -"ahw" = ( -/obj/structure/reagent_dispensers/peppertank{ - pixel_y = 32 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 10 - }, -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/security/brig) -"ahx" = ( -/obj/machinery/status_display/ai{ - pixel_y = 32 - }, -/obj/machinery/light/small{ dir = 1 }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 6 +/turf/open/floor/plasteel, +/area/security/brig) +"ahw" = ( +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) +"ahx" = ( +/obj/machinery/airalarm/unlocked{ + dir = 4; + pixel_x = -23 }, -/obj/effect/turf_decal/delivery, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ +/obj/machinery/light{ dir = 8 }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/machinery/computer/card/minor/hos{ + icon_state = "computer"; + dir = 4 + }, /turf/open/floor/plasteel/dark, -/area/security/brig) +/area/crew_quarters/heads/hos) "ahy" = ( -/obj/machinery/suit_storage_unit/security, -/obj/machinery/firealarm{ - dir = 8; - pixel_x = 24 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ - dir = 8 +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/cable/white{ + icon_state = "1-2" }, +/obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ - dir = 10 + dir = 1 }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/security/brig) -"ahz" = ( -/obj/structure/reagent_dispensers/fueltank, -/obj/effect/turf_decal/stripes/line{ - dir = 10 +/obj/machinery/door/airlock/command{ + name = "Head of Security's Office"; + req_access_txt = "58" }, /turf/open/floor/plasteel, -/area/asteroid/nearstation) +/area/crew_quarters/heads/hos) +"ahz" = ( +/obj/structure/sign/nanotrasen{ + pixel_x = -32 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) "ahA" = ( /obj/structure/rack, /obj/item/clothing/suit/fire/firefighter, @@ -4857,78 +4872,40 @@ /turf/open/floor/plasteel, /area/asteroid/nearstation) "ahB" = ( -/obj/machinery/photocopier, -/obj/machinery/firealarm{ - dir = 4; - pixel_x = -24 - }, -/obj/structure/cable/white{ - icon_state = "2-4" - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/turf/open/floor/plasteel/dark, -/area/security/detectives_office) +/obj/effect/turf_decal/sand/plating, +/obj/item/stack/ore/silver, +/obj/item/stack/ore/iron, +/turf/open/floor/plating, +/area/asteroid/nearstation) "ahC" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/structure/cable/white{ - icon_state = "4-8" - }, -/obj/structure/cable/white{ - icon_state = "2-4" - }, -/obj/structure/cable/white{ - icon_state = "1-8" - }, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) -"ahD" = ( -/obj/structure/table/wood, -/obj/item/paper_bin, -/obj/item/pen, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/obj/structure/cable/white{ - icon_state = "2-8" - }, -/obj/structure/cable/white{ - icon_state = "4-8" - }, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) -"ahE" = ( -/obj/structure/table/wood, -/obj/machinery/button/door{ - id = "detectivewindows"; - name = "Privacy Shutters"; - pixel_x = 26; - pixel_y = -26; - req_access_txt = "4" - }, -/obj/machinery/light_switch{ - pixel_x = 38; - pixel_y = -26 - }, -/obj/structure/cable/white{ - icon_state = "2-8" - }, -/obj/item/folder/red, -/obj/item/hand_labeler, -/turf/open/floor/plasteel/grimy, -/area/security/detectives_office) -"ahF" = ( -/obj/structure/table/wood, -/obj/item/flashlight/lamp, -/obj/item/storage/secure/safe{ +/obj/machinery/status_display/ai{ pixel_x = 32 }, -/obj/structure/extinguisher_cabinet{ - pixel_x = 24; - pixel_y = -26 +/turf/open/floor/plasteel/dark, +/area/security/brig) +"ahD" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 9 + }, +/turf/open/floor/plasteel, +/area/asteroid/nearstation) +"ahE" = ( +/obj/structure/table/wood, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ + dir = 8 + }, +/obj/item/folder/red, +/obj/item/stamp/hos, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) +"ahF" = ( +/obj/machinery/holopad, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/cable/white{ + icon_state = "1-2" }, /turf/open/floor/plasteel/grimy, -/area/security/detectives_office) +/area/crew_quarters/heads/hos) "ahG" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -5177,21 +5154,24 @@ /turf/open/floor/plasteel/dark, /area/hallway/primary/starboard/fore) "aid" = ( +/obj/structure/table/wood, +/obj/machinery/button/door{ + id = "detectivewindows"; + name = "Privacy Shutters"; + pixel_x = 26; + pixel_y = -26; + req_access_txt = "4" + }, +/obj/machinery/light_switch{ + pixel_x = 38; + pixel_y = -26 + }, /obj/structure/cable/white{ - icon_state = "1-2" + icon_state = "2-8" }, -/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/obj/effect/landmark/start/head_of_security, -/turf/open/floor/plasteel, -/area/security/brig) +/obj/machinery/recharger, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) "aie" = ( /obj/machinery/conveyor{ dir = 1; @@ -5297,23 +5277,15 @@ /turf/open/floor/plasteel/dark, /area/medical/morgue) "aim" = ( -/obj/structure/rack, -/obj/item/storage/box/rubbershot, -/obj/item/storage/box/rubbershot, -/obj/item/storage/box/rubbershot{ - pixel_x = 3; - pixel_y = -3 +/obj/structure/chair/office/dark{ + dir = 8 }, -/obj/item/gun/ballistic/shotgun/riot, -/obj/structure/extinguisher_cabinet{ - pixel_x = -26 +/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ + dir = 8 }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/plasteel/dark, -/area/security/brig) +/obj/effect/landmark/start/head_of_security, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) "ain" = ( /obj/machinery/firealarm{ pixel_y = 26 @@ -5379,24 +5351,17 @@ /turf/open/floor/plasteel, /area/security/brig) "ais" = ( -/obj/machinery/door/firedoor, -/obj/structure/cable/white{ - icon_state = "2-8" +/obj/machinery/computer/secure_data{ + dir = 8 }, -/obj/structure/cable/white{ - icon_state = "2-4" +/obj/machinery/light{ + dir = 4 }, -/obj/machinery/door/airlock/security/glass{ - name = "Security E.V.A. Storage"; - req_access_txt = "63" +/obj/machinery/status_display{ + pixel_x = 32 }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/turf/open/floor/plasteel, -/area/security/brig) +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) "ait" = ( /obj/structure/cable/white{ icon_state = "0-8" @@ -5405,40 +5370,44 @@ /turf/open/floor/plating, /area/security/brig) "aiu" = ( -/obj/structure/cable/white, -/obj/effect/spawner/structure/window/reinforced, -/obj/machinery/door/poddoor/preopen{ - id = "detectivewindows"; - name = "Detective Privacy Blast door" +/obj/machinery/suit_storage_unit/security, +/obj/structure/extinguisher_cabinet{ + pixel_x = -26 }, -/turf/open/floor/plating, -/area/security/detectives_office) +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/end{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) "aiv" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/security{ - name = "Detective's Office"; - req_access_txt = "4" +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 10 }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/structure/cable/white{ - icon_state = "1-2" +/obj/effect/turf_decal/delivery, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 }, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/turf/open/floor/plasteel, -/area/security/detectives_office) +/turf/open/floor/plasteel/dark, +/area/security/brig) "aiw" = ( -/obj/structure/cable/white, -/obj/effect/spawner/structure/window/reinforced, -/obj/machinery/door/poddoor/preopen{ - id = "detectivewindows"; - name = "Detective Privacy Blast door" +/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden, +/obj/item/stack/cable_coil/white, +/obj/item/stack/cable_coil/white, +/obj/item/stack/sheet/glass, +/obj/item/stack/sheet/glass, +/obj/item/circuitboard/machine/microwave, +/turf/open/floor/plating{ + icon_state = "panelscorched" }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plating, -/area/security/detectives_office) +/area/maintenance/starboard/central) "aix" = ( /obj/machinery/door/airlock/maintenance_hatch{ name = "Maintenance Hatch"; @@ -5700,34 +5669,33 @@ /turf/open/floor/plasteel, /area/medical/medbay/zone3) "aiR" = ( -/obj/structure/rack, -/obj/item/gun/energy/e_gun/advtaser{ - pixel_x = -3; - pixel_y = 3 +/obj/machinery/suit_storage_unit/security, +/obj/machinery/firealarm{ + dir = 8; + pixel_x = 24 }, -/obj/item/gun/energy/e_gun/advtaser, -/obj/machinery/light{ +/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 }, -/obj/machinery/status_display{ - pixel_x = -32 +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/red{ - dir = 1 +/obj/effect/turf_decal/stripes/end{ + dir = 8 }, /turf/open/floor/plasteel/dark, /area/security/brig) "aiS" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 + dir = 10 }, +/obj/structure/table, +/obj/item/extinguisher/mini, +/obj/item/tank/internals/emergency_oxygen/engi, +/obj/item/clothing/mask/breath, /turf/open/floor/plasteel, -/area/security/brig) +/area/asteroid/nearstation) "aiT" = ( /obj/structure/rack, /obj/item/gun/energy/ionrifle, @@ -6351,22 +6319,20 @@ /turf/open/floor/plating, /area/asteroid/nearstation) "ajR" = ( -/obj/structure/rack, -/obj/item/gun/energy/e_gun{ - pixel_x = -3; - pixel_y = 3 +/obj/machinery/photocopier, +/obj/machinery/firealarm{ + dir = 4; + pixel_x = -24 }, -/obj/item/gun/energy/e_gun, -/obj/item/radio/intercom{ - name = "Station Intercom"; - pixel_x = -26 +/obj/structure/cable/white{ + icon_state = "2-4" }, -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/tile/red{ - dir = 1 +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 }, /turf/open/floor/plasteel/dark, -/area/security/brig) +/area/crew_quarters/heads/hos) "ajS" = ( /obj/structure/cable/white{ icon_state = "2-4" @@ -7546,32 +7512,18 @@ /turf/open/floor/plating, /area/asteroid/nearstation) "alH" = ( -/obj/item/grenade/barrier{ - pixel_x = -3; - pixel_y = 1 +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/cable/white{ + icon_state = "4-8" }, -/obj/item/grenade/barrier, -/obj/item/grenade/barrier{ - pixel_x = 3; - pixel_y = -1 +/obj/structure/cable/white{ + icon_state = "2-4" }, -/obj/item/grenade/barrier{ - pixel_x = 6; - pixel_y = -2 +/obj/structure/cable/white{ + icon_state = "1-8" }, -/obj/structure/table/reinforced, -/obj/machinery/newscaster/security_unit{ - pixel_x = -32 - }, -/obj/machinery/camera{ - c_tag = "Brig Control Room"; - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/plasteel, -/area/security/brig) +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) "alI" = ( /turf/closed/wall/r_wall, /area/crew_quarters/heads/chief) @@ -8166,27 +8118,11 @@ /turf/open/floor/plating, /area/asteroid/nearstation) "amH" = ( -/obj/item/storage/box/chemimp{ - pixel_x = 6 +/obj/structure/sign/warning/nosmoking{ + pixel_y = 32 }, -/obj/item/storage/box/trackimp{ - pixel_x = -3 - }, -/obj/item/storage/lockbox/loyalty, -/obj/structure/table/reinforced, -/obj/machinery/airalarm/unlocked{ - dir = 4; - pixel_x = -23 - }, -/obj/machinery/camera{ - c_tag = "Security - Office"; - dir = 4 - }, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/turf/open/floor/plasteel, -/area/security/brig) +/turf/open/floor/plasteel/dark, +/area/library) "amI" = ( /obj/machinery/holopad, /obj/structure/cable/white{ @@ -8643,24 +8579,15 @@ /turf/closed/wall/r_wall, /area/engine/atmos) "any" = ( -/obj/item/storage/box/firingpins, -/obj/item/storage/box/firingpins, -/obj/structure/table/reinforced, -/obj/machinery/light{ - dir = 8 +/obj/structure/table/wood, +/obj/machinery/newscaster{ + pixel_y = 32 }, -/obj/machinery/requests_console{ - department = "Security"; - name = "Security RC"; - pixel_x = -32 - }, -/obj/item/gun/energy/e_gun/dragnet, -/obj/item/gun/energy/e_gun/dragnet, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/turf/open/floor/plasteel, -/area/security/brig) +/obj/item/clipboard, +/obj/item/folder/red, +/obj/item/hand_labeler, +/turf/open/floor/wood, +/area/security/detectives_office) "anz" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -9241,25 +9168,18 @@ /turf/open/floor/engine/vacuum, /area/engine/atmos) "aos" = ( -/obj/item/storage/box/teargas{ - pixel_x = 3; - pixel_y = 3 +/obj/structure/table/wood, +/obj/item/flashlight/lamp, +/obj/item/storage/secure/safe{ + pixel_x = 32 }, -/obj/item/storage/box/handcuffs, -/obj/item/storage/box/flashbangs{ - pixel_x = -3; - pixel_y = -3 +/obj/structure/extinguisher_cabinet{ + pixel_x = 24; + pixel_y = -26 }, -/obj/structure/table/reinforced, -/obj/machinery/firealarm{ - dir = 4; - pixel_x = -24 - }, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/turf/open/floor/plasteel, -/area/security/brig) +/obj/item/toy/figure/hos, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/heads/hos) "aot" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -9997,6 +9917,21 @@ /obj/machinery/air_sensor/atmos/mix_tank, /turf/open/floor/engine/vacuum, /area/engine/atmos) +"apw" = ( +/obj/structure/extinguisher_cabinet{ + pixel_x = -26 + }, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/rack, +/obj/item/storage/box/firingpins, +/obj/item/storage/box/firingpins, +/obj/item/gun/energy/e_gun/dragnet, +/obj/item/gun/energy/e_gun/dragnet, +/turf/open/floor/plasteel/dark, +/area/security/brig) "apx" = ( /obj/machinery/door/firedoor, /obj/structure/cable/white{ @@ -10281,6 +10216,21 @@ /obj/item/storage/box/lights/mixed, /turf/open/floor/plating, /area/maintenance/port/central) +"apR" = ( +/obj/structure/rack, +/obj/item/storage/box/rubbershot, +/obj/item/storage/box/rubbershot, +/obj/item/storage/box/rubbershot{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/gun/ballistic/shotgun/riot, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) "apS" = ( /obj/machinery/door/airlock/maintenance_hatch{ name = "Maintenance Hatch"; @@ -10562,6 +10512,22 @@ /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, /area/hallway/primary/starboard/fore) +"aqo" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/door/airlock/external{ + name = "External Airlock"; + req_access_txt = "13" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/maintenance/port) "aqp" = ( /obj/machinery/autolathe, /obj/structure/extinguisher_cabinet{ @@ -10639,6 +10605,37 @@ /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, /area/quartermaster/miningdock) +"aqw" = ( +/obj/structure/cable/white, +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/door/poddoor/preopen{ + id = "detectivewindows"; + name = "HoS Privacy Blast door" + }, +/turf/open/floor/plating, +/area/crew_quarters/heads/hos) +"aqx" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 4 + }, +/obj/effect/landmark/xeno_spawn, +/obj/structure/sign/warning/vacuum{ + pixel_x = -32; + pixel_y = 32 + }, +/obj/effect/turf_decal/delivery, +/turf/open/floor/plasteel, +/area/maintenance/port) +"aqy" = ( +/obj/structure/cable/white, +/obj/effect/spawner/structure/window/reinforced, +/obj/machinery/door/poddoor/preopen{ + id = "detectivewindows"; + name = "HoS Privacy Blast door" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/open/floor/plating, +/area/crew_quarters/heads/hos) "aqz" = ( /turf/closed/wall/r_wall, /area/engine/atmos) @@ -10646,6 +10643,19 @@ /obj/effect/spawner/structure/window/plasma/reinforced, /turf/open/floor/plating, /area/engine/atmos) +"aqB" = ( +/obj/machinery/status_display{ + pixel_x = -32 + }, +/obj/machinery/light{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line, +/turf/open/floor/plasteel, +/area/security/brig) "aqC" = ( /obj/machinery/meter, /obj/structure/grille, @@ -10654,6 +10664,13 @@ }, /turf/closed/wall/r_wall, /area/engine/atmos) +"aqD" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/security/brig) "aqE" = ( /obj/machinery/meter, /obj/structure/grille, @@ -10670,10 +10687,35 @@ /turf/closed/wall/r_wall, /area/engine/atmos) "aqG" = ( -/obj/structure/door_assembly/door_assembly_mhatch, -/obj/effect/turf_decal/sand/plating, -/turf/open/floor/plating, -/area/maintenance/port/fore) +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/security/brig) +"aqH" = ( +/obj/item/radio/intercom{ + name = "Station Intercom"; + pixel_x = -26 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/bot, +/obj/structure/rack, +/obj/item/gun/energy/e_gun/advtaser{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/energy/e_gun/advtaser, +/turf/open/floor/plasteel/dark, +/area/security/brig) "aqI" = ( /obj/structure/closet/secure_closet/security/sec, /obj/item/radio/intercom{ @@ -10791,22 +10833,15 @@ /turf/open/floor/plasteel, /area/security/brig) "aqQ" = ( -/obj/machinery/door/firedoor, -/obj/structure/table/reinforced, -/obj/item/clipboard, -/obj/item/folder/red, -/obj/item/toy/figure/hos, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/effect/turf_decal/tile/red{ - dir = 1 - }, -/obj/effect/turf_decal/tile/red, -/obj/effect/turf_decal/tile/red{ - dir = 4 +/obj/machinery/firealarm{ + dir = 4; + pixel_x = -24 }, /obj/effect/turf_decal/tile/red{ dir = 8 }, +/obj/structure/closet/secure_closet/warden, +/obj/item/toy/figure/warden, /turf/open/floor/plasteel, /area/security/brig) "aqR" = ( @@ -11065,6 +11100,36 @@ }, /turf/open/floor/plasteel, /area/quartermaster/miningdock) +"arn" = ( +/obj/structure/rack, +/obj/item/gun/energy/e_gun{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/energy/e_gun, +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) +"aro" = ( +/turf/closed/wall, +/area/maintenance/port/fore) +"arp" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Maintenance Hatch"; + req_access_txt = "12" + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"arq" = ( +/turf/closed/wall/r_wall, +/area/maintenance/port/fore) "arr" = ( /obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/n2o{ dir = 8 @@ -11082,6 +11147,45 @@ }, /turf/open/floor/plasteel/dark, /area/engine/atmos) +"ars" = ( +/turf/open/floor/plating, +/area/maintenance/port/fore) +"art" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 10 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"aru" = ( +/obj/machinery/newscaster/security_unit{ + pixel_x = -32 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/structure/table/reinforced, +/obj/item/grenade/barrier{ + pixel_x = -3; + pixel_y = 1 + }, +/obj/item/grenade/barrier, +/obj/item/grenade/barrier{ + pixel_x = 3; + pixel_y = -1 + }, +/obj/item/grenade/barrier{ + pixel_x = 6; + pixel_y = -2 + }, +/obj/machinery/camera{ + c_tag = "Brig Control Room"; + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) "arv" = ( /obj/machinery/atmospherics/pipe/simple/green/visible{ dir = 9 @@ -11183,9 +11287,11 @@ /turf/open/floor/plasteel, /area/engine/atmos) "arB" = ( -/obj/effect/turf_decal/sand/plating, -/turf/open/floor/plating, -/area/maintenance/port/fore) +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/security/brig) "arC" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall/r_wall, @@ -11578,6 +11684,21 @@ icon_state = "platingdmg3" }, /area/maintenance/port/central) +"asa" = ( +/obj/structure/cable/white{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/security/brig) "asb" = ( /obj/structure/cable/white{ icon_state = "2-8" @@ -11723,6 +11844,47 @@ }, /turf/open/floor/plating, /area/maintenance/starboard/central) +"asm" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Maintenance Hatch"; + req_access_txt = "12" + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"asn" = ( +/obj/machinery/airalarm/unlocked{ + dir = 4; + pixel_x = -23 + }, +/obj/machinery/camera{ + c_tag = "Security - Office"; + dir = 4 + }, +/obj/machinery/light{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/structure/table/reinforced, +/obj/item/storage/box/chemimp{ + pixel_x = 6 + }, +/obj/item/storage/box/trackimp{ + pixel_x = -3 + }, +/obj/item/storage/lockbox/loyalty, +/turf/open/floor/plasteel/dark, +/area/security/brig) "aso" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 @@ -11743,6 +11905,13 @@ }, /turf/open/floor/plating, /area/maintenance/starboard/central) +"asq" = ( +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/landmark/start/warden, +/turf/open/floor/plasteel, +/area/security/brig) "asr" = ( /obj/effect/turf_decal/loading_area{ dir = 4 @@ -11779,6 +11948,10 @@ }, /turf/open/floor/plasteel, /area/maintenance/starboard) +"asu" = ( +/obj/structure/girder, +/turf/open/floor/plating, +/area/maintenance/port/fore) "asv" = ( /turf/open/floor/engine/n2, /area/engine/atmos) @@ -11798,6 +11971,60 @@ }, /turf/open/floor/plasteel, /area/engine/atmos) +"asy" = ( +/obj/item/cigbutt, +/turf/open/floor/plating, +/area/maintenance/port/fore) +"asz" = ( +/obj/machinery/requests_console{ + department = "Security"; + name = "Security RC"; + pixel_x = -32 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/structure/table/reinforced, +/obj/item/storage/box/teargas{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/storage/box/handcuffs, +/obj/item/storage/box/flashbangs{ + pixel_x = -3; + pixel_y = -3 + }, +/turf/open/floor/plasteel/dark, +/area/security/brig) +"asA" = ( +/obj/structure/reagent_dispensers/fueltank, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"asB" = ( +/obj/machinery/door/firedoor, +/obj/structure/table/reinforced, +/obj/item/clipboard, +/obj/item/folder/red, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/item/toy/figure/secofficer, +/turf/open/floor/plasteel, +/area/security/brig) "asC" = ( /obj/machinery/atmospherics/pipe/simple/yellow/visible{ dir = 10 @@ -11811,6 +12038,26 @@ dir = 1 }, /area/engine/atmos) +"asD" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/structure/chair/comfy/black{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) +"asE" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Maintenance Hatch"; + req_access_txt = "12" + }, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/maintenance/port/fore) "asF" = ( /obj/machinery/atmospherics/pipe/simple/supply/visible{ dir = 5 @@ -12166,6 +12413,11 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, /area/storage/primary) +"ata" = ( +/obj/structure/window/reinforced/tinted/fulltile, +/obj/structure/grille, +/turf/open/floor/plating, +/area/security/detectives_office) "atb" = ( /obj/structure/cable/white{ icon_state = "1-8" @@ -12258,6 +12510,10 @@ }, /turf/open/floor/plasteel/dark, /area/crew_quarters/bar/atrium) +"atk" = ( +/obj/effect/spawner/structure/window/reinforced/tinted, +/turf/open/floor/plating, +/area/security/detectives_office) "atl" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 5 @@ -12314,6 +12570,89 @@ }, /turf/open/floor/plasteel, /area/maintenance/starboard) +"atr" = ( +/obj/structure/table/wood, +/obj/item/storage/secure/briefcase, +/obj/item/restraints/handcuffs, +/obj/item/grenade/smokebomb, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/security/detectives_office) +"ats" = ( +/obj/item/clothing/suit/jacket{ + desc = "All the class of a trenchcoat without the security fibers."; + icon_state = "greydet"; + name = "trenchcoat" + }, +/obj/item/clothing/suit/jacket{ + desc = "All the class of a trenchcoat without the security fibers."; + icon_state = "detective"; + name = "trenchcoat" + }, +/obj/item/clothing/head/fedora, +/obj/item/clothing/head/fedora{ + icon_state = "detective" + }, +/obj/machinery/light/small{ + dir = 1 + }, +/obj/structure/closet/secure_closet/detective, +/turf/open/floor/wood, +/area/security/detectives_office) +"att" = ( +/obj/structure/dresser, +/turf/open/floor/wood, +/area/security/detectives_office) +"atu" = ( +/obj/structure/table/wood, +/obj/item/crowbar/red, +/obj/item/book/manual/wiki/security_space_law{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/book/manual/wiki/detective, +/obj/item/camera/detective, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/security/detectives_office) +"atv" = ( +/obj/effect/landmark/start/detective, +/obj/structure/chair/comfy/brown{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on, +/obj/machinery/light_switch{ + pixel_x = 8; + pixel_y = -24 + }, +/obj/machinery/camera{ + c_tag = "Detective's Office - Quarters"; + dir = 1 + }, +/turf/open/floor/wood, +/area/security/detectives_office) +"atw" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on, +/turf/open/floor/wood, +/area/security/detectives_office) "atx" = ( /obj/machinery/air_sensor/atmos/nitrogen_tank, /turf/open/floor/engine/n2, @@ -12328,6 +12667,109 @@ }, /turf/open/floor/plasteel, /area/engine/atmos) +"atz" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/turf/closed/wall, +/area/security/detectives_office) +"atA" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/door/airlock/security{ + name = "Detective's Office"; + req_access_txt = "4" + }, +/turf/open/floor/plasteel, +/area/security/detectives_office) +"atB" = ( +/obj/machinery/portable_atmospherics/canister/air, +/obj/effect/turf_decal/stripes/line{ + dir = 6 + }, +/obj/structure/sign/poster/ripped{ + pixel_x = 32 + }, +/turf/open/floor/plasteel, +/area/maintenance/starboard/aft) +"atC" = ( +/obj/machinery/photocopier, +/obj/item/newspaper{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/newspaper, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/structure/sign/poster/official/cohiba_robusto_ad{ + pixel_x = -32 + }, +/obj/machinery/light_switch{ + pixel_x = 8; + pixel_y = 24 + }, +/obj/machinery/camera{ + c_tag = "Detective's Office - Desk" + }, +/obj/item/radio/intercom{ + name = "Station Intercom"; + pixel_x = -6; + pixel_y = 22 + }, +/turf/open/floor/wood, +/area/security/detectives_office) +"atD" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/open/floor/wood, +/area/security/detectives_office) +"atE" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/machinery/door/airlock/external{ + name = "External Airlock"; + req_access_txt = "13" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 8 + }, +/turf/open/floor/plasteel, +/area/maintenance/port) +"atF" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/security/detectives_office) +"atG" = ( +/obj/structure/table/wood, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ + dir = 8 + }, +/obj/machinery/computer/med_data/laptop{ + icon_state = "laptop"; + dir = 4 + }, +/obj/machinery/requests_console{ + department = "Detective's Office"; + name = "Detective RC"; + pixel_x = -30 + }, +/turf/open/floor/wood, +/area/security/detectives_office) +"atH" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/chair/office/dark, +/obj/effect/landmark/start/detective, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on{ + dir = 8 + }, +/turf/open/floor/wood, +/area/security/detectives_office) "atI" = ( /obj/structure/cable/white{ icon_state = "0-2" @@ -12355,6 +12797,9 @@ }, /turf/open/floor/plasteel/dark/corner, /area/engine/atmos) +"atJ" = ( +/turf/open/floor/wood, +/area/security/detectives_office) "atK" = ( /obj/structure/sign/poster/contraband/random{ pixel_x = 32 @@ -12724,6 +13169,12 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on, /turf/open/floor/plasteel/dark, /area/crew_quarters/bar/atrium) +"auh" = ( +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ + dir = 1 + }, +/turf/closed/wall/r_wall, +/area/science/research) "aui" = ( /obj/machinery/status_display, /turf/closed/wall, @@ -12838,6 +13289,51 @@ /obj/machinery/atmospherics/pipe/manifold/general/visible, /turf/open/floor/plasteel, /area/engine/atmos) +"aur" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/cable/white{ + icon_state = "1-2" + }, +/obj/structure/sign/departments/security{ + pixel_x = 32 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/open/floor/plating, +/area/maintenance/starboard/aft) +"aus" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/turf/closed/wall, +/area/security/detectives_office) +"aut" = ( +/obj/structure/table/wood, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 9 + }, +/obj/machinery/computer/security/wooden_tv{ + pixel_x = 0; + pixel_y = 4 + }, +/turf/open/floor/wood, +/area/security/detectives_office) +"auu" = ( +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 8 + }, +/obj/structure/table/wood, +/obj/machinery/computer/secure_data/laptop{ + dir = 1; + icon_state = "laptop"; + pixel_y = 4 + }, +/turf/open/floor/wood, +/area/security/detectives_office) "auv" = ( /obj/machinery/atmospherics/pipe/simple/cyan/visible{ dir = 10 @@ -12856,6 +13352,44 @@ }, /turf/open/floor/plasteel, /area/engine/atmos) +"aux" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 8 + }, +/turf/open/floor/wood, +/area/security/detectives_office) +"auy" = ( +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 8 + }, +/obj/structure/cable/white{ + icon_state = "1-2" + }, +/obj/structure/cable/white{ + icon_state = "1-4" + }, +/turf/open/floor/plating, +/area/maintenance/starboard/aft) +"auz" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/door/airlock/security{ + name = "Detective's Office"; + req_access_txt = "4" + }, +/obj/machinery/door/firedoor, +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/turf/open/floor/plasteel, +/area/security/detectives_office) "auA" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -13319,6 +13853,15 @@ }, /turf/open/floor/plasteel/dark, /area/crew_quarters/bar/atrium) +"avm" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/turf/open/floor/wood, +/area/security/detectives_office) "avn" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 @@ -13465,12 +14008,66 @@ }, /turf/open/floor/plasteel/dark, /area/engine/atmos) +"avz" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 9 + }, +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/obj/effect/landmark/event_spawn, +/turf/open/floor/wood, +/area/security/detectives_office) +"avA" = ( +/obj/structure/cable/white{ + icon_state = "2-8" + }, +/turf/open/floor/wood, +/area/security/detectives_office) +"avB" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/structure/sign/warning/vacuum{ + pixel_x = -32; + pixel_y = 32 + }, +/turf/open/floor/plating, +/area/maintenance/port) +"avC" = ( +/obj/structure/sign/poster/ripped, +/turf/closed/wall/rust, +/area/security/detectives_office) +"avD" = ( +/obj/machinery/vending/cigarette, +/obj/machinery/firealarm{ + dir = 1; + pixel_y = -26 + }, +/turf/open/floor/wood, +/area/security/detectives_office) "avE" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ dir = 10 }, /turf/open/floor/plasteel, /area/engine/atmos) +"avF" = ( +/obj/structure/table/wood, +/obj/machinery/airalarm{ + dir = 1; + pixel_y = -22 + }, +/obj/item/clothing/mask/cigarette/cigar/cohiba{ + pixel_x = 3 + }, +/obj/item/clothing/mask/cigarette/cigar/havana{ + pixel_x = -3 + }, +/obj/item/clothing/mask/cigarette/cigar, +/obj/item/clothing/glasses/sunglasses, +/turf/open/floor/wood, +/area/security/detectives_office) "avG" = ( /obj/machinery/light{ dir = 4 @@ -13489,6 +14086,22 @@ }, /turf/open/floor/plasteel/dark/corner, /area/engine/atmos) +"avH" = ( +/obj/structure/table/wood, +/obj/structure/cable/white, +/obj/machinery/power/apc{ + areastring = "/area/security/detectives_office"; + name = "Detective's Office APC"; + pixel_y = -26 + }, +/obj/item/storage/briefcase{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/storage/secure/briefcase, +/obj/item/taperecorder, +/turf/open/floor/wood, +/area/security/detectives_office) "avI" = ( /obj/structure/cable/white{ icon_state = "2-4" @@ -14076,6 +14689,12 @@ "awA" = ( /turf/open/floor/engine/o2, /area/engine/atmos) +"awB" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/turf/open/floor/plating, +/area/maintenance/port) "awC" = ( /obj/machinery/atmospherics/pipe/simple/green/visible{ dir = 4 @@ -14099,11 +14718,39 @@ }, /turf/open/floor/plasteel/dark, /area/engine/atmos) +"awD" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/cable/white{ + icon_state = "1-2" + }, +/obj/item/cigbutt, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/maintenance/starboard/aft) +"awE" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 10 + }, +/turf/open/floor/plating, +/area/maintenance/port) "awF" = ( /obj/machinery/atmospherics/pipe/simple/cyan/visible, /obj/effect/turf_decal/stripes/line, /turf/open/floor/plasteel, /area/engine/atmos) +"awG" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/maintenance/port) +"awH" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/light/small, +/obj/structure/closet/emcloset/anchored, +/turf/open/floor/plasteel, +/area/maintenance/port) "awI" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ dir = 5 @@ -14130,6 +14777,12 @@ }, /turf/open/floor/plasteel/dark/corner, /area/engine/atmos) +"awL" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 5 + }, +/turf/open/floor/plating, +/area/maintenance/port) "awM" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -14391,14 +15044,70 @@ /obj/item/wirecutters, /turf/open/floor/plasteel, /area/maintenance/starboard) +"axh" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/turf/open/floor/plating/airless, +/area/maintenance/port) "axi" = ( /obj/machinery/air_sensor/atmos/oxygen_tank, /turf/open/floor/engine/o2, /area/engine/atmos) +"axj" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/turf/open/floor/plasteel, +/area/maintenance/port) +"axk" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/closet/crate, +/obj/item/stack/ore/silver, +/obj/item/stack/ore/iron, +/obj/item/stack/ore/iron, +/obj/item/stack/ore/iron, +/obj/item/pickaxe/emergency, +/turf/open/floor/plasteel, +/area/maintenance/port) +"axl" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Maintenance Hatch"; + req_access_txt = "12" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/maintenance/port) "axm" = ( /obj/machinery/atmospherics/components/unary/thermomachine/freezer, /turf/open/floor/plasteel, /area/engine/atmos) +"axn" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 1 + }, +/turf/open/floor/plasteel/dark, +/area/maintenance/port) "axo" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/visible, /obj/effect/turf_decal/stripes/line{ @@ -14674,6 +15383,23 @@ }, /turf/open/floor/carpet, /area/crew_quarters/dorms) +"axL" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + name = "Maintenance Hatch"; + req_access_txt = "12" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/maintenance/port) "axM" = ( /obj/structure/table/wood, /obj/item/lipstick/random{ @@ -14822,6 +15548,13 @@ }, /turf/open/floor/plasteel/dark, /area/crew_quarters/bar/atrium) +"axU" = ( +/obj/structure/table/wood, +/obj/item/paper_bin, +/obj/item/pen, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/open/floor/plasteel/dark, +/area/maintenance/port) "axV" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/machinery/newscaster{ @@ -14837,6 +15570,20 @@ "axW" = ( /turf/closed/wall, /area/hallway/secondary/exit) +"axX" = ( +/obj/effect/landmark/xeno_spawn, +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 1 + }, +/turf/open/floor/wood, +/area/maintenance/port) +"axY" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/item/cigbutt, +/turf/open/floor/plating, +/area/maintenance/port) "ayh" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/visible{ dir = 8 @@ -15699,12 +16446,6 @@ icon_state = "platingdmg1" }, /area/maintenance/starboard/central) -"azQ" = ( -/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden, -/turf/open/floor/plating{ - icon_state = "panelscorched" - }, -/area/maintenance/starboard/central) "azR" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 @@ -24213,43 +24954,6 @@ }, /turf/open/floor/plasteel/dark, /area/hallway/primary/starboard) -"aPz" = ( -/obj/effect/spawner/structure/window/reinforced/tinted, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) -"aPA" = ( -/obj/structure/table/wood, -/obj/item/storage/secure/briefcase, -/obj/item/restraints/handcuffs, -/obj/item/grenade/smokebomb, -/obj/effect/decal/cleanable/cobweb, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) -"aPB" = ( -/obj/structure/closet/secure_closet/personal/cabinet, -/obj/item/clothing/suit/jacket{ - desc = "All the class of a trenchcoat without the security fibers."; - icon_state = "greydet"; - name = "trenchcoat" - }, -/obj/item/clothing/suit/jacket{ - desc = "All the class of a trenchcoat without the security fibers."; - icon_state = "detective"; - name = "trenchcoat" - }, -/obj/item/clothing/head/fedora, -/obj/item/clothing/head/fedora{ - icon_state = "detective" - }, -/obj/machinery/light/small{ - dir = 1 - }, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) -"aPC" = ( -/obj/structure/dresser, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) "aPG" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -24730,41 +25434,6 @@ }, /turf/open/floor/plasteel/dark, /area/hallway/primary/starboard) -"aQB" = ( -/obj/structure/table/wood, -/obj/item/crowbar/red, -/obj/item/book/manual/wiki/security_space_law{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/book/manual/wiki/detective, -/obj/item/camera/detective, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/maintenance/starboard/aft) -"aQC" = ( -/obj/structure/chair/office/dark{ - dir = 4 - }, -/obj/effect/decal/cleanable/blood/old, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) -"aQD" = ( -/obj/effect/landmark/xeno_spawn, -/obj/machinery/atmospherics/components/unary/vent_pump/on, -/turf/open/floor/wood{ - icon_state = "wood-broken" - }, -/area/maintenance/starboard/aft) "aQG" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ dir = 6 @@ -25089,19 +25758,6 @@ "aRz" = ( /turf/closed/wall, /area/maintenance/starboard) -"aRA" = ( -/obj/structure/barricade/wooden, -/obj/machinery/door/airlock/maintenance_hatch{ - name = "Maintenance Hatch"; - req_access_txt = "12" - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/turf/open/floor/plasteel, -/area/maintenance/starboard/aft) "aRB" = ( /obj/effect/turf_decal/bot, /obj/structure/table, @@ -25293,13 +25949,6 @@ }, /turf/open/floor/plasteel/dark, /area/library) -"aRV" = ( -/obj/structure/sign/warning/nosmoking{ - pixel_y = 32 - }, -/obj/machinery/vending/games, -/turf/open/floor/plasteel/dark, -/area/library) "aRW" = ( /obj/structure/chair/office/dark{ dir = 4 @@ -25634,44 +26283,10 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/plasteel, /area/maintenance/starboard/aft) -"aSG" = ( -/obj/machinery/portable_atmospherics/canister/air, -/obj/effect/turf_decal/stripes/line{ - dir = 6 - }, -/turf/open/floor/plasteel, -/area/maintenance/starboard/aft) "aSH" = ( /obj/structure/sign/poster/contraband/random, /turf/closed/wall, /area/maintenance/starboard) -"aSI" = ( -/obj/effect/decal/cleanable/cobweb, -/obj/machinery/photocopier, -/obj/item/newspaper{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/newspaper, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) -"aSJ" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/wood{ - icon_state = "wood-broken2" - }, -/area/maintenance/starboard/aft) -"aSK" = ( -/obj/structure/table/wood, -/obj/item/storage/fancy/cigarettes/cigpack_uplift{ - pixel_x = 6 - }, -/obj/item/storage/fancy/cigarettes/cigpack_carp{ - pixel_x = -3 - }, -/obj/item/lighter, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) "aSM" = ( /obj/machinery/door/airlock/command{ name = "Telecomms Server Room"; @@ -26266,31 +26881,6 @@ icon_state = "panelscorched" }, /area/maintenance/starboard/aft) -"aTU" = ( -/obj/structure/table/wood, -/obj/item/poster/random_contraband{ - pixel_x = 3; - pixel_y = 3 - }, -/obj/item/poster/random_contraband{ - pixel_x = -3; - pixel_y = -3 - }, -/obj/item/poster/random_contraband, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) -"aTV" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/wood{ - icon_state = "wood-broken7" - }, -/area/maintenance/starboard/aft) -"aTW" = ( -/obj/structure/chair/comfy/black{ - dir = 4 - }, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) "aTX" = ( /obj/machinery/light{ dir = 8 @@ -26654,48 +27244,6 @@ }, /turf/closed/wall/r_wall, /area/science/research) -"aUJ" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 10 - }, -/turf/closed/wall/r_wall, -/area/science/research) -"aUK" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/structure/cable/white{ - icon_state = "1-2" - }, -/turf/open/floor/plating{ - icon_state = "platingdmg3" - }, -/area/maintenance/starboard/aft) -"aUL" = ( -/obj/structure/table/wood, -/obj/item/electronics/firelock, -/obj/item/electronics/airlock, -/obj/machinery/light/small{ - dir = 8 - }, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) -"aUM" = ( -/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ - dir = 8 - }, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) -"aUN" = ( -/obj/structure/chair/comfy/brown{ - dir = 4 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 8 - }, -/obj/effect/landmark/xeno_spawn, -/turf/open/floor/wood{ - icon_state = "wood-broken5" - }, -/area/maintenance/starboard/aft) "aUO" = ( /obj/structure/cable{ icon_state = "4-8" @@ -27153,50 +27701,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall/r_wall, /area/science/research) -"aVF" = ( -/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ - dir = 8 - }, -/obj/structure/cable/white{ - icon_state = "1-2" - }, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) -"aVG" = ( -/obj/machinery/door/airlock/maintenance_hatch{ - name = "Maintenance Hatch"; - req_access_txt = "12" - }, -/obj/structure/barricade/wooden, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) -"aVH" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/turf/open/floor/wood{ - icon_state = "wood-broken" - }, -/area/maintenance/starboard/aft) -"aVI" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 9 - }, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) -"aVJ" = ( -/obj/machinery/vending/cigarette, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) "aVK" = ( /obj/machinery/atmospherics/pipe/heat_exchanging/simple{ dir = 6 @@ -27645,28 +28149,6 @@ }, /turf/open/floor/circuit/green/telecomms/mainframe, /area/science/research) -"aWw" = ( -/obj/structure/table/wood, -/obj/item/clipboard, -/obj/item/electronics/airalarm, -/obj/item/electronics/airlock, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) -"aWx" = ( -/obj/structure/table/wood, -/obj/item/stack/cable_coil/white, -/obj/item/stack/cable_coil/white, -/obj/item/stack/sheet/glass, -/obj/item/stack/sheet/glass, -/obj/item/circuitboard/machine/microwave, -/turf/open/floor/wood, -/area/maintenance/starboard/aft) -"aWy" = ( -/obj/structure/table/wood, -/obj/item/storage/briefcase, -/obj/item/taperecorder, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) "aWz" = ( /obj/machinery/atmospherics/pipe/heat_exchanging/simple, /obj/structure/lattice, @@ -29817,50 +30299,12 @@ "baj" = ( /turf/closed/wall, /area/maintenance/port) -"bak" = ( -/turf/open/floor/plasteel/dark, -/area/maintenance/port) -"bal" = ( -/obj/effect/landmark/xeno_spawn, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/maintenance/port) "bam" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, /turf/open/floor/plasteel/dark, /area/maintenance/port) -"ban" = ( -/obj/machinery/door/airlock/maintenance_hatch{ - name = "Maintenance Hatch"; - req_access_txt = "12" - }, -/obj/structure/barricade/wooden, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/turf/open/floor/plasteel, -/area/maintenance/port) "bao" = ( /obj/structure/cable/white{ icon_state = "1-2" @@ -30361,12 +30805,6 @@ /obj/item/flashlight/lamp, /turf/open/floor/plating, /area/maintenance/port) -"bbd" = ( -/obj/structure/table/wood, -/obj/item/paper_bin, -/obj/item/pen, -/turf/open/floor/plasteel/dark, -/area/maintenance/port) "bbe" = ( /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -36438,11 +36876,6 @@ }, /turf/open/floor/plasteel, /area/hallway/secondary/exit) -"bte" = ( -/obj/structure/window/reinforced/tinted/fulltile, -/obj/structure/grille, -/turf/open/floor/plating, -/area/maintenance/starboard/aft) "btk" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 4 @@ -40071,9 +40504,6 @@ "swZ" = ( /turf/closed/wall/r_wall/rust, /area/security/brig) -"sxc" = ( -/turf/closed/wall/r_wall/rust, -/area/asteroid/nearstation) "sxC" = ( /turf/closed/wall/r_wall/rust, /area/ai_monitored/turret_protected/ai) @@ -41056,10 +41486,6 @@ }, /turf/open/floor/plasteel/dark, /area/tcommsat/server) -"sJR" = ( -/obj/structure/sign/poster/contraband/random, -/turf/closed/wall/rust, -/area/maintenance/starboard/aft) "sJS" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/structure/cable/white{ @@ -71508,7 +71934,7 @@ aac aad aad aad -aac +bvg aac aaa aaa @@ -71764,10 +72190,10 @@ aad aad aad aad -aad -aad -aac -aac +baj +aqo +baj +baj aaa aaa aaa @@ -72022,9 +72448,9 @@ aMJ aMJ sJV aMJ -abu -aad -aad +aqx +awH +baj aaa aaa aaa @@ -72279,9 +72705,9 @@ buJ buO sKn aMJ -acG -sxc -aad +atE +sKx +baj aac aaa aaa @@ -72536,8 +72962,8 @@ buK sKj buR aMJ -afM -afL +avB +aOH aad aac aaa @@ -72793,8 +73219,8 @@ buL buP buS aMJ -afM -aad +awB +baj aad aad aaa @@ -73050,8 +73476,8 @@ buK sKj buR aMJ -aad -aad +awB +baj aad aad aaa @@ -73307,8 +73733,8 @@ buN buQ buU sJV -agE -afL +awB +aOH aad aaa aaa @@ -73564,9 +73990,9 @@ sJV aMJ aMJ sJV -abi -ahu -sxc +awB +aOH +sKx aac aaa aaa @@ -73820,10 +74246,10 @@ bvo aad bvo aad -abi -aad -aad -aac +baj +awE +awL +baj aac aac aac @@ -74077,10 +74503,10 @@ aUQ aVK aUQ aXc -aad -ahu -aad -aad +baj +aOH +axY +awG aac aac aac @@ -74335,10 +74761,10 @@ aVL aUR aUQ bvo -sxc -aad +sKx +awB +baj aac -bvI aad aac aad @@ -74592,9 +75018,9 @@ aUU aWz aVL bvo -ahu -bvg -bvg +aOH +axh +baj aad aad aad @@ -74805,7 +75231,7 @@ aad aad aad abi -abu +agK abu aad aad @@ -74849,10 +75275,10 @@ aUU aWz aUQ bvg -bvo -aad -aad -aad +baj +awB +baj +baj aad aad aad @@ -75106,10 +75532,10 @@ aUU aWz aVL bvg -bvg -aad -aad -aad +awG +awB +axj +baj aad aad aad @@ -75320,9 +75746,9 @@ aad aad aad adv +aaW abj -abj -abu +ahB abu acF pNE @@ -75363,10 +75789,10 @@ aUU aUU aUQ bvg -bvo -aad -aad -aad +baj +awB +axk +baj aad aad aad @@ -75620,10 +76046,10 @@ aUU aWz aVL bvg -aad -aad -aad -aad +baj +awB +aZo +baj aad aad aad @@ -75836,9 +76262,9 @@ abT abi aaV ajQ -abi -abT -acG +aro +aro +asm bko cWv cWv @@ -75877,10 +76303,10 @@ aUU aWz aUQ bvo -aad -aad -aad -aad +baj +awE +awL +baj aad aad aad @@ -76089,16 +76515,16 @@ aad aad aad aad -aad -aad -aad -aad -aad -aad -agE -abi -aad -abi +aaW +abj +abj +abu +arp +ars +ars +asu +asA +asD sAx arA asH @@ -76135,8 +76561,8 @@ aWz aVL bvo aOH -aZo baj +axl sKE baj aZo @@ -76346,16 +76772,16 @@ aad aad abi agE -aad -aad -aad -aad -afL -aad +abj abi aad aad -aaV +arq +art +ars +asy +ars +ars aqz aqz aqz @@ -76393,7 +76819,7 @@ aUQ aad sKx aZp -bak +bam bbc bbQ dEa @@ -76602,19 +77028,19 @@ aad aad aad aad -aad -aad -afL -aaV -ajQ -aik -afL -aad -aad -abi -afM -aqG -arB +abj +agF +agF +agF +agF +agF +agF +agF +agF +agF +ars +asE +ars asI atK auD @@ -76650,9 +77076,9 @@ aVL aad aOH aZq -bal -bbd -bbR +axn +axU +axX bcG bdE bev @@ -76859,15 +77285,15 @@ aad aad aad aad -afL +abu agF +apw +aqB +aqH agF -agF -swZ -agF -agF -agF -swZ +aru +asn +asz agF agF agF @@ -77118,14 +77544,14 @@ aad abT agE swZ -aim -aiR -ajR +apR +aqD +arn akT -alH -amH -any -aos +arB +asq +anC +aqQ akT aqI arD @@ -77164,7 +77590,7 @@ sJH aQU aQU aZs -ban +axL bap bbT bcI @@ -77371,15 +77797,15 @@ aad aad aad aad -aad +ahu afL -abj +aaW swZ ain -aiS +aqG ajS akU -aid +asa amI anz aot @@ -77628,9 +78054,9 @@ aad aad aad aad -aad -afM -aaV +ahu +abu +abj agF aio aiT @@ -77885,8 +78311,8 @@ abi abi swN abi -abT -acG +agF +afT agF agF aip @@ -78142,10 +78568,10 @@ aaW abj adv ael -swN -abu agF -ahv +afU +ahz +aiu aiq aiV ajV @@ -78399,10 +78825,10 @@ abj abS abu acF -abi -afN -swZ -ahw +agF +agG +afU +aiv air aiW ajW @@ -78439,7 +78865,7 @@ aEt aYA aPT aQW -aRV +amH aTd aUe aUW @@ -78656,11 +79082,11 @@ abS acD acF aem -afb -adw -agF -ahx -ais +afg +afU +afU +agH +ahv aiX agm akY @@ -78913,10 +79339,10 @@ abu acE adx acF -adz -adw agF -ahy +afU +ahC +aiR ait aiY ajY @@ -79169,10 +79595,10 @@ aaW abu acF acF -aeo -abi acF agF +afe +agF swZ agF aiZ @@ -79424,13 +79850,13 @@ aad aad abi abi -acG +acJ adz swN -abi +abT acF -agG -ahz +ahD +aiS agF aja aka @@ -79440,7 +79866,7 @@ aka ala aoy apD -aqQ +asB arM asR agF @@ -79684,9 +80110,9 @@ abT acH adw aep -acF +afh aem -agH +afP ahA swZ ajb @@ -79937,15 +80363,15 @@ aad aad aad aad -abv -abv -abv -swG -swG -abv -abv -swG -agF +abU +abU +abU +abU +abU +abU +abU +abU +abU ajc ajc aip @@ -80194,15 +80620,15 @@ aac aac aac aad -abv -acI -adA -aeq -afd -afP -agI -ahB -aiu +abU +acK +adD +adC +afb +agL +ahx +ajR +aqw ajd iaa alb @@ -80451,15 +80877,15 @@ aaa aaa aaa aaa -abU -acJ -adB -aer -afe -afQ -agJ -ahC -aiv +abV +acG +acL +adE +afd +agM +ahF +alH +ahy aje akd alc @@ -80708,15 +81134,15 @@ aac aac aad aad -swG -acK -adC +abU +acM +aeo aes -aff -afR -agK -ahD -aiw +afQ +afN +ahE +agI +aqy ajf ake ald @@ -80964,16 +81390,16 @@ aad aad aad aad -abv -abv -acL -adD -abv -afg -afS -agL -ahE -aiu +abU +abU +adA +aeq +aff +afi +ahw +aim +aid +aqw ajg akf oyD @@ -81221,16 +81647,16 @@ aad aad aad aad -abv -abV -acM -adE -abv -afh -afT -agM -ahF -afU +abU +acI +adB +aer +aff +afS +agJ +ais +aos +abU ajh akg ale @@ -81478,16 +81904,16 @@ aad aad aad aad -abv -abv -abv -abv -swG -afi -afU -afU -afU -afU +abU +abU +abU +abU +abU +afR +abU +abU +abU +abU aji akh alf @@ -88444,7 +88870,7 @@ aww aum sDM ayT -azQ +aiw aAW aCf aCf @@ -90263,7 +90689,7 @@ aae aRy aSF aTS -aUJ +auh aVE sKh aVE @@ -90514,16 +90940,16 @@ aRz sJd sJq aaa -bte -aPz -aPz -sOM -aSG +ata +atk +atk +abv +atB aTT -aUK -aVF +aur +auy sOZ -sPa +awD sOT sPk aZl @@ -90771,16 +91197,16 @@ sIU sJe sIU aae -bte -aPA -aQB -sOM -sJR -sON -sOY -aVG -sJR -sOM +ata +atr +atu +abv +swG +swG +aus +auz +avC +abv sOY sOV aZm @@ -91028,16 +91454,16 @@ sIU sJe sIU aaa -bte -aPB -aQC -sOM -aSI -aTU -aUL -aVH -aWw -sOM +ata +ats +atv +atz +atC +atG +aut +avm +avD +abv sPi aYv die @@ -91285,16 +91711,16 @@ sIU sJg sIU aae -bte -aPC -aQD -aRA -aSJ -aTV -aUM -aVI -aWx -sOM +ata +att +atw +atA +atD +atH +auu +avz +avF +abv sON tms hXc @@ -91542,16 +91968,16 @@ sJN cRz abN aaa -bte -aPz -aPz -sOM -aSK -aTW -aUN -aVJ -aWy -sOM +ata +atk +atk +abv +any +atJ +aux +avA +avH +abv aad orV iio @@ -91802,13 +92228,13 @@ aae aae aae aae -sON -aRy -aRy -aRy -aRy -aRy -sON +swG +atF +atF +atF +atF +atF +swG aad hns iXB diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/_flags/_flags.dm similarity index 95% rename from code/__DEFINES/flags.dm rename to code/__DEFINES/_flags/_flags.dm index 68410aec99..dae3df6f0b 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -22,6 +22,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 //FLAGS BITMASK ///This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not. #define HEAR_1 (1<<3) +///Projectiles will use default chance-based ricochet handling on things with this. +#define DEFAULT_RICOCHET_1 (1<<4) ///Conducts electricity (metal etc.). #define CONDUCT_1 (1<<5) ///For machines and structures that should not break into parts, eg, holodeck stuff. @@ -44,11 +46,6 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 /// Early returns mob.face_atom() #define BLOCK_FACE_ATOM_1 (1<<17) -/// If the thing can reflect light (lasers/energy) -#define RICOCHET_SHINY (1<<0) -/// If the thing can reflect matter (bullets/bomb shrapnel) -#define RICOCHET_HARD (1<<1) - //turf-only flags #define NOJAUNT_1 (1<<0) #define UNUSED_RESERVATION_TURF_1 (1<<1) @@ -140,3 +137,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define MOBILITY_FLAGS_DEFAULT (MOBILITY_MOVE | MOBILITY_STAND | MOBILITY_PICKUP | MOBILITY_USE | MOBILITY_UI | MOBILITY_STORAGE | MOBILITY_PULL | MOBILITY_RESIST) #define MOBILITY_FLAGS_ANY_INTERACTION (MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_UI | MOBILITY_STORAGE) + +// melee_attack_chain() attackchain_flags +/// The attack is from a parry counterattack. +#define ATTACKCHAIN_PARRY_COUNTERATTACK (1<<0) diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/_flags/item_flags.dm similarity index 74% rename from code/__DEFINES/obj_flags.dm rename to code/__DEFINES/_flags/item_flags.dm index a904d7a7e6..c6a3886ed3 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/_flags/item_flags.dm @@ -1,20 +1,3 @@ -// Flags for the obj_flags var on /obj - - -#define EMAGGED (1<<0) -#define IN_USE (1<<1) //If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! -#define CAN_BE_HIT (1<<2) //can this be bludgeoned by items? -#define BEING_SHOCKED (1<<3) //Whether this thing is currently (already) being shocked by a tesla -#define DANGEROUS_POSSESSION (1<<4) //Admin possession yes/no -#define ON_BLUEPRINTS (1<<5) //Are we visible on the station blueprints at roundstart? -#define UNIQUE_RENAME (1<<6) //can you customize the description/name of the thing? -#define USES_TGUI (1<<7) //put on things that use tgui on ui_interact instead of custom/old UI. -#define FROZEN (1<<8) -#define SHOVABLE_ONTO (1<<9) //called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check. -#define BLOCK_Z_FALL (1<<10) - -// If you add new ones, be sure to add them to /obj/Initialize as well for complete mapping support - // Flags for the item_flags var on /obj/item #define BEING_REMOVED (1<<0) @@ -41,6 +24,10 @@ #define NO_UNIFORM_REQUIRED (1<<11) ///Damage when attacking people is not affected by combat mode. #define NO_COMBAT_MODE_FORCE_MODIFIER (1<<12) +/// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all. +#define ITEM_CAN_PARRY (1<<13) +/// This item can be used in the directional blocking system. Only a basic check used to determine if we should proceed with directional block handling at all. +#define ITEM_CAN_BLOCK (1<<14) // Flags for the clothing_flags var on /obj/item/clothing diff --git a/code/__DEFINES/_flags/obj_flags.dm b/code/__DEFINES/_flags/obj_flags.dm new file mode 100644 index 0000000000..ebb9b4bda0 --- /dev/null +++ b/code/__DEFINES/_flags/obj_flags.dm @@ -0,0 +1,15 @@ +// Flags for the obj_flags var on /obj + +#define EMAGGED (1<<0) +#define IN_USE (1<<1) //If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! +#define CAN_BE_HIT (1<<2) //can this be bludgeoned by items? +#define BEING_SHOCKED (1<<3) //Whether this thing is currently (already) being shocked by a tesla +#define DANGEROUS_POSSESSION (1<<4) //Admin possession yes/no +#define ON_BLUEPRINTS (1<<5) //Are we visible on the station blueprints at roundstart? +#define UNIQUE_RENAME (1<<6) //can you customize the description/name of the thing? +#define USES_TGUI (1<<7) //put on things that use tgui on ui_interact instead of custom/old UI. +#define FROZEN (1<<8) +#define SHOVABLE_ONTO (1<<9) //called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check. +#define BLOCK_Z_FALL (1<<10) + +// If you add new ones, be sure to add them to /obj/Initialize as well for complete mapping support diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 0acf1cb0dd..bf053635af 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -29,10 +29,11 @@ #define EFFECT_DROWSY "drowsy" #define EFFECT_JITTER "jitter" +// mob/living/var/combat_flags variable. /// Default combat flags for those affected by sprinting (combat mode has been made into its own component) -#define COMBAT_FLAGS_DEFAULT NONE +#define COMBAT_FLAGS_DEFAULT (COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE) /// Default combat flags for everyone else (so literally everyone but humans). -#define COMBAT_FLAGS_SPRINT_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_SPRINT_FORCED) +#define COMBAT_FLAGS_SPRINT_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_SPRINT_FORCED | COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE) /// The user wants sprint mode on #define COMBAT_FLAG_SPRINT_TOGGLED (1<<0) @@ -50,6 +51,16 @@ #define COMBAT_FLAG_SOFT_STAMCRIT (1<<6) /// Force sprint mode on at all times, overrides everything including sprint disable traits. #define COMBAT_FLAG_SPRINT_FORCED (1<<7) +/// This mob is capable of using the active parrying system. +#define COMBAT_FLAG_PARRY_CAPABLE (1<<8) +/// This mob is capable of using the active blocking system. +#define COMBAT_FLAG_BLOCK_CAPABLE (1<<9) +/// This mob is capable of unarmed parrying +#define COMBAT_FLAG_UNARMED_PARRY (1<<10) +/// This mob is currently actively blocking +#define COMBAT_FLAG_ACTIVE_BLOCKING (1<<11) +/// This mob is currently starting an active block +#define COMBAT_FLAG_ACTIVE_BLOCK_STARTING (1<<12) // Helpers for getting someone's stamcrit state. Cast to living. #define NOT_STAMCRIT 0 @@ -112,18 +123,6 @@ #define GRAB_NECK 2 #define GRAB_KILL 3 -/// 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" #define ATTACK_EFFECT_KICK "kick" @@ -176,15 +175,6 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define EMBED_THROWSPEED_THRESHOLD 4 //The minimum value of an item's throw_speed for it to embed (Unless it has embedded_ignore_throwspeed_threshold set to 1) #define EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER 8 //Coefficient of multiplication for the damage the item does when removed without a surgery (this*item.w_class) #define EMBEDDED_UNSAFE_REMOVAL_TIME 150 //A Time in ticks, total removal time = (this/item.w_class) -#define EMBEDDED_JOSTLE_CHANCE 5 //Chance for embedded objects to cause pain every time they move (jostle) -#define EMBEDDED_JOSTLE_PAIN_MULTIPLIER 1 //Coefficient of multiplication for the damage the item does while -#define EMBEDDED_PAIN_STAM_PCT 0.0 //This percentage of all pain will be dealt as stam damage rather than brute (0-1) -#define EMBED_CHANCE_TURF_MOD -15 //You are this many percentage points less likely to embed into a turf (good for things glass shards and spears vs walls) - -#define EMBED_HARMLESS list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE) -#define EMBED_HARMLESS_SUPERIOR list("pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE, "embed_chance" = 100, "fall_chance" = 0.1) -#define EMBED_POINTY list("ignore_throwspeed_threshold" = TRUE) -#define EMBED_POINTY_SUPERIOR list("embed_chance" = 100, "ignore_throwspeed_threshold" = TRUE) //Gun weapon weight #define WEAPON_LIGHT 1 @@ -199,11 +189,6 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define EGUN_SELFCHARGE 1 #define EGUN_SELFCHARGE_BORG 2 -//suppression defines -#define SUPPRESSED_NONE 0 -#define SUPPRESSED_QUIET 1 ///standard suppressed -#define SUPPRESSED_VERY 2 /// no message - ///Time to spend without clicking on other things required for your shots to become accurate. #define GUN_AIMING_TIME (2 SECONDS) @@ -287,74 +272,4 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( * should the current-attack-damage be lower than the item force multiplied by this value, * a "inefficiently" prefix will be added to the message. */ -#define INEFFICIENT_ATTACK_MSG_THRESHOLD 0.7 - - -//bullet_act() return values -#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting. -#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting. -#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. - -#define NICE_SHOT_RICOCHET_BONUS 10 //if the shooter has the NICE_SHOT trait and they fire a ricocheting projectile, add this to the ricochet chance and auto aim angle - -/// 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. -#define mob_check_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\ - 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. -#define mob_run_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\ - do_run_block(TRUE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list) - -/// Bitflags for check_block() and run_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_WEAR_SUIT 75 -#define BLOCK_PRIORITY_CLOTHING 50 -#define BLOCK_PRIORITY_UNIFORM 25 - -#define BLOCK_PRIORITY_DEFAULT BLOCK_PRIORITY_HELD_ITEM +#define FEEBLE_ATTACK_MSG_THRESHOLD 0.5 diff --git a/code/__DEFINES/combat/attack_types.dm b/code/__DEFINES/combat/attack_types.dm new file mode 100644 index 0000000000..a401c0f2ee --- /dev/null +++ b/code/__DEFINES/combat/attack_types.dm @@ -0,0 +1,32 @@ +// 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 was from a parry counterattack. Do not attempt to parry-this! +#define ATTACK_TYPE_PARRY_COUNTERATTACK (1<<5) + +// Requires for datum definitions to not error with must be a constant statement when used in lists as text associative keys. +// KEEP IN SYNC WITH ABOVE! + +#define TEXT_ATTACK_TYPE_MELEE "1" +#define TEXT_ATTACK_TYPE_PROJECTILE "2" +#define TEXT_ATTACK_TYPE_UNARMED "4" +#define TEXT_ATTACK_TYPE_THROWN "8" +#define TEXT_ATTACK_TYPE_TACKLE "16" +#define TEXT_ATTACK_TYPE_PARRY_COUNTERATTACK "32" + +GLOBAL_LIST_INIT(attack_type_names, list( + TEXT_ATTACK_TYPE_MELEE = "Melee", + TEXT_ATTACK_TYPE_PROJECTILE = "Projectile", + TEXT_ATTACK_TYPE_UNARMED = "Unarmed", + TEXT_ATTACK_TYPE_THROWN = "Thrown", + TEXT_ATTACK_TYPE_TACKLE = "Tackle", + TEXT_ATTACK_TYPE_PARRY_COUNTERATTACK = "Parry Counterattack" +)) diff --git a/code/__DEFINES/combat/block.dm b/code/__DEFINES/combat/block.dm new file mode 100644 index 0000000000..bc85947d71 --- /dev/null +++ b/code/__DEFINES/combat/block.dm @@ -0,0 +1,80 @@ +/// 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. +#define mob_check_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\ + 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. +#define mob_run_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\ + do_run_block(TRUE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list) + +// Don't ask why there's block_parry.dm and this. This is for the run_block() system, which is the "parent" system of the directional block and parry systems. + +/// 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) +/// Attack should change the amount of damage incurred. This means something calling run_block() has to handle it! +#define BLOCK_SHOULD_CHANGE_DAMAGE (1<<9) +/// Attack should scale by this percent, 0 for no block and 100 for full blocked +#define BLOCK_SHOULD_PARTIAL_MITIGATE (1<<10) + +/// 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" +/// Tells the caller we got blocked by active directional block. +#define BLOCK_RETURN_ACTIVE_BLOCK "active_block" +/// Tells the caller our damage mitigation for their attack. +#define BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED "damage_mitigated" +/// For [BLOCK_CHANGE_DAMAGE]. Set damage to this. +#define BLOCK_RETURN_SET_DAMAGE_TO "set_damage_to" +/// For [BLOCK_SHOULD_PARTIAL_MITIGATE]. Percentage mitigation. +#define BLOCK_RETURN_MITIGATION_PERCENT "partial_mitigation" +/// Used internally by run_parry proc, use on an on_active_parry() proc to override parrying efficiency. +#define BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY "override_parry_efficiency" +/// Always set to 100 by run_block() if BLOCK_SUCCESS is in return value. Otherwise, defaults to mitigation percent if not set. Used by projectile/proc/on_hit(). +#define BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE "projectile_block_percentage" + +/// Default if the above isn't set in the list. +#define DEFAULT_REDIRECT_METHOD_PROJECTILE REDIRECT_METHOD_DEFLECT + +/// Block priorities. Higher means it's checked sooner. +// THESE MUST NEVER BE 0! Block code uses ! instead of isnull for the speed boost. +#define BLOCK_PRIORITY_ACTIVE_BLOCK 200 +#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/combat/block_parry.dm b/code/__DEFINES/combat/block_parry.dm new file mode 100644 index 0000000000..d48d7c8713 --- /dev/null +++ b/code/__DEFINES/combat/block_parry.dm @@ -0,0 +1,72 @@ +// We can't determine things like NORTHEAST vs NORTH *and* EAST without making our own flags :( +#define BLOCK_DIR_NORTH (1<<0) +#define BLOCK_DIR_NORTHEAST (1<<1) +#define BLOCK_DIR_NORTHWEST (1<<2) +#define BLOCK_DIR_WEST (1<<3) +#define BLOCK_DIR_EAST (1<<4) +#define BLOCK_DIR_SOUTH (1<<5) +#define BLOCK_DIR_SOUTHEAST (1<<6) +#define BLOCK_DIR_SOUTHWEST (1<<7) +#define BLOCK_DIR_ONTOP (1<<8) + +GLOBAL_LIST_INIT(dir2blockdir, list( + "[NORTH]" = BLOCK_DIR_NORTH, + "[NORTHEAST]" = BLOCK_DIR_NORTHEAST, + "[NORTHWEST]" = BLOCK_DIR_NORTHWEST, + "[WEST]" = BLOCK_DIR_WEST, + "[EAST]" = BLOCK_DIR_EAST, + "[SOUTH]" = BLOCK_DIR_SOUTH, + "[SOUTHEAST]" = BLOCK_DIR_SOUTHEAST, + "[SOUTHWEST]" = BLOCK_DIR_SOUTHWEST, + "[NONE]" = BLOCK_DIR_ONTOP + )) + +#define DIR2BLOCKDIR(d) (GLOB.dir2blockdir["[d]"]) + +GLOBAL_LIST_INIT(block_direction_names, list( + "[BLOCK_DIR_NORTH]" = "Front", + "[BLOCK_DIR_NORTHEAST]" = "Front Right", + "[BLOCK_DIR_NORTHWEST]" = "Front Left", + "[BLOCK_DIR_WEST]" = "Left", + "[BLOCK_DIR_EAST]" = "Right", + "[BLOCK_DIR_SOUTH]" = "Behind", + "[BLOCK_DIR_SOUTHEAST]" = "Behind Right", + "[BLOCK_DIR_SOUTHWEST]" = "Behind Left", + "[BLOCK_DIR_ONTOP]" = "Ontop" +)) + +/// If this is the value of active_block_starting it signals we want to interrupt the start +#define ACTIVE_BLOCK_STARTING_INTERRUPT "INTERRUPT" + +/// ""types"" of parry "items" +#define UNARMED_PARRY "unarmed" +#define MARTIAL_PARRY "martial" +#define ITEM_PARRY "item" + +/// Parry phase we're in +#define NOT_PARRYING 0 +#define PARRY_WINDUP 1 +#define PARRY_ACTIVE 2 +#define PARRY_SPINDOWN 3 + +// /datum/block_parry_data/var/parry_flags +/// Default handling for audio/visual feedback +#define PARRY_DEFAULT_HANDLE_FEEDBACK (1<<0) +/// Lock sprinting while parrying +#define PARRY_LOCK_SPRINTING (1<<1) +/// Lock attacking while parrying +#define PARRY_LOCK_ATTACKING (1<<2) + +/// Parry effects. +/// Automatically melee attacks back normally, LMB equivalent action of an harm intent attack. List association should be defaulting to 1, being the attack damage multiplier for said counterattack +#define PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN "melee_counterattack_chain" +/// List association should be TRUE. +#define PARRY_DISARM_ATTACKER "disarm_attacker" +/// List association should be duration or null for just plain knockdown. +#define PARRY_KNOCKDOWN_ATTACKER "knockdown_attacker" +/// List association should be duration. +#define PARRY_STAGGER_ATTACKER "stagger_attacker" +/// List association should be amount of time to daze attacker. +#define PARRY_DAZE_ATTACKER "daze_attacker" +/// Set to TRUE in list association to ignore adjacency checks +#define PARRY_COUNTERATTACK_IGNORE_ADJACENCY "ignore_adjacency" diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm index f1548bbe0f..683681a9a6 100644 --- a/code/__DEFINES/construction.dm +++ b/code/__DEFINES/construction.dm @@ -67,7 +67,9 @@ //tablecrafting defines #define CAT_NONE "" #define CAT_WEAPONRY "Weaponry" -#define CAT_WEAPON "Weapons" +#define CAT_WEAPON "Ranged Weapons" +#define CAT_MELEE "Melee Weapons" +#define CAT_OTHER "Misc" #define CAT_AMMO "Ammunition" #define CAT_PARTS "Weapon Parts" #define CAT_ROBOT "Robots" diff --git a/code/__DEFINES/flags/do_after.dm b/code/__DEFINES/flags/do_after.dm new file mode 100644 index 0000000000..26802736cf --- /dev/null +++ b/code/__DEFINES/flags/do_after.dm @@ -0,0 +1,32 @@ +/// Requires absolute stillness from the user +#define DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER (1<<0) +/// Requires absolute stillness from the target +#define DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET (1<<1) +/// Requires that the user is on a turf. +#define DO_AFTER_REQUIRES_USER_ON_TURF (1<<2) +/// Requires relative stillness to our target via dx and dy coordinate difference but only if both are spacedrifting. Specify DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY to say otherwise. +#define DO_AFTER_DISALLOW_MOVING_RELATIVE (1<<3) +/// Breaks if active hand item changes. Requires a tool be specified, otherwise defaults to active item +#define DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE (1<<4) +/// Breaks if the user has no free hands. If a tool is specified, allows that as well. +#define DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL (1<<5) +/// Do not display progressbar. +#define DO_AFTER_NO_PROGRESSBAR (1<<6) +/// Do not check do_after_coefficient() +#define DO_AFTER_NO_COEFFICIENT (1<<7) +/// For relative stillness, allow non spacedrift relative movement +#define DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY (1<<8) + +/// Ignores checks. +#define DO_AFTER_PROCEED "PROCEED" +/// Uses all other checks +#define DO_AFTER_CONTINUE "CONTINUE" +/// Breaks +#define DO_AFTER_STOP "STOP" + +/// Stage - initiating a do_after +#define DO_AFTER_STARTING 1 +/// Stage - main loop of a do_after +#define DO_AFTER_PROGRESSING 2 +/// Stage - Last check of a do_after +#define DO_AFTER_FINISHING 3 diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 4b488030a0..de9ada7560 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -1,3 +1,6 @@ +/// Checks if something is a BYOND object datatype rather than a primitive, or whatever's closest to one. +#define is_object_datatype(object) (object && !ispath(object) && !istext(object) && !isnum(object)) + // simple is_type and similar inline helpers #define in_range(source, user) (get_dist(source, user) <= 1 && (get_step(source, 0)?:z) == (get_step(user, 0)?:z)) diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index 1f3ae63940..699f39e79f 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -8,5 +8,68 @@ //ids #define MOVESPEED_ID_SANITY "mood_sanity" -#define MOVESPEED_ID_MOB_GRAB_STATE "mob_grab_state" +#define MOVESPEED_ID_MOB_WALK_RUN_CONFIG_SPEED "MOB_WALK_RUN" +#define MOVESPEED_ID_MOB_GRAB_STATE "MOB_GRAB_STATE" +#define MOVESPEED_ID_MOB_EQUIPMENT "MOB_EQUIPMENT" +#define MOVESPEED_ID_MOB_GRAVITY "MOB_GRAVITY" +#define MOVESPEED_ID_CONFIG_SPEEDMOD "MOB_CONFIG_MODIFIER" + +#define MOVESPEED_ID_SLIME_REAGENTMOD "SLIME_REAGENT_MODIFIER" +#define MOVESPEED_ID_SLIME_HEALTHMOD "SLIME_HEALTH_MODIFIER" +#define MOVESPEED_ID_SLIME_TEMPMOD "SLIME_TEMPERATURE_MODIFIER" + +#define MOVESPEED_ID_SLIME_STATUS "SLIME_STATUS" + +#define MOVESPEED_ID_TARANTULA_WEB "TARANTULA_WEB" + +#define MOVESPEED_ID_LIVING_TURF_SPEEDMOD "LIVING_TURF_SPEEDMOD" +#define MOVESPEED_ID_LIVING_LIMBLESS "LIVING_LIMBLESS" + +#define MOVESPEED_ID_CARBON_SOFTCRIT "CARBON_SOFTCRIT" +#define MOVESPEED_ID_CARBON_OLDSPEED "CARBON_DEPRECATED_SPEED" + +#define MOVESPEED_ID_DNA_VAULT "DNA_VAULT" + +#define MOVESPEED_ID_YELLOW_ORB "YELLOW_ORB" + +#define MOVESPEED_ID_TARFOOT "TARFOOT" + +#define MOVESPEED_ID_SEPIA "SEPIA" + +#define MOVESPEED_ID_MONKEY_REAGENT_SPEEDMOD "MONKEY_REAGENT_SPEEDMOD" +#define MOVESPEED_ID_MONKEY_TEMPERATURE_SPEEDMOD "MONKEY_TEMPERATURE_SPEEDMOD" +#define MOVESPEED_ID_MONKEY_HEALTH_SPEEDMOD "MONKEY_HEALTH_SPEEDMOD" + +#define MOVESPEED_ID_CHANGELING_MUSCLES "CHANGELING_MUSCLES" + +#define MOVESPEED_ID_SIMPLEMOB_VARSPEED "SIMPLEMOB_VARSPEED_MODIFIER" +#define MOVESPEED_ID_ADMIN_VAREDIT "ADMIN_VAREDIT_MODIFIER" + +#define MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD "PAI_SPACEWALK_MODIFIER" + +#define MOVESPEED_ID_SPECIES "SPECIES_SPEED_MOD" + +#define MOVESPEED_ID_PRONE_DRAGGING "PRONE_DRAG" +#define MOVESPEED_ID_HUMAN_CARRYING "HUMAN_CARRY" +#define MOVESPEED_ID_SHRINK_RAY "SHRUNKEN_SPEED_MODIFIER" + +#define MOVESPEED_ID_SLAUGHTER "SLAUGHTER" + +#define MOVESPEED_ID_CYBER_THRUSTER "CYBER_IMPLANT_THRUSTER" +#define MOVESPEED_ID_JETPACK "JETPACK" + +#define MOVESPEED_ID_MKULTRA "MKULTRA" + +#define MOVESPEED_ID_TASED_STATUS "TASED" +#define MOVESPEED_ID_ELECTROSTAFF "ELECTROSTAFF" + +#define MOVESPEED_ID_SHOVE "SHOVE" +#define MOVESPEED_ID_FAT "FAT" +#define MOVESPEED_ID_COLD "COLD" +#define MOVESPEED_ID_HUNGRY "HUNGRY" +#define MOVESPEED_ID_DAMAGE_SLOWDOWN "DAMAGE" +#define MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING "FLYING" + +#define MOVESPEED_ID_ACTIVE_BLOCK "ACTIVE_BLOCK" + #define MOVESPEED_ID_MOB_WALK_RUN "mob_walk_run" diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm new file mode 100644 index 0000000000..1bd67fbe02 --- /dev/null +++ b/code/__DEFINES/projectiles.dm @@ -0,0 +1,14 @@ +/// This atom should be ricocheted off of from its inherent properties using standard % chance handling. +#define PROJECTILE_RICOCHET_YES 1 +/// This atom should not be ricocheted off of from its inherent properties. +#define PROJECTILE_RICOCHET_NO 2 +/// This atom should prevent any kind of projectile ricochet from its inherent properties. +#define PROJECTILE_RICOCHET_PREVENT 3 +/// This atom should force a projectile ricochet from its inherent properties. +#define PROJECTILE_RICOCHET_FORCE 4 + +//bullet_act() return values +#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting. +#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting. +#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. diff --git a/code/__DEFINES/storage/volumetrics.dm b/code/__DEFINES/storage/volumetrics.dm index d5da40a722..c3f45976ce 100644 --- a/code/__DEFINES/storage/volumetrics.dm +++ b/code/__DEFINES/storage/volumetrics.dm @@ -24,7 +24,7 @@ GLOBAL_LIST_INIT(default_weight_class_to_volume, list( // Let's keep all of this in one place. given what we put above anyways.. // volume amount for items -#define ITEM_VOLUME_DISK DEFAULT_VOLUME_TINY +#define ITEM_VOLUME_DISK 1 // #define SAMPLE_VOLUME_AMOUNT 2 diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 9233a69108..01990ddc17 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -39,6 +39,8 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using #define SATURDAY "Sat" #define SUNDAY "Sun" +#define WEEKDAY2NUM(D) (D == SUNDAY ? 1 : D == MONDAY ? 2 : D == TUESDAY ? 3 : D == WEDNESDAY ? 4 : D == THURSDAY ? 5 : D == FRIDAY ? 6 : 7) //this looks ugly, but switch statements can't be used as vars, so *shrug + #define SECONDS *10 #define MINUTES SECONDS*60 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 5084cf11f9..f3f9f1e8b5 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -67,6 +67,8 @@ #define TRAIT_BLIND "blind" #define TRAIT_MUTE "mute" #define TRAIT_EMOTEMUTE "emotemute" +#define TRAIT_LOOC_MUTE "looc_mute" //Just like unconsciousness, it disables LOOC salt. +#define TRAIT_AOOC_MUTE "aooc_mute" //Same as above but for AOOC. #define TRAIT_DEAF "deaf" #define TRAIT_NEARSIGHT "nearsighted" #define TRAIT_FAT "fat" @@ -157,7 +159,6 @@ #define TRAIT_PASSTABLE "passtable" #define TRAIT_GIANT "giant" #define TRAIT_DWARF "dwarf" -#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good.... #define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance" #define TRAIT_AGEUSIA "ageusia" #define TRAIT_HEAVY_SLEEPER "heavy_sleeper" @@ -255,7 +256,7 @@ #define BOOK_TRAIT "granter (book)" // knowledge is power // unique trait sources, still defines -#define STATUE_MUTE "statue" +#define STATUE_TRAIT "statue" #define CLONING_POD_TRAIT "cloning-pod" #define VIRTUAL_REALITY_TRAIT "vr_trait" #define CHANGELING_DRAIN "drain" @@ -296,4 +297,7 @@ #define CLOWNOP_TRAIT "clown-op" #define MEGAFAUNA_TRAIT "megafauna" #define DEATHSQUAD_TRAIT "deathsquad" -#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name +/// This trait is added by the active directional block system. +#define ACTIVE_BLOCK_TRAIT "active_block" +/// This trait is added by the parry system. +#define ACTIVE_PARRY_TRAIT "active_parry" diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 9b877e8fb0..5fb4d05d42 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -104,7 +104,7 @@ GLOBAL_VAR_INIT(cmp_field, "name") var/a_sign = num2sign(initial(A.value) * -1) var/b_sign = num2sign(initial(B.value) * -1) - // Neutral traits go last. + // Neutral traits go last if(a_sign == 0) a_sign = 2 if(b_sign == 0) diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm new file mode 100644 index 0000000000..f1f483c345 --- /dev/null +++ b/code/__HELPERS/do_after.dm @@ -0,0 +1,323 @@ +/** + * Higher overhead "advanced" version of do_after. + * @params + * - atom/user is the atom doing the action or the "physical" user + * - delay is time in deciseconds + * - atom/target is the atom the action is being done to, defaults to user + * - do_after_flags see __DEFINES/flags/do_after.dm for details. + * - datum/callback/extra_checks - Every time this ticks, extra_checks() is invoked with (user, delay, target, time_left, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool). + * Stage can be DO_AFTER_STARTING, DO_AFTER_PROGRESSING, DO_AFTER_FINISHING + * If it returns DO_AFTER_STOP, this breaks. + * If it returns nothing, all other checks are done. + * If it returns DO_AFTER_PROCEED, all other checks are ignored. + * - required_mobility_flags is checked with CHECK_ALL_MOBILITY. Will immediately fail if the user isn't a mob. + * - requried_combat_flags is checked with CHECK_MULTIPLE_BITFIELDS. Will immediately fail if the user isn't a mob. + * - mob/living/mob_redirect - advanced option: If this is specified, movement and mobility/combat flag checks will use this instead of user. Progressbars will also go to this. + * - obj/item/tool - The tool we're using. See do_after flags for details. + */ +#define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, world.time - starttime, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool) +#define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || (required_mobility_flags && !CHECK_ALL_MOBILITY(living_user, required_mobility_flags)) || (required_combat_flags && !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags)))) +#define TIMELEFT (world.time - starttime) +/proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool) + // CHECK AND SET VARIABLES + if(!user) + return FALSE + if(!target) + target = user + if((user.loc == null) || (target.loc == null)) + return FALSE + var/mob/living/living_user = mob_redirect + if(!living_user && isliving(user)) + living_user = user + var/stage = DO_AFTER_STARTING + var/startlocuser = user.loc + var/startloctarget = target.loc + var/turf/userturf = get_turf(user) + var/turf/targetturf = get_turf(target) + if(!userturf || !targetturf) + return FALSE + if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) + return FALSE + var/starttime = world.time + var/endtime = world.time + delay + var/obj/item/initially_held_item = mob_redirect?.get_active_held_item() + if(!(do_after_flags & DO_AFTER_NO_COEFFICIENT) && living_user) + delay *= living_user.do_after_coefficent() + var/atom/movable/AM_user = ismovable(user) && user + var/drifting = AM_user?.Process_Spacemove(NONE) && AM_user.inertia_dir + var/initial_dx = targetturf.x - userturf.x + var/initial_dy = targetturf.y - userturf.y + var/dx = initial_dx + var/dy = initial_dy + // DO OUR STARTING CHECKS + var/cb_return + INVOKE_CALLBACK + if(cb_return == DO_AFTER_STOP) + return FALSE + else if(cb_return != DO_AFTER_PROCEED) + if(CHECK_FLAG_FAILURE) + return FALSE + // SETUP LOOP + var/datum/progressbar/progbar + if(living_user) + if(!(do_after_flags & DO_AFTER_NO_PROGRESSBAR)) + progbar = new(living_user, delay, target) + // MAIN LOOP + . = TRUE + if(!delay) + return + var/obj/item/held + var/locchanged + var/ctu + var/ctt + while(world.time < endtime) + stoplag(1) + progbar?.update(TIMELEFT) + if(QDELETED(user) || QDELETED(target) || (user.loc == null) || (target.loc == null)) + . = FALSE + break + INVOKE_CALLBACK + if(cb_return == DO_AFTER_STOP) + . = FALSE + break + else if(cb_return == DO_AFTER_PROCEED) + continue + // otherwise, go through our normal checks. + if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget))) + . = FALSE + break + else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE) + ctu = get_turf(user) + ctt = get_turf(target) + locchanged = (userturf != ctu) || (targetturf != ctt) + userturf = ctu + targetturf = ctt + dx = targetturf.x - userturf.x + dy = targetturf.y - userturf.y + if((dx != initial_dx) || (dy != initial_dy)) + . = FALSE + break + if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) + . = FALSE + break + if(!AM_user.inertia_dir) + drifting = FALSE + if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) + return FALSE + if(CHECK_FLAG_FAILURE) + . = FALSE + break + held = living_user?.get_active_held_item() + if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item))) + . = FALSE + break + if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes()))) + . = FALSE + break + + // CLEANUP + qdel(progbar) + // If we failed, just return. + if(!.) + return FALSE + // DO FINISHING CHECKS + if(QDELETED(user) || QDELETED(target)) + return FALSE + INVOKE_CALLBACK + if(cb_return == DO_AFTER_STOP) + return FALSE + else if(cb_return == DO_AFTER_PROCEED) + return TRUE + if(CHECK_FLAG_FAILURE) + return FALSE + if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget))) + return FALSE + else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE) + ctu = get_turf(user) + ctt = get_turf(target) + locchanged = (userturf != ctu) || (targetturf != ctt) + userturf = ctu + targetturf = ctt + dx = targetturf.x - userturf.x + dy = targetturf.y - userturf.y + if((dx != initial_dx) || (dy != initial_dy)) + return FALSE + if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY)) + return FALSE + if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc)) + return FALSE + held = living_user?.get_active_held_item() + if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item))) + return FALSE + if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes()))) + return FALSE + +#undef INVOKE_CALLBACK +#undef CHECK_FLAG_FAILURE + +/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = FALSE, resume_time = 0 SECONDS) + if(!user || !target) + return 0 + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/target_loc = target.loc + + var/holding = user.get_active_held_item() + var/datum/progressbar/progbar + if (progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + . = 1 + while (world.time + resume_time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime + resume_time) + if(QDELETED(user) || QDELETED(target)) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) + . = 0 + break + if (progress) + qdel(progbar) + + +//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action +/mob/proc/break_do_after_checks(list/checked_health, check_clicks) + if(check_clicks && next_move > world.time) + return FALSE + return TRUE + +//pass a list in the format list("health" = mob's health var) to check health during this +/mob/living/break_do_after_checks(list/checked_health, check_clicks) + if(islist(checked_health)) + if(health < checked_health["health"]) + return FALSE + checked_health["health"] = health + return ..() + +/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE), resume_time = 0 SECONDS) + if(!user) + return 0 + var/atom/Tloc = null + if(target && !isturf(target)) + Tloc = target.loc + + var/atom/Uloc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/holding = user.get_active_held_item() + + var/holdingnull = 1 //User's hand started out empty, check for an empty hand + if(holding) + holdingnull = 0 //Users hand started holding something, check to see if it's still holding that + + delay *= user.do_after_coefficent() + + var/datum/progressbar/progbar + if (progress) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + . = 1 + var/mob/living/L = isliving(user) && user //evals to last thing eval'd + while (world.time + resume_time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime + resume_time) + + if(drifting && !user.inertia_dir) + drifting = 0 + Uloc = user.loc + + if(L && !CHECK_ALL_MOBILITY(L, required_mobility_flags)) + . = 0 + break + + if(QDELETED(user) || user.stat || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) + . = 0 + break + + if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc)) + if((Uloc != Tloc || Tloc != user) && !drifting) + . = 0 + break + + if(needhand) + //This might seem like an odd check, but you can still need a hand even when it's empty + //i.e the hand is used to pull some item/tool out of the construction + if(!holdingnull) + if(!holding) + . = 0 + break + if(user.get_active_held_item() != holding) + . = 0 + break + if (progress) + qdel(progbar) + +/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 + . = 1 + return + +/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks) + if(!user || !targets) + return 0 + if(!islist(targets)) + targets = list(targets) + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/list/originalloc = list() + for(var/atom/target in targets) + originalloc[target] = target.loc + + var/holding = user.get_active_held_item() + var/datum/progressbar/progbar + if(progress) + progbar = new(user, time, targets[1]) + + var/endtime = world.time + time + var/starttime = world.time + . = 1 + mainloop: + while(world.time < endtime) + stoplag(1) + if(progress) + progbar.update(world.time - starttime) + if(QDELETED(user) || !targets) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + for(var/atom/target in targets) + if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) + . = 0 + break mainloop + if(progbar) + qdel(progbar) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index e62bcc6738..91826ee314 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -320,173 +320,6 @@ GLOBAL_LIST_EMPTY(species_list) else return "unknown" -/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = FALSE, resume_time = 0 SECONDS) - if(!user || !target) - return 0 - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/target_loc = target.loc - - var/holding = user.get_active_held_item() - var/datum/progressbar/progbar - if (progress) - progbar = new(user, time, target) - - var/endtime = world.time+time - var/starttime = world.time - . = 1 - while (world.time + resume_time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime + resume_time) - if(QDELETED(user) || QDELETED(target)) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - if (progress) - qdel(progbar) - - -//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action -/mob/proc/break_do_after_checks(list/checked_health, check_clicks) - if(check_clicks && next_move > world.time) - return FALSE - return TRUE - -//pass a list in the format list("health" = mob's health var) to check health during this -/mob/living/break_do_after_checks(list/checked_health, check_clicks) - if(islist(checked_health)) - if(health < checked_health["health"]) - return FALSE - checked_health["health"] = health - return ..() - -/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE), resume_time = 0 SECONDS) - if(!user) - return 0 - var/atom/Tloc = null - if(target && !isturf(target)) - Tloc = target.loc - - var/atom/Uloc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/holding = user.get_active_held_item() - - var/holdingnull = 1 //User's hand started out empty, check for an empty hand - if(holding) - holdingnull = 0 //Users hand started holding something, check to see if it's still holding that - - delay *= user.do_after_coefficent() - - var/datum/progressbar/progbar - if (progress) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - . = 1 - var/mob/living/L = isliving(user) && user //evals to last thing eval'd - while (world.time + resume_time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime + resume_time) - - if(drifting && !user.inertia_dir) - drifting = 0 - Uloc = user.loc - - if(L && !CHECK_ALL_MOBILITY(L, required_mobility_flags)) - . = 0 - break - - if(QDELETED(user) || user.stat || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - - if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc)) - if((Uloc != Tloc || Tloc != user) && !drifting) - . = 0 - break - - if(needhand) - //This might seem like an odd check, but you can still need a hand even when it's empty - //i.e the hand is used to pull some item/tool out of the construction - if(!holdingnull) - if(!holding) - . = 0 - break - if(user.get_active_held_item() != holding) - . = 0 - break - if (progress) - qdel(progbar) - -/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 - . = 1 - return - -/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks) - if(!user || !targets) - return 0 - if(!islist(targets)) - targets = list(targets) - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/list/originalloc = list() - for(var/atom/target in targets) - originalloc[target] = target.loc - - var/holding = user.get_active_held_item() - var/datum/progressbar/progbar - if(progress) - progbar = new(user, time, targets[1]) - - var/endtime = world.time + time - var/starttime = world.time - . = 1 - mainloop: - while(world.time < endtime) - stoplag(1) - if(progress) - progbar.update(world.time - starttime) - if(QDELETED(user) || !targets) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - for(var/atom/target in targets) - if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break mainloop - if(progbar) - qdel(progbar) - /proc/is_species(A, species_datum) . = FALSE if(ishuman(A)) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index e4debe6fdb..c3004a4501 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -49,6 +49,8 @@ GLOBAL_LIST_INIT(bitfields, list( "DROPDEL" = DROPDEL, "NOBLUDGEON" = NOBLUDGEON, "ABSTRACT" = ABSTRACT, + "ITEM_CAN_BLOCK" = ITEM_CAN_BLOCK, + "ITEM_CAN_PARRY" = ITEM_CAN_PARRY ), "admin_flags" = list( "BUILDMODE" = R_BUILDMODE, @@ -123,6 +125,7 @@ GLOBAL_LIST_INIT(bitfields, list( "UNUSED_RESERVATION_TURF_1" = UNUSED_RESERVATION_TURF_1, "CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1, "HEAR_1" = HEAR_1, + "DEFAULT_RICOCHET_1" = DEFAULT_RICOCHET_1, "CONDUCT_1" = CONDUCT_1, "NO_LAVA_GEN_1" = NO_LAVA_GEN_1, "NODECONSTRUCT_1" = NODECONSTRUCT_1, @@ -137,10 +140,6 @@ GLOBAL_LIST_INIT(bitfields, list( "BLOCK_FACE_ATOM_1" = BLOCK_FACE_ATOM_1, "PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1 ), - "flags_ricochet" = list( - "RICOCHET_SHINY" = RICOCHET_SHINY, - "RICOCHET_HARD" = RICOCHET_HARD - ), "clothing_flags" = list( "LAVAPROTECT" = LAVAPROTECT, "STOPSPRESSUREDAMAGE" = STOPSPRESSUREDAMAGE, diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 357f317450..ecbcb6ec01 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -17,6 +17,9 @@ // eg: 10*0.5 = 5 deciseconds of delay // DOES NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK +/mob/proc/timeToNextMove() + return max(0, next_move - world.time) + /mob/proc/changeNext_move(num) next_move = world.time + ((num+next_move_adjust)*next_move_modifier) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index b2d74844cf..106db7f40a 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -7,17 +7,17 @@ *and lastly *afterattack. The return value does not matter. */ -/obj/item/proc/melee_attack_chain(mob/user, atom/target, params) +/obj/item/proc/melee_attack_chain(mob/user, atom/target, params, flags, damage_multiplier = 1) if(isliving(user)) var/mob/living/L = user - if(!CHECK_MOBILITY(L, MOBILITY_USE)) + if(!CHECK_MOBILITY(L, MOBILITY_USE) && !(flags & ATTACKCHAIN_PARRY_COUNTERATTACK)) to_chat(L, "You are unable to swing [src] right now!") return if(tool_behaviour && target.tool_act(user, src, tool_behaviour)) return if(pre_attack(target, user, params)) return - if(target.attackby(src,user, params)) + if(target.attackby(src, user, params, flags, damage_multiplier)) return if(QDELETED(src) || QDELETED(target)) return @@ -52,15 +52,15 @@ /obj/attackby(obj/item/I, mob/living/user, params) return ..() || ((obj_flags & CAN_BE_HIT) && I.attack_obj(src, user)) -/mob/living/attackby(obj/item/I, mob/living/user, params) +/mob/living/attackby(obj/item/I, mob/living/user, params, attackchain_flags, damage_multiplier) if(..()) return TRUE I.attack_delay_done = FALSE //Should be set TRUE in pre_attacked_by() - . = I.attack(src, user) + . = I.attack(src, user, attackchain_flags, damage_multiplier) if(!I.attack_delay_done) //Otherwise, pre_attacked_by() should handle it. user.changeNext_move(I.click_delay) -/obj/item/proc/attack(mob/living/M, mob/living/user) +/obj/item/proc/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_ITEM_NO_ATTACK) return SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user) @@ -79,7 +79,7 @@ M.lastattackerckey = user.ckey user.do_attack_animation(M) - M.attacked_by(src, user) + M.attacked_by(src, user, attackchain_flags, damage_multiplier) log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") add_fingerprint(user) @@ -104,8 +104,8 @@ /atom/movable/proc/attacked_by() return -/obj/attacked_by(obj/item/I, mob/living/user) - var/totitemdamage = I.force +/obj/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) + var/totitemdamage = I.force * damage_multiplier var/bad_trait var/stamloss = user.getStaminaLoss() @@ -134,10 +134,12 @@ take_damage(totitemdamage, I.damtype, "melee", 1) return TRUE -/mob/living/attacked_by(obj/item/I, mob/living/user) - var/totitemdamage = pre_attacked_by(I, user) - if((user != src) && mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, null, null) & BLOCK_SUCCESS) +/mob/living/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) + var/list/block_return = list() + var/totitemdamage = pre_attacked_by(I, user) * damage_multiplier + if((user != src) && mob_run_block(I, totitemdamage, "the [I.name]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, null, block_return) & BLOCK_SUCCESS) return FALSE + totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) send_item_attack_message(I, user, null, totitemdamage) I.do_stagger_action(src, user, totitemdamage) if(I.force) @@ -151,7 +153,7 @@ user.add_mob_blood(src) return TRUE //successful attack -/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user) +/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(I.force < force_threshold || I.damtype == STAMINA) playsound(loc, 'sound/weapons/tap.ogg', I.get_clamped_volume(), 1, -1) user.changeNext_move(I.click_delay) //pre_attacked_by not called @@ -211,8 +213,8 @@ var/message_verb = "attacked" if(I.attack_verb && I.attack_verb.len) message_verb = "[pick(I.attack_verb)]" - if(current_force < I.force * INEFFICIENT_ATTACK_MSG_THRESHOLD) - message_verb = "inefficiently [message_verb]" + if(current_force < I.force * FEEBLE_ATTACK_MSG_THRESHOLD) + message_verb = "[pick("feebly", "limply", "saplessly")] [message_verb]" else if(!I.force) return var/message_hit_area = "" diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index bb37129b95..cd8612fd19 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -117,6 +117,11 @@ paralysis_type = "legs" resilience = TRAUMA_RESILIENCE_ABSOLUTE +/datum/brain_trauma/severe/paralysis/spinesnapped + random_gain = FALSE + paralysis_type = "legs" + resilience = TRAUMA_RESILIENCE_LOBOTOMY + /datum/brain_trauma/severe/narcolepsy name = "Narcolepsy" desc = "Patient may involuntarily fall asleep during normal activities." diff --git a/code/datums/components/combat_mode.dm b/code/datums/components/combat_mode.dm index b53d407162..b9952e9133 100644 --- a/code/datums/components/combat_mode.dm +++ b/code/datums/components/combat_mode.dm @@ -119,6 +119,8 @@ if(hud_icon) hud_icon.combat_on = FALSE hud_icon.update_icon() + source.stop_active_blocking() + source.end_parry_sequence() ///Changes the user direction to (try) keep match the pointer. /datum/component/combat_mode/proc/on_move(atom/movable/source, dir, atom/oldloc, forced) diff --git a/code/datums/components/crafting/guncrafting.dm b/code/datums/components/crafting/guncrafting.dm index f332d53fc8..4626ef69d1 100644 --- a/code/datums/components/crafting/guncrafting.dm +++ b/code/datums/components/crafting/guncrafting.dm @@ -35,17 +35,34 @@ k// PARTS // desc = "A twenty bore shotgun barrel." icon_state = "barrel_shotgun" +/obj/item/weaponcrafting/improvised_parts/barrel_pistol + name = "pistol barrel" + desc = "A pipe with a small diameter and some holes finely cut into it. It fits .32 ACP bullets. Probably." + icon_state = "barrel_pistol" + w_class = WEIGHT_CLASS_SMALL + // RECEIVERS /obj/item/weaponcrafting/improvised_parts/rifle_receiver name = "bolt action receiver" - desc = "A crudely constructed receiver to create an improvised bolt-action breechloaded rifle." + desc = "A crudely constructed receiver to create an improvised bolt-action breechloaded rifle. It's generic enough to modify to create other rifles, potentially." icon_state = "receiver_rifle" w_class = WEIGHT_CLASS_SMALL +/obj/item/weaponcrafting/improvised_parts/pistol_receiver + name = "pistol receiver" + desc = "A receiver to connect house and connects all the parts to make an improvised pistol." + icon_state = "receiver_pistol" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/weaponcrafting/improvised_parts/laser_receiver + name = "energy emitter assembly" + desc = "A mixture of components haphazardly wired together to form an energy emitter." + icon_state = "laser_assembly" + /obj/item/weaponcrafting/improvised_parts/shotgun_receiver name = "break-action assembly" - desc = "An improvised receiver to create a break-action breechloaded shotgun." + desc = "An improvised receiver to create a break-action breechloaded shotgun. Parts of this are still useful if you want to make another type of shotgun, however." icon_state = "receiver_shotgun" w_class = WEIGHT_CLASS_SMALL @@ -62,3 +79,14 @@ k// PARTS // desc = "A crudely fashioned wooden body to help keep higher calibre improvised weapons from blowing themselves apart." icon_state = "wooden_body" +/obj/item/weaponcrafting/improvised_parts/wooden_grip + name = "wooden pistol grip" + desc = "A nice wooden grip hollowed out for pistol magazines." + icon_state = "wooden_pistolgrip" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/weaponcrafting/improvised_parts/makeshift_lens + name = "makeshift focusing lens" + desc = "A properly made lens made with actual glassworking tools would perform much better, but this will have to do." + icon_state = "focusing_lens" + w_class = WEIGHT_CLASS_TINY diff --git a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm index 7d608a55b2..9d712ef059 100644 --- a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm +++ b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm @@ -6,7 +6,7 @@ tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER) time = 50 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_OTHER /datum/crafting_recipe/pin_removal/check_requirements(mob/user, list/collected_requirements) var/obj/item/gun/G = collected_requirements[/obj/item/gun][1] @@ -22,7 +22,7 @@ /obj/item/shield/riot = 1) time = 40 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/strobeshield/New() ..() @@ -38,7 +38,7 @@ tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER) time = 100 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/spear name = "Spear" @@ -49,7 +49,7 @@ parts = list(/obj/item/shard = 1) time = 40 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/stunprod name = "Stunprod" @@ -59,7 +59,7 @@ /obj/item/assembly/igniter = 1) time = 40 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/teleprod name = "Teleprod" @@ -70,7 +70,7 @@ /obj/item/stack/ore/bluespace_crystal = 1) time = 40 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/bola name = "Bola" @@ -88,7 +88,7 @@ /obj/item/stack/sheet/metal = 1) time = 40 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/tailwhip name = "Liz O' Nine Tails" @@ -97,7 +97,7 @@ /obj/item/stack/cable_coil = 1) time = 40 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/catwhip name = "Cat O' Nine Tails" @@ -106,7 +106,7 @@ /obj/item/stack/cable_coil = 1) time = 40 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE /datum/crafting_recipe/chainsaw name = "Chainsaw" @@ -117,7 +117,7 @@ tools = list(TOOL_WELDER) time = 50 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE ////////////////// ///BOMB CRAFTING// @@ -134,7 +134,7 @@ parts = list(/obj/item/stock_parts/matter_bin = 1, /obj/item/grenade/chem_grenade = 2) time = 30 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_OTHER /datum/crafting_recipe/chemical_payload2 name = "Chemical Payload (Gibtonite)" @@ -147,7 +147,7 @@ parts = list(/obj/item/stock_parts/matter_bin = 1, /obj/item/grenade/chem_grenade = 2) time = 50 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_OTHER /datum/crafting_recipe/molotov name = "Molotov" @@ -169,7 +169,7 @@ parts = list(/obj/item/reagent_containers/food/drinks/soda_cans = 1) time = 15 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_OTHER /datum/crafting_recipe/lance name = "Explosive Lance (Grenade)" @@ -180,7 +180,7 @@ /obj/item/grenade = 1) time = 15 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE ////////////////// ///GUNS CRAFTING// @@ -276,6 +276,49 @@ category = CAT_WEAPONRY subcategory = CAT_WEAPON +/datum/crafting_recipe/ipistol + name = "Improvised Pistol (.32)" + result = /obj/item/gun/ballistic/automatic/pistol/improvised/nomag + reqs = list(/obj/item/weaponcrafting/improvised_parts/barrel_pistol = 1, + /obj/item/weaponcrafting/improvised_parts/pistol_receiver = 1, + /obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1, + /obj/item/weaponcrafting/improvised_parts/wooden_grip = 1, + /obj/item/stack/sheet/plastic = 15, + /obj/item/stack/sheet/plasteel = 1) + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER, TOOL_WIRECUTTER) + time = 100 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + +/datum/crafting_recipe/ilaser + name = "Improvised Energy Gun" + result = /obj/item/gun/energy/e_gun/old/improvised + reqs = list(/obj/item/weaponcrafting/improvised_parts/laser_receiver = 1, + /obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1, + /obj/item/weaponcrafting/improvised_parts/makeshift_lens = 1, + /obj/item/stock_parts/cell = 1, + /obj/item/stack/sheet/metal = 10, + /obj/item/stack/sheet/plasteel = 5, + /obj/item/stack/cable_coil = 10) + tools = list(TOOL_SCREWDRIVER) + time = 100 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + +/datum/crafting_recipe/ilaser/upgraded + name = "Improvised Energy Gun Upgrade" + result = /obj/item/gun/energy/e_gun/old/improvised/upgraded + reqs = list(/obj/item/gun/energy/e_gun/old/improvised = 1, + /obj/item/glasswork/glass_base/lens = 1, + /obj/item/stock_parts/capacitor/quadratic = 2, + /obj/item/stock_parts/micro_laser/ultra = 1, + /obj/item/stock_parts/cell/bluespace = 1, + /obj/item/stack/cable_coil = 5) + tools = list(TOOL_SCREWDRIVER, TOOL_MULTITOOL) + time = 100 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + ////////////////// ///AMMO CRAFTING// ////////////////// @@ -399,6 +442,17 @@ category = CAT_WEAPONRY subcategory = CAT_AMMO +/datum/crafting_recipe/m32acp + name = ".32ACP Empty Magazine" + result = /obj/item/ammo_box/magazine/m32acp/empty + reqs = list(/obj/item/stack/sheet/metal = 3, + /obj/item/stack/sheet/plasteel = 1, + /obj/item/stack/packageWrap = 1) + tools = list(TOOL_WELDER,TOOL_SCREWDRIVER) + time = 5 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + //////////////////// // PARTS CRAFTING // //////////////////// @@ -423,13 +477,24 @@ category = CAT_WEAPONRY subcategory = CAT_PARTS +/datum/crafting_recipe/pistol_barrel + name = "Improvised Pistol Barrel" + result = /obj/item/weaponcrafting/improvised_parts/barrel_pistol + reqs = list(/obj/item/pipe = 1, + /obj/item/stack/sheet/plasteel = 1) + tools = list(TOOL_WELDER,TOOL_SAW) + time = 150 + category = CAT_WEAPONRY + subcategory = CAT_PARTS + // RECEIVERS /datum/crafting_recipe/rifle_receiver name = "Improvised Rifle Receiver" result = /obj/item/weaponcrafting/improvised_parts/rifle_receiver - reqs = list(/obj/item/stack/sheet/metal = 20) - tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) // Rifle is the easiest to craft and can be made at an autolathe, this is a very light kick in the shin for dual-wielding ishotguns. + reqs = list(/obj/item/stack/sheet/metal = 10, + /obj/item/stack/sheet/plasteel = 1) + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) time = 50 category = CAT_WEAPONRY subcategory = CAT_PARTS @@ -439,11 +504,33 @@ result = /obj/item/weaponcrafting/improvised_parts/shotgun_receiver reqs = list(/obj/item/stack/sheet/metal = 10, /obj/item/stack/sheet/plasteel = 1) - tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) // Increased cost is to stop dual-wield alpha striking. ishotgun is a rvolver and can be duel-wielded + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) // Dual wielding has been removed, plasteel is a soft timesink to obtain for most to make mass production harder. time = 50 category = CAT_WEAPONRY subcategory = CAT_PARTS +/datum/crafting_recipe/pistol_receiver + name = "Improvised Pistol Receiver" + result = /obj/item/weaponcrafting/improvised_parts/pistol_receiver + reqs = list(/obj/item/stack/sheet/metal = 5, + /obj/item/stack/sheet/plasteel = 1) + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER, TOOL_SAW) + time = 50 + category = CAT_WEAPONRY + subcategory = CAT_PARTS + +/datum/crafting_recipe/laser_receiver + name = "Energy Weapon Assembly" + result = /obj/item/weaponcrafting/improvised_parts/laser_receiver + reqs = list(/obj/item/stack/sheet/metal = 10, + /obj/item/stock_parts/capacitor = 2, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/assembly/prox_sensor = 1) + tools = list(TOOL_SCREWDRIVER, TOOL_MULTITOOL, TOOL_WELDER) // Prox sensor and multitool for the circuit board, welder for extremely ghetto soldering. + time = 150 + category = CAT_WEAPONRY + subcategory = CAT_PARTS + // MISC /datum/crafting_recipe/trigger_assembly @@ -455,3 +542,13 @@ time = 150 category = CAT_WEAPONRY subcategory = CAT_PARTS + +/datum/crafting_recipe/makeshift_lens + name = "Makeshift Lens" + result = /obj/item/weaponcrafting/improvised_parts/makeshift_lens + reqs = list(/obj/item/stack/sheet/metal = 1, + /obj/item/stack/sheet/glass = 2) + tools = list(TOOL_WELDER) // Glassmaking lets you make non-makeshift lenses. + time = 50 + category = CAT_WEAPONRY + subcategory = CAT_PARTS diff --git a/code/datums/components/swarming.dm b/code/datums/components/swarming.dm index 64844c4c32..e840788766 100644 --- a/code/datums/components/swarming.dm +++ b/code/datums/components/swarming.dm @@ -11,6 +11,17 @@ RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, .proc/join_swarm) RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, .proc/leave_swarm) +/datum/component/swarming/Destroy() + if(is_swarming) + for(var/A in swarm_members) + var/datum/component/swarming/other_swarm = A + other_swarm.swarm_members -= src + swarm_members -= other_swarm + if(!length(other_swarm.swarm_members)) + other_swarm.unswarm() + unswarm() + return ..() + /datum/component/swarming/proc/join_swarm(datum/source, atom/movable/AM) var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming) if(!other_swarm) diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index 28b1447305..bd12ecc092 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -354,7 +354,7 @@ playsound(user, 'sound/effects/blobattack.ogg', 60, TRUE) playsound(user, 'sound/effects/splat.ogg', 70, TRUE) user.emote("scream") - user.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic) // oopsie indeed! + user.gain_trauma(/datum/brain_trauma/severe/paralysis/spinesnapped) // oopsie indeed! shake_camera(user, 7, 7) user.overlay_fullscreen("flash", /obj/screen/fullscreen/flash) user.clear_fullscreen("flash", 4.5) diff --git a/code/datums/elements/flavor_text.dm b/code/datums/elements/flavor_text.dm index f44215d9ac..92251861ed 100644 --- a/code/datums/elements/flavor_text.dm +++ b/code/datums/elements/flavor_text.dm @@ -117,9 +117,9 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code return FALSE var/lower_name = lowertext(flavor_name) - var/new_text = stripped_multiline_input(user, "Set the [lower_name] displayed on 'examine'. [addendum]", flavor_name, texts_by_atom[usr], max_len, TRUE) + var/new_text = stripped_multiline_input(user, "Set the [lower_name] displayed on 'examine'. [addendum]", flavor_name, html_decode(texts_by_atom[usr]), max_len, TRUE) if(!isnull(new_text) && (user in texts_by_atom)) - texts_by_atom[user] = html_decode(new_text) + texts_by_atom[user] = new_text to_chat(src, "Your [lower_name] has been updated.") return TRUE return FALSE diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm index 0877e4021a..8241f685d7 100644 --- a/code/datums/martial/_martial.dm +++ b/code/datums/martial/_martial.dm @@ -10,6 +10,10 @@ var/help_verb var/pacifism_check = TRUE //are the martial arts combos/attacks unable to be used by pacifist. var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts + /// Can we be used to unarmed parry? + var/can_martial_parry = FALSE + /// Set this variable to something not null, this'll be the preferred unarmed parry in most cases if [can_martial_parry] is TRUE. YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING! + var/datum/block_parry_data/block_parry_data var/pugilist = FALSE /datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) @@ -91,4 +95,4 @@ ///Gets called when a projectile hits the owner. Returning anything other than BULLET_ACT_HIT will stop the projectile from hitting the mob. /datum/martial_art/proc/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone) - return BULLET_ACT_HIT \ No newline at end of file + return BULLET_ACT_HIT diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm index 848fdc6a41..e3c7726d61 100644 --- a/code/datums/martial/boxing.dm +++ b/code/datums/martial/boxing.dm @@ -66,9 +66,9 @@ return /obj/item/clothing/gloves/boxing/dropped(mob/user) + . = ..() if(!ishuman(user)) return var/mob/living/carbon/human/H = user if(H.get_item_by_slot(SLOT_GLOVES) == src) style.remove(H) - return diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm index f054867de4..50438d9d8d 100644 --- a/code/datums/martial/krav_maga.dm +++ b/code/datums/martial/krav_maga.dm @@ -203,6 +203,7 @@ style.teach(H,1) /obj/item/clothing/gloves/krav_maga/dropped(mob/user) + . = ..() if(!ishuman(user)) return var/mob/living/carbon/human/H = user diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index f7923d029f..87fcf78964 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -480,12 +480,12 @@ return /obj/item/storage/belt/champion/wrestling/dropped(mob/user) + . = ..() if(!ishuman(user)) return var/mob/living/carbon/human/H = user if(H.get_item_by_slot(SLOT_BELT) == src) style.remove(H) - return //Subtype of wrestling, reserved for the wrestling belts found in the holodeck /datum/martial_art/wrestling/holodeck diff --git a/code/game/atoms.dm b/code/game/atoms.dm index ff22638b57..5875334ed6 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -6,14 +6,6 @@ var/flags_1 = NONE var/interaction_flags_atom = NONE - - var/flags_ricochet = NONE - - ///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this - var/ricochet_chance_mod = 1 - ///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom - var/ricochet_damage_mod = 0.33 - var/datum/reagents/reagents = null //This atom's HUD (med/sec, etc) images. Associative list. @@ -149,20 +141,18 @@ return ..() -/atom/proc/handle_ricochet(obj/item/projectile/P) - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - var/a_incidence_s = abs(incidence_s) - if(a_incidence_s > 90 && a_incidence_s < 270) - return FALSE - if((P.flag in list("bullet", "bomb")) && P.ricochet_incidence_leeway) - if((a_incidence_s < 90 && a_incidence_s < 90 - P.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > P.ricochet_incidence_leeway)) - return - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.setAngle(new_angle_s) - return TRUE +/** + * Checks if a projectile should ricochet off of us. Projectiles get final say. + * [__DEFINES/projectiles.dm] for return values. + */ +/atom/proc/check_projectile_ricochet(obj/item/projectile/P) + return (flags_1 & DEFAULT_RICOCHET_1)? PROJECTILE_RICOCHET_YES : PROJECTILE_RICOCHET_NO + +/** + * Handle a projectile ricochet. Return TRUE if we did something to the projectile like reflecting it/whatnot. + */ +/atom/proc/handle_projectile_ricochet(obj/item/projectile/P) + return FALSE /atom/proc/CanPass(atom/movable/mover, turf/target) return !density diff --git a/code/game/gamemodes/gangs/dominator.dm b/code/game/gamemodes/gangs/dominator.dm index a375c4f8b5..db145ffacc 100644 --- a/code/game/gamemodes/gangs/dominator.dm +++ b/code/game/gamemodes/gangs/dominator.dm @@ -145,7 +145,7 @@ new /obj/item/stack/sheet/plasteel(src.loc) qdel(src) -/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user) +/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) add_fingerprint(user) return ..() @@ -240,4 +240,4 @@ #undef DOM_BLOCKED_SPAM_CAP #undef DOM_REQUIRED_TURFS -#undef DOM_HULK_HITS_REQUIRED \ No newline at end of file +#undef DOM_HULK_HITS_REQUIRED diff --git a/code/game/machinery/porta_turret/portable_turret_cover.dm b/code/game/machinery/porta_turret/portable_turret_cover.dm index 2403f903a9..7fdb9b38be 100644 --- a/code/game/machinery/porta_turret/portable_turret_cover.dm +++ b/code/game/machinery/porta_turret/portable_turret_cover.dm @@ -70,7 +70,7 @@ else return ..() -/obj/machinery/porta_turret_cover/attacked_by(obj/item/I, mob/user) +/obj/machinery/porta_turret_cover/attacked_by(obj/item/I, mob/user, attackchain_flags = NONE, damage_multiplier = 1) return parent_turret.attacked_by(I, user) /obj/machinery/porta_turret_cover/attack_alien(mob/living/carbon/alien/humanoid/user) diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm index 56bb8cceda..395ac810f4 100644 --- a/code/game/mecha/mecha_defense.dm +++ b/code/game/mecha/mecha_defense.dm @@ -282,7 +282,7 @@ else return ..() -/obj/mecha/attacked_by(obj/item/I, mob/living/user) +/obj/mecha/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) mecha_log_message("Attacked by [I]. Attacker - [user]") return ..() diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 9893e9c7d3..5b2006b6f3 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -611,13 +611,13 @@ /obj/item/clothing/mask/breath = 5, /obj/item/clothing/mask/breath/medical = 1 ) - + /obj/effect/spawner/lootdrop/welder_tools/no_turf spawn_on_turf = FALSE /obj/effect/spawner/lootdrop/low_tools/no_turf spawn_on_turf = FALSE - + /obj/effect/spawner/lootdrop/breathing_tanks/no_turf spawn_on_turf = FALSE @@ -644,3 +644,90 @@ /obj/effect/spawner/lootdrop/glowstick/no_turf spawn_on_turf = FALSE + +// Random Parts + +/obj/effect/spawner/lootdrop/stock_parts + name = "random stock parts spawner" + lootcount = 1 + loot = list( + /obj/item/stock_parts/capacitor, + /obj/item/stock_parts/scanning_module, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/micro_laser, + /obj/item/stock_parts/matter_bin, + /obj/item/stock_parts/cell + ) + +// Random Weapon Parts + +/obj/effect/spawner/lootdrop/weapon_parts + name = "random weapon parts spawner 50%" + lootcount = 1 + spawn_on_turf = FALSE + loot = list("" = 50, + /obj/item/weaponcrafting/improvised_parts/barrel_rifle = 10, + /obj/item/weaponcrafting/improvised_parts/barrel_shotgun = 5, + /obj/item/weaponcrafting/improvised_parts/barrel_pistol = 5, + /obj/item/weaponcrafting/improvised_parts/rifle_receiver = 10, + /obj/item/weaponcrafting/improvised_parts/shotgun_receiver = 3, + /obj/item/weaponcrafting/improvised_parts/pistol_receiver = 3, + /obj/item/weaponcrafting/improvised_parts/laser_receiver = 1, + /obj/item/weaponcrafting/improvised_parts/trigger_assembly = 10, + /obj/item/weaponcrafting/improvised_parts/makeshift_lens = 3, + ) + +/obj/effect/spawner/lootdrop/weapon_parts + name = "random weapon parts spawner 25%" + lootcount = 1 + spawn_on_turf = FALSE + loot = list("" = 75, + /obj/item/weaponcrafting/improvised_parts/barrel_rifle = 5, + /obj/item/weaponcrafting/improvised_parts/barrel_pistol = 5, + /obj/item/weaponcrafting/improvised_parts/rifle_receiver = 5, + /obj/item/weaponcrafting/improvised_parts/pistol_receiver = 2, + /obj/item/weaponcrafting/improvised_parts/trigger_assembly = 5, + /obj/item/weaponcrafting/improvised_parts/makeshift_lens = 3, + ) + +/obj/effect/spawner/lootdrop/ammo + name = "random ammo 75%" + lootcount = 1 + spawn_on_turf = FALSE + loot = list("" = 25, + /obj/item/ammo_box/c32mm = 15, + /obj/item/ammo_box/r32mm = 15, + /obj/item/ammo_box/magazine/wt550m9 = 1, + /obj/item/ammo_casing/shotgun/buckshot = 7, + /obj/item/ammo_casing/shotgun/rubbershot = 7, + /obj/item/ammo_casing/a762 = 15, + /obj/item/ammo_box/a762 = 15, + ) + +/obj/effect/spawner/lootdrop/ammo/fiftypercent + name = "random ammo 50%" + lootcount = 1 + spawn_on_turf = FALSE + loot = list("" = 50, + /obj/item/ammo_box/c32mm = 7, + /obj/item/ammo_box/r32mm = 7, + /obj/item/ammo_box/magazine/wt550m9 = 2, + /obj/item/ammo_casing/shotgun/buckshot = 10, + /obj/item/ammo_casing/shotgun/rubbershot = 10, + /obj/item/ammo_casing/a762 = 7, + /obj/item/ammo_box/a762 = 7, + ) + +/obj/effect/spawner/lootdrop/ammo/shotgun + name = "random ammo 50%" + lootcount = 1 + spawn_on_turf = FALSE + loot = list("" = 50, + /obj/item/ammo_box/shotgun/loaded/buckshot = 5, + /obj/item/ammo_box/shotgun/loaded/beanbag = 5, + /obj/item/ammo_box/shotgun/loaded/incendiary = 5, + /obj/item/ammo_casing/shotgun/buckshot = 8, + /obj/item/ammo_casing/shotgun/rubbershot = 9, + /obj/item/ammo_casing/shotgun = 8, + /obj/item/ammo_casing/shotgun/incendiary = 10, + ) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 883c9717b6..5aa47cfb76 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -144,6 +144,13 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb var/list/grind_results //A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only var/list/juice_results //A reagent list containing blah blah... but when JUICED in a grinder! + /* Our block parry data. Should be set in init, or something if you are using it. + * This won't be accessed without ITEM_CAN_BLOCK or ITEM_CAN_PARRY so do not set it unless you have to to save memory. + * If you decide it's a good idea to leave this unset while turning the flags on, you will runtime. Enjoy. + * If this is set to a path, it'll run get_block_parry_data(path). YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING! + */ + var/datum/block_parry_data/block_parry_data + ///Skills vars //list of skill PATHS exercised when using this item. An associated bitfield can be set to indicate additional ways the skill is used by this specific item. var/list/datum/skill/used_skills @@ -250,9 +257,10 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb . += "[src] is made of cold-resistant materials." if(resistance_flags & FIRE_PROOF) . += "[src] is made of fire-retardant materials." - - - + + if(item_flags & (ITEM_CAN_BLOCK | ITEM_CAN_PARRY)) + var/datum/block_parry_data/data = return_block_parry_datum(block_parry_data) + . += "[src] has the capacity to be used to block and/or parry. \[Show Stats\]" if(!user.research_scanner) return @@ -414,6 +422,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb return ITALICS | REDUCE_RANGE /obj/item/proc/dropped(mob/user) + SHOULD_CALL_PARENT(TRUE) for(var/X in actions) var/datum/action/A = X A.Remove(user) @@ -426,6 +435,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb // called just as an item is picked up (loc is not yet changed) /obj/item/proc/pickup(mob/user) + SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user) item_flags |= IN_INVENTORY diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 7036a78bc4..9117df8e6b 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -187,6 +187,15 @@ if(ID_LOCKED_BANK_ACCOUNT) registered_account = new /datum/bank_account/remote/non_transferable(pick(GLOB.redacted_strings)) +/obj/item/card/id/Destroy() + if(bank_support == ID_LOCKED_BANK_ACCOUNT) + QDEL_NULL(registered_account) + else + registered_account = null + if(my_store) + my_store.my_card = null + my_store = null + return ..() /obj/item/card/id/vv_edit_var(var_name, var_value) . = ..() diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index ae237919ce..bfbacb1a34 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -803,6 +803,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM to_chat(user, "You need to close the cap first!") /obj/item/clothing/mask/vape/dropped(mob/user) + . = ..() var/mob/living/carbon/C = user if(C.get_item_by_slot(SLOT_WEAR_MASK) == src) ENABLE_BITFIELD(reagents.reagents_holder_flags, NO_REACT) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index e8f43f316f..28e1e1294f 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -655,9 +655,10 @@ GENETICS SCANNER amount += inaccurate return DisplayTimeText(max(1,amount)) -/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src) +/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src, visible = TRUE) var/icon = target - user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(user))] [target].", "You use the analyzer on [icon2html(icon, user)] [target].") + if(visible) + user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(user))] [target].", "You use the analyzer on [icon2html(icon, user)] [target].") to_chat(user, "Results of analysis of [icon2html(icon, user)] [target].") var/list/airs = islist(mixture) ? mixture : list(mixture) diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index ebf4863108..aec3c333b7 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -105,9 +105,28 @@ sharpness = IS_SHARP embedding = list("embed_chance" = 75, "impact_pain_mult" = 10) armour_penetration = 35 - block_chance = 50 + item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/energy_sword var/list/possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER) +/datum/block_parry_data/energy_sword + parry_time_windup = 0 + parry_time_active = 25 + parry_time_spindown = 0 + // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing. + parry_time_windup_visual_override = 1 + parry_time_active_visual_override = 3 + parry_time_spindown_visual_override = 12 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while + parry_time_perfect = 2.5 // first ds isn't perfect + parry_time_perfect_leeway = 1.5 + parry_imperfect_falloff_percent = 5 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 65 // VERY generous + parry_efficiency_perfect = 100 + parry_failed_stagger_duration = 4 SECONDS + parry_cooldown = 0.5 SECONDS + /obj/item/melee/transforming/energy/sword/Initialize(mapload) . = ..() set_sword_color() diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index 71437d4706..741607edc3 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -61,13 +61,25 @@ force = 18 throwforce = 15 w_class = WEIGHT_CLASS_BULKY - block_chance = 50 armour_penetration = 75 sharpness = IS_SHARP attack_verb = list("slashed", "cut") hitsound = 'sound/weapons/rapierhit.ogg' custom_materials = list(/datum/material/iron = 1000) total_mass = 3.4 + item_flags = NEEDS_PERMIT | ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/captain_saber + +/datum/block_parry_data/captain_saber + parry_time_windup = 0.5 + parry_time_active = 4 + parry_time_spindown = 1 + parry_time_perfect = 0.75 + parry_time_perfect_leeway = 0.75 + parry_imperfect_falloff_percent = 30 + parry_efficiency_perfect = 100 + parry_failed_stagger_duration = 3 SECONDS + parry_failed_clickcd_duration = 2 SECONDS /obj/item/melee/sabre/Initialize() . = ..() @@ -150,7 +162,6 @@ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' force = 15 throwforce = 25 - block_chance = 50 armour_penetration = 200 //Apparently this gives it the ability to pierce block flags_1 = CONDUCT_1 obj_flags = UNIQUE_RENAME @@ -159,16 +170,47 @@ attack_verb = list("stabs", "punctures", "pierces", "pokes") hitsound = 'sound/weapons/rapierhit.ogg' total_mass = 0.4 + item_flags = ITEM_CAN_PARRY | NEEDS_PERMIT + block_parry_data = /datum/block_parry_data/traitor_rapier + +// Fast, efficient parry. +/datum/block_parry_data/traitor_rapier + parry_time_windup = 0.5 + parry_time_active = 5 + parry_time_spindown = 0 + parry_time_active_visual_override = 3 + parry_time_spindown_visual_override = 2 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING + parry_time_perfect = 0 + parry_time_perfect_leeway = 3 + parry_time_perfect_leeway_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 1 + ) + parry_imperfect_falloff_percent_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 50 // useless after 3rd decisecond + ) + parry_imperfect_falloff_percent = 30 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 1 + parry_efficiency_perfect = 100 + parry_data = list( + PARRY_DISARM_ATTACKER = TRUE, + PARRY_KNOCKDOWN_ATTACKER = 10 + ) + parry_failed_stagger_duration = 2 SECONDS + parry_failed_clickcd_duration = CLICK_CD_RANGE + parry_cooldown = 0 + +/obj/item/melee/rapier/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text) + . = ..() + if((attack_type & ATTACK_TYPE_PROJECTILE) && (parry_efficiency >= 100)) + . |= BLOCK_SHOULD_REDIRECT + return_list[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT /obj/item/melee/rapier/Initialize() . = ..() AddComponent(/datum/component/butchering, 20, 65, 0) -/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 ..() - /obj/item/melee/rapier/on_exit_storage(datum/component/storage/S) var/obj/item/storage/belt/sabre/rapier/B = S.parent if(istype(B)) @@ -191,10 +233,9 @@ . = ..() if(iscarbon(target)) var/mob/living/carbon/H = target - var/loss = H.getStaminaLoss() H.Dizzy(10) H.adjustStaminaLoss(30) - if((loss > 40) && prob(loss)) // if above 40, roll for sleep using 1% every 1 stamina damage + if(CHECK_STAMCRIT(H) != NOT_STAMCRIT) H.Sleeping(180) /obj/item/melee/classic_baton @@ -664,4 +705,4 @@ . = ..() overlay = mutable_appearance(icon, overlay_state) overlay.appearance_flags = RESET_COLOR - add_overlay(overlay) \ No newline at end of file + add_overlay(overlay) diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index bfbfdf9762..00a9ff22f4 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -140,7 +140,8 @@ if(jsonlist["icon_state"]) icon_state = jsonlist["icon_state"] item_state = jsonlist["item_state"] - icon = 'config/plushies/sprites.dmi' + var/static/config_sprites = file("config/plushies/sprites.dmi") + icon = config_sprites if(jsonlist["attack_verb"]) attack_verb = jsonlist["attack_verb"] if(jsonlist["squeak_override"]) diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index 2f697553f5..4b8728e426 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -358,6 +358,7 @@ check_amount() /obj/item/borg/lollipop/dropped(mob/user) + . = ..() check_amount() /obj/item/borg/lollipop/proc/check_amount() //Doesn't even use processing ticks. diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index b9cde1664f..aefa5d3cc8 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -1,7 +1,8 @@ /obj/item/shield name = "shield" icon = 'icons/obj/shields.dmi' - block_chance = 50 + item_flags = ITEM_CAN_BLOCK + block_parry_data = /datum/block_parry_data/shield armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) /// Shield flags var/shield_flags = SHIELD_FLAGS_DEFAULT @@ -22,6 +23,18 @@ /// Shield bashing push distance var/shieldbash_push_distance = 1 +/datum/block_parry_data/shield + block_damage_multiplier = 0.25 + block_stamina_efficiency = 2.5 + block_stamina_cost_per_second = 3.5 + block_slowdown = 0 + block_lock_attacking = FALSE + block_lock_sprinting = TRUE + block_start_delay = 1.5 + block_damage_absorption = 5 + block_resting_stamina_penalty_multiplier = 2 + block_projectile_mitigation = 75 + /obj/item/shield/examine(mob/user) . = ..() if(shield_flags & SHIELD_CAN_BASH) @@ -154,6 +167,22 @@ icon_state = "shield_bash" duration = 3 +/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(ismovable(object)) + var/atom/movable/AM = object + if(CHECK_BITFIELD(shield_flags, SHIELD_TRANSPARENT) && (AM.pass_flags & PASSGLASS)) + return BLOCK_NONE + if(attack_type & ATTACK_TYPE_THROWN) + final_block_chance += 30 + if(attack_type & ATTACK_TYPE_TACKLE) + final_block_chance = 100 + . = ..() + 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/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) + on_shield_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance) + /obj/item/shield/riot name = "riot shield" desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder." @@ -172,20 +201,7 @@ var/repair_material = /obj/item/stack/sheet/mineral/titanium var/can_shatter = TRUE shield_flags = SHIELD_FLAGS_DEFAULT | SHIELD_TRANSPARENT - max_integrity = 75 - -/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(ismovable(object)) - var/atom/movable/AM = object - if(CHECK_BITFIELD(shield_flags, SHIELD_TRANSPARENT) && (AM.pass_flags & PASSGLASS)) - return BLOCK_NONE - if(attack_type & ATTACK_TYPE_THROWN) - final_block_chance += 30 - if(attack_type & ATTACK_TYPE_TACKLE) - final_block_chance = 100 - . = ..() - if(. & BLOCK_SUCCESS) - on_shield_block(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + max_integrity = 450 /obj/item/shield/riot/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/melee/baton)) @@ -238,13 +254,13 @@ lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 55 //Weak + max_integrity = 300 obj/item/shield/riot/bullet_proof name = "bullet resistant shield" desc = "A far more frail shield made of resistant plastics and kevlar meant to block ballistics." armor = list("melee" = 30, "bullet" = 80, "laser" = 0, "energy" = 0, "bomb" = -40, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - max_integrity = 55 //Weaker + max_integrity = 300 /obj/item/shield/riot/roman name = "\improper Roman shield" @@ -255,13 +271,13 @@ obj/item/shield/riot/bullet_proof righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' repair_material = /obj/item/stack/sheet/mineral/wood shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 65 + max_integrity = 250 /obj/item/shield/riot/roman/fake desc = "Bears an inscription on the inside: \"Romanes venio domus\". It appears to be a bit flimsy." block_chance = 0 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - max_integrity = 30 + max_integrity = 40 /obj/item/shield/riot/roman/shatter(mob/living/carbon/human/owner) playsound(owner, 'sound/effects/grillehit.ogg', 100) @@ -279,7 +295,7 @@ obj/item/shield/riot/bullet_proof repair_material = /obj/item/stack/sheet/mineral/wood block_chance = 30 shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 55 + max_integrity = 150 /obj/item/shield/riot/buckler/shatter(mob/living/carbon/human/owner) playsound(owner, 'sound/effects/bang.ogg', 50) @@ -297,13 +313,16 @@ obj/item/shield/riot/bullet_proof throw_speed = 3 throw_range = 4 w_class = WEIGHT_CLASS_NORMAL - var/active = 0 + var/active = FALSE /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/can_active_block() + return ..() && active + /obj/item/shield/riot/tele/attack_self(mob/living/user) active = !active icon_state = "teleriot[active]" @@ -335,8 +354,7 @@ obj/item/shield/riot/bullet_proof icon_state = "makeshift_shield" custom_materials = list(/datum/material/iron = 18000) slot_flags = null - block_chance = 35 - max_integrity = 100 //Made of metal welded together its strong but not unkillable + max_integrity = 300 //Made of metal welded together its strong but not unkillable force = 10 throwforce = 7 @@ -346,7 +364,6 @@ obj/item/shield/riot/bullet_proof armor = list("melee" = 95, "bullet" = 95, "laser" = 75, "energy" = 60, "bomb" = 90, "bio" = 90, "rad" = 0, "fire" = 90, "acid" = 10) //Armor for the item, dosnt transfer to user item_state = "metal" icon_state = "metal" - block_chance = 75 //1/4 shots will hit* force = 16 slowdown = 2 throwforce = 15 //Massive pice of metal @@ -357,19 +374,17 @@ obj/item/shield/riot/bullet_proof /obj/item/shield/riot/tower/swat name = "swat shield" desc = "A massive, heavy shield that can block a lot of attacks, can take a lot of abuse before breaking." - max_integrity = 175 - block_chance = 50 + max_integrity = 250 /obj/item/shield/riot/implant name = "telescoping shield implant" desc = "A compact, arm-mounted telescopic shield. While nigh-indestructible when powered by a host user, it will eventually overload from damage. Recharges while inside its implant." item_state = "metal" icon_state = "metal" - block_chance = 50 slowdown = 1 shield_flags = SHIELD_FLAGS_DEFAULT - max_integrity = 60 - obj_integrity = 60 + max_integrity = 100 + obj_integrity = 100 can_shatter = FALSE item_flags = SLOWS_WHILE_IN_HAND var/recharge_timerid diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 6553cd2f7c..55719c2758 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -242,6 +242,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \ null, \ new/datum/stack_recipe("wooden firearm body", /obj/item/weaponcrafting/improvised_parts/wooden_body, 10, time = 40), \ new/datum/stack_recipe("rifle stock", /obj/item/weaponcrafting/stock, 10, time = 40), \ + new/datum/stack_recipe("pistol grip", /obj/item/weaponcrafting/improvised_parts/wooden_grip, 5, time = 40), \ new/datum/stack_recipe("rolling pin", /obj/item/kitchen/rollingpin, 2, time = 30), \ new/datum/stack_recipe("wooden bucket", /obj/item/reagent_containers/glass/bucket/wood, 2, time = 30), \ new/datum/stack_recipe("wooden buckler", /obj/item/shield/riot/buckler, 20, time = 40), \ diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index 60fc761bfe..fa0c9ba693 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -170,10 +170,12 @@ return disarming || (user.a_intent != INTENT_HARM) /obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE) - if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; check_shields() handles that + var/list/return_list = list() + if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; check_shields() handles that playsound(L, 'sound/weapons/genhit.ogg', 50, 1) return FALSE var/stunpwr = stamforce + stunpwr = block_calculate_resultant_damage(stunpwr, return_list) var/obj/item/stock_parts/cell/our_cell = get_cell() if(!our_cell) switch_status(FALSE) diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm index 95712f386a..00fbc41516 100644 --- a/code/game/objects/items/tanks/tanks.dm +++ b/code/game/objects/items/tanks/tanks.dm @@ -153,6 +153,10 @@ return (BRUTELOSS) +/obj/item/tank/attack_ghost(mob/dead/observer/O) + . = ..() + atmosanalyzer_scan(air_contents, O, src, FALSE) + /obj/item/tank/attackby(obj/item/W, mob/user, params) add_fingerprint(user) if(istype(W, /obj/item/assembly_holder)) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index f3c35b0495..cd630a2c82 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -446,6 +446,7 @@ throw_range = 5 force_unwielded = 0 force_wielded = 0 + block_parry_data = null attack_verb = list("attacked", "struck", "hit") total_mass_on = TOTAL_MASS_TOY_SWORD sharpness = IS_BLUNT diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 6ad0b789b5..10bee608c2 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -30,6 +30,8 @@ var/wieldsound = null var/unwieldsound = null var/slowdown_wielded = 0 + /// Do we need to be wielded to actively block/parry? + var/requires_wield_to_block_parry = TRUE item_flags = SLOWS_WHILE_IN_HAND /obj/item/twohanded/proc/unwield(mob/living/carbon/user, show_message = TRUE) @@ -90,6 +92,12 @@ user.put_in_inactive_hand(O) set_slowdown(slowdown + slowdown_wielded) +/obj/item/twohanded/can_active_block() + return ..() && (!requires_wield_to_block_parry || wielded) + +/obj/item/twohanded/can_active_parry() + return ..() && (!requires_wield_to_block_parry || wielded) + /obj/item/twohanded/dropped(mob/user) . = ..() //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand @@ -129,6 +137,7 @@ return ..() /obj/item/twohanded/offhand/dropped(mob/living/user, show_message = TRUE) //Only utilized by dismemberment since you can't normally switch to the offhand to drop it. + . = ..() var/obj/I = user.get_active_held_item() if(I && istype(I, /obj/item/twohanded)) var/obj/item/twohanded/thw = I @@ -274,6 +283,8 @@ throw_range = 5 w_class = WEIGHT_CLASS_SMALL var/w_class_on = WEIGHT_CLASS_BULKY + item_flags = ITEM_CAN_PARRY | SLOWS_WHILE_IN_HAND | ITEM_CAN_BLOCK + block_parry_data = /datum/block_parry_data/dual_esword force_unwielded = 3 force_wielded = 34 wieldsound = 'sound/weapons/saberon.ogg' @@ -284,7 +295,6 @@ var/saber_color = "green" light_color = "#00ff00"//green attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 75 max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) resistance_flags = FIRE_PROOF @@ -298,6 +308,42 @@ total_mass = 0.4 //Survival flashlights typically weigh around 5 ounces. var/total_mass_on = 3.4 +/datum/block_parry_data/dual_esword + block_damage_absorption = 2 + block_damage_multiplier = 0.15 + block_damage_multiplier_override = list( + ATTACK_TYPE_MELEE = 0.25 + ) + block_start_delay = 0 // instantaneous block + block_stamina_cost_per_second = 2.5 + block_stamina_efficiency = 3 + block_lock_sprinting = TRUE + // no attacking while blocking + block_lock_attacking = TRUE + block_projectile_mitigation = 75 + + parry_time_windup = 0 + parry_time_active = 8 + parry_time_spindown = 0 + // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing. + parry_time_windup_visual_override = 1 + parry_time_active_visual_override = 3 + parry_time_spindown_visual_override = 4 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying. + parry_time_perfect = 2 // first ds isn't perfect + parry_time_perfect_leeway = 1 + parry_imperfect_falloff_percent = 10 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 25 // VERY generous + parry_efficiency_perfect = 90 + parry_failed_stagger_duration = 3 SECONDS + parry_failed_clickcd_duration = CLICK_CD_MELEE + + // more efficient vs projectiles + block_stamina_efficiency_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 4 + ) + /obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user) if(wielded) user.visible_message("[user] begins spinning way too fast! It looks like [user.p_theyre()] trying to commit suicide!") @@ -849,6 +895,7 @@ return (BRUTELOSS) /obj/item/twohanded/pitchfork/demonic/pickup(mob/living/user) + . = ..() if(isliving(user) && user.mind && user.owns_soul() && !is_devil(user)) var/mob/living/U = user U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \ @@ -1030,13 +1077,12 @@ force_wielded = 10 throwforce = 15 //if you are a madman and finish someone off with this, power to you. throw_speed = 1 - item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND - block_chance = 30 + item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | ITEM_CAN_BLOCK | ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/electrostaff attack_verb = list("struck", "beaten", "thwacked", "pulped") total_mass = 5 //yeah this is a heavy thing, beating people with it while it's off is not going to do you any favors. (to curb stun-kill rampaging without it being on) var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high var/on = FALSE - var/can_block_projectiles = FALSE //can't block guns var/lethal_cost = 400 //10000/400*20 = 500. decent enough? var/lethal_damage = 20 var/lethal_stam_cost = 4 @@ -1046,6 +1092,43 @@ var/stun_status_duration = 25 var/stun_stam_cost = 3.5 +// haha security desword time /s +/datum/block_parry_data/electrostaff + block_damage_absorption = 0 + block_damage_multiplier = 1 + can_block_attack_types = ~ATTACK_TYPE_PROJECTILE // only able to parry non projectiles + block_damage_multiplier_override = list( + TEXT_ATTACK_TYPE_MELEE = 0.5, // only useful on melee and unarmed + TEXT_ATTACK_TYPE_UNARMED = 0.3 + ) + block_start_delay = 0.5 // near instantaneous block + block_stamina_cost_per_second = 3 + block_stamina_efficiency = 2 // haha this is a horrible idea + // more slowdown that deswords because security + block_slowdown = 2 + // no attacking while blocking + block_lock_attacking = TRUE + + parry_time_windup = 1 + parry_time_active = 5 + parry_time_spindown = 0 + parry_time_spindown_visual_override = 1 + parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING // no attacking while parrying + parry_time_perfect = 0 + parry_time_perfect_leeway = 0.5 + parry_efficiency_perfect = 100 + parry_imperfect_falloff_percent = 1 + parry_imperfect_falloff_percent_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 45 // really crappy vs projectiles + ) + parry_time_perfect_leeway_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 1 // extremely harsh window for projectiles + ) + // not extremely punishing to fail, but no spamming the parry. + parry_cooldown = 2.5 SECONDS + parry_failed_stagger_duration = 1.5 SECONDS + parry_failed_clickcd_duration = 1 SECONDS + /obj/item/twohanded/electrostaff/Initialize(mapload) . = ..() if(ispath(cell)) @@ -1061,11 +1144,6 @@ var/mob/living/silicon/robot/R = loc . = R.get_cell() -/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() return min(stun_cost, lethal_cost) @@ -1183,22 +1261,23 @@ return if(iscyborg(target)) return ..() - if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; run_block() handles that + var/list/return_list = list() + if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; run_block() handles that playsound(target, 'sound/weapons/genhit.ogg', 50, 1) return FALSE if(user.a_intent != INTENT_HARM) - if(stun_act(target, user)) + if(stun_act(target, user, null, return_list)) user.do_attack_animation(target) user.adjustStaminaLossBuffered(stun_stam_cost) return - else if(!harm_act(target, user)) + else if(!harm_act(target, user, null, return_list)) return ..() //if you can't fry them just beat them with it else //we did harm act them user.do_attack_animation(target) user.adjustStaminaLossBuffered(lethal_stam_cost) -/obj/item/twohanded/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE) - var/stunforce = stun_stamdmg +/obj/item/twohanded/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list()) + var/stunforce = block_calculate_resultant_damage(stun_stamdmg, block_return) if(!no_charge_and_force) if(!on) target.visible_message("[user] has bapped [target] with [src]. Luckily it was off.", \ @@ -1228,8 +1307,8 @@ H.forcesay(GLOB.hit_appends) return TRUE -/obj/item/twohanded/electrostaff/proc/harm_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE) - var/lethal_force = lethal_damage +/obj/item/twohanded/electrostaff/proc/harm_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list()) + var/lethal_force = block_calculate_resultant_damage(lethal_damage, block_return) if(!no_charge_and_force) if(!on) return FALSE //standard item attack diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 3752181d43..67dcd71e65 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -122,11 +122,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander/pickup(mob/living/user) + . = ..() to_chat(user, "The power of Scotland protects you! You are shielded from all stuns and knockdowns.") user.add_stun_absorption("highlander", INFINITY, 1, " is protected by the power of Scotland!", "The power of Scotland absorbs the stun!", " is protected by the power of Scotland!") user.ignore_slowdown(HIGHLANDER) /obj/item/claymore/highlander/dropped(mob/living/user) + . = ..() user.unignore_slowdown(HIGHLANDER) if(!QDELETED(src)) qdel(src) //If this ever happens, it's because you lost an arm diff --git a/code/game/objects/structures/petrified_statue.dm b/code/game/objects/structures/petrified_statue.dm index a8a5a577c2..97eadff5f6 100644 --- a/code/game/objects/structures/petrified_statue.dm +++ b/code/game/objects/structures/petrified_statue.dm @@ -4,11 +4,13 @@ icon_state = "human_male" density = TRUE anchored = TRUE + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 max_integrity = 200 - var/timer = 240 //eventually the person will be freed + var/timer = 8 MINUTES //eventually the person will be freed var/mob/living/petrified_mob -/obj/structure/statue/petrified/New(loc, mob/living/L, statue_timer) +/obj/structure/statue/petrified/Initialize(mapload, mob/living/L, statue_timer) + . = ..() if(statue_timer) timer = statue_timer if(L) @@ -17,25 +19,18 @@ L.buckled.unbuckle_mob(L,force=1) L.visible_message("[L]'s skin rapidly turns to marble!", "Your body freezes up! Can't... move... can't... think...") L.forceMove(src) - ADD_TRAIT(L, TRAIT_MUTE, STATUE_MUTE) + ADD_TRAIT(L, TRAIT_MUTE, STATUE_TRAIT) + ADD_TRAIT(L, TRAIT_EMOTEMUTE, STATUE_TRAIT) + ADD_TRAIT(L, TRAIT_LOOC_MUTE, STATUE_TRAIT) + ADD_TRAIT(L, TRAIT_AOOC_MUTE, STATUE_TRAIT) + ADD_TRAIT(L, TRAIT_MOBILITY_NOMOVE, STATUE_TRAIT) + ADD_TRAIT(L, TRAIT_MOBILITY_NOPICKUP, STATUE_TRAIT) + ADD_TRAIT(L, TRAIT_MOBILITY_NOUSE, STATUE_TRAIT) L.faction += "mimic" //Stops mimics from instaqdeling people in statues L.status_flags |= GODMODE obj_integrity = L.health + 100 //stoning damaged mobs will result in easier to shatter statues max_integrity = obj_integrity - START_PROCESSING(SSobj, src) - ..() - -/obj/structure/statue/petrified/process() - if(!petrified_mob) - STOP_PROCESSING(SSobj, src) - timer-- - petrified_mob.Stun(40) //So they can't do anything while petrified - if(timer <= 0) - STOP_PROCESSING(SSobj, src) - qdel(src) - -/obj/structure/statue/petrified/contents_explosion(severity, target) - return + QDEL_IN(src, timer) /obj/structure/statue/petrified/handle_atom_del(atom/A) if(A == petrified_mob) @@ -59,7 +54,13 @@ if(petrified_mob) petrified_mob.status_flags &= ~GODMODE petrified_mob.forceMove(loc) - REMOVE_TRAIT(petrified_mob, TRAIT_MUTE, STATUE_MUTE) + REMOVE_TRAIT(petrified_mob, TRAIT_MUTE, STATUE_TRAIT) + REMOVE_TRAIT(petrified_mob, TRAIT_EMOTEMUTE, STATUE_TRAIT) + REMOVE_TRAIT(petrified_mob, TRAIT_LOOC_MUTE, STATUE_TRAIT) + REMOVE_TRAIT(petrified_mob, TRAIT_AOOC_MUTE, STATUE_TRAIT) + REMOVE_TRAIT(petrified_mob, TRAIT_MOBILITY_NOMOVE, STATUE_TRAIT) + REMOVE_TRAIT(petrified_mob, TRAIT_MOBILITY_NOPICKUP, STATUE_TRAIT) + REMOVE_TRAIT(petrified_mob, TRAIT_MOBILITY_NOUSE, STATUE_TRAIT) petrified_mob.take_overall_damage((petrified_mob.health - obj_integrity + 100)) //any new damage the statue incurred is transfered to the mob petrified_mob.faction -= "mimic" petrified_mob = null diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm index ca552be0fe..5c74eb4ecc 100644 --- a/code/game/turfs/simulated/wall/mineral_walls.dm +++ b/code/game/turfs/simulated/wall/mineral_walls.dm @@ -191,8 +191,7 @@ icon = 'icons/turf/walls/shuttle_wall.dmi' icon_state = "map-shuttle" explosion_block = 3 - flags_1 = CAN_BE_DIRTY_1 - flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD + flags_1 = CAN_BE_DIRTY_1 | DEFAULT_RICOCHET_1 sheet_type = /obj/item/stack/sheet/mineral/titanium smooth = SMOOTH_MORE|SMOOTH_DIAGONAL canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium) @@ -303,4 +302,4 @@ /turf/closed/wall/mineral/plastitanium/copyTurf(turf/T) . = ..() - T.transform = transform \ No newline at end of file + T.transform = transform diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 0ed92bebd1..da5d3c643d 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -12,10 +12,7 @@ baseturfs = /turf/open/floor/plating - flags_ricochet = RICOCHET_HARD - - ///lower numbers are harder. Used to determine the probability of a hulk smashing through. Also, (hardness - 40) is used as a modifier for objects trying to embed in this (hardness of 30 results in a -10% chance) - var/hardness = 40 + var/hardness = 40 //lower numbers are harder. Used to determine the probability of a hulk smashing through. var/slicing_duration = 100 //default time taken to slice the wall var/sheet_type = /obj/item/stack/sheet/metal var/sheet_amount = 2 @@ -44,6 +41,17 @@ /turf/closed/wall/attack_tk() return +/turf/closed/wall/handle_projectile_ricochet(obj/item/projectile/P) //A huge pile of shitcode! + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + if(abs(incidence_s) > 90 && abs(incidence_s) < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + return TRUE + /turf/closed/wall/proc/dismantle_wall(devastated=0, explode=0) if(devastated) devastate_wall() diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index df54df5c7d..bff431a6c7 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -346,7 +346,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null //print the key if(islist(key)) recursive_list_print(output, key, datum_handler, atom_handler) - else if(is_proper_datum(key) && (datum_handler || (isatom(key) && atom_handler))) + else if(is_object_datatype(key) && (datum_handler || (isatom(key) && atom_handler))) if(isatom(key) && atom_handler) output += atom_handler.Invoke(key) else @@ -360,7 +360,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null var/value = input[key] if(islist(value)) recursive_list_print(output, value, datum_handler, atom_handler) - else if(is_proper_datum(value) && (datum_handler || (isatom(value) && atom_handler))) + else if(is_object_datatype(value) && (datum_handler || (isatom(value) && atom_handler))) if(isatom(value) && atom_handler) output += atom_handler.Invoke(value) else @@ -498,7 +498,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null if(length(select_text)) var/text = islist(select_text)? select_text.Join() : select_text var/static/result_offset = 0 - showmob << browse(text, "window=SDQL-result-[result_offset++]") + showmob << browse(text, "window=SDQL-result-[result_offset++];size=800x1200") show_next_to_key = null if(qdel_on_finish) qdel(src) @@ -646,7 +646,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null switch(query_tree[1]) if("call") for(var/i in found) - if(!is_proper_datum(i)) + if(!is_object_datatype(i)) continue world.SDQL_var(i, query_tree["call"][1], null, i, superuser, src) obj_count_finished++ @@ -664,7 +664,10 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null var/list/text_list = list() var/print_nulls = !(options & SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS) obj_count_finished = select_refs + var/n = 0 for(var/i in found) + if(++n == 20000) + text_list += "
TRUNCATED - 20000 OBJECT LIMIT HIT" SDQL_print(i, text_list, print_nulls) select_refs[REF(i)] = TRUE SDQL2_TICK_CHECK @@ -675,7 +678,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null if("set" in query_tree) var/list/set_list = query_tree["set"] for(var/d in found) - if(!is_proper_datum(d)) + if(!is_object_datatype(d)) continue SDQL_internal_vv(d, set_list) obj_count_finished++ @@ -685,47 +688,72 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null obj_count_finished = length(obj_count_finished) state = SDQL2_STATE_SWITCHING -/datum/SDQL2_query/proc/SDQL_print(object, list/text_list, print_nulls = TRUE) - if(is_proper_datum(object)) - text_list += "[REF(object)] : [object]" - if(istype(object, /atom)) - var/atom/A = object - var/turf/T = A.loc - var/area/a - if(istype(T)) - text_list += " at [T] [ADMIN_COORDJMP(T)]" - a = T.loc - else - var/turf/final = get_turf(T) //Recursive, hopefully? - if(istype(final)) - text_list += " at [final] [ADMIN_COORDJMP(final)]" - a = final.loc +/** + * Recursively prints out an object to text list for SDQL2 output to admins, with VV links and all. + * Recursion limit: 50 + * Limit imposed by callers should be around 10000 objects + * Seriously, if you hit those limits, you're doing something wrong. + */ +/datum/SDQL2_query/proc/SDQL_print(datum/object, list/text_list, print_nulls = TRUE, recursion = 1, linebreak = TRUE) + if(recursion > 50) + text_list += "
RECURSION LIMIT REACHED.
" + return + if(is_object_datatype(object)) + if(!islist(object)) + text_list += "[object.type] [REF(object)]: [object]" + if(istype(object, /atom)) + if(istype(object, /turf)) + var/turf/T = object + text_list += " [ADMIN_COORDJMP(T)] at [T.loc]" else - text_list += " at nonexistant location" - if(a) - text_list += " in area [a]" - if(T.loc != a) - text_list += " inside [T]" - text_list += "
" - else if(islist(object)) - var/list/L = object - var/first = TRUE - text_list += "\[" - for (var/x in L) - if (!first) - text_list += ", " - first = FALSE - SDQL_print(x, text_list) - if (!isnull(x) && !isnum(x) && L[x] != null) - text_list += " -> " - SDQL_print(L[L[x]], text_list) - text_list += "]
" + var/atom/A = object + var/atom/container = A.loc + if(isturf(container)) + text_list += " in [container] [ADMIN_COORDJMP(container)] at [container.loc]" + else if(container) + var/turf/T = get_turf(container) + var/cref = REF(container) + text_list += " in [container]([cref])" + if(T) + text_list += " on [T] [ADMIN_COORDJMP(T)] at[T.loc]" + else + text_list += " in nullspace" + else // lists are snowflake and get special treatment. + text_list += "/list [REF(object)] \[
" + var/list/L = object + if(length(L)) + for(var/key in object) + if(islist(key)) + text_list += "" + SDQL_print(key, text_list, TRUE, recursion + 1, FALSE) + text_list += "" + else + SDQL_print(key, text_list, TRUE, recursion, FALSE) + if(IS_VALID_ASSOC_KEY(key) && !isnull(L[key])) + var/value = L[key] + text_list += " --> " + if(islist(value)) + text_list += "" + SDQL_print(value, text_list, TRUE, recursion + 1, FALSE) + text_list += "" + else + SDQL_print(value, text_list, TRUE, recursion, FALSE) + text_list += "
" + text_list += "\]" + if(linebreak) + text_list += "
" else if(isnull(object)) if(print_nulls) - text_list += "NULL
" + text_list += "NULL" + else if(istext(object)) + text_list += "\"[object]\"" + else if(isnum(object) || ispath(object)) + text_list += "[object]" else - text_list += "[object]
" + text_list += "UNKNOWN: [object]" + if(linebreak) + text_list += "
" /datum/SDQL2_query/CanProcCall() if(!allow_admin_interact) @@ -957,7 +985,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null var/static/list/exclude = list("usr", "src", "marked", "global") var/long = start < expression.len var/datum/D - if(is_proper_datum(object)) + if(is_object_datatype(object)) D = object if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude)) //3 == length("SS") + 1 @@ -1161,9 +1189,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null query_list += word return query_list -/proc/is_proper_datum(thing) - return istype(thing, /datum) || istype(thing, /client) - /obj/effect/statclick/SDQL2_delete/Click() var/datum/SDQL2_query/Q = target Q.delete_click() diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm index 5116cd5cd6..ab9f4a534c 100644 --- a/code/modules/admin/view_variables/debug_variables.dm +++ b/code/modules/admin/view_variables/debug_variables.dm @@ -16,11 +16,19 @@ header = "
  • " var/item + var/name_part = VV_HTML_ENCODE(name) + if(level > 0 || islist(D)) //handling keys in assoc lists + if(istype(name,/datum)) + name_part = "[VV_HTML_ENCODE(name)] [REF(name)]" + else if(islist(name)) + var/list/L = name + name_part = " /list ([length(L)]) [REF(name)]" + if (isnull(value)) - item = "[VV_HTML_ENCODE(name)] = null" + item = "[name_part] = null" else if (istext(value)) - item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" + item = "[name_part] = \"[VV_HTML_ENCODE(value)]\"" else if (isicon(value)) #ifdef VARSICON @@ -28,33 +36,31 @@ var/rnd = rand(1,10000) var/rname = "tmp[REF(I)][rnd].png" usr << browse_rsc(I, rname) - item = "[VV_HTML_ENCODE(name)] = ([value]) " + item = "[name_part] = ([value]) " #else - item = "[VV_HTML_ENCODE(name)] = /icon ([value])" + item = "[name_part] = /icon ([value])" #endif else if (isfile(value)) - item = "[VV_HTML_ENCODE(name)] = '[value]'" + item = "[name_part] = '[value]'" - else if(istype(value, /matrix)) // Needs to be before datum + else if(istype(value,/matrix)) // Needs to be before datum var/matrix/M = value - item = {"[VV_HTML_ENCODE(name)] = - - - - -
      - - - -
    [M.a][M.d]0
    [M.b][M.e]0
    [M.c][M.f]1
     
    "} //TODO link to modify_transform wrapper for all matrices - + item = {"[name_part] = +
      + + + + + + +
    [M.a][M.d]0
    [M.b][M.e]0
    [M.c][M.f]1
     
    "} //TODO link to modify_transform wrapper for all matrices else if (istype(value, /datum)) var/datum/DV = value if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it. - item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV] [DV.type]" + item = "[name_part] = [DV] [DV.type] [REF(value)]" else - item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV.type]" + item = "[name_part] = [DV.type] [REF(value)]" else if (islist(value)) var/list/L = value @@ -72,19 +78,19 @@ items += debug_variable(key, val, level + 1, sanitize = sanitize) - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + item = "[name_part] = /list ([L.len])" else - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + item = "[name_part] = /list ([L.len])" else if (name in GLOB.bitfields) var/list/flags = list() for (var/i in GLOB.bitfields[name]) if (value & GLOB.bitfields[name][i]) flags += i - item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(jointext(flags, ", "))]" + item = "[name_part] = [VV_HTML_ENCODE(jointext(flags, ", "))]" else - item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" + item = "[name_part] = [VV_HTML_ENCODE(value)]" return "[header][item]
  • " -#undef VV_HTML_ENCODE +#undef VV_HTML_ENCODE \ No newline at end of file diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm index a3a1403e58..384df935f5 100644 --- a/code/modules/antagonists/blob/blob/blobs/shield.dm +++ b/code/modules/antagonists/blob/blob/blobs/shield.dm @@ -45,8 +45,22 @@ desc = "A solid wall of slightly twitching tendrils with a reflective glow." damaged_desc = "A wall of twitching tendrils with a reflective glow." icon_state = "blob_glow" - flags_ricochet = RICOCHET_SHINY point_return = 8 max_integrity = 100 brute_resist = 1 explosion_block = 2 + +/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P) + return PROJECTILE_RICOCHET_FORCE + +/obj/structure/blob/shield/reflective/handle_projectile_ricochet(obj/item/projectile/P) + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + if(abs(incidence_s) > 90 && abs(incidence_s) < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + visible_message("[P] reflects off [src]!") + return TRUE diff --git a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm index a21c0656dc..2aed251189 100644 --- a/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm +++ b/code/modules/antagonists/bloodsucker/items/bloodsucker_stake.dm @@ -167,4 +167,4 @@ ///obj/item/pipe = 2) time = 80 category = CAT_WEAPONRY - subcategory = CAT_WEAPON + subcategory = CAT_MELEE diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 5378ea2276..e4417a6d64 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -432,15 +432,18 @@ /obj/item/shield/changeling name = "shield-like mass" desc = "A mass of tough, boney tissue. You can still see the fingers as a twisted pattern in the shield." - item_flags = ABSTRACT | DROPDEL + item_flags = ABSTRACT | DROPDEL | ITEM_CAN_BLOCK icon = 'icons/obj/items_and_weapons.dmi' icon_state = "ling_shield" lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi' righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' - block_chance = 50 + block_parry_data = /datum/block_parry_data/shield/changeling var/remaining_uses //Set by the changeling ability. +/datum/block_parry_data/shield/changeling + block_slowdown = 0 + /obj/item/shield/changeling/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT) @@ -451,7 +454,7 @@ 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) +/obj/item/shield/changeling/active_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)) diff --git a/code/modules/antagonists/clockcult/clock_structure.dm b/code/modules/antagonists/clockcult/clock_structure.dm index 13da9c5a42..2464015b6b 100644 --- a/code/modules/antagonists/clockcult/clock_structure.dm +++ b/code/modules/antagonists/clockcult/clock_structure.dm @@ -101,7 +101,7 @@ return 1 return ..() -/obj/structure/destructible/clockwork/attacked_by(obj/item/I, mob/living/user) +/obj/structure/destructible/clockwork/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(is_servant_of_ratvar(user) && immune_to_servant_attacks) return FALSE return ..() diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index ffab8174e5..8f0f9a658c 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -273,6 +273,7 @@ knockdown = 20 /obj/item/restraints/legcuffs/bola/cult/pickup(mob/living/user) + . = ..() if(!iscultist(user)) to_chat(user, "The bola seems to take on a life of its own!") ensnare(user) diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm index f09592161d..7d7031dad4 100644 --- a/code/modules/antagonists/devil/true_devil/_true_devil.dm +++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm @@ -116,8 +116,7 @@ /mob/living/carbon/true_devil/get_ear_protection() return 2 - -/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone) +/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone, attackchain_flags = NONE, damage_multiplier = 1) var/totitemdamage = pre_attacked_by(I, user) totitemdamage *= check_weakness(I, user) apply_damage(totitemdamage, I.damtype, def_zone) diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm index c229a4ba27..33fd160b1a 100644 --- a/code/modules/atmospherics/machinery/components/components_base.dm +++ b/code/modules/atmospherics/machinery/components/components_base.dm @@ -162,6 +162,9 @@ to_chat(user, "Access denied.") return UI_CLOSE +/obj/machinery/atmospherics/components/attack_ghost(mob/dead/observer/O) + . = ..() + atmosanalyzer_scan(airs, O, src, FALSE) // Tool acts diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm index c466a422b2..4a6170c251 100644 --- a/code/modules/atmospherics/machinery/pipes/pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/pipes.dm @@ -111,3 +111,10 @@ pipe_color = paint_color update_node_icon() return TRUE + +/obj/machinery/atmospherics/pipe/attack_ghost(mob/dead/observer/O) + . = ..() + if(parent) + atmosanalyzer_scan(parent.air, O, src, FALSE) + else + to_chat(O, "[src] doesn't have a pipenet, which is probably a bug.") diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm index 8bf0554070..952db8315a 100644 --- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm +++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm @@ -147,10 +147,14 @@ /obj/machinery/portable_atmospherics/analyzer_act(mob/living/user, obj/item/I) atmosanalyzer_scan(air_contents, user, src) -/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) +/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user, attackchain_flags = NONE, damage_multiplier = 1) if(I.force < 10 && !(stat & BROKEN)) take_damage(0) else investigate_log("was smacked with \a [I] by [key_name(user)].", INVESTIGATE_ATMOS) add_fingerprint(user) ..() + +/obj/machinery/portable_atmospherics/attack_ghost(mob/dead/observer/O) + . = ..() + atmosanalyzer_scan(air_contents, O, src, FALSE) diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 7d87a82afb..0402dc683f 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -133,3 +133,6 @@ var/parallax_movedir = 0 var/parallax_layers_max = 3 var/parallax_animate_timer + + //world.time of when the crew manifest can be accessed + var/crew_manifest_delay diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 0b6bf4909d..347a77059a 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1638,19 +1638,19 @@ GLOBAL_LIST_EMPTY(preferences_datums) age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) if("flavor_text") - var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", features["flavor_text"], MAX_FLAVOR_LEN, TRUE) + var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", html_decode(features["flavor_text"]), MAX_FLAVOR_LEN, TRUE) if(!isnull(msg)) - features["flavor_text"] = html_decode(msg) + features["flavor_text"] = msg if("silicon_flavor_text") - var/msg = stripped_multiline_input(usr, "Set the silicon flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Silicon Flavor Text", features["silicon_flavor_text"], MAX_FLAVOR_LEN, TRUE) + var/msg = stripped_multiline_input(usr, "Set the silicon flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Silicon Flavor Text", html_decode(features["silicon_flavor_text"]), MAX_FLAVOR_LEN, TRUE) if(!isnull(msg)) - features["silicon_flavor_text"] = html_decode(msg) + features["silicon_flavor_text"] = msg if("ooc_notes") - var/msg = stripped_multiline_input(usr, "Set always-visible OOC notes related to content preferences. THIS IS NOT FOR CHARACTER DESCRIPTIONS!", "OOC notes", features["ooc_notes"], MAX_FLAVOR_LEN, TRUE) + var/msg = stripped_multiline_input(usr, "Set always-visible OOC notes related to content preferences. THIS IS NOT FOR CHARACTER DESCRIPTIONS!", "OOC notes", html_decode(features["ooc_notes"]), MAX_FLAVOR_LEN, TRUE) if(!isnull(msg)) - features["ooc_notes"] = html_decode(msg) + features["ooc_notes"] = msg if("hair") var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#"+hair_color) as color|null diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index ffbdcdd113..1ec3fc6404 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 32 +#define SAVEFILE_VERSION_MAX 33 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -194,6 +194,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car if(current_version < 31) S["wing_color"] >> features["wings_color"] S["horn_color"] >> features["horns_color"] + + if(current_version < 33) + features["flavor_text"] = html_encode(features["flavor_text"]) + features["silicon_flavor_text"] = html_encode(features["silicon_flavor_text"]) + features["ooc_notes"] = html_encode(features["ooc_notes"]) /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) diff --git a/code/modules/client/verbs/aooc.dm b/code/modules/client/verbs/aooc.dm index bc32d3c222..1a019bba80 100644 --- a/code/modules/client/verbs/aooc.dm +++ b/code/modules/client/verbs/aooc.dm @@ -13,21 +13,38 @@ GLOBAL_VAR_INIT(normal_aooc_colour, "#ce254f") if(!mob) return + if(!(prefs.toggles & CHAT_OOC)) + to_chat(src, " You have OOC muted.") + return + if(jobban_isbanned(mob, "OOC")) + to_chat(src, "You have been banned from OOC.") + return + if(!holder) - if(mob.stat == DEAD) - to_chat(usr, "You cannot use AOOC while dead.") - return - if(!is_special_character(mob)) - to_chat(usr, "You aren't an antagonist!") - if(prefs.muted & MUTE_OOC) - to_chat(src, "You cannot use AOOC (muted).") - return - if(jobban_isbanned(src.mob, "OOC")) - to_chat(src, "You are banned from OOC.") - return if(!GLOB.aooc_allowed) to_chat(src, "AOOC is currently muted.") return + if(prefs.muted & MUTE_OOC) + to_chat(src, "You cannot use AOOC (muted).") + return + if(!is_special_character(mob)) + to_chat(usr, "You aren't an antagonist!") + if(handle_spam_prevention(msg,MUTE_OOC)) + return + if(findtext(msg, "byond://")) + to_chat(src, "Advertising other servers is not allowed.") + log_admin("[key_name(src)] has attempted to advertise in LOOC: [msg]") + return + if(mob.stat) + to_chat(usr, "You cannot use AOOC while unconscious or dead.") + return + if(isdead(mob)) + to_chat(src, "You cannot use AOOC while ghosting.") + return + if(HAS_TRAIT(mob, TRAIT_AOOC_MUTE)) + to_chat(src, "You cannot use AOOC right now.") + return + if(QDELETED(src)) return diff --git a/code/modules/client/verbs/looc.dm b/code/modules/client/verbs/looc.dm index 075cabcbbb..da39eabc12 100644 --- a/code/modules/client/verbs/looc.dm +++ b/code/modules/client/verbs/looc.dm @@ -38,11 +38,15 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC") log_admin("[key_name(src)] has attempted to advertise in LOOC: [msg]") return if(mob.stat) - to_chat(src, "You cannot salt in LOOC while unconscious or dead.") + to_chat(src, "You cannot use LOOC while unconscious or dead.") return - if(istype(mob, /mob/dead)) + if(isdead(mob)) to_chat(src, "You cannot use LOOC while ghosting.") return + if(HAS_TRAIT(mob, TRAIT_LOOC_MUTE)) + to_chat(src, "You cannot use LOOC right now.") + return + msg = emoji_parse(msg) diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index 3d55e569dc..4166beef05 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -469,7 +469,7 @@ Contains: desc = "Voices echo from the hardsuit, driving the user insane. This one is pretty battle-worn, but still fearsome." armor = list("melee" = 55, "bullet" = 40, "laser" = 40, "energy" = 40, "bomb" = 40, "bio" = 80, "rad" = 80, "fire" = 60, "acid" = 60) slowdown = 0.8 - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/inquisitor/old + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/beserker/old charges = 6 /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/beserker/old diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 89b70f8951..85bcf4959d 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -330,8 +330,8 @@ if(!override) qdel(src) -/obj/structure/spacevine/attacked_by(obj/item/I, mob/living/user) - var/damage_dealt = I.force +/obj/structure/spacevine/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) + var/damage_dealt = I.force * damage_multiplier if(I.get_sharpness()) damage_dealt *= 4 if(I.damtype == BURN) diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index 4e6be19488..c2a58b0821 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -54,6 +54,15 @@ tastes = list("fish" = 1, "chips" = 1) foodtype = MEAT | VEGETABLES | FRIED +/obj/item/reagent_containers/food/snacks/fishfry + name = "fish fry" + desc = "All that and no bag of chips..." + icon_state = "fish_fry" + list_reagents = list (/datum/reagent/consumable/nutriment = 6, /datum/reagent/consumable/nutriment/vitamin = 3) + filling_color = "#ee7676" + tastes = list("fish" = 1, "pan seared vegtables" = 1) + foodtype = MEAT | VEGETABLES | FRIED + /obj/item/reagent_containers/food/snacks/sushi_basic name = "funa hosomaki" desc = "A small cylindrical kudzu skin, filled with rice and fish." diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm index 48bd0f1da4..d65907daea 100644 --- a/code/modules/food_and_drinks/food/snacks_pastry.dm +++ b/code/modules/food_and_drinks/food/snacks_pastry.dm @@ -144,6 +144,15 @@ is_decorated = TRUE filling_color = "#879630" +/obj/item/reagent_containers/food/snacks/donut/laugh + name = "sweet pea donut" + desc = "Goes great with a glass of Bastion Burbon!" + icon_state = "donut_laugh" + bonus_reagents = list(/datum/reagent/consumable/laughter = 3) + tastes = list("donut" = 3, "fizzy tutti frutti" = 1,) + is_decorated = TRUE + filling_color = "#803280" + //////////////////////JELLY DONUTS///////////////////////// /obj/item/reagent_containers/food/snacks/donut/jelly @@ -234,6 +243,15 @@ is_decorated = TRUE filling_color = "#879630" +/obj/item/reagent_containers/food/snacks/donut/jelly/laugh + name = "sweet pea jelly donut" + desc = "Goes great with a glass of Bastion Burbon!" + icon_state = "jelly_laugh" + bonus_reagents = list(/datum/reagent/consumable/laughter = 3) + tastes = list("jelly" = 3, "donut" = 1, "fizzy tutti frutti" = 1) + is_decorated = TRUE + filling_color = "#803280" + //////////////////////////SLIME DONUTS///////////////////////// /obj/item/reagent_containers/food/snacks/donut/jelly/slimejelly @@ -315,6 +333,15 @@ is_decorated = TRUE filling_color = "#879630" +/obj/item/reagent_containers/food/snacks/donut/jelly/slimejelly/laugh + name = "sweet pea jelly donut" + desc = "Goes great with a glass of Bastion Burbon!" + icon_state = "jelly_laugh" + bonus_reagents = list(/datum/reagent/consumable/laughter = 3) + tastes = list("jelly" = 3, "donut" = 1, "fizzy tutti frutti" = 1) + is_decorated = TRUE + filling_color = "#803280" + /obj/item/reagent_containers/food/snacks/donut/glaze name = "glazed donut" desc = "A sugar glazed donut." diff --git a/code/modules/food_and_drinks/food/snacks_salad.dm b/code/modules/food_and_drinks/food/snacks_salad.dm index a977eac3c2..be7c3d25da 100644 --- a/code/modules/food_and_drinks/food/snacks_salad.dm +++ b/code/modules/food_and_drinks/food/snacks_salad.dm @@ -124,4 +124,22 @@ trash = /obj/item/kitchen/knife bonus_reagents = list(/datum/reagent/medicine/earthsblood = 1, /datum/reagent/iron = 4) tastes = list("iron" = 1, "conspiracy" = 1) - foodtype = VEGETABLES \ No newline at end of file + foodtype = VEGETABLES + +/obj/item/reagent_containers/food/snacks/salad/edensalad + name = "\improper Salad of Eden" + desc = "A salad brimming with untapped potential." + icon_state = "eden_salad" + trash = /obj/item/reagent_containers/glass/bowl + list_reagents = list(/datum/reagent/consumable/nutriment = 7, /datum/reagent/consumable/nutriment/vitamin = 5, /datum/reagent/medicine/earthsblood = 3, /datum/reagent/medicine/omnizine = 5, /datum/reagent/drug/happiness = 2) + tastes = list("hope" = 1) + foodtype = VEGETABLES + +/obj/item/reagent_containers/food/snacks/salad/gumbo + name = "black eyed gumbo" + desc = "A spicy and savory meat and rice dish." + icon_state = "gumbo" + trash = /obj/item/reagent_containers/glass/bowl + list_reagents = list(/datum/reagent/consumable/capsaicin = 2, /datum/reagent/consumable/nutriment/vitamin = 3, /datum/reagent/consumable/nutriment = 5) + tastes = list("building heat" = 2, "savory meat and vegtables" = 1) + foodtype = GRAIN | MEAT | VEGETABLES diff --git a/code/modules/food_and_drinks/food/snacks_soup.dm b/code/modules/food_and_drinks/food/snacks_soup.dm index 3d70b32f2e..a6a251a84c 100644 --- a/code/modules/food_and_drinks/food/snacks_soup.dm +++ b/code/modules/food_and_drinks/food/snacks_soup.dm @@ -262,3 +262,13 @@ tastes = list("bungo" = 2, "hot curry" = 4, "tropical sweetness" = 1) filling_color = "#E6A625" foodtype = VEGETABLES | FRUIT | DAIRY + +/obj/item/reagent_containers/food/snacks/soup/peasoup + name = "pea soup" + desc = "A humble split pea soup." + icon_state = "peasoup" + bonus_reagents = list (/datum/reagent/consumable/nutriment/vitamin = 6, /datum/reagent/medicine/oculine = 2) + list_reagents = list (/datum/reagent/consumable/nutriment = 8) + tastes = list("creamy peas"= 2, "parsnip" = 1) + filling_color = "#9dc530" + foodtype = VEGETABLES diff --git a/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm b/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm index feba35da97..f09d3d6728 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm @@ -1,5 +1,5 @@ #define STORAGE_CAPACITY 30 -#define LIQUID_CAPACIY 200 +#define LIQUID_CAPACITY 200 #define MIXER_CAPACITY 100 /obj/machinery/food_cart @@ -19,7 +19,7 @@ /obj/machinery/food_cart/Initialize() . = ..() - create_reagents(LIQUID_CAPACIY, OPENCONTAINER | NO_REACT) + create_reagents(LIQUID_CAPACITY, OPENCONTAINER | NO_REACT) mixer = new /obj/item/reagent_containers(src, MIXER_CAPACITY) mixer.name = "Mixer" @@ -60,6 +60,9 @@ return food_stored >= STORAGE_CAPACITY /obj/machinery/food_cart/attackby(obj/item/O, mob/user, params) + if(O.tool_behaviour == TOOL_WRENCH) + default_unfasten_wrench(user, O, 0) + return TRUE if(istype(O, /obj/item/reagent_containers/food/drinks/drinkingglass)) var/obj/item/reagent_containers/food/drinks/drinkingglass/DG = O if(!DG.reagents.total_volume) //glass is empty @@ -106,7 +109,7 @@ return if(href_list["disposeI"]) - reagents.del_reagent(href_list["disposeI"]) + reagents.del_reagent(text2path(href_list["disposeI"])) if(href_list["dispense"]) if(stored_food[href_list["dispense"]]-- <= 0) @@ -116,9 +119,13 @@ if(sanitize(O.name) == href_list["dispense"]) O.forceMove(drop_location()) break + log_combat(usr, src, "dispensed [O] from", null, "with [stored_food[href_list["dispense"]]] remaining") if(href_list["portion"]) - portion = clamp(input("How much drink do you want to dispense per glass?") as num, 0, 50) + portion = clamp(input("How much drink do you want to dispense per glass?") as num|null, 0, 50) + + if (isnull(portion)) + return if(href_list["pour"] || href_list["m_pour"]) if(glasses-- <= 0) @@ -127,16 +134,16 @@ else var/obj/item/reagent_containers/food/drinks/drinkingglass/DG = new(loc) if(href_list["pour"]) - reagents.trans_id_to(DG, href_list["pour"], portion) + reagents.trans_id_to(DG, text2path(href_list["pour"]), portion) if(href_list["m_pour"]) - mixer.reagents.trans_id_to(DG, href_list["m_pour"], portion) + mixer.reagents.trans_id_to(DG, text2path(href_list["m_pour"]), portion) if(href_list["mix"]) - if(reagents.trans_id_to(mixer, href_list["mix"], portion) == 0) + if(reagents.trans_id_to(mixer, text2path(href_list["mix"]), portion) == 0) to_chat(usr, "[mixer] is full!") if(href_list["transfer"]) - if(mixer.reagents.trans_id_to(src, href_list["transfer"], portion) == 0) + if(mixer.reagents.trans_id_to(src, text2path(href_list["transfer"]), portion) == 0) to_chat(usr, "[src] is full!") updateDialog() @@ -152,5 +159,5 @@ qdel(src) #undef STORAGE_CAPACITY -#undef LIQUID_CAPACIY +#undef LIQUID_CAPACITY #undef MIXER_CAPACITY diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_donut.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_donut.dm index ac5576ac25..c376ab4025 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_donut.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_donut.dm @@ -121,6 +121,15 @@ datum/crafting_recipe/food/donut/meat ) result = /obj/item/reagent_containers/food/snacks/donut/matcha + +/datum/crafting_recipe/food/donut/laugh + name = "Sweet Pea Donut" + reqs = list( + /datum/reagent/consumable/laughsyrup = 3, + /obj/item/reagent_containers/food/snacks/donut/plain = 1 + ) + result = /obj/item/reagent_containers/food/snacks/donut/laugh + ////////////////////////////////////////////////////JELLY DONUTS/////////////////////////////////////////////////////// /datum/crafting_recipe/food/donut/jelly/apple @@ -187,6 +196,14 @@ datum/crafting_recipe/food/donut/meat ) result = /obj/item/reagent_containers/food/snacks/donut/jelly/trumpet +/datum/crafting_recipe/food/donut/jelly/laugh + name = "Sweet Pea Jelly Donut" + reqs = list( + /datum/reagent/consumable/laughsyrup = 3, + /obj/item/reagent_containers/food/snacks/donut/jelly/plain = 1 + ) + result = /obj/item/reagent_containers/food/snacks/donut/jelly/laugh + ////////////////////////////////////////////////////SLIME DONUTS/////////////////////////////////////////////////////// /datum/crafting_recipe/food/donut/slimejelly/apple @@ -253,3 +270,11 @@ datum/crafting_recipe/food/donut/meat /obj/item/reagent_containers/food/snacks/donut/jelly/slimejelly/plain = 1 ) result = /obj/item/reagent_containers/food/snacks/donut/jelly/slimejelly/matcha + +/datum/crafting_recipe/food/donut/slimejelly/laugh + name = "Sweet Pea Jelly Donut" + reqs = list( + /datum/reagent/consumable/laughsyrup = 3, + /obj/item/reagent_containers/food/snacks/donut/jelly/slimejelly/plain = 1 + ) + result = /obj/item/reagent_containers/food/snacks/donut/jelly/slimejelly/laugh diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm index e7fdeffe14..60b363c168 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm @@ -171,3 +171,14 @@ ) result = /obj/item/reagent_containers/food/snacks/salad/ricepork subcategory = CAT_MEAT + +/datum/crafting_recipe/food/gumbo + name = "Black eyed gumbo" + reqs = list( + /obj/item/reagent_containers/food/snacks/salad/boiledrice = 1, + /obj/item/reagent_containers/food/snacks/grown/peas = 1, + /obj/item/reagent_containers/food/snacks/grown/chili = 1, + /obj/item/reagent_containers/food/snacks/meat/cutlet = 1 + ) + result = /obj/item/reagent_containers/food/snacks/salad/gumbo + subcategory = CAT_MEAT diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm index f25aaab6fd..7cf70dacf2 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm @@ -93,4 +93,17 @@ /obj/item/reagent_containers/food/snacks/grown/cabbage = 1 ) result = /obj/item/reagent_containers/food/snacks/salad/caesar - subcategory = CAT_SALAD \ No newline at end of file + subcategory = CAT_SALAD + + +/datum/crafting_recipe/food/edensalad + name = "Salad of Eden" + reqs = list( + /obj/item/reagent_containers/glass/bowl =1, + /obj/item/reagent_containers/food/snacks/grown/ambrosia/vulgaris = 1, + /obj/item/reagent_containers/food/snacks/grown/ambrosia/deus = 1, + /obj/item/reagent_containers/food/snacks/grown/ambrosia/gaia = 1, + /obj/item/reagent_containers/food/snacks/grown/peace = 1 + ) + result = /obj/item/reagent_containers/food/snacks/salad/edensalad + subcategory = CAT_SALAD diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm index 45083e0374..d1c13115a5 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_seafood.dm @@ -135,4 +135,14 @@ /obj/item/reagent_containers/food/snacks/carpmeat = 1 ) result = /obj/item/reagent_containers/food/snacks/fishandchips - subcategory = CAT_SEAFOOD \ No newline at end of file + subcategory = CAT_SEAFOOD + +/datum/crafting_recipe/food/fishfry + name = "Fish fry" + reqs = list( + /obj/item/reagent_containers/food/snacks/grown/corn = 1, + /obj/item/reagent_containers/food/snacks/grown/peas =1, + /obj/item/reagent_containers/food/snacks/carpmeat = 1 + ) + result = /obj/item/reagent_containers/food/snacks/fishfry + subcategory = CAT_SEAFOOD diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm index ae8bd6a49c..8f4b4c2726 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm @@ -255,4 +255,16 @@ /obj/item/reagent_containers/glass/bowl = 1 ) result= /obj/item/reagent_containers/food/snacks/soup/wish - subcategory = CAT_SOUP \ No newline at end of file + subcategory = CAT_SOUP + + +/datum/crafting_recipe/food/peasoup + name = "Pea soup" + reqs = list( + /datum/reagent/water = 10, + /obj/item/reagent_containers/food/snacks/grown/peas = 2, + /obj/item/reagent_containers/food/snacks/grown/parsnip = 1, + /obj/item/reagent_containers/food/snacks/grown/carrot = 1 + ) + result = /obj/item/reagent_containers/food/snacks/soup/peasoup + subcategory = CAT_SOUP diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index d63e011f5b..83bb67b879 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -189,6 +189,14 @@ begin_day = 22 begin_month = APRIL +/datum/holiday/lesbianvisibility + name = "Lesbian Visibility Day" + begin_day = 26 + begin_month = APRIL + +/datum/holiday/lesbianvisibility/greet() + return "Today is Lesbian Visibility Day!" + /datum/holiday/labor name = "Labor Day" begin_day = 1 @@ -292,6 +300,14 @@ /datum/holiday/programmers/getStationPrefix() return pick("span>","DEBUG: ","null","/list","EVENT PREFIX NOT FOUND") //Portability +/datum/holiday/bivisibility + name = "Bisexual Visibility Day" + begin_day = 23 + begin_month = SEPTEMBER + +/datum/holiday/bivisibility/greet() + return "Today is Bisexual Visibility Day!" + /datum/holiday/questions name = "Stupid-Questions Day" begin_day = 28 @@ -314,12 +330,25 @@ begin_month = OCTOBER drone_hat = /obj/item/clothing/head/papersack/smiley +/datum/holiday/comingoutday + name = "Coming Out Day" + begin_day = 11 + begin_month = OCTOBER + /datum/holiday/boss name = "Boss' Day" begin_day = 16 begin_month = OCTOBER drone_hat = /obj/item/clothing/head/that +/datum/holiday/intersexawareness + name = "Intersex Awareness Day" + begin_day = 26 + begin_month = OCTOBER + +/datum/holiday/intersexawareness/greet() + return "Today is Intersex Awareness Day! It has been [text2num(time2text(world.timeofday, "YYYY")) - 1996] years since the first public protest speaking out against the human rights issues faced by intersex people." + /datum/holiday/halloween name = HALLOWEEN begin_day = 28 @@ -359,6 +388,23 @@ begin_month = NOVEMBER drone_hat = /obj/item/reagent_containers/food/snacks/grown/moonflower +/datum/holiday/transawareness + name = "Transgender Awareness Week" + begin_day = 13 + begin_month = NOVEMBER + end_day = 19 + +/datum/holiday/transawareness/greet() + return "This week is Transgender Awareness Week!" + +/datum/holiday/transremembrance + name = "Transgender Day of Remembrance" + begin_day = 20 + begin_month = NOVEMBER + +/datum/holiday/transremembrance/greet() + return "Today is the Transgender Day of Remembrance." + /datum/holiday/hello name = "Saying-'Hello' Day" begin_day = 21 @@ -397,6 +443,26 @@ begin_month = OCTOBER begin_weekday = MONDAY +/datum/holiday/aceawareness + name = "Asexual Awareness Week" + begin_month = OCTOBER + +/datum/holiday/aceawareness/greet() + return "This week is Asexual Awareness Week!" + +/datum/holiday/aceawareness/shouldCelebrate(dd, mm, yy, ww, ddd) //Ace awareness week falls on the last full week of October. + if(mm != begin_month) + return FALSE //it's not even the right month + var/daypointer = world.timeofday - ((WEEKDAY2NUM(ddd) - 1) * 24 HOURS) + if(text2num(time2text(daypointer, "MM")) != mm) + return FALSE //it's the beginning of the month and it isn't even a full week + daypointer += (24 HOURS * 6) + if(text2num(time2text(daypointer, "MM")) != mm) + return FALSE //this is the end of the month, and it is not a full week. + daypointer += (24 HOURS * 7) + if(text2num(time2text(daypointer, "MM")) != mm) + return TRUE //the end of next week falls on a different month, meaning that the current week is the last full week + /datum/holiday/mother name = "Mother's Day" begin_week = 2 @@ -421,11 +487,30 @@ /datum/holiday/pride/getStationPrefix() return pick("Pride", "Gay", "Bi", "Trans", "Lesbian", "Ace", "Aro", "Agender", pick("Enby", "Enbie"), "Pan", "Intersex", "Demi", "Poly", "Closeted", "Genderfluid") +/datum/holiday/stonewall + name = "Stonewall Riots Anniversary" + begin_day = 28 + begin_month = JUNE + +/datum/holiday/stonewall/greet() //Not gonna lie, I was fairly tempted to make this use the IC year instead of the IRL year, but I was worried that it would have caused too much confusion. + return "Today marks the [text2num(time2text(world.timeofday, "YYYY")) - 1969]\th anniversary of the riots at the Stonewall Inn!" + /datum/holiday/moth name = "Moth Week" + begin_month = JULY /datum/holiday/moth/shouldCelebrate(dd, mm, yy, ww, ddd) //National Moth Week falls on the last full week of July - return mm == JULY && (ww == 4 || (ww == 5 && ddd == SUNDAY)) + if(mm != begin_month) + return FALSE //it's not even the right month + var/daypointer = world.timeofday - ((WEEKDAY2NUM(ddd) - 1) * 24 HOURS) + if(text2num(time2text(daypointer, "MM")) != mm) + return FALSE //it's the beginning of the month and it isn't even a full week + daypointer += (24 HOURS * 6) + if(text2num(time2text(daypointer, "MM")) != mm) + return FALSE //this is the end of the month, and it is not a full week. + daypointer += (24 HOURS * 7) + if(text2num(time2text(daypointer, "MM")) != mm) + return TRUE //the end of next week falls on a different month, meaning that the current week is the last full week /datum/holiday/moth/getStationPrefix() return pick("Mothball","Lepidopteran","Lightbulb","Moth","Giant Atlas","Twin-spotted Sphynx","Madagascan Sunset","Luna","Death's Head","Emperor Gum","Polyphenus","Oleander Hawk","Io","Rosy Maple","Cecropia","Noctuidae","Giant Leopard","Dysphania Militaris","Garden Tiger") diff --git a/code/modules/keybindings/keybind/__defines.dm b/code/modules/keybindings/keybind/__defines.dm index baa095987c..93d033eea5 100644 --- a/code/modules/keybindings/keybind/__defines.dm +++ b/code/modules/keybindings/keybind/__defines.dm @@ -8,6 +8,7 @@ #define CATEGORY_MISC "MISC" #define CATEGORY_MOVEMENT "MOVEMENT" #define CATEGORY_TARGETING "TARGETING" +#define CATEGORY_COMBAT "COMBAT" #define WEIGHT_HIGHEST 0 #define WEIGHT_ADMIN 10 diff --git a/code/modules/keybindings/keybind/combat.dm b/code/modules/keybindings/keybind/combat.dm new file mode 100644 index 0000000000..457fbb0cb2 --- /dev/null +++ b/code/modules/keybindings/keybind/combat.dm @@ -0,0 +1,38 @@ +/datum/keybinding/living/toggle_combat_mode + hotkey_keys = list("C") + name = "toggle_combat_mode" + full_name = "Toggle combat mode" + category = CATEGORY_COMBAT + description = "Toggles whether or not you're in combat mode." + +/datum/keybinding/living/toggle_combat_mode/down(client/user) + SEND_SIGNAL(user.mob, COMSIG_TOGGLE_COMBAT_MODE) + return TRUE + +/datum/keybinding/living/active_block + hotkey_keys = list("Northwest", "F") // HOME + name = "active_block" + full_name = "Block (Hold)" + category = CATEGORY_COMBAT + description = "Hold down to actively block with your currently in-hand object." + +/datum/keybinding/living/active_block/down(client/user) + var/mob/living/L = user.mob + L.keybind_start_active_blocking() + return TRUE + +/datum/keybinding/living/active_block/up(client/user) + var/mob/living/L = user.mob + L.keybind_stop_active_blocking() + +/datum/keybinding/living/active_parry + hotkey_keys = list("Insert", "G") + name = "active_parry" + full_name = "Parry" + category = CATEGORY_COMBAT + description = "Press to initiate a parry sequence with your currently in-hand object." + +/datum/keybinding/living/active_parry/down(client/user) + var/mob/living/L = user.mob + L.keybind_parry() + return TRUE diff --git a/code/modules/keybindings/keybind/living.dm b/code/modules/keybindings/keybind/living.dm index 38c666e186..b5921b378a 100644 --- a/code/modules/keybindings/keybind/living.dm +++ b/code/modules/keybindings/keybind/living.dm @@ -16,16 +16,6 @@ L.resist() return TRUE -/datum/keybinding/living/toggle_combat_mode - hotkey_keys = list("C") - name = "toggle_combat_mode" - full_name = "Toggle combat mode" - description = "Toggles whether or not you're in combat mode." - -/datum/keybinding/living/toggle_combat_mode/down(client/user) - SEND_SIGNAL(user.mob, COMSIG_TOGGLE_COMBAT_MODE) - return TRUE - /datum/keybinding/living/toggle_resting hotkey_keys = list("V") name = "toggle_resting" diff --git a/code/modules/keybindings/keybind/mob.dm b/code/modules/keybindings/keybind/mob.dm index 083d4a19fd..2ce4dc35a0 100644 --- a/code/modules/keybindings/keybind/mob.dm +++ b/code/modules/keybindings/keybind/mob.dm @@ -17,7 +17,7 @@ return TRUE /datum/keybinding/mob/cycle_intent_right - hotkey_keys = list("Northwest", "F") // HOME + hotkey_keys = list("Unbound") name = "cycle_intent_right" full_name = "Cycle Action Intent Right" description = "" @@ -28,7 +28,7 @@ return TRUE /datum/keybinding/mob/cycle_intent_left - hotkey_keys = list("Insert", "G") + hotkey_keys = list("Unbound") name = "cycle_intent_left" full_name = "Cycle Action Intent Left" description = "" diff --git a/code/modules/lighting/emissive_blocker.dm b/code/modules/lighting/emissive_blocker.dm index b69a474009..04c1bb7302 100644 --- a/code/modules/lighting/emissive_blocker.dm +++ b/code/modules/lighting/emissive_blocker.dm @@ -11,6 +11,7 @@ plane = EMISSIVE_BLOCKER_PLANE layer = EMISSIVE_BLOCKER_LAYER mouse_opacity = MOUSE_OPACITY_TRANSPARENT + rad_flags = RAD_NO_CONTAMINATE | RAD_PROTECT_CONTENTS //Why? //render_targets copy the transform of the target as well, but vis_contents also applies the transform //to what's in it. Applying RESET_TRANSFORM here makes vis_contents not apply the transform. diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index f7e1f24b63..36babd9b95 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -176,9 +176,6 @@ SSticker.queue_delay = 4 qdel(src) - if(!ready && href_list["preference"]) - if(client) - client.prefs.process_link(src, href_list) else if(!href_list["late_join"]) new_player_panel() @@ -582,6 +579,12 @@ qdel(src) /mob/dead/new_player/proc/ViewManifest() + if(!client) + return + if(world.time < client.crew_manifest_delay) + return + client.crew_manifest_delay = world.time + (1 SECONDS) + var/dat = "" dat += "

    Crew Manifest

    " dat += GLOB.data_core.get_manifest(OOC = 1) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 5364486b04..397af1b9d0 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -717,6 +717,12 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set name = "View Crew Manifest" set category = "Ghost" + if(!client) + return + if(world.time < client.crew_manifest_delay) + return + client.crew_manifest_delay = world.time + (1 SECONDS) + var/dat dat += "

    Crew Manifest

    " dat += GLOB.data_core.get_manifest() diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index fad3a7e534..74775203b1 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -333,6 +333,7 @@ I.moveToNullspace() else I.forceMove(newloc) + on_item_dropped(I) if(I.dropped(src) == ITEM_RELOCATED_BY_DROPPED) return FALSE return TRUE diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index 11849641ef..727a22f844 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -77,20 +77,18 @@ return TRUE /mob/living/carbon/alien/humanoid/get_standard_pixel_y_offset(lying = 0) + . = ..() if(leaping) - return -32 - else if(custom_pixel_y_offset) - return custom_pixel_y_offset - else - return initial(pixel_y) + . -= 32 + if(custom_pixel_y_offset) + . += custom_pixel_y_offset /mob/living/carbon/alien/humanoid/get_standard_pixel_x_offset(lying = 0) + . = ..() if(leaping) - return -32 - else if(custom_pixel_x_offset) - return custom_pixel_x_offset - else - return initial(pixel_x) + . -= 32 + if(custom_pixel_x_offset) + . += custom_pixel_x_offset /mob/living/carbon/alien/humanoid/get_permeability_protection(list/target_zones) return 0.8 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 62819f7de8..7b201e7492 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -440,10 +440,9 @@ return /mob/living/carbon/get_standard_pixel_y_offset(lying = 0) + . = ..() if(lying) - return -6 - else - return initial(pixel_y) + . -= 6 /mob/living/carbon/proc/accident(obj/item/I) if(!I || (I.item_flags & ABSTRACT) || HAS_TRAIT(I, TRAIT_NODROP)) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 16c09a8166..ca7547cc94 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -76,11 +76,13 @@ visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded) -/mob/living/carbon/attacked_by(obj/item/I, mob/living/user) - var/totitemdamage = pre_attacked_by(I, user) +/mob/living/carbon/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) + var/totitemdamage = pre_attacked_by(I, user) * damage_multiplier var/impacting_zone = (user == src)? check_zone(user.zone_selected) : ran_zone(user.zone_selected) - if((user != src) && (mob_run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, null) & BLOCK_SUCCESS)) + var/list/block_return = list() + if((user != src) && (mob_run_block(I, totitemdamage, "the [I]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone, block_return) & BLOCK_SUCCESS)) return FALSE + totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) 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] diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index cc390b9329..65e59d0e29 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -20,7 +20,7 @@ if(istype(J) && (movement_dir || J.stabilizers) && J.allow_thrust(0.01, src)) return 1 -/mob/living/carbon/Move(NewLoc, direct) +/mob/living/carbon/Moved() . = ..() if(. && (movement_type & FLOATING)) //floating is easy if(HAS_TRAIT(src, TRAIT_NOHUNGER)) diff --git a/code/modules/mob/living/carbon/human/human_block.dm b/code/modules/mob/living/carbon/human/human_block.dm index 8fe0376a08..4ba7e95564 100644 --- a/code/modules/mob/living/carbon/human/human_block.dm +++ b/code/modules/mob/living/carbon/human/human_block.dm @@ -1,8 +1,11 @@ /mob/living/carbon/human/get_blocking_items() . = ..() if(wear_suit) - . |= wear_suit + if(!.[wear_suit]) + .[wear_suit] = wear_suit.block_priority if(w_uniform) - . |= w_uniform + if(!.[w_uniform]) + .[w_uniform] = w_uniform.block_priority if(wear_neck) - . |= wear_neck + if(!.[wear_neck]) + .[wear_neck] = wear_neck.block_priority diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 0a12718e5d..63296021ff 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -73,7 +73,7 @@ ..() -/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user) +/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(!I || !user) return 0 @@ -90,7 +90,7 @@ SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area) // the attacked_by code varies among species - return dna.species.spec_attacked_by(I, user, affecting, a_intent, src) + return dna.species.spec_attacked_by(I, user, affecting, a_intent, src, attackchain_flags, damage_multiplier) /mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) @@ -209,7 +209,7 @@ /mob/living/carbon/human/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) if(!dam_zone) //Dismemberment successful return TRUE diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 2ed5e349c3..e1a7a3b221 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1700,12 +1700,14 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if("disarm") disarm(M, H, attacker_style) -/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) - var/totitemdamage = H.pre_attacked_by(I, user) +/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H, attackchain_flags = NONE, damage_multiplier = 1) + var/totitemdamage = H.pre_attacked_by(I, user) * damage_multiplier // Allows you to put in item-specific reactions based on species if(user != H) - if(H.mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, null) & BLOCK_SUCCESS) + var/list/block_return = list() + if(H.mob_run_block(I, totitemdamage, "the [I.name]", ((attackchain_flags & ATTACKCHAIN_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE) | ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, block_return) & BLOCK_SUCCESS) return 0 + totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return) if(H.check_martial_melee_block()) H.visible_message("[H] blocks [I]!") return 0 @@ -1720,6 +1722,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/armor_block = H.run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened a hit to your [hit_area].",I.armour_penetration) armor_block = min(90,armor_block) //cap damage reduction at 90% var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) + var/weakness = H.check_weakness(I, user) apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index db6a1e9560..b780af6b8a 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -459,8 +459,18 @@ /datum/action/innate/slime_change/proc/change_form() var/mob/living/carbon/human/H = owner - var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel") - if(select_alteration == "Hair Style") + var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Body Color","Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel") + + if(select_alteration == "Body Color") + var/new_color = input(owner, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null + if(new_color) + var/temp_hsv = RGBtoHSV(new_color) + if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright + H.dna.features["mcolor"] = sanitize_hexcolor(new_color) + H.update_body() + else + to_chat(H, "Invalid color. Your color is not bright enough.") + else if(select_alteration == "Hair Style") if(H.gender == MALE) var/new_style = input(owner, "Select a facial hair style", "Hair Alterations") as null|anything in GLOB.facial_hair_styles_list if(new_style) diff --git a/code/modules/mob/living/carbon/monkey/monkey_defense.dm b/code/modules/mob/living/carbon/monkey/monkey_defense.dm index 50793eb821..8f862af8fa 100644 --- a/code/modules/mob/living/carbon/monkey/monkey_defense.dm +++ b/code/modules/mob/living/carbon/monkey/monkey_defense.dm @@ -141,7 +141,7 @@ /mob/living/carbon/monkey/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) if(!dam_zone) //Dismemberment successful return TRUE diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 3257b0e3bf..87d5abb89e 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -78,8 +78,6 @@ apply_damage(brain, BRAIN, def_zone, blocked) return 1 - - /mob/living/proc/apply_effect(effect = 0,effecttype = EFFECT_STUN, blocked = FALSE, knockdown_stamoverride, knockdown_stammax) var/hit_percent = (100-blocked)/100 if(!effect || (hit_percent <= 0)) @@ -108,7 +106,7 @@ return 1 -/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = FALSE, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax) +/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, irradiate = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = 0, stamina = 0, jitter = 0, kd_stamoverride, kd_stammax) if(blocked >= 100) return BULLET_ACT_BLOCK if(stun) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index d3199d6f8b..5e04e3ec16 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -64,6 +64,8 @@ handle_gravity() + handle_block_parry(seconds) + if(machine) machine.check_eye(src) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index c1038fbb41..5b21abfb84 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -19,6 +19,8 @@ med_hud_set_status() /mob/living/Destroy() + end_parry_sequence() + stop_active_blocking() if(LAZYLEN(status_effects)) for(var/s in status_effects) var/datum/status_effect/S = s diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm new file mode 100644 index 0000000000..e1b90716b6 --- /dev/null +++ b/code/modules/mob/living/living_active_block.dm @@ -0,0 +1,275 @@ +// Active directional block system. Shared code is in [living_blocking_parrying.dm] +/mob/living/proc/stop_active_blocking(was_forced = FALSE) + if(!(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))) + return FALSE + var/obj/item/I = active_block_item + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCKING | COMBAT_FLAG_ACTIVE_BLOCK_STARTING) + active_block_effect_end() + active_block_item = null + REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) + REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT) + remove_movespeed_modifier(/datum/movespeed_modifier/active_block) + var/datum/block_parry_data/data = I.get_block_parry_data() + if(timeToNextMove() < data.block_end_click_cd_add) + changeNext_move(data.block_end_click_cd_add) + return TRUE + +/mob/living/proc/start_active_blocking(obj/item/I) + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) + return FALSE + if(!(I in held_items)) + return FALSE + var/datum/block_parry_data/data = I.get_block_parry_data() + if(!istype(data)) //Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened. + CRASH("start_active_blocking called with an item with no valid data: [I] --> [I.block_parry_data]!") + combat_flags |= COMBAT_FLAG_ACTIVE_BLOCKING + active_block_item = I + if(data.block_lock_attacking) + ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point + if(data.block_lock_sprinting) + ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT) + add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown) + active_block_effect_start() + return TRUE + +/// Visual effect setup for starting a directional block +/mob/living/proc/active_block_effect_start() + visible_message("[src] raises their [active_block_item], dropping into a defensive stance!") + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_OUT) + +/// Visual effect cleanup for starting a directional block +/mob/living/proc/active_block_effect_end() + visible_message("[src] lowers their [active_block_item].") + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN) + +/mob/living/proc/continue_starting_active_block() + if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) + return DO_AFTER_STOP + return (combat_flags & COMBAT_FLAG_ACTIVE_BLOCK_STARTING)? DO_AFTER_CONTINUE : DO_AFTER_STOP + +/mob/living/get_standard_pixel_x_offset() + . = ..() + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) + if(dir & EAST) + . += 8 + if(dir & WEST) + . -= 8 + +/mob/living/get_standard_pixel_y_offset() + . = ..() + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) + if(dir & NORTH) + . += 8 + if(dir & SOUTH) + . -= 8 + +/** + * Proc called by keybindings to toggle active blocking. + */ +/mob/living/proc/keybind_toggle_active_blocking() + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) + return keybind_stop_active_blocking() + else + return keybind_start_active_blocking() + +/** + * Proc called by keybindings to start active blocking. + */ +/mob/living/proc/keybind_start_active_blocking() + if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) + return FALSE + if(!(combat_flags & COMBAT_FLAG_BLOCK_CAPABLE)) + to_chat(src, "You're not something that can actively block.") + return FALSE + // QOL: Instead of trying to just block with held item, grab first available item. + var/obj/item/I = find_active_block_item() + if(!I) + to_chat(src, "You can't block with your bare hands!") + return + if(!I.can_active_block()) + to_chat(src, "[I] is either not capable of being used to actively block, or is not currently in a state that can! (Try wielding it if it's twohanded, for example.)") + return + // QOL: Attempt to toggle on combat mode if it isn't already + SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) + if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) + to_chat(src, "You must be in combat mode to actively block!") + return FALSE + var/datum/block_parry_data/data = I.get_block_parry_data() + var/delay = data.block_start_delay + combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN) + if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I)) + to_chat(src, "You fail to raise [I].") + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) + return + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) + start_active_blocking(I) + +/** + * Gets the first item we can that can block, but if that fails, default to active held item.COMSIG_ENABLE_COMBAT_MODE + */ +/mob/living/proc/find_active_block_item() + var/obj/item/held = get_active_held_item() + if(!held?.can_active_block()) + for(var/obj/item/I in held_items - held) + if(I.can_active_block()) + return I + return held + +/** + * Proc called by keybindings to stop active blocking. + */ +/mob/living/proc/keybind_stop_active_blocking() + combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) + if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING) + stop_active_blocking(FALSE) + return TRUE + +/** + * Returns if we can actively block. + */ +/obj/item/proc/can_active_block() + return block_parry_data && (item_flags & ITEM_CAN_BLOCK) + +/** + * Calculates FINAL ATTACK DAMAGE after mitigation + */ +/obj/item/proc/active_block_calculate_final_damage(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + var/datum/block_parry_data/data = get_block_parry_data() + var/absorption = data.attack_type_list_scan(data.block_damage_absorption_override, attack_type) + var/efficiency = data.attack_type_list_scan(data.block_damage_multiplier_override, attack_type) + var/limit = data.attack_type_list_scan(data.block_damage_limit_override, attack_type) + // must use isnulls to handle 0's. + if(isnull(absorption)) + absorption = data.block_damage_absorption + if(isnull(efficiency)) + efficiency = data.block_damage_multiplier + if(isnull(limit)) + limit = data.block_damage_limit + // now we calculate damage to reduce. + var/final_damage = 0 + // apply limit + if(damage > limit) //clamp and apply overrun + final_damage += (damage - limit) + damage = limit + // apply absorption + damage -= min(absorption, damage) //this way if damage is less than absorption it 0's properly. + // apply multiplier to remaining + final_damage += (damage * efficiency) + return final_damage + +/// Amount of stamina from damage blocked. Note that the damage argument is damage_blocked. +/obj/item/proc/active_block_stamina_cost(mob/living/owner, atom/object, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + var/datum/block_parry_data/data = get_block_parry_data() + var/efficiency = data.attack_type_list_scan(data.block_stamina_efficiency_override, attack_type) + if(isnull(efficiency)) + efficiency = data.block_stamina_efficiency + var/multiplier = 1 + if(!CHECK_MOBILITY(owner, MOBILITY_STAND)) + multiplier = data.attack_type_list_scan(data.block_resting_stamina_penalty_multiplier_override, attack_type) + if(isnull(multiplier)) + multiplier = data.block_resting_stamina_penalty_multiplier + return (damage_blocked / efficiency) * multiplier + +/// Apply the stamina damage to our user, notice how damage argument is stamina_amount. +/obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + var/datum/block_parry_data/data = get_block_parry_data() + if(iscarbon(owner)) + var/mob/living/carbon/C = owner + var/held_index = C.get_held_index_of_item(src) + var/obj/item/bodypart/BP = C.hand_bodyparts[held_index] + if(!BP?.body_zone) + return C.adjustStaminaLossBuffered(stamina_amount) //nah + var/zone = BP.body_zone + var/stamina_to_zone = data.block_stamina_limb_ratio * stamina_amount + var/stamina_to_chest = stamina_amount - stamina_to_zone + var/stamina_buffered = stamina_to_chest * data.block_stamina_buffer_ratio + stamina_to_chest -= stamina_buffered + C.apply_damage(stamina_to_zone, STAMINA, zone) + C.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST) + C.adjustStaminaLossBuffered(stamina_buffered) + else + owner.adjustStaminaLossBuffered(stamina_amount) + +/obj/item/proc/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) + return + +/obj/item/proc/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) + if(!can_active_block()) + return BLOCK_NONE + var/datum/block_parry_data/data = get_block_parry_data() + if(attack_type && !(attack_type & data.can_block_attack_types)) + return BLOCK_NONE + var/incoming_direction + if(isnull(override_direction)) + if(istype(object, /obj/item/projectile)) + var/obj/item/projectile/P = object + incoming_direction = angle2dir(P.Angle) + else + incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) + if(!CHECK_MOBILITY(owner, MOBILITY_STAND) && !(data.block_resting_attack_types_anydir & attack_type) && (!(data.block_resting_attack_types_directional & attack_type) || !can_block_direction(owner.dir, incoming_direction))) + return BLOCK_NONE + else if(!can_block_direction(owner.dir, incoming_direction)) + return BLOCK_NONE + block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE + var/final_damage = active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/damage_blocked = damage - final_damage + var/stamina_cost = active_block_stamina_cost(owner, object, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + active_block_do_stamina_damage(owner, object, stamina_cost, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - final_damage + block_return[BLOCK_RETURN_SET_DAMAGE_TO] = final_damage + . = BLOCK_SHOULD_CHANGE_DAMAGE + if((final_damage <= 0) || (damage <= 0)) + . |= BLOCK_SUCCESS //full block + owner.visible_message("[owner] blocks \the [attack_text] with [src]!") + else + owner.visible_message("[owner] dampens \the [attack_text] with [src]!") + block_return[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = data.block_projectile_mitigation + if(length(data.block_sounds)) + playsound(loc, pickweight(data.block_sounds), 75, TRUE) + on_active_block(owner, object, damage, damage_blocked, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return, override_direction) + +/obj/item/proc/check_active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(!can_active_block()) + return + var/incoming_direction = get_dir(get_turf(attacker) || get_turf(object), src) + if(!can_block_direction(owner.dir, incoming_direction)) + return + block_return[BLOCK_RETURN_ACTIVE_BLOCK] = TRUE + block_return[BLOCK_RETURN_ACTIVE_BLOCK_DAMAGE_MITIGATED] = damage - active_block_calculate_final_damage(owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + +/** + * Gets the block direction bitflags of what we can block. + */ +/obj/item/proc/blockable_directions() + var/datum/block_parry_data/data = get_block_parry_data() + return data.can_block_directions + +/** + * Checks if we can block from a specific direction from our direction. + * + * @params + * * our_dir - our direction. + * * their_dir - their direction. Must be a single direction, or NONE for an attack from the same tile. This is incoming direction. + */ +/obj/item/proc/can_block_direction(our_dir, their_dir) + their_dir = turn(their_dir, 180) + if(our_dir != NORTH) + var/turn_angle = dir2angle(our_dir) + // dir2angle(), ss13 proc is clockwise so dir2angle(EAST) == 90 + // turn(), byond proc is counterclockwise so turn(NORTH, 90) == WEST + their_dir = turn(their_dir, turn_angle) + return (DIR2BLOCKDIR(their_dir) & blockable_directions()) + +/** + * can_block_direction but for "compound" directions to check all of them and return the number of directions that were blocked. + * + * @params + * * our_dir - our direction. + * * their_dirs - list of their directions as we cannot use bitfields here. + */ +/obj/item/proc/can_block_directions_multiple(our_dir, list/their_dirs) + . = FALSE + for(var/i in their_dirs) + . |= can_block_direction(our_dir, i) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm new file mode 100644 index 0000000000..50b51d4d95 --- /dev/null +++ b/code/modules/mob/living/living_active_parry.dm @@ -0,0 +1,332 @@ +// Active parry system goes in here. +/** + * Determines if we can actively parry. + */ +/obj/item/proc/can_active_parry() + return block_parry_data && (item_flags & ITEM_CAN_PARRY) + +/** + * Called from keybindings. + */ +/mob/living/proc/keybind_parry() + initiate_parry_sequence() + +/** + * Initiates a parrying sequence. + */ +/mob/living/proc/initiate_parry_sequence() + if(parrying) + return // already parrying + if(!(combat_flags & COMBAT_FLAG_PARRY_CAPABLE)) + to_chat(src, "You are not something that can parry attacks.") + return + // Prioritize item, then martial art, then unarmed. + // yanderedev else if time + var/obj/item/using_item = get_active_held_item() + var/datum/block_parry_data/data + var/method + if(using_item?.can_active_parry()) + data = using_item.block_parry_data + method = ITEM_PARRY + else if(mind?.martial_art?.can_martial_parry) + data = mind.martial_art.block_parry_data + method = MARTIAL_PARRY + else if(combat_flags & COMBAT_FLAG_UNARMED_PARRY) + data = block_parry_data + method = UNARMED_PARRY + else + // QOL: If none of the above work, try to find another item. + var/obj/item/backup = find_backup_parry_item() + if(!backup) + to_chat(src, "You have nothing to parry with!") + return FALSE + data = backup.block_parry_data + using_item = backup + method = ITEM_PARRY + //QOL: Try to enable combat mode if it isn't already + SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE) + if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) + to_chat(src, "You must be in combat mode to parry!") + return FALSE + data = return_block_parry_datum(data) + var/full_parry_duration = data.parry_time_windup + data.parry_time_active + data.parry_time_spindown + // no system in place to "fallback" if out of the 3 the top priority one can't parry due to constraints but something else can. + // can always implement it later, whatever. + if((data.parry_respect_clickdelay && (next_move > world.time)) || ((parry_end_time_last + data.parry_cooldown) > world.time)) + to_chat(src, "You are not ready to parry (again)!") + return + // Point of no return, make sure everything is set. + parrying = method + if(method == ITEM_PARRY) + active_parry_item = using_item + adjustStaminaLossBuffered(data.parry_stamina_cost) + parry_start_time = world.time + successful_parries = list() + addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration) + if(data.parry_flags & PARRY_LOCK_ATTACKING) + ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT) + if(data.parry_flags & PARRY_LOCK_SPRINTING) + ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT) + handle_parry_starting_effects(data) + return TRUE + +/** + * Tries to find a backup parry item. + * Does not look at active held item. + */ +/mob/living/proc/find_backup_parry_item() + for(var/obj/item/I in held_items - get_active_held_item()) + if(I.can_active_parry()) + return I + +/** + * Called via timer when the parry sequence ends. + */ +/mob/living/proc/end_parry_sequence() + if(!parrying) + return + REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_PARRY_TRAIT) + REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_PARRY_TRAIT) + if(parry_visual_effect) + QDEL_NULL(parry_visual_effect) + var/datum/block_parry_data/data = get_parry_data() + var/list/effect_text = list() + var/successful = FALSE + for(var/efficiency in successful_parries) + if(efficiency >= data.parry_efficiency_considered_successful) + successful = TRUE + break + if(!successful) // didn't parry anything successfully + if(data.parry_failed_stagger_duration) + Stagger(data.parry_failed_stagger_duration) + effect_text += "staggering themselves" + if(data.parry_failed_clickcd_duration) + changeNext_move(data.parry_failed_clickcd_duration) + effect_text += "throwing themselves off balance" + handle_parry_ending_effects(data, effect_text) + parrying = NOT_PARRYING + parry_start_time = 0 + parry_end_time_last = world.time + successful_parries = null + +/** + * Handles starting effects for parrying. + */ +/mob/living/proc/handle_parry_starting_effects(datum/block_parry_data/data) + playsound(src, data.parry_start_sound, 75, 1) + parry_visual_effect = new /obj/effect/abstract/parry/main(null, TRUE, src, data.parry_effect_icon_state, data.parry_time_windup_visual_override || data.parry_time_windup, data.parry_time_active_visual_override || data.parry_time_active, data.parry_time_spindown_visual_override || data.parry_time_spindown) + switch(parrying) + if(ITEM_PARRY) + visible_message("[src] swings [active_parry_item]!") + else + visible_message("[src] rushes forwards!") + +/** + * Handles ending effects for parrying. + */ +/mob/living/proc/handle_parry_ending_effects(datum/block_parry_data/data, list/failed_effect_text) + if(length(successful_parries)) + return + visible_message("[src] fails to connect their parry[failed_effect_text? ", [english_list(failed_effect_text)]" : ""]!") + +/** + * Gets this item's datum/block_parry_data + */ +/obj/item/proc/get_block_parry_data() + return return_block_parry_datum(block_parry_data) + +//Stubs. + +/** + * Called when an attack is parried using this, whether or not the parry was successful. + */ +/obj/item/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) + +/** + * Called when an attack is parried innately, whether or not the parry was successful. + */ +/mob/living/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) + +/** + * Called when an attack is parried using this, whether or not the parry was successful. + */ +/datum/martial_art/proc/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, parry_efficiency, parry_time) + +/** + * Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack. + */ +/obj/item/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text) + +/** + * Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack. + */ +/mob/living/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text) + +/** + * Called when an attack is parried and block_parra_data indicates to use a proc to handle counterattack. + */ +/datum/martial_art/proc/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text) + +/** + * Gets the stage of our parry sequence we're currently in. + */ +/mob/living/proc/get_parry_stage() + if(!parrying) + return NOT_PARRYING + var/datum/block_parry_data/data = get_parry_data() + var/windup_end = data.parry_time_windup + var/active_end = windup_end + data.parry_time_active + var/spindown_end = active_end + data.parry_time_spindown + var/current_time = get_parry_time() + // Not a switch statement because byond switch statements don't support floats at time of writing with "to" keyword. + if(current_time < 0) + return NOT_PARRYING + else if(current_time < windup_end) + return PARRY_WINDUP + else if(current_time <= active_end) // this uses <= on purpose, give a slight bit of advantage because time is rounded to world.tick_lag + return PARRY_ACTIVE + else if(current_time <= spindown_end) + return PARRY_SPINDOWN + else + return NOT_PARRYING + +/** + * Gets the current decisecond "frame" of an active parry. + */ +/mob/living/proc/get_parry_time() + return world.time - parry_start_time + +/// same return values as normal blocking, called with absolute highest priority in the block "chain". +/mob/living/proc/run_parry(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list()) + var/stage = get_parry_stage() + if(stage != PARRY_ACTIVE) + return BLOCK_NONE + var/datum/block_parry_data/data = get_parry_data() + if(attack_type && (!(attack_type & data.parry_attack_types) || (attack_type & ATTACK_TYPE_PARRY_COUNTERATTACK))) // if this attack is from a parry do not parry it lest we infinite loop. + return BLOCK_NONE + var/efficiency = data.get_parry_efficiency(attack_type, get_parry_time()) + switch(parrying) + if(ITEM_PARRY) + if(!active_parry_item.can_active_parry()) + return BLOCK_NONE + . = active_parry_item.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) + if(UNARMED_PARRY) + . = on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) + if(MARTIAL_PARRY) + . = mind.martial_art.on_active_parry(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, get_parry_time()) + if(!isnull(return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY])) // one of our procs overrode + efficiency = return_list[BLOCK_RETURN_OVERRIDE_PARRY_EFFICIENCY] + if(efficiency <= 0) // Do not allow automatically handled/standardized parries that increase damage for now. + return + . |= BLOCK_SHOULD_PARTIAL_MITIGATE + if(isnull(return_list[BLOCK_RETURN_MITIGATION_PERCENT])) // if one of the on_active_parry procs overrode. We don't have to worry about interference since parries are the first thing checked in the [do_run_block()] sequence. + return_list[BLOCK_RETURN_MITIGATION_PERCENT] = clamp(efficiency, 0, 100) // do not allow > 100% or < 0% for now. + if((return_list[BLOCK_RETURN_MITIGATION_PERCENT] >= 100) || (damage <= 0)) + . |= BLOCK_SUCCESS + var/list/effect_text + if(efficiency >= data.parry_efficiency_to_counterattack) + run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + if(data.parry_flags & PARRY_DEFAULT_HANDLE_FEEDBACK) + handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text) + successful_parries += efficiency + if(length(successful_parries) >= data.parry_max_attacks) + end_parry_sequence() + +/mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text) + var/datum/block_parry_data/data = get_parry_data() + if(data.parry_sounds) + playsound(src, pick(data.parry_sounds), 75) + visible_message("[src] parries \the [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""]!") + +/// Run counterattack if any +/mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency) + if(!isliving(attacker)) + return + var/mob/living/L = attacker + var/datum/block_parry_data/data = get_parry_data() + var/list/effect_text = list() + // Always proc so items can override behavior easily + switch(parrying) + if(ITEM_PARRY) + active_parry_item.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text) + if(UNARMED_PARRY) + active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text) + if(MARTIAL_PARRY) + mind.martial_art.active_parry_reflex_counter(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, parry_efficiency, effect_text) + if(Adjacent(attacker) || data.parry_data[PARRY_COUNTERATTACK_IGNORE_ADJACENCY]) + if(data.parry_data[PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN]) + switch(parrying) + if(ITEM_PARRY) + active_parry_item.melee_attack_chain(src, attacker, null, ATTACKCHAIN_PARRY_COUNTERATTACK, data.parry_data[PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN]) + effect_text += "reflexively counterattacking with [active_parry_item]" + if(UNARMED_PARRY) // WARNING: If you are using these two, the attackchain parry counterattack flags and damage multipliers are unimplemented. Be careful with how you handle this. + UnarmedAttack(attacker) + effect_text += "reflexively counterattacking in the process" + if(MARTIAL_PARRY) // Not well implemeneted, recommend custom implementation using the martial art datums. + UnarmedAttack(attacker) + effect_text += "reflexively maneuvering to retaliate" + if(data.parry_data[PARRY_DISARM_ATTACKER]) + L.drop_all_held_items() + effect_text += "disarming" + if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) + L.DefaultCombatKnockdown(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) + effect_text += "knocking them to the ground" + if(data.parry_data[PARRY_STAGGER_ATTACKER]) + L.Stagger(data.parry_data[PARRY_STAGGER_ATTACKER]) + effect_text += "staggering" + if(data.parry_data[PARRY_DAZE_ATTACKER]) + L.Daze(data.parry_data[PARRY_DAZE_ATTACKER]) + effect_text += "dazing" + return effect_text + +/// Gets the datum/block_parry_data we're going to use to parry. +/mob/living/proc/get_parry_data() + if(parrying == ITEM_PARRY) + return active_parry_item.get_block_parry_data() + else if(parrying == UNARMED_PARRY) + return return_block_parry_datum(block_parry_data) + else if(parrying == MARTIAL_PARRY) + return return_block_parry_datum(mind.martial_art.block_parry_data) + +/// Effects +/obj/effect/abstract/parry + icon = 'icons/effects/block_parry.dmi' + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + layer = FLOAT_LAYER + plane = FLOAT_PLANE + vis_flags = VIS_INHERIT_LAYER|VIS_INHERIT_PLANE + /// The person we're on + var/mob/living/owner + +/obj/effect/abstract/parry/main + name = null + +/obj/effect/abstract/parry/main/Initialize(mapload, autorun, mob/living/owner, set_icon_state, windup, active, spindown) + . = ..() + icon_state = set_icon_state + if(owner) + attach_to(owner) + if(autorun) + INVOKE_ASYNC(src, .proc/run_animation, windup, active, spindown) + +/obj/effect/abstract/parry/main/Destroy() + detach_from(owner) + return ..() + +/obj/effect/abstract/parry/main/proc/attach_to(mob/living/attaching) + if(owner) + detach_from(owner) + owner = attaching + owner.vis_contents += src + +/obj/effect/abstract/parry/main/proc/detach_from(mob/living/detaching) + if(detaching == owner) + owner = null + detaching.vis_contents -= src + +/obj/effect/abstract/parry/main/proc/run_animation(windup_time = 2, active_time = 5, spindown_time = 3) + var/matrix/current = transform + transform = matrix(0.1, 0, 0, 0, 0.1, 0) + animate(src, transform = current, time = windup_time) + sleep(active_time) + animate(src, alpha = 0, spindown_time) diff --git a/code/modules/mob/living/living_block.dm b/code/modules/mob/living/living_block.dm index 817863f257..d32265e478 100644 --- a/code/modules/mob/living/living_block.dm +++ b/code/modules/mob/living/living_block.dm @@ -1,31 +1,6 @@ // 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". -/* -/// You can find the mob_check_block() and mob_run_block() macros in __DEFINES/combat.dm - -/// Bitflags for check_block() and run_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) -*/ - /** 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 @@ -39,21 +14,31 @@ * 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. + * attack_direction - Direction of the attack. It is highly recommended to put this in, as the automatic guesswork that's done otherwise is quite inaccurate at times. */ -/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()) +/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(), attack_direction) + if(real_attack) + . = run_parry(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list) //Parry - Highest priority! + if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) + return // 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) + . = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, attack_direction) if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN)) + return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100 return var/list/obj/item/tocheck = get_blocking_items() - sortTim(tocheck, /proc/cmp_item_block_priority_asc) + sortTim(tocheck, /proc/cmp_numeric_dsc, TRUE) // 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) + var/results + if(I == active_block_item) + results = I.active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction) + else + 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 @@ -61,17 +46,29 @@ 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) + if(I == active_block_item) //block is long termed enough we give a damn. parry, not so much. + I.check_active_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list, attack_direction) + else + I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list) + if(. & BLOCK_SUCCESS) + return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = 100 + else if(isnull(return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE])) + return_list[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] = return_list[BLOCK_RETURN_MITIGATION_PERCENT] -/// Gets an unsortedlist of objects to run block checks on. +/// Gets an unsortedlist of objects to run block checks on. List must have associative values for priorities! /mob/living/proc/get_blocking_items() . = list() + if(active_block_item) + var/datum/block_parry_data/data = active_block_item.get_block_parry_data() + .[active_block_item] = data.block_active_priority SEND_SIGNAL(src, COMSIG_LIVING_GET_BLOCKING_ITEMS, .) 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 + if(.[I]) //don't override block/parry. + continue + .[I] = I.block_priority /obj/item /// The 0% to 100% chance for the default implementation of random block rolls. @@ -95,3 +92,15 @@ 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) + +// HELPER PROCS + +/** + * Considers a block return_list and calculates damage to use from that. + */ +/proc/block_calculate_resultant_damage(damage, list/block_return) + if(!isnull(block_return[BLOCK_RETURN_SET_DAMAGE_TO])) // higher priority + return block_return[BLOCK_RETURN_SET_DAMAGE_TO] + else if(!isnull(block_return[BLOCK_RETURN_MITIGATION_PERCENT])) + return damage * ((100 - block_return[BLOCK_RETURN_MITIGATION_PERCENT]) * 0.01) + return damage diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm new file mode 100644 index 0000000000..9f1ad1c27a --- /dev/null +++ b/code/modules/mob/living/living_blocking_parrying.dm @@ -0,0 +1,310 @@ +// yell at me later for file naming +// This file contains stuff relating to the new directional blocking and parry system. +GLOBAL_LIST_EMPTY(block_parry_data) + +/proc/return_block_parry_datum(datum/block_parry_data/type_id_datum) + if(istype(type_id_datum)) + return type_id_datum + if(ispath(type_id_datum)) + . = GLOB.block_parry_data["[type_id_datum]"] + if(!.) + . = GLOB.block_parry_data["[type_id_datum]"] = new type_id_datum + else //text id + return GLOB.block_parry_data["[type_id_datum]"] + +/proc/set_block_parry_datum(id, datum/block_parry_data/data) + if(ispath(id)) + CRASH("Path-fetching of block parry data is only to grab static data, do not attempt to modify global caches of paths. Use string IDs.") + GLOB.block_parry_data["[id]"] = data + +/// Carries data like list data that would be a waste of memory if we initialized the list on every /item as we can cache datums easier. +/datum/block_parry_data + /////////// BLOCKING //////////// + + /// NOTE: FOR ATTACK_TYPE_DEFINE, you MUST wrap it in "[DEFINE_HERE]"! The defines are bitflags, and therefore, NUMBERS! + + /// See defines. Point of reference is someone facing north. + var/can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST + /// Attacks we can block + var/can_block_attack_types = ALL + /// Our slowdown added while blocking + var/block_slowdown = 1 + /// Clickdelay added to user after block ends + var/block_end_click_cd_add = 0 + /// Disallow attacking during block + var/block_lock_attacking = TRUE + /// Disallow sprinting during block + var/block_lock_sprinting = FALSE + /// The priority we get in [mob/do_run_block()] while we're being used to parry. + var/block_active_priority = BLOCK_PRIORITY_ACTIVE_BLOCK + /// Windup before we have our blocking active. + var/block_start_delay = 5 + + /// Amount of "free" damage blocking absorbs + var/block_damage_absorption = 10 + /// Override absorption, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_absorption] + var/list/block_damage_absorption_override + + /// Ratio of damage to allow through above absorption and below limit. Multiplied by damage to determine how much to let through. Lower is better. + var/block_damage_multiplier = 0.5 + /// Override damage overrun efficiency, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_efficiency] + var/list/block_damage_multiplier_override + + /// Upper bound of damage block, anything above this will go right through. + var/block_damage_limit = 80 + /// Override upper bound of damage block, list("[ATTACK_TYPE_DEFINE]" = absorption), see [block_damage_limit] + var/list/block_damage_limit_override + + /// The blocked variable of on_hit() on projectiles is impacted by this. Higher is better, 0 to 100, percentage. + var/block_projectile_mitigation = 50 + + /* + * NOTE: Overrides for attack types for most the block_stamina variables were removed, + * because at the time of writing nothing needed to use it. Add them if you need it, + * it should be pretty easy, just copy [active_block_damage_mitigation] + * for how to override with list. + */ + + /// Default damage-to-stamina coefficient, higher is better. This is based on amount of damage BLOCKED, not initial damage, to prevent damage from "double dipping". + var/block_stamina_efficiency = 2 + /// Override damage-to-stamina coefficient, see [block_efficiency], this should be list("[ATTACK_TYPE_DEFINE]" = coefficient_number) + var/list/block_stamina_efficiency_override + /// Ratio of stamina incurred by blocking that goes to the arm holding the object instead of the chest. Has no effect if this is not held in hand. + var/block_stamina_limb_ratio = 0.5 + /// Ratio of stamina incurred by chest (so after [block_stamina_limb_ratio] runs) that is buffered. + var/block_stamina_buffer_ratio = 1 + + /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block. + var/block_stamina_cost_per_second = 1.5 + + /// Bitfield for attack types that we can block while down. This will work in any direction. + var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE + /// Bitfield for attack types that we can block while down but only in our normal directions. + var/block_resting_attack_types_directional = ATTACK_TYPE_PROJECTILE | ATTACK_TYPE_THROWN + /// Multiplier to stamina damage taken for attacks blocked while downed. + var/block_resting_stamina_penalty_multiplier = 1.5 + /// Override list for multiplier to stamina damage taken for attacks blocked while down. list("[ATTACK_TYPE_DEFINE]" = multiplier_number) + var/list/block_resting_stamina_penalty_multiplier_override + + /// Sounds for blocking + var/list/block_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) + + /////////// PARRYING //////////// + /// Prioriry for [mob/do_run_block()] while we're being used to parry. + // None - Parry is always highest priority! + /// Parry doesn't work if you aren't able to otherwise attack due to clickdelay + var/parry_respect_clickdelay = TRUE + /// Parry stamina cost + var/parry_stamina_cost = 5 + /// Attack types we can block + var/parry_attack_types = ALL + /// Parry flags + var/parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING + + /// Parry windup duration in deciseconds. 0 to this is windup, afterwards is main stage. + var/parry_time_windup = 2 + /// Parry spindown duration in deciseconds. main stage end to this is the spindown stage, afterwards the parry fully ends. + var/parry_time_spindown = 3 + /// Main parry window in deciseconds. This is between [parry_time_windup] and [parry_time_spindown] + var/parry_time_active = 5 + // Visual overrides + /// If set, overrides visual duration of windup + var/parry_time_windup_visual_override + /// If set, overrides visual duration of active period + var/parry_time_active_visual_override + /// If set, overrides visual duration of spindown + var/parry_time_spindown_visual_override + /// Perfect parry window in deciseconds from the start of the main window. 3 with main 5 = perfect on third decisecond of main window. + var/parry_time_perfect = 2.5 + /// Time on both sides of perfect parry that still counts as part of the perfect window. + var/parry_time_perfect_leeway = 1 + /// [parry_time_perfect_leeway] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) + var/list/parry_time_perfect_leeway_override + /// Parry "efficiency" falloff in percent per decisecond once perfect window is over. + var/parry_imperfect_falloff_percent = 20 + /// [parry_imperfect_falloff_percent] override for attack types, list("[ATTACK_TYPE_DEFINE]" = deciseconds) + var/list/parry_imperfect_falloff_percent_override + /// Efficiency in percent on perfect parry. + var/parry_efficiency_perfect = 120 + /// Parry effect data. + var/list/parry_data = list( + PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 1 + ) + /// Efficiency must be at least this to be considered successful + var/parry_efficiency_considered_successful = 0.1 + /// Efficiency must be at least this to run automatic counterattack + var/parry_efficiency_to_counterattack = 0.1 + /// Maximum attacks to parry successfully or unsuccessfully (but not efficiency < 0) during active period, hitting this immediately ends the sequence. + var/parry_max_attacks = INFINITY + /// Visual icon state override for parrying + var/parry_effect_icon_state = "parry_bm_hold" + /// Parrying cooldown, separate of clickdelay. It must be this much deciseconds since their last parry for them to parry with this object. + var/parry_cooldown = 0 + /// Parry start sound + var/parry_start_sound = 'sound/block_parry/sfx-parry.ogg' + /// Sounds for parrying + var/list/parry_sounds = list('sound/block_parry/block_metal1.ogg' = 1, 'sound/block_parry/block_metal1.ogg' = 1) + /// Stagger duration post-parry if you fail to parry an attack + var/parry_failed_stagger_duration = 3.5 SECONDS + /// Clickdelay duration post-parry if you fail to parry an attack + var/parry_failed_clickcd_duration = 2 SECONDS + +/** + * Quirky proc to get average of flags in list that are in attack_type because why is attack_type a flag. + */ +/datum/block_parry_data/proc/attack_type_list_scan(list/L, attack_type) + var/total = 0 + var/div = 0 + for(var/flagtext in L) + if(attack_type & text2num(flagtext)) + total += L[flagtext] + div++ + // if none, return null. + if(!div) + return + return total/div //groan + + +/** + * Gets the percentage efficiency of our parry. + * + * Returns a percentage in normal 0 to 100 scale, but not clamped to just 0 to 100. + * This is a proc to allow for overriding. + * @params + * * attack_type - int, bitfield of the attack type(s) + * * parry_time - deciseconds since start of the parry. + */ +/datum/block_parry_data/proc/get_parry_efficiency(attack_type, parry_time) + var/difference = abs(parry_time - (parry_time_perfect + parry_time_windup)) + var/leeway = attack_type_list_scan(parry_time_perfect_leeway_override, attack_type) + if(isnull(leeway)) + leeway = parry_time_perfect_leeway + difference -= leeway + . = parry_efficiency_perfect + if(difference <= 0) + return + var/falloff = attack_type_list_scan(parry_imperfect_falloff_percent_override, attack_type) + if(isnull(falloff)) + falloff = parry_imperfect_falloff_percent + . -= falloff * difference + +#define RENDER_VARIABLE_SIMPLE(varname, desc) dat += "[#varname]
    [desc][varname]" +#define RENDER_OVERRIDE_LIST(varname, desc) \ + dat += "[#varname]
    [desc]"; \ + var/list/assembled__##varname = list(); \ + for(var/textbit in varname){ \ + assembled__##varname += "[GLOB.attack_type_names[textbit]] = [varname[textbit]]"; \ + } \ + dat += "[english_list(assembled__##varname)]"; +#define RENDER_ATTACK_TYPES(varname, desc) dat += "[#varname]
    [desc]"; \ + var/list/assembled__##varname = list(); \ + for(var/bit in bitfield2list(varname)){ \ + var/name = GLOB.attack_type_names[num2text(bit)]; \ + if(name){ \ + assembled__##varname += "[name]"; \ + } \ + } \ + dat += "[english_list(assembled__##varname)]"; +#define RENDER_BLOCK_DIRECTIONS(varname, desc) \ + dat += "[#varname]
    [desc]"; \ + var/list/assembled__##varname = list(); \ + for(var/bit in bitfield2list(varname)){ \ + var/name = GLOB.block_direction_names[num2text(bit)]; \ + if(name){ \ + assembled__##varname += "[name]"; \ + } \ + } \ + dat += "[english_list(assembled__##varname)]"; + +/datum/block_parry_data/Topic(href, href_list) + . = ..() + if(.) + return + if(href_list["render"]) + var/datum/browser/B = new(usr, REF(src), href_list["name"], 800, 1000) + B.set_content(render_html_readout(href_list["block"], href_list["parry"])) + B.open() + +/** + * Generates a HTML render of this datum for self-documentation + * Maybe make this tgui-next someday haha god this is ugly as sin. + * Does NOT include the popout or title or anything. Just the variables and explanations.. + */ +/datum/block_parry_data/proc/render_html_readout(block_data = FALSE, parry_data = FALSE) + var/list/dat = list() + if(block_data) + dat += "

    Block Stats

    " + RENDER_BLOCK_DIRECTIONS(can_block_directions, "Which directions this can block in.") + RENDER_ATTACK_TYPES(can_block_attack_types, "The kinds of attacks this can block.") + RENDER_VARIABLE_SIMPLE(block_slowdown, "How much slowdown is applied to the user while blocking. Lower is better.") + RENDER_VARIABLE_SIMPLE(block_end_click_cd_add, "How much click delay in deciseconds is applied to the user when blocking ends. Lower is better.") + RENDER_VARIABLE_SIMPLE(block_lock_attacking, "Whether or not (1 or 0) the user is locked from atacking and/or item usage while blocking.") + RENDER_VARIABLE_SIMPLE(block_active_priority, "The priority of this item in the block sequence. This will probably mean nothing to you unless you are a coder.") + RENDER_VARIABLE_SIMPLE(block_start_delay, "The amount of time in deciseconds it takes to start a block with this item. Lower is better.") + RENDER_VARIABLE_SIMPLE(block_damage_absorption, "The amount of damage that is absorbed by default. Higher is better.") + RENDER_OVERRIDE_LIST(block_damage_absorption_override, "Overrides for the above for each attack type") + RENDER_VARIABLE_SIMPLE(block_damage_multiplier, "Damage between absorption and limit is multiplied by this. Lower is better.") + RENDER_OVERRIDE_LIST(block_damage_multiplier_override, "Overrides for the above for each attack type") + RENDER_VARIABLE_SIMPLE(block_damage_limit, "Damage above this passes right through and is not impacted. Higher is better.") + RENDER_OVERRIDE_LIST(block_damage_limit_override, "Overrides for the above for each attack type.") + RENDER_VARIABLE_SIMPLE(block_stamina_efficiency, "Coefficient for stamina damage dealt to user by damage blocked. Higher is better.") + RENDER_OVERRIDE_LIST(block_stamina_efficiency_override, "Overrides for the above for each attack type.") + RENDER_VARIABLE_SIMPLE(block_stamina_limb_ratio, "The ratio of stamina that is applied to the limb holding this object (if applicable) rather than whole body/chest.") + RENDER_VARIABLE_SIMPLE(block_stamina_buffer_ratio, "The ratio of stamina incurred by chest/whole body that is buffered rather than direct (buffer = your stamina buffer, direct = direct stamina damage like from a disabler.)") + RENDER_VARIABLE_SIMPLE(block_stamina_cost_per_second, "The buffered stamina damage the user incurs per second of block. Lower is better.") + RENDER_ATTACK_TYPES(block_resting_attack_types_anydir, "The kinds of attacks you can block while resting/otherwise knocked to the floor from any direction. can_block_attack_types takes precedence.") + RENDER_ATTACK_TYPES(block_resting_attack_types_directional, "The kinds of attacks you can block wihle resting/otherwise knocked to the floor that are directional only. can_block_attack_types takes precedence.") + RENDER_VARIABLE_SIMPLE(block_resting_stamina_penalty_multiplier, "Multiplier to stamina damage incurred from blocking while downed. Lower is better.") + RENDER_OVERRIDE_LIST(block_resting_stamina_penalty_multiplier, "Overrides for the above for each attack type.") + dat += "
    Name/DescriptionValue
    " + if(parry_data) + dat += "

    Parry Stats

    " + RENDER_VARIABLE_SIMPLE(parry_respect_clickdelay, "Whether or not (1 or 0) you can only parry if your attack cooldown isn't in effect.") + RENDER_VARIABLE_SIMPLE(parry_stamina_cost, "Buffered stamina damage incurred by you for parrying with this.") + RENDER_ATTACK_TYPES(parry_attack_types, "Attack types you can parry.") + // parry_flags + dat += "" + RENDER_VARIABLE_SIMPLE(parry_time_windup, "Deciseconds of parry windup.") + RENDER_VARIABLE_SIMPLE(parry_time_spindown, "Deciseconds of parry spindown.") + RENDER_VARIABLE_SIMPLE(parry_time_active, "Deciseconds of active parry window - This is the ONLY time your parry is active.") + RENDER_VARIABLE_SIMPLE(parry_time_windup_visual_override, "Visual effect length override") + RENDER_VARIABLE_SIMPLE(parry_time_spindown_visual_override, "Visual effect length override") + RENDER_VARIABLE_SIMPLE(parry_time_active_visual_override, "Visual effect length override") + RENDER_VARIABLE_SIMPLE(parry_time_perfect, "Deciseconds into the active window considered the 'center' of the perfect period.") + RENDER_VARIABLE_SIMPLE(parry_time_perfect_leeway, "Leeway on both sides of the perfect period's center still considered perfect.") + RENDER_OVERRIDE_LIST(parry_time_perfect_leeway_override, "Override for the above for each attack type") + RENDER_VARIABLE_SIMPLE(parry_imperfect_falloff_percent, "Linear falloff in percent per decisecond for attacks parried outside of perfect window.") + RENDER_OVERRIDE_LIST(parry_imperfect_falloff_percent_override, "Override for the above for each attack type") + RENDER_VARIABLE_SIMPLE(parry_efficiency_perfect, "Efficiency in percentage a parry in the perfect window is considered.") + // parry_data + dat += "" + RENDER_VARIABLE_SIMPLE(parry_efficiency_considered_successful, "Minimum parry efficiency to be considered a successful parry.") + RENDER_VARIABLE_SIMPLE(parry_efficiency_to_counterattack, "Minimum parry efficiency to trigger counterattack effects.") + RENDER_VARIABLE_SIMPLE(parry_max_attacks, "Max attacks parried per parry cycle.") + RENDER_VARIABLE_SIMPLE(parry_effect_icon_state, "Parry effect image name") + RENDER_VARIABLE_SIMPLE(parry_cooldown, "Deciseconds it has to be since the last time a parry sequence ended for you before you can parry again.") + RENDER_VARIABLE_SIMPLE(parry_failed_stagger_duration, "Deciseconds you are staggered for at the of the parry sequence if you do not successfully parry anything.") + RENDER_VARIABLE_SIMPLE(parry_failed_clickcd_duration, "Deciseconds you are put on attack cooldown at the end of the parry sequence if you do not successfully parry anything.") + dat += "
    Name/DescriptionValue
    " + return dat.Join("") +#undef RENDER_VARIABLE_SIMPLE +#undef RENDER_OVERRIDE_LIST +#undef RENDER_ATTACK_TYPES +#undef RENDER_BLOCK_DIRECTIONS + +// MOB PROCS + +/** + * Called every life tick to handle blocking/parrying effects. + */ +/mob/living/proc/handle_block_parry(seconds = 1) + if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING) + var/datum/block_parry_data/data = return_block_parry_datum(active_block_item.block_parry_data) + adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds) + +/mob/living/on_item_dropped(obj/item/I) + if(I == active_block_item) + stop_active_blocking() + if(I == active_parry_item) + end_parry_sequence() + return ..() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b3b49cb7cd..2ca1b63b14 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -1,12 +1,7 @@ -/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = null, soften_text = null, armour_penetration, penetrated_text, silent=FALSE) +/mob/living/proc/run_armor_check(def_zone = null, attack_flag = "melee", absorb_text = "Your armor absorbs the blow!", soften_text = "Your armor softens the blow!", armour_penetration, penetrated_text = "Your armor was penetrated!") var/armor = getarmor(def_zone, attack_flag) - if(armor <= 0) - return armor - if(silent) - return max(0, armor - armour_penetration) - //the if "armor" check is because this is used for everything on /living, including humans if(armor && armour_penetration) armor = max(0, armor - armour_penetration) @@ -15,7 +10,7 @@ else if(armor >= 100) if(absorb_text) to_chat(src, "[absorb_text]") - else + else if(armor > 0) if(soften_text) to_chat(src, "[soften_text]") return armor @@ -71,22 +66,30 @@ CRASH("Invalid rediretion mode [redirection_mode]") /mob/living/bullet_act(obj/item/projectile/P, def_zone) + var/totaldamage = P.damage + var/final_percent = 0 if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself var/list/returnlist = list() var/returned = mob_run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist) + final_percent = returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE] 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) + P.on_hit(src, final_percent, def_zone) return BULLET_ACT_BLOCK + totaldamage = block_calculate_resultant_damage(totaldamage, returnlist) var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null) if(!P.nodamage) - apply_damage(P.damage, P.damage_type, def_zone, armor) + apply_damage(totaldamage, P.damage_type, def_zone, armor) if(P.dismemberment) check_projectile_dismemberment(P, def_zone) - return P.on_hit(src, armor) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK + var/missing = 100 - final_percent + var/armor_ratio = armor * 0.01 + if(missing > 0) + final_percent += missing * armor_ratio + return P.on_hit(src, final_percent, def_zone) ? BULLET_ACT_HIT : BULLET_ACT_BLOCK /mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) return 0 @@ -116,11 +119,14 @@ I = AM throwpower = I.throwforce var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest - if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, null) & BLOCK_SUCCESS) + var/list/block_return = list() + var/total_damage = I.throwforce + if(mob_run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone, block_return) & BLOCK_SUCCESS) hitpush = FALSE skipcatch = TRUE blocked = TRUE - else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding["embed_chance"]) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item())) + total_damage = block_calculate_resultant_damage(total_damage, block_return) + else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item())) embed_item(I) hitpush = FALSE skipcatch = TRUE //can't catch the now embedded item @@ -129,12 +135,9 @@ return TRUE var/dtype = BRUTE var/volume = I.get_volume_by_throwforce_and_or_w_class() - var/nosell_hit = SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, impacting_zone, throwingdatum) // TODO: find a better way to handle hitpush and skipcatch for humans - if(nosell_hit) - skipcatch = TRUE - hitpush = FALSE - + 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... if (I.throwhitsound) //...and throwhitsound is defined... playsound(loc, I.throwhitsound, volume, 1, -1) //...play the weapon's throwhitsound. @@ -151,7 +154,7 @@ visible_message("[src] has been hit by [I].", \ "You have been hit by [I].") 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) + apply_damage(total_damage, dtype, impacting_zone, armor) if(I.thrownby) log_combat(I.thrownby, src, "threw and hit", I) else @@ -321,8 +324,10 @@ var/damage = rand(5, 35) if(M.is_adult) damage = rand(20, 40) - if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected), null) & BLOCK_SUCCESS) + var/list/block_return = list() + if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected), block_return) & BLOCK_SUCCESS) return FALSE + damage = block_calculate_resultant_damage(damage, block_return) if (stat != DEAD) log_combat(M, src, "attacked") @@ -338,13 +343,16 @@ M.visible_message("\The [M] [M.friendly_verb_continuous] [src]!", "You [M.friendly_verb_simple] [src]!", target = src, target_message = "\The [M] [M.friendly_verb_continuous] you!") - return FALSE + return 0 else if(HAS_TRAIT(M, TRAIT_PACIFISM)) to_chat(M, "You don't want to hurt anyone!") return FALSE - if(mob_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), null) & BLOCK_SUCCESS) - return FALSE + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/list/return_list = list() + if(mob_run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected), return_list) & BLOCK_SUCCESS) + return 0 + damage = block_calculate_resultant_damage(damage, return_list) if(M.attack_sound) playsound(loc, M.attack_sound, 50, 1, 1) M.do_attack_animation(src) @@ -352,7 +360,7 @@ "\The [M] [M.attack_verb_continuous] you!", null, COMBAT_MESSAGE_RANGE, null, M, "You [M.attack_verb_simple] [src]!") log_combat(M, src, "attacked") - return TRUE + return damage /mob/living/attack_paw(mob/living/carbon/monkey/M) if (M.a_intent == INTENT_HARM) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index da24f190e8..b037221e2c 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -29,6 +29,26 @@ var/mobility_flags = MOBILITY_FLAGS_DEFAULT + // Combat - Blocking/Parrying system + /// Our block_parry_data for unarmed blocks/parries. Currently only used for parrying, as unarmed block isn't implemented yet. YOU MUST RUN [get_block_parry_data(this)] INSTEAD OF DIRECTLY ACCESSING! + var/datum/block_parry_data/block_parry_data = /datum/block_parry_data // defaults to *something* because [combat_flags] dictates whether or not we can unarmed block/parry. + // Blocking + /// The item the user is actively blocking with if any. + var/obj/item/active_block_item + // Parrying + /// Whether or not the user is in the middle of an active parry. Set to [UNARMED_PARRY], [ITEM_PARRY], [MARTIAL_PARRY] if parrying. + var/parrying = FALSE + /// The itme the user is currently parrying with, if any. + var/obj/item/active_parry_item + /// world.time of parry action start + var/parry_start_time = 0 + /// Current parry effect. + var/obj/effect/abstract/parry/parry_visual_effect + /// world.time of last parry end + var/parry_end_time_last = 0 + /// Successful parries within the current parry cycle. It's a list of efficiency percentages. + var/list/successful_parries + var/confused = 0 //Makes the mob move in random directions. var/hallucination = 0 //Directly affects how long a mob will hallucinate for diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 4b90191dcc..71bcef9aca 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -3,10 +3,21 @@ update_turf_movespeed(loc) //Hide typing indicator if we move. clear_typing_indicator() - if(is_shifted) - is_shifted = FALSE - pixel_x = get_standard_pixel_x_offset(lying) - pixel_y = get_standard_pixel_y_offset(lying) + update_pixel_shifting(TRUE) + +/mob/living/setDir(newdir, ismousemovement) + . = ..() + if(ismousemovement) + update_pixel_shifting() + +/mob/living/proc/update_pixel_shifting(moved = FALSE) + if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING) + animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, flags = ANIMATION_END_NOW) + else if(moved) + if(is_shifted) + is_shifted = FALSE + pixel_x = get_standard_pixel_x_offset(lying) + pixel_y = get_standard_pixel_y_offset(lying) /mob/living/CanPass(atom/movable/mover, turf/target) if((mover.pass_flags & PASSMOB)) diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm index 2bcb3c9b5a..9cefa7a12f 100644 --- a/code/modules/mob/living/silicon/ai/ai_defense.dm +++ b/code/modules/mob/living/silicon/ai/ai_defense.dm @@ -1,4 +1,4 @@ -/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone) +/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone, attackchain_flags = NONE, damage_multiplier = 1) . = ..() if(!.) return FALSE diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 59dfcd3003..bf66556399 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -359,7 +359,8 @@ "Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmed"), "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamed"), "Eyebot" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "eyebotmed"), - "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed") + "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed"), + "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_med") ) var/list/L = list("Medihound" = "medihound", "Medihound Dark" = "medihounddark", "Vale" = "valemed") for(var/a in L) @@ -375,6 +376,8 @@ switch(med_borg_icon) if("Default") cyborg_base_icon = "medical" + if("Zoomba") + cyborg_base_icon = "zoomba_med" if("Droid") cyborg_base_icon = "medical" cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi' @@ -476,7 +479,8 @@ "Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "caneng"), "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinaeng"), "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidereng"), - "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng") + "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng"), + "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_engi") ) var/list/L = list("Pup Dozer" = "pupdozer", "Vale" = "valeeng") for(var/a in L) @@ -492,6 +496,8 @@ switch(engi_borg_icon) if("Default") cyborg_base_icon = "engineer" + if("Zoomba") + cyborg_base_icon = "zoomba_engi" if("Default - Treads") cyborg_base_icon = "engi-tread" special_light_key = "engineer" @@ -572,7 +578,8 @@ "Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "cansec"), "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinasec"), "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidersec"), - "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec") + "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec"), + "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_sec") ) var/list/L = list("K9" = "k9", "Vale" = "valesec", "K9 Dark" = "k9dark") for(var/a in L) @@ -588,6 +595,8 @@ switch(sec_borg_icon) if("Default") cyborg_base_icon = "sec" + if("Zoomba") + cyborg_base_icon = "zoomba_sec" if("Default - Treads") cyborg_base_icon = "sec-tread" special_light_key = "sec" @@ -827,6 +836,7 @@ "(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"), "(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"), "(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"), + "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_jani") ) var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark", "(Janitor) Scrubpuppy" = "scrubpup") @@ -841,6 +851,8 @@ service_icons = sortList(service_icons) var/service_robot_icon = show_radial_menu(R, R , service_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE) switch(service_robot_icon) + if("Zoomba") + cyborg_base_icon = "zoomba_jani" if("(Service) Waitress") cyborg_base_icon = "service_f" special_light_key = "service" @@ -944,6 +956,7 @@ "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamin"), "Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canmin"), "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin"), + "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_miner") ) var/list/L = list("Blade" = "blade", "Vale" = "valemine") for(var/a in L) @@ -987,6 +1000,8 @@ cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi' sleeper_overlay = "valeminesleeper" dogborg = TRUE + if("Zoomba") + cyborg_base_icon = "zoomba_miner" else return FALSE return ..() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 173db6f69b..7a9610fb53 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -396,6 +396,12 @@ return aicamera.selectpicture(user) /mob/living/silicon/proc/ai_roster() + if(!client) + return + if(world.time < client.crew_manifest_delay) + return + client.crew_manifest_delay = world.time + (1 SECONDS) + var/dat = "Crew RosterCrew Roster:

    " dat += GLOB.data_core.get_manifest() diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index cd50ffbae6..0850f0f886 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -33,7 +33,7 @@ /mob/living/silicon/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . if(prob(damage)) for(var/mob/living/N in buckled_mobs) N.DefaultCombatKnockdown(20) @@ -120,6 +120,7 @@ flash_act(affect_silicon = 1) /mob/living/silicon/bullet_act(obj/item/projectile/P, def_zone) + var/totaldamage = P.damage if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself var/list/returnlist = list() var/returned = mob_run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist) @@ -128,22 +129,18 @@ if(returned & BLOCK_REDIRECTED) return BULLET_ACT_FORCE_PIERCE if(returned & BLOCK_SUCCESS) - P.on_hit(src, 100, def_zone) + P.on_hit(src, returnlist[BLOCK_RETURN_PROJECTILE_BLOCK_PERCENTAGE], def_zone) return BULLET_ACT_BLOCK + totaldamage = block_calculate_resultant_damage(totaldamage, returnlist) if((P.damage_type == BRUTE || P.damage_type == BURN)) - adjustBruteLoss(P.damage) - if(prob(P.damage*1.5)) - for(var/mob/living/M in buckled_mobs) - M.visible_message("[M] is knocked off of [src]!", - "You are knocked off of [src]!") - unbuckle_mob(M) - M.DefaultCombatKnockdown(40) - if(P.stun || P.knockdown) + adjustBruteLoss(totaldamage) + if((P.damage >= 10) || P.stun || P.knockdown || (P.stamina >= 20)) for(var/mob/living/M in buckled_mobs) - unbuckle_mob(M) M.visible_message("[M] is knocked off of [src] by the [P]!", "You are knocked off of [src] by the [P]!") - P.on_hit(src) + unbuckle_mob(M) + M.DefaultCombatKnockdown(40) + P.on_hit(src, 0, def_zone) return BULLET_ACT_HIT /mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/static) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 1e56e91ae1..278bb37d0d 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -94,7 +94,7 @@ /mob/living/simple_animal/attack_animal(mob/living/simple_animal/M) . = ..() if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + var/damage = . return attack_threshold_check(damage, M.melee_damage_type) /mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/M) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 97f3dad3f0..39eccf9ad4 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -98,6 +98,10 @@ hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) //Diagnostic HUD views + var/commissioned = FALSE // Will other (noncommissioned) bots salute this bot? + var/can_salute = TRUE + var/salute_delay = 60 SECONDS + /mob/living/simple_animal/bot/proc/get_mode() if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player. if(paicard) @@ -251,6 +255,14 @@ if(!on || client) return + if(!commissioned && can_salute) + for(var/mob/living/simple_animal/bot/B in get_hearers_in_view(5, get_turf(src))) + if(B.commissioned) + visible_message("[src] performs an elaborate salute for [B]!") + can_salute = FALSE + addtimer(VARSET_CALLBACK(src, can_salute, TRUE), salute_delay) + break + switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command. if(BOT_RESPONDING) //Called by the AI. call_mode() diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 73099d8d9f..f6aad5c03f 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -14,7 +14,7 @@ model = "Cleanbot" bot_core_type = /obj/machinery/bot_core/cleanbot window_id = "autoclean" - window_name = "Automatic Station Cleaner v1.2" + window_name = "Automatic Station Cleaner v1.3" pass_flags = PASSMOB path_image_color = "#993299" weather_immunities = list("lava","ash") @@ -36,8 +36,62 @@ var/next_dest var/next_dest_loc + var/obj/item/weapon + var/weapon_orig_force = 0 + var/chosen_name + + var/list/stolen_valor + + var/static/list/officers = list("Captain", "Head of Personnel", "Head of Security") + var/static/list/command = list("Captain" = "Cpt.","Head of Personnel" = "Lt.") + var/static/list/security = list("Head of Security" = "Maj.", "Warden" = "Sgt.", "Detective" = "Det.", "Security Officer" = "Officer") + var/static/list/engineering = list("Chief Engineer" = "Chief Engineer", "Station Engineer" = "Engineer", "Atmospherics Technician" = "Technician") + var/static/list/medical = list("Chief Medical Officer" = "C.M.O.", "Medical Doctor" = "M.D.", "Chemist" = "Pharm.D.") + var/static/list/research = list("Research Director" = "Ph.D.", "Roboticist" = "M.S.", "Scientist" = "B.S.") + var/static/list/legal = list("Lawyer" = "Esq.") + + var/list/prefixes + var/list/suffixes + +/mob/living/simple_animal/bot/cleanbot/proc/deputize(obj/item/W, mob/user) + if(in_range(src, user)) + to_chat(user, "You attach \the [W] to \the [src].") + user.transferItemToLoc(W, src) + weapon = W + weapon_orig_force = weapon.force + if(!emagged) + weapon.force = weapon.force / 2 + add_overlay(image(icon=weapon.lefthand_file,icon_state=weapon.item_state)) + +/mob/living/simple_animal/bot/cleanbot/proc/update_titles() + var/working_title = "" + + for(var/pref in prefixes) + for(var/title in pref) + if(title in stolen_valor) + working_title += pref[title] + " " + if(title in officers) + commissioned = TRUE + break + + working_title += chosen_name + + for(var/suf in suffixes) + for(var/title in suf) + if(title in stolen_valor) + working_title += " " + suf[title] + break + + name = working_title + +/mob/living/simple_animal/bot/cleanbot/examine(mob/user) + . = ..() + if(weapon) + . += " Is that \a [weapon] taped to it...?" + /mob/living/simple_animal/bot/cleanbot/Initialize() . = ..() + chosen_name = name get_targets() icon_state = "cleanbot[on]" @@ -45,6 +99,18 @@ access_card.access += J.get_access() prev_access = access_card.access + stolen_valor = list() + + prefixes = list(command, security, engineering) + suffixes = list(research, medical, legal) + +/mob/living/simple_animal/bot/cleanbot/Destroy() + if(weapon) + var/atom/Tsec = drop_location() + weapon.force = weapon_orig_force + drop_part(weapon, Tsec) + return ..() + /mob/living/simple_animal/bot/cleanbot/turn_on() ..() icon_state = "cleanbot[on]" @@ -57,6 +123,8 @@ /mob/living/simple_animal/bot/cleanbot/bot_reset() ..() + if(weapon && (emagged == 2)) + weapon.force = weapon_orig_force ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable. target = null oldloc = null @@ -66,6 +134,22 @@ text_dehack = "[name]'s software has been reset!" text_dehack_fail = "[name] does not seem to respond to your repair code!" +/mob/living/simple_animal/bot/cleanbot/Crossed(atom/movable/AM) + . = ..() + + zone_selected = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + if(weapon && has_gravity() && ismob(AM)) + var/mob/living/carbon/C = AM + if(!istype(C)) + return + + if(!(C.job in stolen_valor)) + stolen_valor += C.job + update_titles() + + weapon.attack(C, src) + C.Knockdown(20) + /mob/living/simple_animal/bot/cleanbot/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) if(bot_core.allowed(user) && !open && !emagged) @@ -79,6 +163,11 @@ else to_chat(user, "The [src] doesn't seem to respect your authority.") + else if(istype(W, /obj/item/kitchen/knife) && user.a_intent != INTENT_HARM) + to_chat(user, "You start attaching \the [W] to \the [src]...") + if(do_after(user, 25, target = src)) + deputize(W, user) + else if(istype(W, /obj/item/mop/advanced)) if(bot_core.allowed(user) && open && !CHECK_BITFIELD(upgrades,UPGRADE_CLEANER_ADVANCED_MOP)) to_chat(user, "You replace \the [src] old mop with a new better one!") @@ -87,10 +176,10 @@ window_name = "Automatic Station Cleaner v2.1 BETA" //New! qdel(W) if(!open) - to_chat(user, "The [src] access pannle is not open!") + to_chat(user, "The [src] access panel is not open!") return if(!bot_core.allowed(user)) - to_chat(user, "The [src] access pannel locked off to you!") + to_chat(user, "The [src] access panel locked off to you!") return else to_chat(user, "The [src] already has this mop!") @@ -116,6 +205,8 @@ /mob/living/simple_animal/bot/cleanbot/emag_act(mob/user) . = ..() if(emagged == 2) + if(weapon) + weapon.force = weapon_orig_force if(user) to_chat(user, "[src] buzzes and beeps.") diff --git a/code/modules/mob/living/simple_animal/friendly/cockroach.dm b/code/modules/mob/living/simple_animal/friendly/cockroach.dm index 9a34f2c176..384fa8146f 100644 --- a/code/modules/mob/living/simple_animal/friendly/cockroach.dm +++ b/code/modules/mob/living/simple_animal/friendly/cockroach.dm @@ -23,6 +23,7 @@ speak_emote = list("chitters") density = FALSE ventcrawler = VENTCRAWLER_ALWAYS + mob_size = MOB_SIZE_TINY gold_core_spawnable = FRIENDLY_SPAWN verb_say = "chitters" verb_ask = "chitters inquisitively" diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index d86362c974..e00e0648b5 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -22,6 +22,7 @@ friendly_verb_continuous = "pinches" friendly_verb_simple = "pinch" ventcrawler = VENTCRAWLER_ALWAYS + mob_size = MOB_SIZE_TINY var/obj/item/inventory_head var/obj/item/inventory_mask gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm index df416047e2..bb93f95ee4 100644 --- a/code/modules/mob/living/simple_animal/friendly/sloth.dm +++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm @@ -17,6 +17,7 @@ response_disarm_simple = "gently push aside" response_harm_continuous = "kicks" response_harm_simple = "kick" + mob_size = MOB_SIZE_SMALL mob_biotypes = MOB_ORGANIC|MOB_BEAST gold_core_spawnable = FRIENDLY_SPAWN melee_damage_lower = 18 diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index e41fa7c896..e8991df358 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -117,7 +117,7 @@ Move(get_step(src,chosen_dir)) face_atom(target) //Looks better if they keep looking at you when dodging -/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) +/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) FindTarget(list(user), 1) return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm index 9d395ca5ff..c4f78b6e26 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm @@ -103,7 +103,7 @@ IGNORE_PROC_IF_NOT_TARGET(attack_slime) return BULLET_ACT_FORCE_PIERCE return ..() -/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L) +/mob/living/simple_animal/hostile/asteroid/curseblob/attacked_by(obj/item/I, mob/living/L, attackchain_flags = NONE, damage_multiplier = 1) if(L != set_target) L.changeNext_move(I.click_delay) //pre_attacked_by not called return diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 0ef5cabc91..f7876b3516 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -3,6 +3,7 @@ icon = 'icons/mob/slimes.dmi' icon_state = "grey baby slime" pass_flags = PASSTABLE + mob_size = MOB_SIZE_SMALL ventcrawler = VENTCRAWLER_ALWAYS gender = NEUTER var/is_adult = 0 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index e14ff2f721..4c692e7175 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1086,3 +1086,10 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) for(var/obj/item/I in held_items) if(I.item_flags & SLOWS_WHILE_IN_HAND) . += I.slowdown + +/** + * Mostly called by doUnEquip() + * Like item dropped() on mob side. + */ +/mob/proc/on_item_dropped(obj/item/I) + return diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm index d20fc4c6c0..a2176ca95e 100644 --- a/code/modules/movespeed/modifiers/mobs.dm +++ b/code/modules/movespeed/modifiers/mobs.dm @@ -119,3 +119,7 @@ /datum/movespeed_modifier/liver_cirrhosis blacklisted_movetypes = FLOATING variable = TRUE + +/datum/movespeed_modifier/active_block + variable = TRUE + flags = IGNORE_NOSLOW diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 3c3f3c9541..c18eebbb55 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -506,7 +506,7 @@ cell = null qdel(src) -/obj/machinery/light/attacked_by(obj/item/I, mob/living/user) +/obj/machinery/light/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) ..() if(status == LIGHT_BROKEN || status == LIGHT_EMPTY) if(on && (I.flags_1 & CONDUCT_1)) diff --git a/code/modules/projectiles/ammunition/ballistic/pistol.dm b/code/modules/projectiles/ammunition/ballistic/pistol.dm index 461166ab0d..2077b108d7 100644 --- a/code/modules/projectiles/ammunition/ballistic/pistol.dm +++ b/code/modules/projectiles/ammunition/ballistic/pistol.dm @@ -44,7 +44,6 @@ desc = "A 9mm incendiary bullet casing." projectile_type = /obj/item/projectile/bullet/incendiary/c9mm - // .50AE (Desert Eagle) /obj/item/ammo_casing/a50AE @@ -53,3 +52,16 @@ caliber = ".50" projectile_type = /obj/item/projectile/bullet/a50AE +// .32 ACP (Improvised Pistol) + +/obj/item/ammo_casing/c32acp + name = ".32 bullet casing" + desc = "A .32 bullet casing." + caliber = "c32acp" + projectile_type = /obj/item/projectile/bullet/c32acp + +/obj/item/ammo_casing/r32acp + name = ".32 rubber bullet casing" + desc = "A .32 rubber bullet casing." + caliber = "c32acp" + projectile_type = /obj/item/projectile/bullet/r32acp diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm index 174645dd11..492b91ec2d 100644 --- a/code/modules/projectiles/ammunition/energy/laser.dm +++ b/code/modules/projectiles/ammunition/energy/laser.dm @@ -12,6 +12,15 @@ e_cost = 200 select_name = "kill" +/obj/item/ammo_casing/energy/lasergun/improvised + projectile_type = /obj/item/projectile/beam/weak/improvised + e_cost = 200 + select_name = "kill" + +/obj/item/ammo_casing/energy/lasergun/improvised/upgraded + projectile_type = /obj/item/projectile/beam/weak + e_cost = 100 + /obj/item/ammo_casing/energy/laser/hos e_cost = 100 diff --git a/code/modules/projectiles/boxes_magazines/external/pistol.dm b/code/modules/projectiles/boxes_magazines/external/pistol.dm index 63b0483875..1852b839f4 100644 --- a/code/modules/projectiles/boxes_magazines/external/pistol.dm +++ b/code/modules/projectiles/boxes_magazines/external/pistol.dm @@ -66,3 +66,15 @@ caliber = ".50" max_ammo = 7 multiple_sprites = 1 + +/obj/item/ammo_box/magazine/m32acp + name = "pistol magazine (.32)" + desc = "A crudely construction pistol magazine that holds .32 ACP rounds. It looks like it can only fit eight bullets." + icon_state = "32acp" + ammo_type = /obj/item/ammo_casing/c32acp + caliber = "c32acp" + max_ammo = 8 + multiple_sprites = 2 + +/obj/item/ammo_box/magazine/m32acp/empty + start_empty = 1 diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm index 7029cd4071..c2dd5bb42d 100644 --- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm +++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm @@ -53,6 +53,7 @@ ..() /obj/item/minigunpack/dropped(mob/user) + . = ..() if(armed) user.dropItemToGround(gun, TRUE) @@ -125,6 +126,7 @@ return /obj/item/gun/ballistic/minigun/dropped(mob/user) + . = ..() if(ammo_pack) ammo_pack.attach_gun(user) else @@ -144,4 +146,5 @@ . = ..() /obj/item/gun/ballistic/minigun/dropped(mob/living/user) + . = ..() ammo_pack.attach_gun(user) diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index e15c49b891..319ec16345 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -115,6 +115,7 @@ icon_state = "flatgun" /obj/item/gun/ballistic/automatic/pistol/stickman/pickup(mob/living/user) + . = ..() to_chat(user, "As you try to pick up [src], it slips out of your grip..") if(prob(50)) to_chat(user, "..and vanishes from your vision! Where the hell did it go?") @@ -155,3 +156,19 @@ 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 + +////////////Improvised Pistol//////////// + +/obj/item/gun/ballistic/automatic/pistol/improvised + name = "Improvised Pistol" + desc = "An improvised pocket-sized pistol that fires .32 calibre rounds. It looks incredibly flimsy." + icon_state = "ipistol" + item_state = "pistol" + mag_type = /obj/item/ammo_box/magazine/m32acp + fire_delay = 7.5 + can_suppress = FALSE + w_class = WEIGHT_CLASS_SMALL + spread = 15 // Keep the spread between 15 and 20. This hardlocks it into being a mid-range pistol, the magazine size means you're allowed to miss. Fills the mid-range niche that slugs/rifle and buckshot doesn't fill. + +/obj/item/gun/ballistic/automatic/pistol/improvised/nomag + spawnwithmagazine = FALSE // For crafting as you shouldn't get eight bullets for free otherwise people will reaper reload. diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index b8b5ad6ec3..f34dbc6abc 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -319,7 +319,6 @@ /obj/item/gun/ballistic/revolver/doublebarrel/improvised name = "improvised shotgun" - desc = "Essentially a tube that aims shotgun shells." desc = "A shoddy break-action breechloaded shotgun. Its lacklustre construction will probably result in it hurting people less than a normal shotgun." icon_state = "ishotgun" item_state = "shotgun" @@ -330,8 +329,9 @@ mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised sawn_desc = "I'm just here for the gasoline." unique_reskin = null - projectile_damage_multiplier = 0.8 + projectile_damage_multiplier = 0.9 var/slung = FALSE + weapon_weight = WEAPON_HEAVY /obj/item/gun/ballistic/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) ..() @@ -358,7 +358,7 @@ /obj/item/gun/ballistic/revolver/doublebarrel/improvised/sawn name = "sawn-off improvised shotgun" - desc = "A single-shot shotgun. Better not miss." + desc = "The barrel and stock have been sawn and filed down; it can fit in backpacks. You still need two hands to fire this, if you value unbroken wrists." icon_state = "ishotgun" item_state = "gun" w_class = WEIGHT_CLASS_NORMAL diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index a182ce2e35..8b8c5dceb1 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -131,6 +131,7 @@ desc = "A bolt-action breechloaded rifle that takes 7.62mm bullets." mag_type = /obj/item/ammo_box/magazine/internal/boltaction/improvised can_bayonet = FALSE + var/slung = FALSE /obj/item/gun/ballistic/shotgun/boltaction/pump(mob/M) playsound(M, 'sound/weapons/shotgunpump.ogg', 60, 1) @@ -152,6 +153,22 @@ . = ..() . += "The bolt is [bolt_open ? "open" : "closed"]." +/obj/item/gun/ballistic/shotgun/boltaction/improvised/attackby(obj/item/A, mob/user, params) + ..() + if(istype(A, /obj/item/stack/cable_coil) && !sawn_off) + if(A.use_tool(src, user, 0, 10, max_level = JOB_SKILL_BASIC)) + slot_flags = ITEM_SLOT_BACK + to_chat(user, "You tie the lengths of cable to the rifle, making a sling.") + slung = TRUE + update_icon() + else + to_chat(user, "You need at least ten lengths of cable if you want to make a sling!") + +/obj/item/gun/ballistic/shotgun/boltaction/improvised/update_icon() + ..() + if(slung) + icon_state += "sling" + /obj/item/gun/ballistic/shotgun/boltaction/enchanted name = "enchanted bolt action rifle" desc = "Careful not to lose your head." diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 72ac9620b5..e64da116f3 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -240,3 +240,20 @@ chambered.BB.damage *= 5 process_fire(target, user, TRUE, params) + +//////////////// +// IMPROVISED // +//////////////// + +/obj/item/gun/energy/e_gun/old/improvised + name = "improvised energy rifle" + desc = "A crude imitation of an energy gun. It works, however the beams are poorly focused and most of the energy is wasted before it reaches the target. Welp, it still burns things." + icon_state = "improvised" + ammo_x_offset = 1 + shaded_charge = 1 + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/improvised) + +/obj/item/gun/energy/e_gun/old/improvised/upgraded + name = "makeshift energy rifle" + desc = "The new lens and upgraded parts gives this a higher capacity and more energy output, however, the shoddy construction still leaves it inferior to Nanotrasen's own energy weapons." + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/improvised/upgraded) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index b46e6f5ead..d2643eeeb7 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -45,6 +45,8 @@ var/pixels_range_leftover = 0 /// "leftover" tick pixels and stuff yeah, so we don't round off things and introducing tracing inaccuracy. var/pixels_tick_leftover = 0 + /// Used to detect jumps in the middle of a pixel_move. Yes, this is ugly as sin code-wise but it works. + var/pixel_move_interrupted = FALSE /// Pixels moved per second. var/pixels_per_second = TILES_TO_PIXELS(12.5) @@ -56,25 +58,9 @@ var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle var/spread = 0 //amount (in degrees) of projectile spread animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy - /// how many times we've ricochet'd so far (instance variable, not a stat) var/ricochets = 0 - /// how many times we can ricochet max - var/ricochets_max = 0 - /// 0-100, the base chance of ricocheting, before being modified by the atom we shoot and our chance decay - var/ricochet_chance = 0 - /// 0-1 (or more, I guess) multiplier, the ricochet_chance is modified by multiplying this after each ricochet - var/ricochet_decay_chance = 0.7 - /// 0-1 (or more, I guess) multiplier, the projectile's damage is modified by multiplying this after each ricochet - var/ricochet_decay_damage = 0.7 - /// On ricochet, if nonzero, we consider all mobs within this range of our projectile at the time of ricochet to home in on like Revolver Ocelot, as governed by ricochet_auto_aim_angle - var/ricochet_auto_aim_range = 0 - /// On ricochet, if ricochet_auto_aim_range is nonzero, we'll consider any mobs within this range of the normal angle of incidence to home in on, higher = more auto aim - var/ricochet_auto_aim_angle = 30 - /// the angle of impact must be within this many degrees of the struck surface, set to 0 to allow any angle - var/ricochet_incidence_leeway = 40 - - ///If the object being hit can pass ths damage on to something else, it should not do it for this bullet - var/force_hit = FALSE + var/ricochets_max = 2 + var/ricochet_chance = 30 //Hitscan var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored. @@ -147,11 +133,6 @@ var/temporary_unstoppable_movement = FALSE - ///If defined, on hit we create an item of this type then call hitby() on the hit target with this - var/shrapnel_type - ///If TRUE, hit mobs even if they're on the floor and not our target - var/hit_stunned_targets = FALSE - /obj/item/projectile/Initialize() . = ..() permutated = list() @@ -167,7 +148,6 @@ on_range() /obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range - SEND_SIGNAL(src, COMSIG_PROJECTILE_RANGE_OUT) qdel(src) //to get the correct limb (if any) for the projectile hit message @@ -184,17 +164,17 @@ /obj/item/projectile/proc/prehit(atom/target) return TRUE -/obj/item/projectile/proc/on_hit(atom/target, blocked = FALSE) +/** + * Called when we hit something. + * + * @params + * * target - what we hit + * * blocked - 0 to 100 percentage mitigation/block + * * def zone - where we hit if we hit a mob. + */ +/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, def_zone) if(fired_from) SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle) - - // i know that this is probably more with wands and gun mods in mind, but it's a bit silly that the projectile on_hit signal doesn't ping the projectile itself. - // maybe we care what the projectile thinks! See about combining these via args some time when it's not 5AM - var/obj/item/bodypart/hit_limb - if(isliving(target)) - var/mob/living/L = target - hit_limb = L.check_limb_hit(def_zone) - SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_ON_HIT, firer, target, Angle, hit_limb) var/turf/target_loca = get_turf(target) var/hitx @@ -246,13 +226,10 @@ new impact_effect_type(target_loca, hitx, hity) var/organ_hit_text = "" - var/limb_hit = hit_limb + var/limb_hit = L.check_limb_hit(def_zone)//to get the correct message info. if(limb_hit) organ_hit_text = " in \the [parse_zone(limb_hit)]" - - if(suppressed==SUPPRESSED_VERY) - playsound(loc, hitsound, 5, TRUE, -1) - else if(suppressed) + if(suppressed) playsound(loc, hitsound, 5, 1, -1) to_chat(L, "You're shot by \a [src][organ_hit_text]!") else @@ -282,45 +259,54 @@ else return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume -/obj/item/projectile/proc/on_ricochet(atom/A) - if(!ricochet_auto_aim_angle || !ricochet_auto_aim_range) - return - - var/mob/living/unlucky_sob - var/best_angle = ricochet_auto_aim_angle - if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT)) - best_angle += NICE_SHOT_RICOCHET_BONUS - for(var/mob/living/L in range(ricochet_auto_aim_range, src.loc)) - if(L.stat == DEAD || !isInSight(src, L)) - continue - var/our_angle = abs(closer_angle_difference(Angle, get_projectile_angle(src.loc, L.loc))) - if(our_angle < best_angle) - best_angle = our_angle - unlucky_sob = L - - if(unlucky_sob) - setAngle(get_projectile_angle(src, unlucky_sob.loc)) - /obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache) beam_segments[beam_index] = pcache beam_index = pcache beam_segments[beam_index] = null -/obj/item/projectile/Bump(atom/A) - var/datum/point/pcache = trajectory.copy_to() - var/turf/T = get_turf(A) - if(trajectory && ricochets < ricochets_max && check_ricochet_flag(A) && check_ricochet(A)) - ricochets++ - if(A.handle_ricochet(src)) - on_ricochet(A) - ignore_source_check = TRUE - decayedRange = max(0, decayedRange - reflect_range_decrease) - ricochet_chance *= ricochet_decay_chance - damage *= ricochet_decay_damage - range = decayedRange - if(hitscan) - store_hitscan_collision(pcache) +/** + * Determines if we should ricochet off of something. + * By default, asks the thing if we should ricochet off it, but because we're called first, we get final say. + * Returns TRUE or FALSE. + */ +/obj/item/projectile/proc/check_ricochet(atom/A) + if(ricochets > ricochets_max) //safety thing, we don't care about what the other thing says about this. + return FALSE + var/them = A.check_projectile_ricochet(src) + switch(them) + if(PROJECTILE_RICOCHET_PREVENT) + return FALSE + if(PROJECTILE_RICOCHET_FORCE) return TRUE + if(PROJECTILE_RICOCHET_NO) + return FALSE + if(PROJECTILE_RICOCHET_YES) + return prob(ricochet_chance) + else + CRASH("Invalid return value for projectile ricochet check from [A].") + +/** + * Handles ricocheting off of something. + * By default, also asks the thing to handle it, but because we're called first, we get final say. + */ +/obj/item/projectile/proc/handle_ricochet(atom/A) + ricochets++ + ignore_source_check = TRUE + decayedRange = max(0, decayedRange - reflect_range_decrease) + pixel_move_interrupted = TRUE + range = decayedRange + return A.handle_projectile_ricochet(src) + +/obj/item/projectile/Bump(atom/A) + if(!trajectory) + return + var/turf/T = get_turf(A) + if(check_ricochet(A)) + var/datum/point/pcache = trajectory.copy_to() + if(hitscan) + store_hitscan_collision(pcache) + handle_ricochet(A) + return TRUE var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. if(def_zone && check_zone(def_zone) != BODY_ZONE_CHEST) @@ -395,21 +381,6 @@ return T //Returns null if nothing at all was found. -/obj/item/projectile/proc/check_ricochet(atom/A) - var/chance = ricochet_chance * A.ricochet_chance_mod - if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT)) - chance += NICE_SHOT_RICOCHET_BONUS - if(prob(chance)) - return TRUE - return FALSE - -/obj/item/projectile/proc/check_ricochet_flag(atom/A) - if((flag in list("energy", "laser")) && (A.flags_ricochet & RICOCHET_SHINY)) - return TRUE - if((flag in list("bomb", "bullet")) && (A.flags_ricochet & RICOCHET_HARD)) - return TRUE - return FALSE - /// one move is a tile. /obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight. if(!trajectory && isnull(forced_angle) && isnull(Angle)) @@ -447,8 +418,6 @@ /obj/item/projectile/proc/fire(angle, atom/direct_target) if(fired_from) SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original) //If no angle needs to resolve it from xo/yo! - if(shrapnel_type) - AddElement(/datum/element/embed, projectile_payload = shrapnel_type) if(!log_override && firer && original) log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]") if(direct_target) @@ -489,6 +458,7 @@ /obj/item/projectile/proc/setAngle(new_angle, hitscan_store_segment = TRUE) //wrapper for overrides. Angle = new_angle + pixel_move_interrupted = TRUE if(!nondirectional_sprite) var/matrix/M = new M.Turn(Angle) @@ -515,6 +485,7 @@ trajectory.initialize_location(target.x, target.y, target.z, 0, 0) if(hitscan) record_hitscan_start(RETURN_PRECISE_POINT(src)) + pixel_move_interrupted = TRUE if(zc) after_z_change(old, target) @@ -563,7 +534,7 @@ * The proc to make the projectile go, using a simulated pixel movement line trace. * Note: deciseconds_equivalent is currently only used for homing, times is the number of times to move pixel_increment_amount. * Trajectory multiplier directly modifies the factor of pixel_increment_amount to go per time. - * It's complicated, so probably just don'ot mess with this unless you know what you're doing. + * It's complicated, so probably just don't mess with this unless you know what you're doing. */ /obj/item/projectile/proc/pixel_move(times, hitscanning = FALSE, deciseconds_equivalent = world.tick_lag, trajectory_multiplier = 1, allow_animation = TRUE) if(!loc || !trajectory) @@ -573,6 +544,7 @@ M.Turn(Angle) transform = M var/forcemoved = FALSE + pixel_move_interrupted = FALSE // reset that var/turf/oldloc = loc var/old_px = pixel_x var/old_py = pixel_y @@ -608,7 +580,9 @@ if(!--safety) CRASH("[type] took too long (allowed: [CEILING(pixel_increment_amount/world.icon_size,1)*2] moves) to get to its location.") step_towards(src, T) - if(QDELETED(src)) + if(QDELETED(src) || pixel_move_interrupted) // this doesn't take into account with pixel_move_interrupted the portion of the move cut off by any forcemoves, but we're opting to ignore that for now + // the reason is the entire point of moving to pixel speed rather than tile speed is smoothness, which will be crucial when pixel movement is done in the future + // reverting back to tile is more or less the only way of fixing this issue. return pixels_range_leftover += pixel_increment_amount if(pixels_range_leftover > world.icon_size) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 2f12f0f69b..d95c3b5028 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -39,6 +39,9 @@ /obj/item/projectile/beam/weak damage = 15 +/obj/item/projectile/beam/weak/improvised + damage = 10 + /obj/item/projectile/beam/weak/penetrator armour_penetration = 50 diff --git a/code/modules/projectiles/projectile/bullets/pistol.dm b/code/modules/projectiles/projectile/bullets/pistol.dm index 319640a4d5..62ff4adb11 100644 --- a/code/modules/projectiles/projectile/bullets/pistol.dm +++ b/code/modules/projectiles/projectile/bullets/pistol.dm @@ -47,4 +47,16 @@ if(L.getStaminaLoss() >= 60) L.Sleeping(300) else - L.adjustStaminaLoss(25) \ No newline at end of file + L.adjustStaminaLoss(25) + +// .32 ACP (Improvised Pistol) + +/obj/item/projectile/bullet/c32acp + name = ".32 bullet" + damage = 13 + +/obj/item/projectile/bullet/r32acp + name = ".32 rubber bullet" + damage = 3 + eyeblur = 1 + stamina = 20 diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 0e0056f958..52fd89e37c 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -857,3 +857,13 @@ taste_mult = 2.5 //sugar's 1.5, capsacin's 1.5, so a good middle ground. taste_description = "smokey sweetness" value = REAGENT_VALUE_COMMON + +/datum/reagent/consumable/laughsyrup + name = "Laughin' Syrup" + description = "The product of juicing Laughin' Peas. Fizzy, and seems to change flavour based on what it's used with!" + nutriment_factor = 5 * REAGENTS_METABOLISM + color = "#803280" + taste_mult = 2 + taste_description = "fizzy sweetness" + value = REAGENT_VALUE_COMMON + \ No newline at end of file diff --git a/code/modules/research/bepis.dm b/code/modules/research/bepis.dm index c6ce45f160..20ca7987d5 100644 --- a/code/modules/research/bepis.dm +++ b/code/modules/research/bepis.dm @@ -33,8 +33,8 @@ var/inaccuracy_percentage = 1.5 var/positive_cash_offset = 0 var/negative_cash_offset = 0 - var/minor_rewards = list(/obj/item/stack/circuit_stack/full, //To add a new minor reward, add it here. - /obj/item/airlock_painter/decal, + var/minor_rewards = list(/obj/item/stack/circuit_stack/full, //To add a new minor reward, add it here. + /obj/item/flashlight/flashdark, /obj/item/pen/survival, /obj/item/circuitboard/machine/sleeper/party, /obj/item/toy/sprayoncan) diff --git a/code/modules/research/designs/autolathe_desings/autolathe_designs_sec_and_hacked.dm b/code/modules/research/designs/autolathe_desings/autolathe_designs_sec_and_hacked.dm index 9cbef4abdd..9768d80a59 100644 --- a/code/modules/research/designs/autolathe_desings/autolathe_designs_sec_and_hacked.dm +++ b/code/modules/research/designs/autolathe_desings/autolathe_designs_sec_and_hacked.dm @@ -30,6 +30,14 @@ build_path = /obj/item/ammo_box/c38 category = list("initial", "Security") +/datum/design/r32acp + name = "Rubber Pistol Bullet (.32)" + id = "r32acp" + build_type = AUTOLATHE + materials = list(/datum/material/iron = 250) + build_path = /obj/item/ammo_casing/r32acp + category = list("initial", "Security") + ///////////////// ///Hacked Gear // ///////////////// @@ -78,7 +86,7 @@ name = "Rifle Receiver" id = "rifle_receiver" build_type = AUTOLATHE - materials = list(/datum/material/iron = 40000) + materials = list(/datum/material/iron = 24000) build_path = /obj/item/weaponcrafting/improvised_parts/rifle_receiver category = list("hacked", "Security") @@ -197,3 +205,23 @@ materials = list(/datum/material/iron = 5500) build_path = /obj/item/clothing/head/foilhat category = list("hacked", "Misc") + +/datum/design/c32acp + name = "Pistol Bullet (.32)" + id = "c32acp" + build_type = AUTOLATHE + materials = list(/datum/material/iron = 500) + build_path = /obj/item/ammo_casing/c32acp + category = list("hacked", "Security") + +///////////////// +// Magazines // +///////////////// + +/datum/design/m32acp + name = "Empty .32 Magazine" + id = "m32acp" + build_type = AUTOLATHE + materials = list(/datum/material/iron = 10000) + build_path = /obj/item/ammo_box/magazine/m32acp/empty + category = list("hacked", "Security") diff --git a/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm b/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm index ea73df568f..320f856b35 100644 --- a/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm +++ b/code/modules/research/designs/autolathe_desings/autolathe_designs_tcomms_and_misc.dm @@ -281,3 +281,10 @@ build_path = /obj/item/weaponcrafting/improvised_parts/trigger_assembly category = list("initial", "Misc") +/datum/design/focusing_lens + name = "Makeshift Lens" + id = "makeshift_lens" + build_type = AUTOLATHE + materials = list(/datum/material/iron = 2000, /datum/material/glass = 4000) + build_path = /obj/item/weaponcrafting/improvised_parts/makeshift_lens + category = list("initial", "Misc") diff --git a/code/modules/surgery/advanced/lobotomy.dm b/code/modules/surgery/advanced/lobotomy.dm index 2b2d11e54f..4a52e446bc 100644 --- a/code/modules/surgery/advanced/lobotomy.dm +++ b/code/modules/surgery/advanced/lobotomy.dm @@ -1,6 +1,6 @@ /datum/surgery/advanced/lobotomy name = "Lobotomy" - desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, but might cause another permanent trauma in return." + desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, at the cost of severe, albeit repairable, brain damage." steps = list( /datum/surgery_step/incise, /datum/surgery_step/retract_skin, @@ -43,11 +43,14 @@ target.mind.remove_antag_datum(/datum/antagonist/brainwashed) switch(rand(1,6))//Now let's see what hopefully-not-important part of the brain we cut off if(1) - target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC) + target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY) if(2) - target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_MAGIC) + target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY) if(3) - target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC) + target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_SURGERY) + // you're cutting off a part of the brain.w + var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN) + B.applyOrganDamage(50, 100) return TRUE /datum/surgery_step/lobotomize/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) @@ -59,11 +62,11 @@ B.applyOrganDamage(80) switch(rand(1,3)) if(1) - target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC) + target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_LOBOTOMY) if(2) - target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_MAGIC) + target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY) if(3) - target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC) + target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_LOBOTOMY) else user.visible_message("[user] suddenly notices that the brain [user.p_they()] [user.p_were()] working on is not there anymore.", "You suddenly notice that the brain you were working on is not there anymore.") return FALSE diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm index 958a4e65c4..d45cb8d26f 100644 --- a/code/modules/vehicles/cars/car.dm +++ b/code/modules/vehicles/cars/car.dm @@ -49,7 +49,7 @@ mob_exit(M, silent) return TRUE -/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user) +/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) if(!I.force) return FALSE if(occupants[user]) @@ -85,4 +85,4 @@ if(!silent) M.visible_message("[M] is forced into \the [src]!") M.forceMove(src) - add_occupant(M, VEHICLE_CONTROL_KIDNAPPED) \ No newline at end of file + add_occupant(M, VEHICLE_CONTROL_KIDNAPPED) diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm index 2b9890b863..cd21b61dbe 100644 --- a/code/modules/vehicles/cars/clowncar.dm +++ b/code/modules/vehicles/cars/clowncar.dm @@ -36,7 +36,7 @@ visible_message("[src] spews out a ton of space lube!") new /obj/effect/particle_effect/foam(loc) //YEET -/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user) +/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) . = ..() if(istype(I, /obj/item/reagent_containers/food/snacks/grown/banana)) var/obj/item/reagent_containers/food/snacks/grown/banana/banana = I @@ -129,4 +129,4 @@ icon_state = initial(icon_state) /obj/vehicle/sealed/car/clowncar/proc/StopDroppingOil() - droppingoil = FALSE \ No newline at end of file + droppingoil = FALSE diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm index ddd7736522..92e40bc3a8 100644 --- a/code/modules/vending/assist.dm +++ b/code/modules/vending/assist.dm @@ -14,7 +14,8 @@ /obj/item/stock_parts/cell/upgraded = 2) premium = list(/obj/item/stock_parts/cell/upgraded/plus = 2, /obj/item/flashlight/lantern = 2, - /obj/item/beacon = 2) + /obj/item/beacon = 2, + /obj/item/airlock_painter/decal = 5) product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!" armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) refill_canister = /obj/item/vending_refill/assist @@ -24,4 +25,4 @@ payment_department = NO_FREEBIES /obj/item/vending_refill/assist - icon_state = "refill_engi" \ No newline at end of file + icon_state = "refill_engi" diff --git a/code/modules/vending/engivend.dm b/code/modules/vending/engivend.dm index a0248d2d12..50e0fb9cde 100644 --- a/code/modules/vending/engivend.dm +++ b/code/modules/vending/engivend.dm @@ -15,6 +15,7 @@ /obj/item/electronics/airalarm = 10, /obj/item/electronics/firealarm = 10, /obj/item/electronics/firelock = 10, + /obj/item/airlock_painter/decal = 5, /obj/item/rcd_ammo = 3 ) contraband = list(/obj/item/stock_parts/cell/potato = 3, @@ -35,4 +36,4 @@ cost_multiplier_per_dept = list(ACCOUNT_ENG = 0) /obj/item/vending_refill/engivend - icon_state = "refill_engi" \ No newline at end of file + icon_state = "refill_engi" diff --git a/code/modules/vending/kinkmate.dm b/code/modules/vending/kinkmate.dm index c647964c63..24fb685eac 100644 --- a/code/modules/vending/kinkmate.dm +++ b/code/modules/vending/kinkmate.dm @@ -20,7 +20,11 @@ /obj/item/clothing/under/misc/gear_harness = 10, /obj/item/dildo/custom = 5, /obj/item/electropack/shockcollar = 3, - /obj/item/assembly/signaler = 3 + /obj/item/assembly/signaler = 3, + /obj/item/clothing/under/shorts/polychromic/pantsu, + /obj/item/clothing/under/misc/poly_bottomless, + /obj/item/clothing/under/misc/poly_tanktop, + /obj/item/clothing/under/misc/poly_tanktop/female ) contraband = list( /obj/item/clothing/neck/petcollar/locked = 2, diff --git a/code/modules/vending/youtool.dm b/code/modules/vending/youtool.dm index c59020d3a4..c936d9c32c 100644 --- a/code/modules/vending/youtool.dm +++ b/code/modules/vending/youtool.dm @@ -18,7 +18,8 @@ /obj/item/clothing/gloves/color/fyellow = 4, /obj/item/multitool = 2) premium = list(/obj/item/clothing/gloves/color/yellow = 2, - /obj/item/weldingtool/hugetank = 2) + /obj/item/weldingtool/hugetank = 2, + /obj/item/airlock_painter/decal = 3) armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) refill_canister = /obj/item/vending_refill/tool resistance_flags = FIRE_PROOF @@ -28,4 +29,4 @@ cost_multiplier_per_dept = list(ACCOUNT_ENG = 0) /obj/item/vending_refill/tool - icon_state = "refill_engi" \ No newline at end of file + icon_state = "refill_engi" diff --git a/config/plushies/defines.txt b/config/plushies/defines.txt index e69de29bb2..e7a92f6ac2 100644 --- a/config/plushies/defines.txt +++ b/config/plushies/defines.txt @@ -0,0 +1,2 @@ +# EXAMPLE +# SNOWFLAKE_PLUSHIES example {"name":"example","desc":"thanks, coders.","icon_state":"","attack_verb":["thumped","whomped","bumped"],"squeak_override":{"sound/weapons/magout.ogg":1}} diff --git a/config/plushies/plushie_config.txt b/config/plushies/plushie_config.txt deleted file mode 100644 index 7cd1d88f3e..0000000000 --- a/config/plushies/plushie_config.txt +++ /dev/null @@ -1,2 +0,0 @@ -# EXAMPLE -# SNOWFLAKE_PLUSHIES example {"name":"example","desc":"thanks, coders.","icon_state":"","attack_verb":["thumped","whomped","bumped"],"squeak_override":{"sound/weapons/magout.ogg":1}} diff --git a/html/changelog.html b/html/changelog.html index 0f38f090b7..ce00531001 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,91 @@ -->
    +

    15 June 2020

    +

    Anturk, kevinz000 updated:

    +
      +
    • VV now properly allows access to datums in associative lists
    • +
    • SDQL2 printout has been upgraded for the 5th time.
    • +
    +

    kevinz000 updated:

    +
      +
    • plushies now work. credits to timothytea.
    • +
    • You can now tape knives to cleanbots
    • +
    +

    kevinz000 (port from VOREStation) updated:

    +
      +
    • Ported zoomba skins for cyborgs.
    • +
    + +

    14 June 2020

    +

    Bhijn updated:

    +
      +
    • raw HTML can no longer be used in flavortext
    • +
    • Added Lesbian Visibility Day (April 26th)
    • +
    • Added Bisexual Visibility Day (September 23rd)
    • +
    • Added Coming Out Day! (October 11th)
    • +
    • Added Intersex Awareness Day (October 26th)
    • +
    • Added Transgender Awareness Week (November 13th - 19th)
    • +
    • Added the Transgender Day of Remembrance (November 20th)
    • +
    • Added Asexual Awareness Week (Last full week of October, this year it'll be Oct. 25-31)
    • +
    • Added the Stonewall Riot Anniversary (June 28)
    • +
    • Moth week is now *actually* the last full week of July, instead of the 4th of every weekday + the 5th Sunday.
    • +
    +

    Floof Ball / Kathrin Morrison / Floof Ball#0798 updated:

    +
      +
    • Improvised Pistol + 32 ACP ammo.
    • +
    • Improvised Energy Gun. Fires 5 shots of 10 burn damage each. Can be upgraded with a lens made from glassworking and T4 parts for a minor buff.
    • +
    • Ammo + gun part loot spawners for mappers.
    • +
    • New sprites for Improvised Rifle, the ability to sling the rifle and a sprite for that.
    • +
    • New sprites for the Improvised Shotgun and its sling sprite.
    • +
    • Improvised shotguns are now two-handed only.
    • +
    • Improvised shotguns now only have a much less harsh 0.9* modifier, keeping them two hits to crit with slugs and buckshot. It can no longer be dual-wielded but can still be sawn off for w_class medium (can fit in backpacks).
    • +
    • Missing handsaw icons added in.
    • +
    • Crafting table cleaned up into sections.
    • +
    +

    Ghommie updated:

    +
      +
    • changed the weak attack message prefix from "inefficiently" to "limply", "feebly" and "saplessly" and lowered the threshold.
    • +
    • Fixing old beserker hardsuits having the wrong helmet type.
    • +
    +

    The0bserver and Stewydeadmike updated:

    +
      +
    • Hey, there's a bit of dust in this recipe book! Recipes using peas? How many recipes does this book even have?
    • +
    • Adds 6 new food items, and 1 new consumable reagent using all forms of the recently discovered peas. Ask your local botanist and chef for them today!
    • +
    +

    YakumoChen updated:

    +
      +
    • Adds polychrome options to loadout
    • +
    • Adds risque polychrome options to Kinkmate vendors
    • +
    +

    kevinz000 updated:

    +
      +
    • emissive blockers can no longer be radioactive.
    • +
    • Ghosts can now scan air inside most objects that contain air by clicking on them.
    • +
    • Directional blocking has been added, keybound to G. This will reduce a portion of incoming damage if done with eligible items at the cost of stamina damage incurred to the user based on damage blocked as well as while active, as well as in most cases preventing the user from doing any attacks while this is active.
    • +
    • Parrying has been added, keybound to F. Timing-based counterattacks, effect heavily dependent on the item, WIP.
    • +
    • Disks are now smaller.
    • +
    + +

    12 June 2020

    +

    EmeraldSundisk updated:

    +
      +
    • The Detective's Office has been commandeered in order to serve a Head of Security. Detectives will find their new office within starboard maintenance.
    • +
    • Adds the Head of Security's standard equipment to their new office.
    • +
    • Slight adjustment/expansion to the main security department
    • +
    • Additional maintenance work
    • +
    +

    timothyteakettle updated:

    +
      +
    • Crabs, cockroaches, slimes and crabs are now small enough to fit in pet carriers
    • +
    + +

    11 June 2020

    +

    Ghommie updated:

    +
      +
    • Balanced vending machine prices to be generally more affordable, a minority has been priced up though.
    • +
    +

    10 June 2020

    DeltaFire15 updated:

      diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index fbe6f85c08..a25fb6595c 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -25505,9 +25505,29 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. all the quirky little soulbits. - bugfix: box brig miscellaneous issues - bugfix: box station hos office -2020-04-09: +2020-04-15: + Arturlang: + - rscadd: Adds garlic, a mutation of onions + - rscadd: You can now make garlic necklaces. + - tweak: Tweaked hunger to be more the same as blood level for bloodsuckers + - tweak: Bloodsuckers no longer get zero healing from regenerative cores, the core + now heals their wounds but not their blood. + - balance: Bloodsucker heal is now based a lot more on blood level. + - bugfix: You can no longer be effectively immortal when fully auged as a bloodsucker. + - bugfix: Regenerative cores will regain their old names when they are renewed, + no more working decayed cores. + - code_imp: Removes a lot of unnecesiry clutter of comments and tries to make the + vars more consistent for bloodsucker code. + - code_imp: Made the regenerative core use one proc instead of copypasta + Auris456852: + - rscadd: Added printer sound for admins that plays when someone messages Centcomm + or the Syndicate. Just like RP! Bhijn: - admin: The traitor panel now actually shows a list of contractor targets. + - server: Fail2Topic now supports Linux. Do beware that this requires some sysop + experience to properly set up! + - config: Fail2Topic is now disabled by default, and the out-of-the-box config files + have been updated to be a little more detailed. - rscadd: Added a lfwb-inspired orbiting pixel + flashing outline animation to the sprint and combat mode buttons. This can be toggled via the preferences menu, and is completely independent from all HUD themes. @@ -25528,9 +25548,14 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. has come to an end. - rscadd: Underwear now fulfills its purpose. - rscadd: Privates visibility preferences. + - refactor: refactored polychromic clothing into an element. - tweak: Blacklists unsynthetizable reagents from botany bees honey production. + - bugfix: Fixed toilet cistern loot spawning on the floor. - tweak: The megafauna's hitbox doesn't include 0 alpha sections anymore. + - bugfix: Wizard robes & co work again now. + - bugfix: Fixed some spell casting message spam. - rscadd: Added M/F body preferences. + - bugfix: fixed yet another few airless issues with the space hermit ruin. - bugfix: Intellectual property infringment is not cool. - bugfix: Near-station nuclear explosions now display the on-station nuke explosion cinematic, consistently with its clearance of the station level and its nuclear @@ -25538,17 +25563,32 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - balance: Nuclear bombs can't be anchored in space areas (not just turfs) anymore, as a quick effortless way to discourage players from anchoring the device on the edge of the map. + - bugfix: Fixed a whacky miniature cell duping issue. + - bugfix: Liver failure is back! + - tweak: standarized a few (prefs off) side effects from enlargment chems on livers + to do organ damage instead without the blood volume whackiness. + - tweak: High liver damage now slows mobs down. - bugfix: Fixing an issue with the split personality removing the original owner from the round if the body died while the stranger was in control. + - bugfix: fixed a few floaty sprite accessories. - bugfix: Fixed a few mutations not working correctly. + - bugfix: Fixed the phantom thief component, again. - refactor: Made votes obfuscation settings more robust. - admin: Also included them in custom votes. - tweak: The map rotation vote will only hide ongoing votes now, not the results. + - balance: The crusher's vortex talisman trophy now has a cooldown between each + spawned wall. + - refactor: Backend body size preferences. - bugfix: Infiltrator's boots don't stop slips and "space wind" through the power of runtime errors anymore, and properly silence the user's footsteps now. - tweak: Chaplains are now inelegible for bloodsuckers. - bugfix: Fixed crafting hud icon overlapping the second pocket on 1:1 screen mode. - code_imp: Removed some dead flightsuit code leftovers. + - rscadd: Reenabled the swarmers event. Also blacklisted another dozen other machineries + and structures that may be critical to the shift or station integrity from swarmers' + HUNGER for materials. + - bugfix: Fixed find_safe_turfs() searching for turfs with concentration of oxygen + lower than 16 rather higher. KathrinBailey: - rscadd: Radiation shuttesr to all supermatters. - rscadd: Windowed shutters to armouries where relevant. @@ -25605,8 +25645,12 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. curve for threat level. - tweak: '"Chaotic" storyteller now simply ramps up threat level as round goes on.' - rscadd: '"Classic" storyteller, basically doing what "random" did before.' + - rscadd: Latejoin changelings for dynamic + - bugfix: Average threat calculation works now + - bugfix: Contamination is back. Seris02: - balance: stops hijackers from being able to remotely blow up borgs + - rscadd: wall walking boots - bugfix: made color picking for character appearance show the colors when you pick them - tweak: the sergal markings @@ -25616,6 +25660,8 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - rscadd: Ports over TG's Mortars and Pestles. - balance: Water, Holywater and Unholywater will now now quickly purge itself if you have 151u in your system + - rscadd: Most crate types can now be made, some costing more do to function over + fashion - rscadd: Medical Mechs syringe gun now knows many more life saving chems, like Insulin, Dexalin, Prussian Blue, Kelotane and Bicaridine - balance: torches take less staminda to use. @@ -25633,6 +25679,9 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. Xantholne: - bugfix: bumbles will stop sleeping so much actioninja: + - rscadd: Washing machines now support arbitrary dye color + - rscadd: Washing machines now dye nearly every item. + - refactor: lots of backend changes to clothing overlays, report any issues - rscadd: Better glowing lights actioninja (ported by Ghommie): - refactor: repathed all under clothing, keep an eye out for errors. @@ -25647,6 +25696,9 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. now - rscdel: quarters that found their way out of the quartermaster's back room kappa-sama: + - rscdel: normies can no longer steal circuit codes + - balance: doubles the Stam damage of nonlethal krav stompers + - bugfix: no longer Krav Maga stomp people that are standing - balance: no more 20pop requirement for noslips keronshb: - tweak: Reverts Mining Base RNG Placement @@ -25661,6 +25713,10 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - bugfix: turfs properly initialize atom colors if they're colored. - tweak: cloners now stabilize mutations while someone's cloning, meaning active genes will not life tick. + - code_imp: datum/pipeline return_air stack trace now gives a reference so it's + actually marginally useful if caught in round. + - rscadd: Volumetric storage is here. + - rscadd: Traitor chaplains can now become neutered versions of cults. - bugfix: beams should no longer go across the map and mess everything up if their source or target isn't on a turf. kiwedespars: @@ -25687,6 +25743,7 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - balance: Scatterlaser is now in-line with buckshot. Nukies can't purchase scatterlaser shot. - rscdel: Null crates are no longer available in cargo! + - bugfix: Sleeping Carp and Rising Bass now dodge. Again. - bugfix: Russian Revolver can no longer be exploited for a free 357. - balance: Several guns now shoot exactly where you click regardless of movement or turning. @@ -25709,3 +25766,196 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - bugfix: insidious balaclava muzzlepsprite works - rscadd: You can now choose your name and color as a holoparasite/guardian/holocarp! - bugfix: fixes lings being able to get mechanical holoparasites +2020-04-16: + ForrestWick: + - tweak: changed a certain item to be called meatball, ended racism, thank you obama + Linzolle: + - tweak: remove any slurs, etc. to comply with GitHub's ToS +2020-04-19: + Anonymous: + - rscadd: Xenohybrids will now scream like xeno. + Arturlang: + - bugfix: You can no longer spam craft things using the crafting menu + Detective-Google: + - bugfix: uncorks some of Lambda's rooms. + Ghommie: + - rscadd: Custom skin tone preferences. + - tweak: Normalized box dorm lockers. Also removed a straight jacket found in the + same area. + Jake Park: + - bugfix: fixed path name for youtool vending + Putnam3145: + - bugfix: Objectives now clean theirselves up instead of leaving null entries in + lists everywhere. + Seris02: + - bugfix: stops magboots from not updating slowdowns + Trilbyspaceclone: + - rscadd: Maints have seen an uptick in left over types of welders, and tools. As + well as different types of masks + - rscadd: New type of 02 locker - Rng! It can have almost any type of gas/breath + mask and almost any type of o2 tank as well as even plasma men internals - Fancy! + - tweak: Tool lockers have 70% odds to have a spare random tool inside! + - rscadd: 12 new more drinks for most races! + - rscadd: New animations for mauna loa, and colour swap from red to blue for a Paramedic + Hardsuit helm + - tweak: Lowers cog champ ((the drink)) flare rate + - rscadd: Six more Sci based bounties have been posted at your local Cargo Bounty + Request console + - bugfix: Mimes have made catnip plants not become invisible. How helpful. + - rscadd: Honey Palm now distills into mead rather then wine + UristMcAstronaut: + - rscadd: Adds circuit analyzers to maps and to integrated circuit printer and circuitry + starter crate. + kappa-sama: + - balance: aranesp heals 10 instead of 18 stamina per tick + - rscdel: removed roundstart hyper earrape screams from xenohybrids + kevinz000: + - bugfix: you can no longer stun xenos + - balance: Contractor kits are now poplocked to 30 players. + - rscadd: Shield bashing has been added + necromanceranne: + - rscadd: You can now craft armwraps! + - balance: Pugilists disarm you more easily and are harder to disarm. They also + get a discount on disarm. + - balance: Pugilists only suffer a flat 10% chance to miss you. It's just like old + punches! Kinda. + - balance: Chaplain's armbands are a +2, up from a +1! + - balance: Martial artists spend stamina when they disarm. + - balance: Rising Bass had several moves shortened and made stronger. Has a disarm + override attack which does stamina damage and trips people on a disarm stun + punch. + - balance: CQC had it's disarm move altered to be a stronger version of Krav Maga's. + Dizzies and disarms on a disarm stun punch or just does some stamina damage + and brute damage. + - balance: Sleeping Carp can punch you to the floor on a harm stun punch. + - bugfix: Hugs of the Northstar are no longer nodrop. + - bugfix: Adding in some overrides and proper flag checks for martial arts. + - bugfix: Stun thresholding stops disarm spams at extremely high stamina loss. + - tweak: Ashen Arrows are actually called Ashen Arrows in the crafting menu. +2020-06-08: + DeltaFire15: + - bugfix: Delinging now properly removes their special role + - balance: Keycard auth devices now require two seperate IDs with sufficient access + to auth. + Linzolle: + - bugfix: shotguns no longer delete chambered shells while firing + kevinz000: + - rscadd: test + - rscdel: test + shellspeed1: + - rscadd: Internal tanks are now printable at the engineering lathe. + timothyteakettle: + - bugfix: newly created areas using blueprints now maintain the previous areas noteleport + value + - bugfix: kudzu seeds now actually spawn vines +2020-06-09: + Anonymous: + - rscadd: Added Orville-inspired clothing as a worthy alternative to Trek stuff. + - tweak: Adds chaplain role allowance to the TMP Service Uniform loadout. + - tweak: Adds paramedic in every medsci mentioned uniform loadout. + DeltaFire15: + - bugfix: Offstation AIs can once again only interact with their z-level + Ghommie: + - bugfix: Fixing IC material containers interaction with stacks, for real. + Trilbyspaceclone: + - tweak: Gasses like BZ and Masiam seem to just sell for less in cargo, markets + seem to change it seems + kevinz000: + - balance: plantpeople should stop dying in the halls now + - rscadd: Modifier-independent hotkey bindings have been added. +2020-06-10: + DeltaFire15: + - bugfix: Golems / simillar now inherit the neutered antag datum if the creator + is neutered. + Ghommie: + - bugfix: Fixing missing pill type buttons from the chem master UI. + Naksu: + - code_imp: Lighting corner updates are ever so slightly faster. + Putnam for helping me code the contamination clearing on people: + - rscadd: Lab made Zeolites have been remade anew and more affective now that they + refined the best possable way to mix and make a supper Zeolite capable of clearing + contamination form not only people but items! + Trilbyspaceclone: + - tweak: Tank Dispender has been moved into toxin storage from toxins + kevinz000: + - rscadd: traitor classes can now be poplocked. hijack/glorious death are now locked + to 25/20 respectively. + timothyteakettle: + - rscadd: adds a new fermichem, used for creating sentient plushies! + - rscadd: Mice can now breed using cheese wedges + - rscadd: Royal cheese can be crafted to convert a mouse into king rat +2020-06-11: + Ghommie: + - balance: Balanced vending machine prices to be generally more affordable, a minority + has been priced up though. +2020-06-12: + EmeraldSundisk: + - tweak: The Detective's Office has been commandeered in order to serve a Head of + Security. Detectives will find their new office within starboard maintenance. + - rscadd: Adds the Head of Security's standard equipment to their new office. + - tweak: Slight adjustment/expansion to the main security department + - rscadd: Additional maintenance work + timothyteakettle: + - rscadd: Crabs, cockroaches, slimes and crabs are now small enough to fit in pet + carriers +2020-06-14: + Bhijn: + - bugfix: raw HTML can no longer be used in flavortext + - rscadd: Added Lesbian Visibility Day (April 26th) + - rscadd: Added Bisexual Visibility Day (September 23rd) + - rscadd: Added Coming Out Day! (October 11th) + - rscadd: Added Intersex Awareness Day (October 26th) + - rscadd: Added Transgender Awareness Week (November 13th - 19th) + - rscadd: Added the Transgender Day of Remembrance (November 20th) + - rscadd: Added Asexual Awareness Week (Last full week of October, this year it'll + be Oct. 25-31) + - rscadd: Added the Stonewall Riot Anniversary (June 28) + - bugfix: Moth week is now *actually* the last full week of July, instead of the + 4th of every weekday + the 5th Sunday. + Floof Ball / Kathrin Morrison / Floof Ball#0798: + - rscadd: Improvised Pistol + 32 ACP ammo. + - rscadd: Improvised Energy Gun. Fires 5 shots of 10 burn damage each. Can be upgraded + with a lens made from glassworking and T4 parts for a minor buff. + - rscadd: Ammo + gun part loot spawners for mappers. + - rscadd: New sprites for Improvised Rifle, the ability to sling the rifle and a + sprite for that. + - rscadd: New sprites for the Improvised Shotgun and its sling sprite. + - balance: Improvised shotguns are now two-handed only. + - balance: Improvised shotguns now only have a much less harsh 0.9* modifier, keeping + them two hits to crit with slugs and buckshot. It can no longer be dual-wielded + but can still be sawn off for w_class medium (can fit in backpacks). + - bugfix: Missing handsaw icons added in. + - tweak: Crafting table cleaned up into sections. + Ghommie: + - tweak: changed the weak attack message prefix from "inefficiently" to "limply", + "feebly" and "saplessly" and lowered the threshold. + - bugfix: Fixing old beserker hardsuits having the wrong helmet type. + The0bserver and Stewydeadmike: + - rscadd: Hey, there's a bit of dust in this recipe book! Recipes using peas? How + many recipes does this book even have? + - rscadd: Adds 6 new food items, and 1 new consumable reagent using all forms of + the recently discovered peas. Ask your local botanist and chef for them today! + YakumoChen: + - tweak: Adds polychrome options to loadout + - tweak: Adds risque polychrome options to Kinkmate vendors + kevinz000: + - bugfix: emissive blockers can no longer be radioactive. + - rscadd: Ghosts can now scan air inside most objects that contain air by clicking + on them. + - rscadd: Directional blocking has been added, keybound to G. This will reduce a + portion of incoming damage if done with eligible items at the cost of stamina + damage incurred to the user based on damage blocked as well as while active, + as well as in most cases preventing the user from doing any attacks while this + is active. + - rscadd: Parrying has been added, keybound to F. Timing-based counterattacks, effect + heavily dependent on the item, WIP. + - balance: Disks are now smaller. +2020-06-15: + Anturk, kevinz000: + - bugfix: VV now properly allows access to datums in associative lists + - rscadd: SDQL2 printout has been upgraded for the 5th time. + kevinz000: + - bugfix: plushies now work. credits to timothytea. + - rscadd: You can now tape knives to cleanbots + kevinz000 (port from VOREStation): + - imageadd: Ported zoomba skins for cyborgs. diff --git a/html/changelogs/AutoChangeLog-pr-12375.yml b/html/changelogs/AutoChangeLog-pr-12375.yml new file mode 100644 index 0000000000..9d9b79a3a6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12375.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "You can't blink nor use LOOC/AOOC as a petrified statue anymore." diff --git a/html/changelogs/AutoChangeLog-pr-12393.yml b/html/changelogs/AutoChangeLog-pr-12393.yml new file mode 100644 index 0000000000..28136bed22 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12393.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - tweak: "BEPIS decal painter has been moved to venders, replacing it being the flashdark" diff --git a/html/changelogs/AutoChangeLog-pr-12495.yml b/html/changelogs/AutoChangeLog-pr-12495.yml deleted file mode 100644 index c3ac32debb..0000000000 --- a/html/changelogs/AutoChangeLog-pr-12495.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Ghommie" -delete-after: True -changes: - - balance: "Balanced vending machine prices to be generally more affordable, a minority has been priced up though." diff --git a/html/changelogs/AutoChangeLog-pr-12509.yml b/html/changelogs/AutoChangeLog-pr-12509.yml new file mode 100644 index 0000000000..a5e13bada0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12509.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - rscadd: "slimes can now change their color using alter form" diff --git a/html/changelogs/AutoChangeLog-pr-12518.yml b/html/changelogs/AutoChangeLog-pr-12518.yml new file mode 100644 index 0000000000..91580faf20 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12518.yml @@ -0,0 +1,5 @@ +author: "kevinz000" +delete-after: True +changes: + - refactor: "projectile ricochets now use a less hilariously terrible way of being handled and should be easier to w" + - bugfix: "projectile runtime/ricocheting" diff --git a/html/changelogs/AutoChangeLog-pr-12520.yml b/html/changelogs/AutoChangeLog-pr-12520.yml new file mode 100644 index 0000000000..22890dc4fd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12520.yml @@ -0,0 +1,4 @@ +author: "kevinz000" +delete-after: True +changes: + - balance: "Lobotomy no longer has a 50% chance of giving you a nigh-unremovable trauma, but does 50 brain damage even on success. On failure, it will give you a lobotomy-class trauma." diff --git a/html/changelogs/AutoChangeLog-pr-12521.yml b/html/changelogs/AutoChangeLog-pr-12521.yml new file mode 100644 index 0000000000..1977538e26 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12521.yml @@ -0,0 +1,4 @@ +author: "kevinz000" +delete-after: True +changes: + - balance: "spinesnapping from tackling now only gives a lobotomy class trauma instead of magic." diff --git a/html/changelogs/AutoChangeLog-pr-12523.yml b/html/changelogs/AutoChangeLog-pr-12523.yml new file mode 100644 index 0000000000..58f1c7fe28 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12523.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - bugfix: "Food carts now function as intended, allowing the pouring and mixing of drinks." diff --git a/icons/effects/block_parry.dmi b/icons/effects/block_parry.dmi new file mode 100644 index 0000000000..4a0ade5e61 Binary files /dev/null and b/icons/effects/block_parry.dmi differ diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi index d307b5b6c9..0a1372ac2b 100644 Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi index 082bfb3c3e..9bb41bf527 100644 Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index c75afde4cc..bebb625440 100644 Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ diff --git a/icons/obj/food/donut.dmi b/icons/obj/food/donut.dmi index 395739eccc..534e0e37bc 100644 Binary files a/icons/obj/food/donut.dmi and b/icons/obj/food/donut.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index 641bd61018..e7196190cf 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/icons/obj/food/soupsalad.dmi b/icons/obj/food/soupsalad.dmi index fd47e89aa8..378927e8f9 100644 Binary files a/icons/obj/food/soupsalad.dmi and b/icons/obj/food/soupsalad.dmi differ diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi index 691d5c8362..33872719fe 100644 Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ diff --git a/icons/obj/guns/gun_parts.dmi b/icons/obj/guns/gun_parts.dmi index 8e3168769d..7f088f6af5 100644 Binary files a/icons/obj/guns/gun_parts.dmi and b/icons/obj/guns/gun_parts.dmi differ diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi index 5c8fab82e8..8c50e7da27 100644 Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ diff --git a/icons/obj/tools.dmi b/icons/obj/tools.dmi index 0ff7d4a607..90eea65cb3 100644 Binary files a/icons/obj/tools.dmi and b/icons/obj/tools.dmi differ diff --git a/modular_citadel/code/modules/client/loadout/uniform.dm b/modular_citadel/code/modules/client/loadout/uniform.dm index a4d6c2cd09..e667626968 100644 --- a/modular_citadel/code/modules/client/loadout/uniform.dm +++ b/modular_citadel/code/modules/client/loadout/uniform.dm @@ -254,6 +254,30 @@ category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/keyholesweater +/datum/gear/polyjump + name = "Polychromic Jumpsuit" + category = SLOT_W_UNIFORM + path = /obj/item/clothing/under/misc/polyjumpsuit + cost = 2 + +/datum/gear/polyskirt + name = "Polychromic Jumpskirt" + category = SLOT_W_UNIFORM + path = /obj/item/clothing/under/dress/skirt/polychromic + cost = 2 + +/datum/gear/polysuit + name = "Polychromic Button-up Shirt" + category = SLOT_W_UNIFORM + path = /obj/item/clothing/under/misc/poly_shirt + cost = 3 + +/datum/gear/polypleated + name = "Polychromic Pleated Sweaterskirt" + category = SLOT_W_UNIFORM + path = /obj/item/clothing/under/dress/skirt/polychromic/pleated + cost = 3 + /datum/gear/polykilt name = "Polychromic Kilt" category = SLOT_W_UNIFORM diff --git a/sound/block_parry/block_metal1.ogg b/sound/block_parry/block_metal1.ogg new file mode 100644 index 0000000000..c0f98249cd Binary files /dev/null and b/sound/block_parry/block_metal1.ogg differ diff --git a/sound/block_parry/block_metal2.ogg b/sound/block_parry/block_metal2.ogg new file mode 100644 index 0000000000..58c1233cea Binary files /dev/null and b/sound/block_parry/block_metal2.ogg differ diff --git a/sound/block_parry/block_wood1.ogg b/sound/block_parry/block_wood1.ogg new file mode 100644 index 0000000000..a01f3dbe01 Binary files /dev/null and b/sound/block_parry/block_wood1.ogg differ diff --git a/sound/block_parry/block_wood2.ogg b/sound/block_parry/block_wood2.ogg new file mode 100644 index 0000000000..398a59e248 Binary files /dev/null and b/sound/block_parry/block_wood2.ogg differ diff --git a/sound/block_parry/sfx-parry.ogg b/sound/block_parry/sfx-parry.ogg new file mode 100644 index 0000000000..3429031bd9 Binary files /dev/null and b/sound/block_parry/sfx-parry.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 4e8b060faf..ae27de2d30 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -48,7 +48,6 @@ #include "code\__DEFINES\events.dm" #include "code\__DEFINES\exports.dm" #include "code\__DEFINES\fantasy_affixes.dm" -#include "code\__DEFINES\flags.dm" #include "code\__DEFINES\food.dm" #include "code\__DEFINES\footsteps.dm" #include "code\__DEFINES\hud.dm" @@ -77,7 +76,6 @@ #include "code\__DEFINES\movespeed_modification.dm" #include "code\__DEFINES\nanites.dm" #include "code\__DEFINES\networks.dm" -#include "code\__DEFINES\obj_flags.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\pool.dm" @@ -85,6 +83,7 @@ #include "code\__DEFINES\preferences.dm" #include "code\__DEFINES\procpath.dm" #include "code\__DEFINES\profile.dm" +#include "code\__DEFINES\projectiles.dm" #include "code\__DEFINES\qdel.dm" #include "code\__DEFINES\radiation.dm" #include "code\__DEFINES\radio.dm" @@ -119,10 +118,17 @@ #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\wall_dents.dm" #include "code\__DEFINES\wires.dm" +#include "code\__DEFINES\_flags\_flags.dm" +#include "code\__DEFINES\_flags\item_flags.dm" +#include "code\__DEFINES\_flags\obj_flags.dm" #include "code\__DEFINES\admin\keybindings.dm" +#include "code\__DEFINES\combat\attack_types.dm" +#include "code\__DEFINES\combat\block.dm" +#include "code\__DEFINES\combat\block_parry.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\flags\do_after.dm" #include "code\__DEFINES\flags\shields.dm" #include "code\__DEFINES\mapping\maploader.dm" #include "code\__DEFINES\material\worth.dm" @@ -144,6 +150,7 @@ #include "code\__HELPERS\custom_holoforms.dm" #include "code\__HELPERS\dates.dm" #include "code\__HELPERS\dna.dm" +#include "code\__HELPERS\do_after.dm" #include "code\__HELPERS\donator_groupings.dm" #include "code\__HELPERS\files.dm" #include "code\__HELPERS\game.dm" @@ -2166,6 +2173,7 @@ #include "code\modules\keybindings\keybind\admin.dm" #include "code\modules\keybindings\keybind\carbon.dm" #include "code\modules\keybindings\keybind\client.dm" +#include "code\modules\keybindings\keybind\combat.dm" #include "code\modules\keybindings\keybind\emote.dm" #include "code\modules\keybindings\keybind\human.dm" #include "code\modules\keybindings\keybind\living.dm" @@ -2316,7 +2324,10 @@ #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_active_block.dm" +#include "code\modules\mob\living\living_active_parry.dm" #include "code\modules\mob\living\living_block.dm" +#include "code\modules\mob\living\living_blocking_parrying.dm" #include "code\modules\mob\living\living_defense.dm" #include "code\modules\mob\living\living_defines.dm" #include "code\modules\mob\living\living_mobility.dm"