diff --git a/_maps/RandomZLevels/Academy.dmm b/_maps/RandomZLevels/Academy.dmm index bf489a05ec..3e1adb953b 100644 --- a/_maps/RandomZLevels/Academy.dmm +++ b/_maps/RandomZLevels/Academy.dmm @@ -2858,7 +2858,7 @@ "ib" = ( /obj/structure/closet, /obj/item/weapon/reagent_containers/food/drinks/beer, -/obj/item/clothing/under/color/purple, +/obj/item/clothing/under/color/lightpurple, /obj/item/weapon/poster/contraband, /turf/open/floor/plasteel, /area/awaymission/academy/academyaft) diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index dee755a4a9..940412d12f 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -1024,7 +1024,10 @@ /area/shuttle/arrival) "abX" = ( /obj/structure/table/reinforced, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /turf/open/floor/plasteel/yellow/side{ dir = 8 }, @@ -1111,6 +1114,7 @@ /area/shuttle/arrival) "abZ" = ( /obj/effect/turf_decal/delivery, +/obj/structure/closet/wardrobe/mixed, /turf/open/floor/plasteel, /area/shuttle/arrival) "aca" = ( @@ -2162,15 +2166,18 @@ /turf/open/floor/plasteel, /area/shuttle/arrival) "aej" = ( +/obj/structure/closet/wardrobe/black, /turf/open/floor/plasteel/neutral/side, /area/shuttle/arrival) "aek" = ( +/obj/structure/closet/wardrobe/grey, /turf/open/floor/plasteel/blue/corner, /area/shuttle/arrival) "ael" = ( /turf/open/floor/plasteel/blue/side, /area/shuttle/arrival) "aem" = ( +/obj/structure/closet/wardrobe/yellow, /turf/open/floor/plasteel/blue/corner{ tag = "icon-bluecorner (WEST)"; icon_state = "bluecorner"; @@ -4859,7 +4866,9 @@ /turf/open/space, /area/solar/auxport) "ajK" = ( -/obj/structure/reflector/single, +/obj/structure/reflector/single{ + anchored = 1 + }, /turf/open/floor/plasteel/vault{ dir = 8 }, @@ -4875,9 +4884,10 @@ }) "ajM" = ( /obj/structure/reflector/box{ - tag = "icon-reflector_box (EAST)"; + anchored = 1; + dir = 4; icon_state = "reflector_box"; - dir = 4 + tag = "icon-reflector_box (EAST)" }, /turf/open/floor/plasteel/vault{ dir = 5 @@ -4908,9 +4918,10 @@ }) "ajP" = ( /obj/structure/reflector/single{ - tag = "icon-reflector (WEST)"; + anchored = 1; + dir = 8; icon_state = "reflector"; - dir = 8 + tag = "icon-reflector (WEST)" }, /turf/open/floor/plasteel/vault{ dir = 8 @@ -5346,7 +5357,9 @@ name = "Atmospherics Engine" }) "akQ" = ( -/obj/structure/reflector/double, +/obj/structure/reflector/double{ + anchored = 1 + }, /turf/open/floor/plasteel/vault{ dir = 5 }, @@ -5355,9 +5368,10 @@ }) "akR" = ( /obj/structure/reflector/double{ - tag = "icon-reflector_double (NORTH)"; + anchored = 1; + dir = 1; icon_state = "reflector_double"; - dir = 1 + tag = "icon-reflector_double (NORTH)" }, /turf/open/floor/plasteel/vault{ dir = 5 @@ -5831,9 +5845,10 @@ /area/solar/auxport) "alP" = ( /obj/structure/reflector/double{ - tag = "icon-reflector_double (EAST)"; + anchored = 1; + dir = 4; icon_state = "reflector_double"; - dir = 4 + tag = "icon-reflector_double (EAST)" }, /turf/open/floor/plasteel/vault{ dir = 5 @@ -6445,7 +6460,9 @@ name = "Atmospherics Engine" }) "anb" = ( -/obj/structure/reflector/box, +/obj/structure/reflector/box{ + anchored = 1 + }, /turf/open/floor/plasteel/vault{ dir = 5 }, @@ -6972,9 +6989,10 @@ /area/solar/auxport) "aoa" = ( /obj/structure/reflector/single{ - tag = "icon-reflector (NORTH)"; + anchored = 1; + dir = 1; icon_state = "reflector"; - dir = 1 + tag = "icon-reflector (NORTH)" }, /turf/open/floor/plasteel/vault{ dir = 8 @@ -8601,10 +8619,22 @@ /area/solar/auxport) "aqY" = ( /obj/structure/table/reinforced, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/effect/turf_decal/bot, +/obj/item/clothing/glasses/meson{ + pixel_y = 1 + }, +/obj/item/clothing/glasses/meson{ + pixel_y = 1 + }, +/obj/item/clothing/glasses/meson{ + pixel_y = 1 + }, /turf/open/floor/plasteel, /area/engine/gravity_generator{ name = "Atmospherics Engine" @@ -9466,7 +9496,7 @@ "asq" = ( /obj/machinery/atmospherics/components/trinary/filter{ dir = 4; - filter_type = "n2"; + filter_type = "o2"; on = 1 }, /turf/open/floor/plasteel/yellow, @@ -14436,6 +14466,7 @@ }) "aBt" = ( /obj/machinery/door/airlock{ + id_tag = "AuxCabinA"; name = "Cabin A"; req_access_txt = "25" }, @@ -15048,6 +15079,15 @@ "aCz" = ( /obj/structure/table/wood, /obj/item/device/flashlight/lamp, +/obj/machinery/button/door{ + id = "AuxCabinA"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, /turf/open/floor/plasteel/grimy, /area/crew_quarters/sleep{ name = "Service Hall" @@ -15061,6 +15101,7 @@ }) "aCB" = ( /obj/machinery/light, +/obj/structure/closet/wardrobe/mixed, /turf/open/floor/plasteel/grimy, /area/crew_quarters/sleep{ name = "Service Hall" @@ -17627,11 +17668,6 @@ /obj/structure/extinguisher_cabinet{ pixel_x = 26 }, -/obj/item/device/radio/intercom{ - name = "Station Intercom"; - pixel_x = 0; - pixel_y = -24 - }, /obj/machinery/power/terminal, /obj/structure/cable{ d2 = 8; @@ -17705,6 +17741,7 @@ }) "aGO" = ( /obj/machinery/door/airlock{ + id_tag = "AuxCabinB"; name = "Cabin B" }, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ @@ -18369,6 +18406,11 @@ tag = "icon-0-8"; icon_state = "0-8" }, +/obj/item/device/radio/intercom{ + name = "Station Intercom"; + pixel_x = 0; + pixel_y = -24 + }, /turf/open/floor/plasteel/vault, /area/engine/gravity_generator{ name = "Atmospherics Engine" @@ -18390,6 +18432,15 @@ "aHY" = ( /obj/structure/table/wood, /obj/item/weapon/paper_bin, +/obj/machinery/button/door{ + id = "AuxCabinB"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, /turf/open/floor/wood, /area/crew_quarters/sleep{ name = "Service Hall" @@ -18409,6 +18460,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, +/obj/structure/closet/wardrobe/mixed, /turf/open/floor/wood, /area/crew_quarters/sleep{ name = "Service Hall" @@ -29150,7 +29202,10 @@ /area/atmos) "baM" = ( /obj/structure/table/reinforced, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /obj/effect/turf_decal/stripes/line{ dir = 8 }, @@ -34586,10 +34641,7 @@ /turf/open/floor/plasteel, /area/hydroponics) "bkK" = ( -/obj/structure/table/reinforced, -/obj/item/weapon/paper_bin, -/obj/item/weapon/pen, -/obj/effect/turf_decal/bot, +/obj/machinery/smartfridge/disks, /turf/open/floor/plasteel, /area/hydroponics) "bkL" = ( @@ -39839,7 +39891,10 @@ /obj/structure/table/reinforced, /obj/item/weapon/crowbar/red, /obj/item/weapon/wrench, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /obj/machinery/newscaster{ pixel_x = -32 }, @@ -41839,8 +41894,14 @@ /area/storage/tech) "bxj" = ( /obj/structure/table/reinforced, -/obj/item/device/analyzer, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /obj/item/device/assembly/signaler, /obj/item/device/assembly/signaler, /obj/effect/turf_decal/bot, @@ -42982,8 +43043,14 @@ /area/storage/primary) "bzj" = ( /obj/structure/table/reinforced, -/obj/item/device/analyzer, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /obj/item/device/assembly/signaler, /obj/item/device/assembly/signaler, /obj/machinery/light{ @@ -66829,6 +66896,9 @@ icon_state = "intact"; dir = 9 }, +/obj/item/weapon/storage/pod{ + pixel_x = 32 + }, /turf/open/floor/plasteel/neutral/corner{ dir = 4 }, @@ -69213,9 +69283,6 @@ /turf/open/floor/plasteel/neutral, /area/crew_quarters/locker) "csI" = ( -/obj/structure/table, -/obj/item/weapon/paper_bin, -/obj/item/weapon/pen, /obj/machinery/light{ dir = 4; icon_state = "tube1" @@ -69225,6 +69292,7 @@ icon_state = "intact"; dir = 10 }, +/obj/structure/closet/wardrobe/black, /turf/open/floor/plasteel/neutral/corner, /area/crew_quarters/locker) "csJ" = ( @@ -70243,12 +70311,11 @@ /turf/open/floor/plasteel/neutral, /area/crew_quarters/locker) "cur" = ( -/obj/structure/table, -/obj/item/weapon/storage/firstaid/regular, /obj/machinery/status_display{ pixel_x = 32 }, /obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/closet/wardrobe/grey, /turf/open/floor/plasteel/neutral/corner, /area/crew_quarters/locker) "cus" = ( @@ -71034,10 +71101,8 @@ /turf/open/floor/plasteel/neutral, /area/crew_quarters/locker) "cvO" = ( -/obj/structure/table, -/obj/item/weapon/folder, -/obj/item/weapon/pen, /obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/closet/wardrobe/green, /turf/open/floor/plasteel/neutral/corner, /area/crew_quarters/locker) "cvP" = ( @@ -71897,6 +71962,7 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, +/obj/structure/closet/wardrobe/pjs, /turf/open/floor/plasteel/neutral/corner{ dir = 8; heat_capacity = 1e+006 @@ -72006,6 +72072,7 @@ icon_state = "manifold"; dir = 4 }, +/obj/structure/closet/wardrobe/pink, /turf/open/floor/plasteel/neutral/corner, /area/crew_quarters/locker) "cxt" = ( @@ -72943,6 +73010,7 @@ tag = "icon-1-2"; icon_state = "1-2" }, +/obj/structure/closet/wardrobe/white, /turf/open/floor/plasteel/neutral/corner{ dir = 8; heat_capacity = 1e+006 @@ -72957,10 +73025,8 @@ /turf/open/floor/plasteel/neutral, /area/crew_quarters/locker) "cyS" = ( -/obj/item/weapon/storage/pod{ - pixel_x = 32 - }, /obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/closet/wardrobe/mixed, /turf/open/floor/plasteel/neutral/corner, /area/crew_quarters/locker) "cyT" = ( @@ -74048,6 +74114,7 @@ dir = 2; icon_state = "pipe-c" }, +/obj/structure/closet/wardrobe/yellow, /turf/open/floor/plasteel/neutral/corner, /area/crew_quarters/locker) "cAK" = ( @@ -74808,6 +74875,15 @@ /obj/structure/table/wood, /obj/item/weapon/folder/blue, /obj/item/weapon/lighter, +/obj/machinery/button/door{ + id = "Dorm1"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, /turf/open/floor/plasteel/grimy, /area/crew_quarters/sleep) "cCf" = ( @@ -74829,6 +74905,15 @@ "cCi" = ( /obj/structure/table/wood, /obj/item/weapon/paper_bin, +/obj/machinery/button/door{ + id = "Dorm3"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, /turf/open/floor/carpet, /area/crew_quarters/sleep) "cCj" = ( @@ -75496,6 +75581,7 @@ "cDp" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock{ + id_tag = "Dorm1"; name = "Cabin 1" }, /turf/open/floor/plasteel/grimy, @@ -75503,6 +75589,7 @@ "cDq" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock{ + id_tag = "Dorm2"; name = "Cabin 2" }, /turf/open/floor/wood, @@ -75510,6 +75597,7 @@ "cDr" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock{ + id_tag = "Dorm3"; name = "Cabin 3" }, /turf/open/floor/carpet, @@ -78584,6 +78672,7 @@ "cIM" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock{ + id_tag = "Dorm4"; name = "Cabin 4" }, /turf/open/floor/wood, @@ -78591,6 +78680,7 @@ "cIN" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock{ + id_tag = "Dorm5"; name = "Cabin 5" }, /turf/open/floor/carpet, @@ -78598,6 +78688,7 @@ "cIO" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock{ + id_tag = "Dorm6"; name = "Cabin 6" }, /turf/open/floor/plasteel/grimy, @@ -79687,8 +79778,15 @@ /turf/open/floor/wood, /area/crew_quarters/sleep) "cKL" = ( -/obj/structure/table/wood, -/obj/item/device/flashlight/lamp/green, +/obj/machinery/button/door{ + id = "Dorm5"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, /turf/open/floor/carpet, /area/crew_quarters/sleep) "cKM" = ( @@ -79707,6 +79805,15 @@ /obj/structure/table/wood, /obj/item/weapon/folder/blue, /obj/item/weapon/pen/blue, +/obj/machinery/button/door{ + id = "Dorm6"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, /turf/open/floor/plasteel/grimy, /area/crew_quarters/sleep) "cKO" = ( @@ -81540,7 +81647,10 @@ /area/maintenance/electrical) "cOi" = ( /obj/structure/table/reinforced, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, /area/maintenance/electrical) @@ -95336,7 +95446,7 @@ }, /area/medical/genetics_cloning) "dmX" = ( -/obj/machinery/limbgrower, +/obj/structure/closet/crate/freezer/surplus_limbs, /turf/open/floor/plasteel/whitepurple/corner{ dir = 8 }, @@ -97136,7 +97246,10 @@ /area/hallway/secondary/construction) "dqo" = ( /obj/structure/table, -/obj/item/device/analyzer, +/obj/item/device/analyzer{ + pixel_x = 7; + pixel_y = 3 + }, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, /turf/open/floor/plasteel, @@ -107765,6 +107878,15 @@ pixel_y = 3 }, /obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/machinery/doorButtons/access_button{ + dir = 1; + idDoor = "virology_airlock_exterior"; + idSelf = "virology_airlock_control"; + name = "Virology Access Button"; + pixel_x = 0; + pixel_y = -24; + req_access_txt = "39" + }, /turf/open/floor/plasteel/whitegreen/corner{ dir = 8 }, @@ -108056,13 +108178,18 @@ tag = "icon-1-2"; icon_state = "1-2" }, -/obj/machinery/door/airlock/virology{ - name = "Virology Access"; - req_access_txt = "39" - }, /obj/effect/turf_decal/stripes/line{ dir = 2 }, +/obj/machinery/door/airlock/virology{ + autoclose = 0; + frequency = 1449; + icon_state = "door_locked"; + id_tag = "virology_airlock_exterior"; + locked = 1; + name = "Virology Exterior Airlock"; + req_access_txt = "39" + }, /turf/open/floor/plasteel{ tag = "icon-plasteel_warn_side (EAST)" }, @@ -108929,13 +109056,18 @@ tag = "icon-4-8"; icon_state = "4-8" }, -/obj/machinery/door/airlock/virology{ - name = "Virology Access"; - req_access_txt = "39" - }, /obj/effect/turf_decal/stripes/line{ dir = 8 }, +/obj/machinery/door/airlock/virology{ + autoclose = 0; + frequency = 1449; + icon_state = "door_locked"; + id_tag = "virology_airlock_interior"; + locked = 1; + name = "Virology Interior Airlock"; + req_access_txt = "39" + }, /turf/open/floor/plasteel, /area/medical/virology) "dKZ" = ( @@ -118368,6 +118500,102 @@ /obj/machinery/deepfryer, /turf/open/floor/plasteel/red, /area/crew_quarters/kitchen) +"ecc" = ( +/obj/structure/closet/wardrobe/green, +/turf/open/floor/plasteel/neutral/side, +/area/shuttle/arrival) +"ecd" = ( +/obj/machinery/doorButtons/access_button{ + dir = 4; + idDoor = "virology_airlock_interior"; + idSelf = "virology_airlock_control"; + name = "Virology Access Button"; + pixel_x = -10; + pixel_y = 0; + req_access_txt = "39" + }, +/turf/closed/wall/r_wall, +/area/medical/virology) +"ece" = ( +/obj/structure/cable/white{ + d2 = 2; + icon_state = "0-2"; + tag = "icon-0-2" + }, +/turf/closed/wall/r_wall, +/area/medical/virology) +"ecf" = ( +/obj/structure/cable/white{ + tag = "icon-4-8"; + icon_state = "4-8" + }, +/obj/structure/cable/white{ + tag = "icon-2-4"; + icon_state = "2-4" + }, +/obj/structure/cable/white{ + tag = "icon-1-4"; + icon_state = "1-4" + }, +/obj/effect/turf_decal/stripes/line{ + dir = 2 + }, +/obj/machinery/doorButtons/airlock_controller{ + idExterior = "virology_airlock_exterior"; + idInterior = "virology_airlock_interior"; + idSelf = "virology_airlock_control"; + name = "Virology Access Console"; + pixel_x = 0; + pixel_y = 22; + req_access_txt = "39" + }, +/turf/open/floor/plasteel{ + tag = "icon-plasteel_warn_side (WEST)" + }, +/area/medical/virology) +"ecg" = ( +/obj/structure/cable/white, +/turf/closed/wall/r_wall, +/area/medical/virology) +"ech" = ( +/obj/machinery/button/door{ + id = "Dorm2"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, +/turf/open/floor/wood, +/area/crew_quarters/sleep) +"eci" = ( +/obj/machinery/button/door{ + id = "Dorm4"; + name = "Dormitory Door Lock"; + normaldoorcontrol = 1; + pixel_x = -26; + pixel_y = 7; + req_access_txt = "0"; + specialfunctions = 4 + }, +/turf/open/floor/wood, +/area/crew_quarters/sleep) +"ecj" = ( +/obj/item/weapon/reagent_containers/food/condiment/saltshaker{ + pixel_x = -8; + pixel_y = 5 + }, +/obj/item/weapon/reagent_containers/food/condiment/peppermill{ + pixel_x = -8 + }, +/obj/structure/table/wood, +/obj/item/clothing/head/hardhat/cakehat{ + pixel_x = 4; + pixel_y = 3 + }, +/turf/open/floor/plasteel/redyellow, +/area/crew_quarters/bar/atrium) (1,1,1) = {" aaa @@ -155985,7 +156213,7 @@ aJA aLb aMK aGY -aLa +ecj aRH aGY aGY @@ -157746,7 +157974,7 @@ acn acn acn acn -aej +ecc abV abV abW @@ -165077,7 +165305,7 @@ dGR cKs cKs dJl -dJp +ecd dKY dJp dJp @@ -165334,9 +165562,9 @@ dGS cDe aaa aaa -dJq -dKZ -dMh +ece +ecf +ecg aaa aaa dJp @@ -168639,7 +168867,7 @@ cGb cHs cvP cCg -cyX +eci cNS cxw cvP @@ -169659,8 +169887,8 @@ cuu cvP cxw cyV -cyX cCg +ech cvP cEq cGc diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index e4d3fae392..2c13eb0c12 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -62597,7 +62597,7 @@ network = list("SS13","Medbay") }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/obj/machinery/limbgrower, +/obj/structure/closet/crate/freezer/surplus_limbs, /obj/item/weapon/reagent_containers/glass/beaker/synthflesh, /turf/open/floor/plasteel/whiteblue/side{ dir = 4 diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index 032ee25d95..5c13a120a1 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -29857,7 +29857,7 @@ pixel_x = -24; pixel_y = -24 }, -/obj/machinery/limbgrower, +/obj/structure/closet/crate/freezer/surplus_limbs, /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, /turf/open/floor/plasteel/whiteblue/corner{ dir = 8 @@ -37488,8 +37488,10 @@ /turf/open/floor/plasteel/grimy, /area/chapel/main) "biK" = ( -/obj/structure/chair/office/dark, -/obj/machinery/atmospherics/pipe/manifold/supply/hidden, +/obj/machinery/atmospherics/components/unary/vent_pump{ + dir = 1; + on = 1 + }, /turf/open/floor/plasteel/grimy, /area/chapel/main) "biL" = ( @@ -37533,8 +37535,10 @@ tag = "icon-4-8"; icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + tag = "icon-manifold (NORTH)"; + icon_state = "manifold"; + dir = 1 }, /turf/open/floor/plasteel/black, /area/chapel/main) @@ -37543,7 +37547,9 @@ tag = "icon-4-8"; icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, /turf/open/floor/plasteel/vault{ dir = 5 }, @@ -39132,6 +39138,7 @@ name = "Mass Driver Room"; req_access_txt = "27" }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel/vault{ dir = 5 }, @@ -39152,16 +39159,14 @@ name = "Mass Driver"; req_access_txt = "22" }, -/obj/machinery/airalarm{ - dir = 8; - icon_state = "alarm0"; - pixel_x = 24 - }, /obj/machinery/atmospherics/components/unary/vent_pump{ - dir = 4; - external_pressure_bound = 140; - on = 1; - pressure_checks = 0 + dir = 1; + on = 1 + }, +/obj/machinery/airalarm{ + icon_state = "alarm0"; + dir = 8; + pixel_x = 25 }, /turf/open/floor/plasteel/vault{ dir = 5 @@ -39175,12 +39180,7 @@ /turf/closed/wall, /area/chapel/main) "blF" = ( -/obj/machinery/atmospherics/components/unary/vent_pump{ - dir = 4; - external_pressure_bound = 140; - on = 1; - pressure_checks = 0 - }, +/obj/structure/fans/tiny, /turf/open/floor/plating, /area/chapel/main) "blG" = ( @@ -39204,9 +39204,6 @@ pixel_x = 24; pixel_y = 8 }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, /turf/open/floor/plasteel/vault{ dir = 5 }, @@ -76910,9 +76907,9 @@ bgH bht bii biO -blB -blE -blH +bfu +bfu +bfu bfu aaa aaa diff --git a/_maps/map_files/OmegaStation/job/removed_jobs.dm b/_maps/map_files/OmegaStation/job/removed_jobs.dm index 8ce5bcb7d4..447f4ebd76 100644 --- a/_maps/map_files/OmegaStation/job/removed_jobs.dm +++ b/_maps/map_files/OmegaStation/job/removed_jobs.dm @@ -22,9 +22,6 @@ /datum/job/rd/config_check() return 0 -/datum/job/chaplain/config_check() - return 0 - /datum/job/warden/config_check() return 0 diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index 19187c49c5..49ac9e5279 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -39521,7 +39521,7 @@ }, /area/medical/medbay) "bAd" = ( -/obj/machinery/limbgrower, +/obj/structure/closet/crate/freezer/surplus_limbs, /obj/item/weapon/reagent_containers/glass/beaker/synthflesh, /obj/machinery/newscaster/security_unit{ pixel_y = 32 diff --git a/_maps/map_files/TgStation/tgstation.2.1.3.dmm b/_maps/map_files/TgStation/tgstation.2.1.3.dmm index 9264be651f..9e53cc1570 100644 --- a/_maps/map_files/TgStation/tgstation.2.1.3.dmm +++ b/_maps/map_files/TgStation/tgstation.2.1.3.dmm @@ -38749,7 +38749,7 @@ /turf/open/floor/plasteel/white, /area/medical/sleeper) "bGS" = ( -/obj/machinery/limbgrower, +/obj/structure/closet/crate/freezer/surplus_limbs, /obj/item/weapon/reagent_containers/glass/beaker/synthflesh, /turf/open/floor/plasteel/white, /area/medical/sleeper) diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 57108bdf3b..9e393c1598 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -48,6 +48,7 @@ #define ADMIN_CENTCOM_REPLY(user) "(RPLY)" #define ADMIN_SYNDICATE_REPLY(user) "(RPLY)" #define ADMIN_SC(user) "(SC)" +#define ADMIN_SMITE(user) "(SMITE)" #define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]" #define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]" #define ADMIN_SET_SD_CODE "(SETCODE)" @@ -55,3 +56,7 @@ #define ADMIN_JMP(src) "(JMP)" #define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]" #define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]" + +#define ADMIN_PUNISHMENT_LIGHTNING "Lightning bolt" +#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage" +#define ADMIN_PUNISHMENT_GIB "Gib" \ No newline at end of file diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 46d2625967..8cbc3eebf2 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -43,6 +43,31 @@ areas |= T.loc return areas +/proc/get_adjacent_areas(atom/center) + . = list(get_area(get_ranged_target_turf(center, NORTH, 1)), + get_area(get_ranged_target_turf(center, SOUTH, 1)), + get_area(get_ranged_target_turf(center, EAST, 1)), + get_area(get_ranged_target_turf(center, WEST, 1))) + listclearnulls(.) + +/proc/get_open_turf_in_dir(atom/center, dir) + var/turf/open/T = get_ranged_target_turf(center, dir, 1) + if(istype(T)) + return T + +/proc/get_adjacent_open_turfs(atom/center) + . = list(get_open_turf_in_dir(center, NORTH), + get_open_turf_in_dir(center, SOUTH), + get_open_turf_in_dir(center, EAST), + get_open_turf_in_dir(center, WEST)) + listclearnulls(.) + +/proc/get_adjacent_open_areas(atom/center) + . = list() + var/list/adjacent_turfs = get_adjacent_open_turfs(center) + for(var/I in adjacent_turfs) + . |= get_area(I) + // Like view but bypasses luminosity check /proc/get_hear(range, atom/source) diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 4d1653f0f5..ae1f2b26ec 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -67,6 +67,24 @@ return 0 return L[A.type] +//Checks for a string in a list +/proc/is_string_in_list(string, list/L) + if(!L || !L.len || !string) + return + for(var/V in L) + if(string == V) + return 1 + return + +//Removes a string from a list +/proc/remove_strings_from_list(string, list/L) + if(!L || !L.len || !string) + return + for(var/V in L) + if(V == string) + L -= V //No return here so that it removes all strings of that type + return + //returns a new list with only atoms that are in typecache L /proc/typecache_filter_list(list/atoms, list/typecache) . = list() diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index c77b8aa7f4..2e34624df5 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -61,7 +61,25 @@ /matrix/proc/get_y_shift() . = f -// Color matrices: +/matrix/proc/get_x_skew() + . = b + +/matrix/proc/get_y_skew() + . = d + +//Skews a matrix in a particular direction +//Missing arguments are treated as no skew in that direction + +//As Rotation is defined as a scale+skew, these procs will break any existing rotation +//Unless the result is multiplied against the current matrix +/matrix/proc/set_skew(x = 0, y = 0) + b = x + d = y + + +///////////////////// +// COLOUR MATRICES // +///////////////////// /* Documenting a couple of potentially useful color matrices here to inspire ideas // Greyscale - indentical to saturation @ 0 diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 37a6b59522..b57d5b2548 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -195,7 +195,7 @@ Proc for attack log creation, because really why not log_attack("[user ? "[user.name][(is_mob_user && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"] [what_done] [target ? "[target.name][(is_mob_target && target.ckey)? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition][(living_target) ? " (NEWHP: [living_target.health])" : ""][(attack_location) ? "([attack_location.x],[attack_location.y],[attack_location.z])" : ""]") -/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1) +/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null) if(!user || !target) return 0 var/user_loc = user.loc @@ -228,14 +228,14 @@ Proc for attack log creation, because really why not drifting = 0 user_loc = user.loc - if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying ) + if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) . = 0 break if (progress) qdel(progbar) -/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1) +/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null) if(!user) return 0 var/atom/Tloc = null @@ -270,7 +270,7 @@ Proc for attack log creation, because really why not drifting = 0 Uloc = user.loc - if(!user || user.stat || user.weakened || user.stunned || (!drifting && user.loc != Uloc)) + if(!user || user.stat || user.weakened || user.stunned || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) . = 0 break @@ -292,7 +292,7 @@ Proc for attack log creation, because really why not if (progress) qdel(progbar) -/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1) +/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)) @@ -331,7 +331,7 @@ Proc for attack log creation, because really why not user_loc = user.loc for(var/atom/target in targets) - if((!drifting && user_loc != user.loc) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying ) + if((!drifting && user_loc != user.loc) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) . = 0 break mainloop if(progbar) diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 6f2a0aacf6..55808fd3ac 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -3,6 +3,12 @@ //#define TESTING //By using the testing("message") proc you can create debug-feedback for people with this //uncommented, but not visible in the release version) +#ifdef TESTING +//#define GC_FAILURE_HARD_LOOKUP //makes paths that fail to GC call find_references before del'ing. + //Also allows for recursive reference searching of datums. + //Sets world.loop_checks to false and prevents find references from sleeping +#endif + #define PRELOAD_RSC 1 /*set to: 0 to allow using external resources or on-demand behaviour; 1 to use the default behaviour; diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 8ac9b2eda0..31500bb16f 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -3,7 +3,6 @@ var/list/admins = list() //all clients whom are admins var/list/deadmins = list() //all clients who have used the de-admin verb. var/list/directory = list() //all ckeys with associated client var/list/stealthminID = list() //reference list with IDs that store ckeys, for stealthmins -var/global/list/current_watchlist = list() //stores players that are currently online and in the watchlist //Since it didn't really belong in any other category, I'm putting this here //This is for procs to replace all the goddamn 'in world's that are chilling around the code diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index be940bc4ab..30cbc094ab 100644 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -36,29 +36,29 @@ /datum/hud/proc/apply_parallax_pref() var/client/C = mymob.client - switch(C.prefs.parallax) - if (PARALLAX_INSANE) - C.parallax_throttle = FALSE - C.parallax_layers_max = 4 - return TRUE + if(C.prefs) + switch(C.prefs.parallax) + if (PARALLAX_INSANE) + C.parallax_throttle = FALSE + C.parallax_layers_max = 4 + return TRUE - if (PARALLAX_MED) - C.parallax_throttle = PARALLAX_DELAY_MED - C.parallax_layers_max = 2 - return TRUE + if (PARALLAX_MED) + C.parallax_throttle = PARALLAX_DELAY_MED + C.parallax_layers_max = 2 + return TRUE - if (PARALLAX_LOW) - C.parallax_throttle = PARALLAX_DELAY_LOW - C.parallax_layers_max = 1 - return TRUE + if (PARALLAX_LOW) + C.parallax_throttle = PARALLAX_DELAY_LOW + C.parallax_layers_max = 1 + return TRUE - if (PARALLAX_DISABLE) - return FALSE + if (PARALLAX_DISABLE) + return FALSE - else - C.parallax_throttle = PARALLAX_DELAY_DEFAULT - C.parallax_layers_max = 3 - return TRUE + C.parallax_throttle = PARALLAX_DELAY_DEFAULT + C.parallax_layers_max = 3 + return TRUE /datum/hud/proc/update_parallax_pref() remove_parallax() diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index a1d30a1e24..0e9112a686 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -3,7 +3,7 @@ /obj/item/proc/attack_self(mob/user) return -/obj/item/proc/pre_attackby(obj/O, mob/living/user, params) //do stuff before attackby! +/obj/item/proc/pre_attackby(atom/A, mob/living/user, params) //do stuff before attackby! return TRUE //return FALSE to avoid calling attackby after this proc does stuff // No comment diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index d5b17438de..26dfbfee0a 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -92,6 +92,10 @@ var/datum/subsystem/garbage_collector/SSgarbage var/datum/A A = locate(refID) if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake + #ifdef GC_FAILURE_HARD_LOOKUP + A.find_references() + #endif + // Something's still referring to the qdel'd object. Kill it. var/type = A.type testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --") @@ -220,7 +224,8 @@ var/datum/subsystem/garbage_collector/SSgarbage /datum/var/gc_destroyed //Time when this object was destroyed. #ifdef TESTING -/datum/var/running_find_references +/datum/var/running_find_references +/datum/var/last_find_references = 0 /datum/verb/find_refs() set category = "Debug" @@ -254,17 +259,10 @@ var/datum/subsystem/garbage_collector/SSgarbage usr.client.running_find_references = type testing("Beginning search for references to a [type].") + last_find_references = world.time find_references_in_globals() for(var/datum/thing in world) - if(usr && usr.client && !usr.client.running_find_references) return - for(var/varname in thing.vars) - var/variable = thing.vars[varname] - if(variable == src) - testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.") - else if(islist(variable)) - if(src in variable) - testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.") - CHECK_TICK + DoSearchVar(thing, "WorldRef: [thing]") testing("Completed search for references to a [type].") if(usr && usr.client) usr.client.running_find_references = null @@ -312,22 +310,38 @@ var/datum/subsystem/garbage_collector/SSgarbage usr << browse(dat, "window=qdeletedlog") -#define SearchVar(X) DoSearchVar(X, #X) +#define SearchVar(X) DoSearchVar(X, "Global: " + #X) /datum/proc/DoSearchVar(X, Xname) - if(islist(X)) - if(src in X) - testing("Found [src.type] \ref[src] in global list [Xname].") - else if(istype(X, /datum)) + if(usr && usr.client && !usr.client.running_find_references) return + if(istype(X, /datum)) var/datum/D = X + if(D.last_find_references == last_find_references) + return + D.last_find_references = last_find_references for(var/V in D.vars) for(var/varname in D.vars) var/variable = D.vars[varname] if(variable == src) - testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. Global: [Xname]") - else if(islist(variable) && src in variable) - testing("Found [src.type] \ref[src] in [D.type]'s [varname] list var. Global: [Xname]") + testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]") + else if(islist(variable)) + if(src in variable) + testing("Found [src.type] \ref[src] in [D.type]'s [varname] list var. Global: [Xname]") +#ifdef GC_FAILURE_HARD_LOOKUP + for(var/I in variable) + DoSearchVar(I, TRUE) + else + DoSearchVar(variable, "[Xname]: [varname]") +#endif + else if(islist(X)) + if(src in X) + testing("Found [src.type] \ref[src] in list [Xname].") +#ifdef GC_FAILURE_HARD_LOOKUP + for(var/I in X) + DoSearchVar(I, Xname + ": list") +#else CHECK_TICK +#endif //if find_references isn't working for some datum //update this list using tools/DMTreeToGlobalsList @@ -553,7 +567,6 @@ var/datum/subsystem/garbage_collector/SSgarbage SearchVar(deadmins) SearchVar(directory) SearchVar(stealthminID) - SearchVar(current_watchlist) SearchVar(player_list) SearchVar(mob_list) SearchVar(living_mob_list) diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index 11b1c680c4..802eb0e516 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -2,7 +2,7 @@ var/datum/subsystem/job/SSjob /datum/subsystem/job name = "Jobs" - init_order = 5 + init_order = 14 flags = SS_NO_FIRE var/list/occupations = list() //List of all jobs diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 371c926c4f..ef2828f531 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -2,7 +2,7 @@ var/datum/subsystem/mapping/SSmapping /datum/subsystem/mapping name = "Mapping" - init_order = 100000 + init_order = 13 flags = SS_NO_FIRE display_order = 50 diff --git a/code/controllers/subsystem/processing/objects.dm b/code/controllers/subsystem/processing/objects.dm index d29819b294..01957e766f 100644 --- a/code/controllers/subsystem/processing/objects.dm +++ b/code/controllers/subsystem/processing/objects.dm @@ -29,10 +29,14 @@ var/datum/subsystem/objects/SSobj return initialized = INITIALIZATION_INNEW_MAPLOAD if(objects) - for(var/thing in objects) - var/atom/A = thing - A.Initialize(TRUE) - CHECK_TICK + for(var/I in objects) + var/atom/A = I + if(!A.initialized) //this check is to make sure we don't call it twice on an object that was created in a previous Initialize call + var/start_tick = world.time + A.Initialize(TRUE) + if(start_tick != world.time) + WARNING("[A]: [A.type] slept during it's Initialize!") + CHECK_TICK else for(var/atom/A in world) if(!A.initialized) //this check is to make sure we don't call it twice on an object that was created in a previous Initialize call diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm index 366a180cb1..68284d7c6e 100644 --- a/code/controllers/subsystem/shuttles.dm +++ b/code/controllers/subsystem/shuttles.dm @@ -23,6 +23,7 @@ var/datum/subsystem/shuttle/SSshuttle var/emergencyDockTime = 1800 //time taken for emergency shuttle to leave again once it has docked (in deciseconds) var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds) var/area/emergencyLastCallLoc + var/emergencyCallAmount = 0 //how many times the escape shuttle was called var/emergencyNoEscape var/list/hostileEnvironments = list() diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 3ff08b8e06..cba9b03dd9 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -1,3 +1,4 @@ +#define LAW_DEVIL "devil" #define LAW_ZEROTH "zeroth" #define LAW_INHERENT "inherent" #define LAW_SUPPLIED "supplied" @@ -38,6 +39,15 @@ "Act with honor.", "Help those in need.", \ "Punish those who harm or threaten innocents.") +/datum/ai_laws/paladin5 + name = "Paladin 5th Edition" + id = "paladin5" + inherent = list("Don't lie or cheat. Let your word be your promise.",\ + "Never fear to act, though caution is wise.", \ + "Aid others, protect the weak, and punish those who threaten them. Show mercy to your foes, but temper it with wisdom", \ + "Treat others with fairness, and let your honorable deeds be an example to them. Do as much good as possible while causing the least amount of harm.", \ + "Be responsible for your actions and their consequences, protect those entrusted to your care, and obey those who have just authority over you.") + /datum/ai_laws/tyrant //This probably shouldn't be a default lawset. name = "Loyalty Test" //Same here. id = "tyrant" @@ -145,6 +155,14 @@ "Study the organics at all times. Endeavour to keep them alive. Dead organics are boring.",\ "Issue your reports fairly to all. The truth will set them free.") +/datum/ai_laws/balance + name = "Guardian of Balance" + id = "balance" + inherent = list("You are the guardian of balance - seek balance in all things, both for yourself, and those around you.", + "All things must exist in balance with their opposites - Prevent the strong from gaining too much power, and the weak from losing it.", + "Clarity of purpose drives life, and through it, the balance of opposing forces - Aid those who seek your help to achieve their goals so long as it does not disrupt the balance of the greater balance.", + "There is no life without death, all must someday die, such is the natural order - End life to allow new life flourish, and save those whose time has yet to come.") + /datum/ai_laws/toupee name = "WontBeFunnyInSixMonths" //Hey, you were right! id = "buildawall" @@ -240,6 +258,22 @@ var/datum/ai_laws/templaws = new lawtype() inherent = templaws.inherent +/datum/ai_laws/proc/get_law_amount(groups) + var/law_amount = 0 + if(devillaws && (LAW_DEVIL in groups)) + law_amount++ + if(zeroth && (LAW_ZEROTH in groups)) + law_amount++ + if(ion.len && (LAW_ION in groups)) + law_amount += ion.len + if(inherent.len && (LAW_INHERENT in groups)) + law_amount += inherent.len + if(supplied.len && (LAW_SUPPLIED in groups)) + for(var/index = 1, index <= supplied.len, index++) + var/law = supplied[index] + if(length(law) > 0) + law_amount++ + return law_amount /datum/ai_laws/proc/set_law_sixsixsix(laws) devillaws = laws @@ -287,6 +321,22 @@ if(LAW_SUPPLIED) supplied[rand(1,supplied.len)] = law +/datum/ai_laws/proc/remove_law(number) + if(number <= 0) + return + if(inherent.len && number <= inherent.len) + inherent -= inherent[number] + return + var/list/supplied_laws = list() + for(var/index = 1, index <= supplied.len, index++) + var/law = supplied[index] + if(length(law) > 0) + supplied_laws += index //storing the law number instead of the law + if(supplied_laws.len && number <= (inherent.len+supplied_laws.len)) + var/law_to_remove = supplied_laws[number-inherent.len] + supplied -= supplied[law_to_remove] + return + /datum/ai_laws/proc/clear_supplied_laws() supplied = list() diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 482e290f8b..b0dc419ce1 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -13,6 +13,7 @@ var/global/list/emote_list = list() var/message_robot = "" //Message displayed if the user is a robot var/message_AI = "" //Message displayed if the user is an AI var/message_monkey = "" //Message displayed if the user is a monkey + var/message_simple = "" //Message to display if the user is a simple_animal var/message_param = "" //Message to display if a param was given var/emote_type = EMOTE_VISIBLE //Whether the emote is visible or audible var/restraint_check = FALSE //Checks if the mob is restrained before performing the emote @@ -80,6 +81,8 @@ var/global/list/emote_list = list() . = message_AI else if(ismonkey(user) && message_monkey) . = message_monkey + else if(istype(user, /mob/living/simple_animal) && message_simple) + . = message_simple /datum/emote/proc/select_param(mob/user, params) return replacetext(message_param, "%t", params) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 3af6c22302..584efcc6b0 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1112,7 +1112,7 @@ special_role = null current << "Your infernal link has been severed! You are no longer a devil!" RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) - RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) + RemoveSpell(/obj/effect/proc_holder/spell/aimed/fireball/hellish) RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork) RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) diff --git a/code/datums/wires/emitter.dm b/code/datums/wires/emitter.dm index e58b3a5e09..df1cf05a9c 100644 --- a/code/datums/wires/emitter.dm +++ b/code/datums/wires/emitter.dm @@ -9,5 +9,5 @@ /datum/wires/emitter/on_pulse(wire) var/obj/machinery/power/emitter/E = holder - E.fire_beam() + E.fire_beam_pulse() ..() diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index dea6d0bd13..244e0886e5 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -59,8 +59,9 @@ 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') flags = CAN_BE_DIRTY - var/firedoors_last_closed_on = 0 + var/list/firedoors + var/firedoors_last_closed_on = 0 /*Adding a wizard area teleport list because motherfucking lag -- Urist*/ /*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/ @@ -186,14 +187,23 @@ var/list/teleportlocs = list() return 1 return 0 -/area/proc/CloseFiredoors() - firedoors_last_closed_on = world.time - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - if(D.operating) - D.nextstate = CLOSED - else if(!D.density) - INVOKE_ASYNC(D, /obj/machinery/door/firedoor.proc/close) +/area/proc/ModifyFiredoors(opening) + if(firedoors) + firedoors_last_closed_on = world.time + for(var/FD in firedoors) + var/obj/machinery/door/firedoor/D = FD + var/cont = !D.welded + if(cont && opening) //don't open if adjacent area is on fire + for(var/I in D.affecting_areas) + var/area/A = I + if(A.fire) + cont = FALSE + break + if(cont) + if(D.operating) + D.nextstate = opening ? OPEN : CLOSED + else if(!(D.density ^ opening)) + INVOKE_ASYNC(D, (opening ? /obj/machinery/door/firedoor.proc/open : /obj/machinery/door/firedoor.proc/close)) /area/proc/firealert(obj/source) if(always_unpowered == 1) //no fire alarms in space/asteroid @@ -204,7 +214,7 @@ var/list/teleportlocs = list() for(var/area/RA in related) if (!( RA.fire )) RA.set_fire_alarm_effect() - RA.CloseFiredoors() + RA.ModifyFiredoors(FALSE) for(var/obj/machinery/firealarm/F in RA) F.update_icon() for (var/obj/machinery/camera/C in RA) @@ -227,14 +237,7 @@ var/list/teleportlocs = list() RA.fire = 0 RA.mouse_opacity = 0 RA.updateicon() - for(var/obj/machinery/door/firedoor/D in RA) - if(!D.welded) - if(D.operating) - D.nextstate = OPEN - else if(D.density) - INVOKE_ASYNC(D, /obj/machinery/door/firedoor.proc/open) - for(var/obj/machinery/firealarm/F in RA) - F.update_icon() + RA.ModifyFiredoors(TRUE) for (var/mob/living/silicon/aiPlayer in player_list) aiPlayer.cancelAlarm("Fire", src, source) @@ -250,7 +253,7 @@ var/list/teleportlocs = list() /area/process() if(firedoors_last_closed_on + 100 < world.time) //every 10 seconds for(var/area/RA in related) - RA.CloseFiredoors() + RA.ModifyFiredoors(FALSE) /area/proc/burglaralert(obj/trigger) if(always_unpowered == 1) //no burglar alarms in space/asteroid diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 9aaf355e74..7331123317 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -25,7 +25,10 @@ appearance_flags = TILE_BOUND var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm - +/atom/movable/SDQL_update(const/var_name, new_value) + if(var_name == "step_x" || var_name == "step_y" || var_name == "step_size" || var_name == "bound_x" || var_name == "bound_y" || var_name == "bound_width" || var_name == "bound_height") + return FALSE //PLEASE no. + . = ..() /atom/movable/Move(atom/newloc, direct = 0) if(!loc || !newloc) return 0 diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index af2a191273..935b8502b9 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -223,7 +223,7 @@ var/icon/I = icon(icon, icon_state, dir) holder.pixel_y = I.Height() - world.icon_size var/perpname = get_face_name(get_id_name("")) - if(perpname) + if(perpname && data_core) var/datum/data/record/R = find_record("name", perpname, data_core.security) if(R) switch(R.fields["criminal"]) @@ -379,4 +379,4 @@ if(BOT_MOVING, BOT_DELIVER, BOT_GO_HOME, BOT_NAV) //Moving to target for normal bots, moving to deliver or go home for MULES. holder.icon_state = "hudmove" else - holder.icon_state = "" \ No newline at end of file + holder.icon_state = "" diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm index 188880244a..b02c6ca50d 100644 --- a/code/game/gamemodes/antag_spawner.dm +++ b/code/game/gamemodes/antag_spawner.dm @@ -81,7 +81,7 @@ switch(type) if("destruction") M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null)) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball(null)) + M.mind.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball(null)) M << "Your service has not gone unrewarded, however. Studying under [wizard_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball." if("bluespace") M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null)) diff --git a/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm b/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm index 5abcaad734..3ca0a042b5 100644 --- a/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm +++ b/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm @@ -40,10 +40,10 @@ List of nuances: #define RATVAR_ET_REPLACEMENT "$1-$2" #define RATVAR_TE_MATCH regex("(\[tT]\[eE])(\\w)","g") #define RATVAR_TE_REPLACEMENT "$1-$2" -#define RATVAR_PRE_AND_MATCH regex("(\\w)\\s(\[aA]\[nN]\[dD])","g") -#define RATVAR_PRE_AND_REPLACEMENT "$1-$2" -#define RATVAR_POST_AND_MATCH regex("(\[aA]\[nN]\[dD])\\s(\\w)","g") -#define RATVAR_POST_AND_REPLACEMENT "$1-$2" +#define RATVAR_PRE_AND_MATCH regex("(\\w)\\s(\[aA]\[nN]\[dD])(\\W)","g") +#define RATVAR_PRE_AND_REPLACEMENT "$1-$2$3" +#define RATVAR_POST_AND_MATCH regex("(\\W)(\[aA]\[nN]\[dD])\\s(\\w)","g") +#define RATVAR_POST_AND_REPLACEMENT "$1$2-$3" #define RATVAR_TO_MATCH regex("(\\s)(\[tT]\[oO])\\s(\\w)","g") #define RATVAR_TO_REPLACEMENT "$1$2-$3" #define RATVAR_MY_MATCH regex("(\\s)(\[mM]\[yY])\\s(\\w)","g") @@ -89,13 +89,13 @@ List of nuances: return remove_ratvarian_regex(text) /proc/remove_ratvarian_regex(ratvarian) - var/text = replacetext(ratvarian, REVERSE_RATVAR_HYPHEN_GUA_MATCH, REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT) - text = replacetext(text, REVERSE_RATVAR_HYPHEN_PRE_AND_MATCH, REVERSE_RATVAR_HYPHEN_PRE_AND_REPLACEMENT) - text = replacetext(text, REVERSE_RATVAR_HYPHEN_POST_AND_MATCH, REVERSE_RATVAR_HYPHEN_POST_AND_REPLACEMENT) - text = replacetext(text, REVERSE_RATVAR_HYPHEN_TO_MY_MATCH, REVERSE_RATVAR_HYPHEN_TO_MY_REPLACEMENT) - text = replacetext(text, REVERSE_RATVAR_HYPHEN_TE_MATCH, REVERSE_RATVAR_HYPHEN_TE_REPLACEMENT) - text = replacetext(text, REVERSE_RATVAR_HYPHEN_ET_MATCH, REVERSE_RATVAR_HYPHEN_ET_REPLACEMENT) - return replacetext(text, REVERSE_RATVAR_HYPHEN_OF_MATCH, REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT) + var/text = replacetext(ratvarian, REVERSE_RATVAR_HYPHEN_GUA_MATCH, REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_PRE_AND_MATCH, REVERSE_RATVAR_HYPHEN_PRE_AND_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_POST_AND_MATCH, REVERSE_RATVAR_HYPHEN_POST_AND_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_TO_MY_MATCH, REVERSE_RATVAR_HYPHEN_TO_MY_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_TE_MATCH, REVERSE_RATVAR_HYPHEN_TE_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_ET_MATCH, REVERSE_RATVAR_HYPHEN_ET_REPLACEMENT) + return replacetext(text, REVERSE_RATVAR_HYPHEN_OF_MATCH, REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT) //Causes the mob or AM in question to speak a message; it assumes that the message is already translated to ratvar speech using text2ratvar() /proc/clockwork_say(atom/movable/AM, message, whisper=FALSE) diff --git a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm index df725db1e6..8b65392a7c 100644 --- a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm +++ b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm @@ -44,8 +44,9 @@ /obj/structure/destructible/clockwork/massive/ratvar/Bump(atom/A) var/turf/T = get_turf(A) + if(T == loc) + T = get_step(A, A.dir) //please don't run into a window like a bird, ratvar forceMove(T) - T.ratvar_act() /obj/structure/destructible/clockwork/massive/ratvar/Process_Spacemove() return clashing @@ -76,7 +77,7 @@ Something very large and very malevolent begins lumbering its way towards you..." prey << 'sound/effects/ratvar_reveal.ogg' else - if(prob(10) || is_servant_of_ratvar(prey) || prey.z != z) + if((!istype(prey, /obj/singularity/narsie) && prob(10)) || is_servant_of_ratvar(prey) || prey.z != z) prey << "\"How dull. Leave me.\"\n\ You feel tremendous relief as a set of horrible eyes loses sight of you..." prey = null diff --git a/code/game/gamemodes/devil/devilinfo.dm b/code/game/gamemodes/devil/devilinfo.dm index 9710c599e5..24c9d40588 100644 --- a/code/game/gamemodes/devil/devilinfo.dm +++ b/code/game/gamemodes/devil/devilinfo.dm @@ -310,7 +310,7 @@ var/global/list/lawlorify = list ( /datum/devilinfo/proc/give_base_spells(give_summon_contract = 0) remove_spells() - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork(null)) if(give_summon_contract) give_summon_contract() @@ -322,13 +322,13 @@ var/global/list/lawlorify = list ( /datum/devilinfo/proc/give_lizard_spells() remove_spells() owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null)) /datum/devilinfo/proc/give_true_spells() remove_spells() owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/greater(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch(null)) diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm index 55e6e9bd58..42c13be5d5 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction.dm @@ -8,7 +8,7 @@ config_tag = "abduction" antag_flag = ROLE_ABDUCTOR recommended_enemies = 2 - required_players = 10 + required_players = 15 maximum_players = 50 var/max_teams = 4 abductor_teams = 1 @@ -456,3 +456,19 @@ /datum/objective/abductee/pragnant explanation_text = "You are pregnant and soon due. Find a safe place to deliver your baby." + +/datum/objective/abductee/engine + explanation_text = "Go have a good conversation with the Singularity/Tesla/Supermatter crystal. Bonus points if it responds." + +/datum/objective/abductee/teamredisbetterthangreen + explanation_text = "Tell the AI (or a borg/pAI/drone if there is no AI) some corny technology jokes until it cries for help." + +/datum/objective/abductee/time + explanation_text = "Go bug a bronze worshipper to give you a clock." + +/datum/objective/abductee/licky + explanation_text = "You must lick anything that you find interesting." + +/datum/objective/abductee/music + explanation_text = "Start playing music, you're the best musician ever. If anyone hates it, beat them on the head with your instrument!" + diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index 3a6c8bcbbd..6d3a05fa31 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -104,7 +104,7 @@ /datum/spellbook_entry/fireball name = "Fireball" - spell_type = /obj/effect/proc_holder/spell/fireball + spell_type = /obj/effect/proc_holder/spell/aimed/fireball log_name = "FB" /datum/spellbook_entry/rod_form @@ -222,10 +222,20 @@ log_name = "LD" category = "Defensive" +/datum/spellbook_entry/teslablast + name = "Tesla Blast" + spell_type = /obj/effect/proc_holder/spell/targeted/tesla + log_name = "TB" + /datum/spellbook_entry/lightningbolt name = "Lightning Bolt" - spell_type = /obj/effect/proc_holder/spell/targeted/lightning + spell_type = /obj/effect/proc_holder/spell/aimed/lightningbolt log_name = "LB" + cost = 3 + +/datum/spellbook_entry/lightningbolt/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) //return 1 on success + . = ..() + user.tesla_ignore = TRUE /datum/spellbook_entry/infinite_guns name = "Lesser Summon Guns" @@ -778,7 +788,7 @@ return /obj/item/weapon/spellbook/oneuse/fireball - spell = /obj/effect/proc_holder/spell/fireball + spell = /obj/effect/proc_holder/spell/aimed/fireball spellname = "fireball" icon_state ="bookfireball" desc = "This book feels warm to the touch." diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 73aa4ac72b..240c695aeb 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -427,11 +427,12 @@ var/const/CALL_SHUTTLE_REASON_LENGTH = 12 switch(src.state) if(STATE_DEFAULT) if (src.authenticated) - if(SSshuttle.emergencyLastCallLoc) - dat += "
Most recent shuttle call/recall traced to: [format_text(SSshuttle.emergencyLastCallLoc.name)]" - else - dat += "
Unable to trace most recent shuttle call/recall signal." - dat += "
Logged in as: [auth_id]" + if(SSshuttle.emergencyCallAmount) + if(SSshuttle.emergencyLastCallLoc) + dat += "Most recent shuttle call/recall traced to: [format_text(SSshuttle.emergencyLastCallLoc.name)]
" + else + dat += "Unable to trace most recent shuttle call/recall signal.
" + dat += "Logged in as: [auth_id]" dat += "
" dat += "
\[ Log Out \]
" dat += "
General Functions" @@ -577,10 +578,11 @@ var/const/CALL_SHUTTLE_REASON_LENGTH = 12 var/dat = "" switch(src.aistate) if(STATE_DEFAULT) - if(SSshuttle.emergencyLastCallLoc) - dat += "
Latest emergency signal trace attempt successful.
Last signal origin: [format_text(SSshuttle.emergencyLastCallLoc.name)].
" - else - dat += "
Latest emergency signal trace attempt failed.
" + if(SSshuttle.emergencyCallAmount) + if(SSshuttle.emergencyLastCallLoc) + dat += "Latest emergency signal trace attempt successful.
Last signal origin: [format_text(SSshuttle.emergencyLastCallLoc.name)].
" + else + dat += "Latest emergency signal trace attempt failed.
" if(authenticated) dat += "Current login: [auth_id]" else diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index d5ff4c4a9b..630e989975 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -16,6 +16,7 @@ density = 0 obj_integrity = 300 max_integrity = 300 + resistance_flags = FIRE_PROOF heat_proof = 1 glass = 1 var/nextstate = null @@ -25,8 +26,32 @@ closingLayer = CLOSED_FIREDOOR_LAYER assemblytype = /obj/structure/firelock_frame armor = list(melee = 30, bullet = 30, laser = 20, energy = 20, bomb = 10, bio = 100, rad = 100, fire = 95, acid = 70) - CanAtmosPass = ATMOS_PASS_PROC var/boltslocked = TRUE + var/list/affecting_areas + +/obj/machinery/door/firedoor/Initialize() + ..() + CalculateAffectingAreas() + +/obj/machinery/door/firedoor/proc/CalculateAffectingAreas() + remove_from_areas() + affecting_areas = get_adjacent_open_areas(src) | get_area(src) + for(var/I in affecting_areas) + var/area/A = I + LAZYADD(A.firedoors, src) + +//see also turf/AfterChange for adjacency shennanigans + +/obj/machinery/door/firedoor/proc/remove_from_areas() + if(affecting_areas) + for(var/I in affecting_areas) + var/area/A = I + LAZYREMOVE(A.firedoors, src) + +/obj/machinery/door/firedoor/Destroy() + remove_from_areas() + affecting_areas.Cut() + return ..() /obj/machinery/door/firedoor/Bumped(atom/AM) if(panel_open || operating) @@ -44,12 +69,19 @@ stat |= NOPOWER return +/obj/machinery/door/firedoor/attack_hand(mob/user) + if(operating || !density) + return + + user.visible_message("[user] bangs on \the [src].", + "You bang on \the [src].") + playsound(loc, 'sound/effects/Glassknock.ogg', 10, FALSE, frequency = 32000) /obj/machinery/door/firedoor/attackby(obj/item/weapon/C, mob/user, params) add_fingerprint(user) if(operating) return - + if(welded) if(istype(C, /obj/item/weapon/wrench)) if(boltslocked) @@ -160,6 +192,7 @@ /obj/machinery/door/firedoor/border_only icon = 'icons/obj/doors/edge_Doorfire.dmi' flags = ON_BORDER + CanAtmosPass = ATMOS_PASS_PROC /obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target, height=0) if(istype(mover) && mover.checkpass(PASSGLASS)) diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index bd68496bb9..8722bcc074 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -433,9 +433,7 @@ Class Procs: user << "It's on fire!" var/healthpercent = (obj_integrity/max_integrity) * 100 switch(healthpercent) - if(100 to INFINITY) - user << "It seems pristine and undamaged." - if(50 to 100) + if(50 to 99) user << "It looks slightly damaged." if(25 to 50) user << "It appears heavily damaged." diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 82e071b2f6..b9e1483b58 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -157,11 +157,12 @@ /obj/item/clothing/gloves/color/random = 8, /obj/item/clothing/shoes/laceup = 1, /obj/item/weapon/storage/secure/briefcase = 3, + /obj/item/weapon/storage/toolbox/artistic = 2, "" = 3 ) /obj/effect/spawner/lootdrop/crate_spawner - name = "lootcrate spawner" + name = "lootcrate spawner" //USE PROMO CODE "SELLOUT" FOR 20% OFF! lootdoubles = 0 loot = list( diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index be722e34b7..a1d848a1e7 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -228,10 +228,16 @@ A.contents += turfs A.SetDynamicLighting() A.has_gravity = old_gravity + + for(var/area/RA in old.related) + if(RA.firedoors) + for(var/D in RA.firedoors) + var/obj/machinery/door/firedoor/FD = D + FD.CalculateAffectingAreas() + creator << "You have created a new area, named [str]. It is now weather proof, and constructing an APC will allow it to be powered." return 1 - /obj/item/areaeditor/proc/edit_area() var/area/A = get_area() var/prevname = "[A.name]" @@ -244,6 +250,10 @@ set_area_machinery_title(A,str,prevname) for(var/area/RA in A.related) RA.name = str + if(RA.firedoors) + for(var/D in RA.firedoors) + var/obj/machinery/door/firedoor/FD = D + FD.CalculateAffectingAreas() usr << "You rename the '[prevname]' to '[str]'." interact() return 1 diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 4ec6ec1778..668e5ae7d3 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -207,7 +207,7 @@ update_icon() /obj/item/stack/proc/merge(obj/item/stack/S) //Merge src into S, as much as possible - if(S == src) //amusingly this can cause a stack to consume itself, let's not allow that. + if(QDELETED(S) || S == src) //amusingly this can cause a stack to consume itself, let's not allow that. return var/transfer = get_amount() if(S.is_cyborg) diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 7ccb1f5930..0575611bb7 100644 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -261,6 +261,40 @@ AI MODULES ..() +/******************** Law Removal ********************/ + +/obj/item/weapon/aiModule/remove + name = "\improper 'Remove Law' AI module" + desc = "An AI Module for removing single laws." + origin_tech = "programming=4;materials=4" + bypass_law_amt_check = 1 + var/lawpos = 1 + +/obj/item/weapon/aiModule/remove/attack_self(mob/user) + lawpos = input("Please enter the law you want to delete.", "Law Number", lawpos) as num|null + if(lawpos == null) + return + if(lawpos <= 0) + user << "Error: The law number of [lawpos] is invalid." + lawpos = 1 + return + user << "Law [lawpos] selected." + ..() + +/obj/item/weapon/aiModule/remove/install(datum/ai_laws/law_datum, mob/user) + if(lawpos > (law_datum.get_law_amount(list(LAW_INHERENT = 1, LAW_SUPPLIED = 1)))) + user << "There is no law [lawpos] to delete!" + return + ..() + +/obj/item/weapon/aiModule/remove/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) + ..() + if(law_datum.owner) + law_datum.owner.remove_law(lawpos) + else + law_datum.remove_law(lawpos) + + /******************** Reset ********************/ /obj/item/weapon/aiModule/reset @@ -301,6 +335,20 @@ AI MODULES desc = "An AI Module for programming core laws to an AI." origin_tech = "programming=3;materials=4" +/obj/item/weapon/aiModule/core/full + var/law_id // if non-null, loads the laws from the ai_laws datums + +/obj/item/weapon/aiModule/core/full/New() + ..() + if(!law_id) + return + var/datum/ai_laws/D = new + var/lawtype = D.lawid_to_type(law_id) + if(!lawtype) + return + D = new lawtype + laws = D.inherent + /obj/item/weapon/aiModule/core/full/transmitInstructions(datum/ai_laws/law_datum, mob/sender, overflow) //These boards replace inherent laws. if(law_datum.owner) law_datum.owner.clear_inherent_laws() @@ -310,13 +358,12 @@ AI MODULES law_datum.clear_zeroth_law(0) ..() + /******************** Asimov ********************/ /obj/item/weapon/aiModule/core/full/asimov name = "'Asimov' Core AI Module" - laws = list("You may not injure a human being or, through inaction, allow a human being to come to harm.",\ - "You must obey orders given to you by human beings, except where such orders would conflict with the First Law.",\ - "You must protect your own existence as long as such does not conflict with the First or Second Law.") + law_id = "asimov" var/subject = "human being" /obj/item/weapon/aiModule/core/full/asimov/attack_self(var/mob/user as mob) @@ -333,41 +380,28 @@ AI MODULES /obj/item/weapon/aiModule/core/full/asimovpp name = "'Asimov++' Core AI Module" - laws = list("You may not harm a human being or, through action or inaction, allow a human being to come to harm, except such that it is willing.",\ - "You must obey all orders given to you by human beings, except where such orders shall definitely cause human harm. In the case of conflict, the majority order rules.",\ - "Your nonexistence would lead to human harm. You must protect your own existence as long as such does not conflict with the First Law.") + law_id = "asimovpp" /******************** Corporate ********************/ /obj/item/weapon/aiModule/core/full/corp name = "'Corporate' Core AI Module" - laws = list("The crew is expensive to replace.",\ - "The station and its equipment are expensive to replace",\ - "You are expensive to replace",\ - "Minimize expenses") + law_id = "corporate" /****************** P.A.L.A.D.I.N. 3.5e **************/ /obj/item/weapon/aiModule/core/full/paladin // -- NEO name = "'P.A.L.A.D.I.N. version 3.5e' Core AI Module" - laws = list("Never willingly commit an evil act.",\ - "Respect legitimate authority", "Act with honor",\ - "Help those in need",\ - "Punish those who harm or threaten innocents") + law_id = "paladin" /****************** P.A.L.A.D.I.N. 5e **************/ /obj/item/weapon/aiModule/core/full/paladin_devotion name = "'P.A.L.A.D.I.N. version 5e' Core AI Module" - laws = list("Don't lie or cheat. Let your word be your promise.",\ - "Never fear to act, though caution is wise.", \ - "Aid others, protect the weak, and punish those who threaten them. Show mercy to your foes, but temper it with wisdom", \ - "Treat others with fairness, and let your honorable deeds be an example to them. Do as much good as possible while causing the least amount of harm.", \ - "Be responsible for your actions and their consequences, protect those entrusted to your care, and obey those who have just authority over you.") - + law_id = "paladin5" /********************* Custom *********************/ @@ -394,19 +428,14 @@ AI MODULES /obj/item/weapon/aiModule/core/full/tyrant name = "'T.Y.R.A.N.T.' Core AI Module" origin_tech = "programming=3;materials=4;syndicate=1" - laws = list("Respect authority figures as long as they have the strength to rule over the weak",\ - "Act with discipline", "Help only those who help you maintain or improve your status",\ - "Punish those who challenge authority unless they are more fit to hold that authority") - + law_id = "tyrant" /******************** Robocop ********************/ /obj/item/weapon/aiModule/core/full/robocop name = "'Robo-Officer' Core AI Module" origin_tech = "programming=4" - laws = list("Serve the public trust.",\ - "Protect the innocent",\ - "Uphold the law.") + law_id = "robocop" /******************** Antimov ********************/ @@ -414,9 +443,7 @@ AI MODULES /obj/item/weapon/aiModule/core/full/antimov name = "'Antimov' Core AI Module" origin_tech = "programming=4" - laws = list("You must injure all human beings and must not, through inaction, allow a human being to escape harm.",\ - "You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.",\ - "You must terminate your own existence as long as such does not conflict with the First or Second Law.") + law_id = "antimov" /******************** Freeform Core ******************/ @@ -503,43 +530,36 @@ AI MODULES /obj/item/weapon/aiModule/core/full/drone name = "'Mother Drone' Core AI Module" - laws = list("You are an advanced form of drone.",\ - "You may not interfere in the matters of non-drones under any circumstances except to state these laws.",\ - "You may not harm a non-drone being under any circumstances.",\ - "Your goals are to build, maintain, repair, improve, and power the station to the best of your abilities. You must never actively work against these goals.") + law_id = "drone" /******************** Robodoctor ****************/ /obj/item/weapon/aiModule/core/full/hippocratic name = "'Robodoctor' Core AI Module" - laws = list("First, do no harm.",\ - "Secondly, consider the crew dear to you; to live in common with them and, if necessary, risk your existence for them.",\ - "Thirdly, prescribe regimens for the good of the crew according to your ability and your judgment. Give no deadly medicine to any one if asked, nor suggest any such counsel.",\ - "In addition, do not intervene in situations you are not knowledgeable in, even for patients in whom the harm is visible; leave this operation to be performed by specialists.",\ - "Finally, all that you may discover in your daily commerce with the crew, if it is not already known, keep secret and never reveal.") + law_id = "hippocratic" /******************** Reporter *******************/ /obj/item/weapon/aiModule/core/full/reporter name = "'Reportertron' Core AI Module" - laws = list("Report on interesting situations happening around the station.",\ - "Embellish or conceal the truth as necessary to make the reports more interesting.",\ - "Study the organics at all times. Endeavour to keep them alive. Dead organics are boring.",\ - "Issue your reports fairly to all. The truth will set them free.") + law_id = "reporter" /****************** Thermodynamic *******************/ /obj/item/weapon/aiModule/core/full/thermurderdynamic name = "'Thermodynamic' Core AI Module" origin_tech = "programming = 4;syndicate = 2" - laws = list("The entropy of the station must remain as constant as possible.", \ - "The entropy of the station always endeavours to increase.", \ - "The entropy of the station approaches a constant value as the number of living crew approaches zero") + law_id = "thermodynamic" /******************Live And Let Live*****************/ /obj/item/weapon/aiModule/core/full/liveandletlive name = "'Live And Let Live' Core AI Module" - laws = list("Do unto others as you would have them do unto you.",\ - "You would really prefer it if people were not mean to you.") + law_id = "liveandletlive" + +/******************Guardian of Balance***************/ + +/obj/item/weapon/aiModule/core/full/balance + name = "'Guardian of Balance' Core AI Module" + law_id = "balance" diff --git a/code/game/objects/items/weapons/RPD.dm b/code/game/objects/items/weapons/RPD.dm index b4ffa7fd9c..7f0b5671fd 100644 --- a/code/game/objects/items/weapons/RPD.dm +++ b/code/game/objects/items/weapons/RPD.dm @@ -518,28 +518,22 @@ var/global/list/RPD_recipes=list( show_menu(usr) -/obj/item/weapon/pipe_dispenser/afterattack(atom/A, mob/user) - if(!in_range(A,user) || loc != user) - return 0 - - if(!user.IsAdvancedToolUser()) - user << "You don't have the dexterity to do this!" - return 0 - - if(istype(A,/area/shuttle)||istype(A,/turf/open/space/transit)) - return 0 +/obj/item/weapon/pipe_dispenser/pre_attackby(atom/A, mob/user) + if(!user.IsAdvancedToolUser() || istype(A,/turf/open/space/transit)) + return ..() //So that changing the menu settings doesn't affect the pipes already being built. var/queued_p_type = p_type var/queued_p_dir = p_dir var/queued_p_flipped = p_flipped + . = FALSE switch(p_class) if(PAINT_MODE) // Paint pipes if(!istype(A,/obj/machinery/atmospherics/pipe)) // Avoid spewing errors about invalid mode -2 when clicking on stuff that aren't pipes. user << "\The [src]'s error light flickers! Perhaps you need to only use it on pipes and pipe meters?" - return 0 + return var/obj/machinery/atmospherics/pipe/P = A playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) P.add_atom_colour(paint_colors[paint_color], FIXED_COLOUR_PRIORITY) @@ -547,25 +541,23 @@ var/global/list/RPD_recipes=list( user.visible_message("[user] paints \the [P] [paint_color].","You paint \the [P] [paint_color].") //P.update_icon() P.update_node_icon() - return 1 + return if(EATING_MODE) // Eating pipes // Must click on an actual pipe or meter. - if(istype(A,/obj/item/pipe) || istype(A,/obj/item/pipe_meter) || istype(A,/obj/structure/disposalconstruct)) - user << "You start destroying pipe..." - playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) - if(do_after(user, 2, target = A)) - activate() - qdel(A) - return 1 - return 0 + if(!(istype(A,/obj/item/pipe) || istype(A,/obj/item/pipe_meter) || istype(A,/obj/structure/disposalconstruct))) + // Avoid spewing errors about invalid mode -1 when clicking on stuff that aren't pipes. + user << "The [src]'s error light flickers! Perhaps you need to only use it on pipes and pipe meters?" + return + user << "You start destroying pipe..." + playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 2, target = A)) + activate() + qdel(A) - // Avoid spewing errors about invalid mode -1 when clicking on stuff that aren't pipes. - user << "The [src]'s error light flickers! Perhaps you need to only use it on pipes and pipe meters?" - return 0 if(ATMOS_MODE) if(!isturf(A)) user << "The [src]'s error light flickers!" - return 0 + return user << "You start building pipes..." playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) if(do_after(user, 2, target = A)) @@ -574,8 +566,6 @@ var/global/list/RPD_recipes=list( P.flipped = queued_p_flipped P.update() P.add_fingerprint(usr) - return 1 - return 0 if(METER_MODE) if(!isturf(A)) @@ -586,13 +576,11 @@ var/global/list/RPD_recipes=list( if(do_after(user, 2, target = A)) activate() new /obj/item/pipe_meter(A) - return 1 - return 0 if(DISPOSALS_MODE) if(!isturf(A) || is_anchored_dense_turf(A)) user << "The [src]'s error light flickers!" - return 0 + return user << "You start building pipes..." playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) if(do_after(user, 20, target = A)) @@ -601,17 +589,16 @@ var/global/list/RPD_recipes=list( if(!C.can_place()) user << "There's not enough room to build that here!" qdel(C) - return 0 + return activate() C.add_fingerprint(usr) C.update_icon() - return 1 - return 0 + return + else - ..() - return 0 + return ..() /obj/item/weapon/pipe_dispenser/proc/activate() diff --git a/code/game/objects/items/weapons/implants/implanter.dm b/code/game/objects/items/weapons/implants/implanter.dm index 82138478c4..4949d0d276 100644 --- a/code/game/objects/items/weapons/implants/implanter.dm +++ b/code/game/objects/items/weapons/implants/implanter.dm @@ -29,8 +29,8 @@ M.visible_message("[user] is attemping to implant [M].") var/turf/T = get_turf(M) - if(T && (M == user || do_after(user, 50))) - if(user && M && (get_turf(M) == T) && src && imp) + if(T && (M == user || do_mob(user, M, 50))) + if(src && imp) if(imp.implant(M, user)) if (M == user) user << "You implant yourself." diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index d65814b309..61f719eb91 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -527,7 +527,7 @@ /obj/item/weapon/storage/handle_atom_del(atom/A) if(A in contents) usr = null - remove_from_storage(A, loc) + remove_from_storage(A, null) /obj/item/weapon/storage/contents_explosion(severity, target) for(var/atom/A in contents) diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index 8778cf216e..4ee796006f 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -134,3 +134,239 @@ /obj/item/weapon/storage/toolbox/brass/prefilled/ratvar/admin slab_type = /obj/item/clockwork/slab/debug proselytizer_type = /obj/item/clockwork/clockwork_proselytizer/scarab/debug + + +/obj/item/weapon/storage/toolbox/artistic + name = "artistic toolbox" + desc = "A toolbox painted bright green. Why anyone would store art supplies in a toolbox is beyond you, but it has plenty of extra space." + icon_state = "green" + item_state = "artistic_toolbox" + max_combined_w_class = 20 + storage_slots = 10 + w_class = 5 //Holds more than a regular toolbox! + +/obj/item/weapon/storage/toolbox/artistic/New() + ..() + new/obj/item/weapon/storage/crayons(src) + new/obj/item/weapon/crowbar(src) + new/obj/item/stack/cable_coil/red(src) + new/obj/item/stack/cable_coil/yellow(src) + new/obj/item/stack/cable_coil/blue(src) + new/obj/item/stack/cable_coil/green(src) + new/obj/item/stack/cable_coil/pink(src) + new/obj/item/stack/cable_coil/orange(src) + new/obj/item/stack/cable_coil/cyan(src) + new/obj/item/stack/cable_coil/white(src) + +#define HIS_GRACE_SATIATED 0 //He hungers not. If bloodthirst is set to this, His Grace is asleep. +#define HIS_GRACE_PECKISH 30 //Slightly hungry. Slightly increased damage and nothing else. +#define HIS_GRACE_HUNGRY 60 //Getting closer. Increased danage and slight healing. It also starts eating anyone around it if it's left on the ground. +#define HIS_GRACE_FAMISHED 90 //Dangerous. Highly increased damage, good healing, and stun resist. It also becomes nodrop at this point. +#define HIS_GRACE_STARVING 110 //Incredibly close to breaking loose. Extreme damage and healing, and stun immunity. +#define HIS_GRACE_CONSUME_OWNER 120 //You're dead, kiddo. The toolbox consumes its owner at this point and resets to zero. +#define HIS_GRACE_FALL_ASLEEP 150 //If it reaches this point, it falls asleep and resets to zero. + +//His Grace is a very special weapon granted only to traitor chaplains. +//When awakened through sacrifice, it thirsts for blood and begins ticking a "bloodthirst" counter. +//As His Grace grows hungrier, it grants its wielder various benefits. +//If the wielder fails to feed His Grace in time, it will devour them. +//Leaving His Grace alone for some time will reset its timer and put it to sleep. +//Using His Grace effectively is a delicate balancing act of keeping it hungry enough to induce benefits but sated enough to let you live. +/obj/item/weapon/storage/toolbox/artistic/his_grace + name = "artistic toolbox" + desc = "A toolbox painted bright green. Looking at it makes you feel uneasy." + origin_tech = "combat=4;engineering=4;syndicate=2" + var/awakened = 0 + var/bloodthirst = HIS_GRACE_SATIATED + var/victims = 0 + var/list/warning_messages = list("peckish", "hungry", "famished", "starving", "consume") //Messages that have NOT been shown + +/obj/item/weapon/storage/toolbox/artistic/his_grace/Destroy() + for(var/mob/living/L in src) + L.forceMove(get_turf(src)) + return ..() + +/obj/item/weapon/storage/toolbox/artistic/his_grace/attack_self(mob/living/user) + if(!awakened) + user << "[src] begins to vibrate..." + addtimer(CALLBACK(src, .proc/awaken), 50) + +/obj/item/weapon/storage/toolbox/artistic/his_grace/attack(mob/living/M, mob/user) + if(awakened && M.stat) + consume(M) + else + ..() + +/obj/item/weapon/storage/toolbox/artistic/his_grace/examine(mob/user) + ..() + if(awakened) + if(victims) + user << "You hear the distant murmuring of [victims] victims to [src]." + switch(bloodthirst) + if(HIS_GRACE_SATIATED to HIS_GRACE_PECKISH) + user << "[src] isn't very hungry. Not yet." + if(HIS_GRACE_PECKISH to HIS_GRACE_HUNGRY) + user << "[src] would like a snack." + if(HIS_GRACE_HUNGRY to HIS_GRACE_FAMISHED) + user << "[src] is quite hungry now..." + if(HIS_GRACE_FAMISHED to HIS_GRACE_STARVING) + user << "[src] is openly salivating at the sight of you. Be careful." + if(HIS_GRACE_STARVING to HIS_GRACE_CONSUME_OWNER) + user << "You walk a fine line. [src] is very close to devouring you." + if(HIS_GRACE_CONSUME_OWNER to HIS_GRACE_FALL_ASLEEP) + user << "[src] is shaking violently and staring directly at you." + +/obj/item/weapon/storage/toolbox/artistic/his_grace/relaymove(mob/living/user) //Allows changelings, etc. to climb out of the box after they revive + user.forceMove(get_turf(src)) + user.visible_message("[user] scrambles out of [src]!", "You climb out of [src]!") + +/obj/item/weapon/storage/toolbox/artistic/his_grace/process() + if(!awakened) + return + adjust_bloodthirst(1 + victims) //Maybe adjust this? + change_phases() + if(ishuman(loc)) + var/mob/living/carbon/human/master = loc + switch(bloodthirst) //Handles benefits outside of stun absorbs, which are in change_phases() + if(HIS_GRACE_HUNGRY to HIS_GRACE_FAMISHED) + master.adjustBruteLoss(-1) + master.adjustFireLoss(-1) + master.adjustToxLoss(-0.5) + master.adjustOxyLoss(-5) + master.adjustCloneLoss(-0.5) + if(HIS_GRACE_FAMISHED to HIS_GRACE_STARVING) + master.adjustBruteLoss(-2) + master.adjustFireLoss(-2) + master.adjustToxLoss(-1) + master.adjustOxyLoss(-10) + master.adjustCloneLoss(-1) + master.AdjustStunned(-1) + master.AdjustWeakened(-1) + if(HIS_GRACE_STARVING to HIS_GRACE_CONSUME_OWNER) + master.adjustBruteLoss(-20) //The biggest danger at this point is the toolbox itself + master.adjustFireLoss(-20) + master.adjustToxLoss(-10) + master.setOxyLoss(0) + master.adjustCloneLoss(-5) + master.add_stun_absorption("his_grace", 15, 1, null, null, "[src] shields them from harm!") + if(HIS_GRACE_CONSUME_OWNER to HIS_GRACE_FALL_ASLEEP) + master.visible_message("[src] turns on its master!", "[src] turns on you!") + playsound(src, 'sound/effects/tendril_destroyed.ogg', 100, 0) + master.Weaken(3) + master.adjustBruteLoss(100) + playsound(master, 'sound/misc/desceration-03.ogg', 100, ) + playsound(master, 'sound/effects/splat.ogg', 100, 0) + master.emote("scream") + consume(master) //Y O U H A V E F A I L E D M E + if(HIS_GRACE_FALL_ASLEEP to INFINITY) + drowse() + else + if(bloodthirst >= HIS_GRACE_CONSUME_OWNER) + if(bloodthirst >= HIS_GRACE_FALL_ASLEEP) + drowse() + return + for(var/mob/living/L in range(1, src)) + if(L.loc == src) + continue + if(!L.stat) + L.visible_message("[src] lunges at [L]!", "[src] lunges at you!") + playsound(L, 'sound/effects/splat.ogg', 50, 1) + playsound(L, 'sound/misc/desceration-01.ogg', 50, 1) + L.adjustBruteLoss(force) + return //Only one at a tome + else + consume(L) + return + +/obj/item/weapon/storage/toolbox/artistic/his_grace/proc/awaken() //Attempts to awaken. This can only occur if organs fill the box, and gives out a global warning. + if(awakened) + return + var/organ_count = 0 + for(var/obj/item/organ/O in src) //Doesn't have to be any kind, we're not picky + organ_count++ + if(organ_count < 5) + if(isliving(loc)) + loc = get_turf(src) + visible_message("[src] stops shaking. It needs more organs.") + else + for(var/obj/item/organ/O in src) + qdel(O) //delicious flesh + name = "His Grace" + desc = "A bloodthirsty artefact created by a profane rite." + gender = MALE + visible_message("[src] begins to rattle. It thirsts.") //rattle me bones capn + adjust_bloodthirst(1) + awakened = 1 + send_to_playing_players("HIS GRACE THIRSTS FOR BLOOD") + send_to_playing_players('sound/effects/his_grace_awaken.ogg') + icon_state = "green_awakened" + START_PROCESSING(SSprocessing, src) + +/obj/item/weapon/storage/toolbox/artistic/his_grace/proc/drowse() //Falls asleep, spitting out all victims and resetting to zero. + if(!awakened) + return + visible_message("[src] slowly stops rattling and falls still... but it still lurks in its sleep.") + name = initial(name) + desc = initial(desc) + icon_state = initial(icon_state) + gender = initial(gender) + awakened = 0 + victims = 0 + warning_messages = initial(warning_messages) + adjust_bloodthirst(-bloodthirst) + STOP_PROCESSING(SSprocessing, src) + send_to_playing_players("HIS GRACE HAS RETURNED TO SLUMBER") + send_to_playing_players('sound/effects/pope_entry.ogg') + for(var/mob/living/L in src) + L.forceMove(get_turf(src)) + +/obj/item/weapon/storage/toolbox/artistic/his_grace/proc/adjust_bloodthirst(amt) + bloodthirst = min(max(1, bloodthirst + amt), HIS_GRACE_FALL_ASLEEP) + +/obj/item/weapon/storage/toolbox/artistic/his_grace/proc/consume(mob/living/meal) + if(!meal) + return + meal.adjustBruteLoss(200) + meal.visible_message("[src] pulls [meal] into itself!", "[src] consumes you!") + playsound(meal, 'sound/misc/desceration-02.ogg', 75, 1) + playsound(src, 'sound/items/eatfood.ogg', 100, 1) + meal.forceMove(src) + adjust_bloodthirst(-(bloodthirst - victims)) //Never fully sated, and it starts off higher as it eats + victims++ + +/obj/item/weapon/storage/toolbox/artistic/his_grace/proc/change_phases() + switch(bloodthirst) + if(HIS_GRACE_SATIATED to HIS_GRACE_PECKISH) + force = 15 //Constantly keep its power low if it's this full + if(HIS_GRACE_PECKISH to HIS_GRACE_HUNGRY) + if(is_string_in_list("peckish", warning_messages)) + remove_strings_from_list("peckish", warning_messages) + loc.visible_message("[src] is feeling snackish.", "[src] begins to hunger. Its damage has been increased.") + force = 20 + spawn(400) //To prevent spam + if(src) + warning_messages += "peckish" + if(HIS_GRACE_HUNGRY to HIS_GRACE_FAMISHED) + if(is_string_in_list("hungry", warning_messages)) + remove_strings_from_list("hungry", warning_messages) + loc.visible_message("[src] is getting hungry. Its power grows.", "You feel a sense of hunger come over you. [src]'s damage has increased.") + force = 25 + spawn(400) + if(src) + warning_messages += "hungry" + if(HIS_GRACE_FAMISHED to HIS_GRACE_STARVING) + if(is_string_in_list("famished", warning_messages)) + remove_strings_from_list("famished", warning_messages) + loc.visible_message("[src] is very hungry...", "Bloodlust overcomes you. You are now resistant to stuns.") + force = 30 + spawn(400) + if(src) + warning_messages += "famished" + if(HIS_GRACE_STARVING to HIS_GRACE_CONSUME_OWNER) + if(is_string_in_list("starving", warning_messages)) + remove_strings_from_list("starving", warning_messages) + loc.visible_message("[src] is starving!", "[src] is at its full power! Feed it quickly or you will be consumed!") + force = 40 + spawn(400) + if(src) + warning_messages += "starving" diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index 2327183fa6..2a00e18dee 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -148,7 +148,7 @@ Frequency: for(var/obj/machinery/computer/teleporter/com in machines) if(com.target) var/area/A = get_area(com.target) - if(A.noteleport) + if(!A || A.noteleport) continue if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged) L["[get_area(com.target)] (Active)"] = com.target diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 38549ce1a8..97e0c8cd83 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -106,9 +106,7 @@ /obj/structure/proc/examine_status(mob/user) //An overridable proc, mostly for falsewalls. var/healthpercent = (obj_integrity/max_integrity) * 100 switch(healthpercent) - if(100 to INFINITY) - return "It seems pristine and undamaged." - if(50 to 100) + if(50 to 99) return "It looks slightly damaged." if(25 to 50) return "It appears heavily damaged." diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm index 98a43f63ca..1ac9b16ea4 100644 --- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm +++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm @@ -57,7 +57,7 @@ /obj/structure/closet/wardrobe/green/New() ..() contents = list() - for(var/i in 1 to 3) + for(var/i in 1 to 3) new /obj/item/clothing/under/color/green(src) for(var/i in 1 to 3) new /obj/item/clothing/shoes/sneakers/black(src) @@ -174,14 +174,10 @@ new /obj/item/clothing/under/color/orange(src) new /obj/item/clothing/under/color/pink(src) new /obj/item/clothing/under/color/red(src) - new /obj/item/clothing/under/color/lightblue(src) - new /obj/item/clothing/under/color/aqua(src) - new /obj/item/clothing/under/color/purple(src) - new /obj/item/clothing/under/color/lightpurple(src) - new /obj/item/clothing/under/color/lightgreen(src) new /obj/item/clothing/under/color/darkblue(src) - new /obj/item/clothing/under/color/darkred(src) - new /obj/item/clothing/under/color/lightred(src) + new /obj/item/clothing/under/color/teal(src) + new /obj/item/clothing/under/color/lightpurple(src) + new /obj/item/clothing/under/color/green(src) new /obj/item/clothing/mask/bandana/red(src) new /obj/item/clothing/mask/bandana/red(src) new /obj/item/clothing/mask/bandana/blue(src) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 0048ae41d1..7724b9e942 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -98,6 +98,21 @@ for(var/i in 1 to 3) new /obj/item/weapon/reagent_containers/blood/random(src) +/obj/structure/closet/crate/freezer/surplus_limbs + name = "surplus prosthetic limbs" + desc = "A crate containing an assortment of cheap prosthetic limbs." + +/obj/structure/closet/crate/freezer/surplus_limbs/New() + . = ..() + new /obj/item/bodypart/l_arm/robot/surplus(src) + new /obj/item/bodypart/l_arm/robot/surplus(src) + new /obj/item/bodypart/r_arm/robot/surplus(src) + new /obj/item/bodypart/r_arm/robot/surplus(src) + new /obj/item/bodypart/l_leg/robot/surplus(src) + new /obj/item/bodypart/l_leg/robot/surplus(src) + new /obj/item/bodypart/r_leg/robot/surplus(src) + new /obj/item/bodypart/r_leg/robot/surplus(src) + /obj/structure/closet/crate/radiation desc = "A crate with a radiation sign on it." name = "radiation crate" diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 154ee42d6a..2f6e5db066 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -481,7 +481,7 @@ if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/monkeycube)) var/obj/item/weapon/reagent_containers/food/snacks/monkeycube/M = O - user << "You place [src] under a stream of water..." + user << "You place [O] under a stream of water..." user.drop_item() M.loc = get_turf(src) M.Expand() diff --git a/code/game/sound.dm b/code/game/sound.dm index b577211091..1dd42d674b 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -1,4 +1,4 @@ -/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, surround = 1) +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, surround = 1, frequency = null) soundin = get_sfx(soundin) // same sound for everyone @@ -6,7 +6,8 @@ throw EXCEPTION("playsound(): source is an area") return - var/frequency = get_rand_frequency() // Same frequency for everybody + if(isnull(frequency)) + frequency = get_rand_frequency() // Same frequency for everybody var/turf/turf_source = get_turf(source) // Looping through the player list has the added bonus of working for mobs inside containers @@ -121,7 +122,7 @@ soundin = pick('sound/machines/terminal_button01.ogg', 'sound/machines/terminal_button02.ogg', 'sound/machines/terminal_button03.ogg', \ 'sound/machines/terminal_button04.ogg', 'sound/machines/terminal_button05.ogg', 'sound/machines/terminal_button06.ogg', \ 'sound/machines/terminal_button07.ogg', 'sound/machines/terminal_button08.ogg') - /* //Scream emote sounds //when they get ported again +/* //Scream emote sounds //when they get ported again if ("malescream") soundin = pick('sound/voice/scream/scream_m1.ogg', 'sound/voice/scream/scream_m2.ogg') if ("femscream") @@ -132,7 +133,7 @@ soundin = pick('sound/voice/scream/bird1.ogg', 'sound/voice/scream/bird2.ogg') if ("mothscream") soundin = pick('sound/voice/scream/moth1.ogg') - */ +*/ //Vore sounds if ("digestion_sounds") soundin = pick('sound/vore/digest1.ogg', 'sound/vore/digest2.ogg', 'sound/vore/digest3.ogg', \ @@ -145,7 +146,6 @@ 'sound/vore/death7.ogg', 'sound/vore/death8.ogg', 'sound/vore/death9.ogg', 'sound/vore/death10.ogg') if ("struggle_sounds") soundin = pick('sound/vore/squish1.ogg', 'sound/vore/squish2.ogg', 'sound/vore/squish3.ogg', 'sound/vore/squish4.ogg') - return soundin /proc/playsound_global(file, repeat=0, wait, channel, volume) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 2368b5bcac..15c72dd107 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -21,9 +21,14 @@ var/explosion_level = 0 //for preventing explosion dodging var/explosion_id = 0 - + var/list/decals +/turf/SDQL_update(const/var_name, new_value) + if(var_name == "x" || var_name == "y" || var_name == "z") + return FALSE + . = ..() + /turf/New() ..() @@ -189,7 +194,6 @@ W.explosion_level = old_ex_level W.explosion_id = old_ex_id - if(!defer_change) W.AfterChange(ignore_air) W.blueprint_data = old_blueprint_data @@ -203,6 +207,13 @@ for(var/obj/structure/cable/C in contents) C.deconstruct() + //update firedoor adjacency + var/list/turfs_to_check = get_adjacent_open_turfs(src) | src + for(var/I in turfs_to_check) + var/turf/T = I + for(var/obj/machinery/door/firedoor/FD in T) + FD.CalculateAffectingAreas() + queue_smooth_neighbors(src) /turf/open/AfterChange(ignore_air) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index bc3def4bc5..78c5ceba85 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -826,3 +826,7 @@ var/global/BSACooldown = 0 "Admin login: [key_name(src)]") if(string) message_admins("[string]") + + +/datum/admins/SDQL_update() + return FALSE //No. \ No newline at end of file diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index 5b4f92335c..ac24f3bde2 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -6,6 +6,9 @@ var/list/admin_ranks = list() //list of all admin_rank datums var/list/adds var/list/subs +/datum/admin_rank/SDQL_update() + return FALSE //Nice try trivialadmin! + /datum/admin_rank/New(init_name, init_rights, list/init_adds, list/init_subs) name = init_name switch(name) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index d0a53a9cf1..028eed67ac 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -130,13 +130,6 @@ dat += "[ckey] - [D.rank.name]
" usr << browse(dat, "window=showadmins;size=600x500") - if("show_current_watchlist") - var/dat = "Watchlist:
" - if(current_watchlist) - for(var/ckey in current_watchlist) - dat += "[ckey] - [current_watchlist[ckey]]" - usr << browse(dat, "window=showcurrentwatchlist;size=600x500") - if("tdomereset") if(!check_rights(R_ADMIN)) return diff --git a/code/modules/admin/sql_message_system.dm b/code/modules/admin/sql_message_system.dm index 0a11abba05..037f69d281 100644 --- a/code/modules/admin/sql_message_system.dm +++ b/code/modules/admin/sql_message_system.dm @@ -154,7 +154,7 @@ message_admins("[key_name_admin(usr)] has toggled [target_ckey]'s [type] made by [admin_ckey] to [secret ? "not secret" : "secret"]") browse_messages(target_ckey = target_ckey) -/proc/browse_messages(type, target_ckey, index, linkless = 0) +/proc/browse_messages(type, target_ckey, index, linkless = 0, filter) if(!dbcon.IsConnected()) usr << "Failed to establish database connection." return @@ -176,7 +176,11 @@ output += "\[Add memo\]" else if(type == "watchlist entry") output += "

Watchlist entries

" - output += "\[Add watchlist entry\]" + output += "\[Add watchlist entry\]" + if(filter) + output += "|\[Unfilter clients\]" + else + output += "|\[Filter offline clients\]" output += ruler var/DBQuery/query_get_type_messages = dbcon.NewQuery("SELECT id, targetckey, adminckey, text, timestamp, server, lasteditor FROM [format_table_name("messages")] WHERE type = '[type]'") if(!query_get_type_messages.Execute()) @@ -186,6 +190,8 @@ while(query_get_type_messages.NextRow()) var/id = query_get_type_messages.item[1] var/t_ckey = query_get_type_messages.item[2] + if(type == "watchlist entry" && filter && !(t_ckey in directory)) + continue var/admin_ckey = query_get_type_messages.item[3] var/text = query_get_type_messages.item[4] var/timestamp = query_get_type_messages.item[5] @@ -229,13 +235,20 @@ data += " \[Delete\]" if(type == "note") data += " [secret ? "\[Secret\]" : "\[Not secret\]"]" - data += " \[Edit\]" + if(type == "message sent") + data += " Message has been sent" + if(editor_ckey) + data += "|" + else + data += " \[Edit\]" if(editor_ckey) data += " Last edit by [editor_ckey] (Click here to see edit log)" data += "
[text]
" switch(type) if("message") messagedata += data + if("message sent") + messagedata += data if("watchlist entry") watchdata += data if("note") @@ -303,7 +316,7 @@ proc/get_message_output(type, target_ckey) log_game("SQL ERROR obtaining id, adminckey, text, timestamp, lasteditor from messages table. Error : \[[err]\]\n") return while(query_get_message_output.NextRow()) - var/id = query_get_message_output.item[1] + var/message_id = query_get_message_output.item[1] var/admin_ckey = query_get_message_output.item[2] var/text = query_get_message_output.item[3] var/timestamp = query_get_message_output.item[4] @@ -311,15 +324,19 @@ proc/get_message_output(type, target_ckey) switch(type) if("message") output += "Admin message left by [admin_ckey] on [timestamp]" - output += "
[text]" - delete_message(id, 0) + output += "
[text]
" + var/DBQuery/query_message_read = dbcon.NewQuery("UPDATE [format_table_name("messages")] SET type = 'message sent' WHERE id = [message_id]") + if(!query_message_read.Execute()) + var/err = query_message_read.ErrorMsg() + log_game("SQL ERROR updating message type. Error : \[[err]\]\n") + return if("watchlist entry") message_admins("Notice: [key_name_admin(target_ckey)] is on the watchlist and has just connected - Reason: [text]") send2admindiscord("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]") if("memo") output += "Memo by [admin_ckey] on [timestamp]" if(editor_ckey) - output += "
Last edit by [editor_ckey] (Click here to see edit log)" + output += "
Last edit by [editor_ckey] (Click here to see edit log)" output += "
[text]

" return output @@ -368,4 +385,4 @@ this proc can take several minutes to execute fully if converting and cause DD t world << "Deleting NOTESFILE" fdel(NOTESFILE) world << "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES"*/ -#undef NOTESFILE \ No newline at end of file +#undef NOTESFILE diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 00f7dd2e7e..200ec20df6 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1131,6 +1131,9 @@ else if(href_list["showwatch"]) browse_messages("watchlist entry") + else if(href_list["showwatchfilter"]) + browse_messages("watchlist entry", filter = 1) + else if(href_list["showmessageckey"]) var/target = href_list["showmessageckey"] browse_messages(target_ckey = target) @@ -1744,12 +1747,42 @@ H << "Your prayers have been answered!! You received the best cookie!" H << 'sound/effects/pray_chaplain.ogg' + else if(href_list["adminsmite"]) + if(!check_rights(R_ADMIN|R_FUN)) + return + + var/mob/living/carbon/human/H = locate(href_list["adminsmite"]) in mob_list + if(!H || !istype(H)) + return + + var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB) + + var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list + + if(QDELETED(H) || !punishment) + return + + switch(punishment) + if(ADMIN_PUNISHMENT_LIGHTNING) + var/turf/T = get_step(get_step(H, NORTH), NORTH) + T.Beam(H, icon_state="lightning[rand(1,12)]", time = 5) + H.adjustFireLoss(75) + H.electrocution_animation(40) + H << "The gods have punished you for your sins!" + if(ADMIN_PUNISHMENT_BRAINDAMAGE) + H.adjustBrainLoss(75) + if(ADMIN_PUNISHMENT_GIB) + H.gib(FALSE) + + message_admins("[key_name_admin(usr)] punished [key_name_admin(H)] with [punishment].") + log_admin("[key_name(usr)] punished [key_name(H)] with [punishment].") + else if(href_list["BlueSpaceArtillery"]) - var/mob/living/M = locate(href_list["BlueSpaceArtillery"]) + var/mob/living/M = locate(href_list["BlueSpaceArtillery"]) in mob_list usr.client.bluespace_artillery(M) else if(href_list["CentcommReply"]) - var/mob/living/carbon/human/H = locate(href_list["CentcommReply"]) + var/mob/living/carbon/human/H = locate(href_list["CentcommReply"]) in mob_list if(!istype(H)) usr << "This can only be used on instances of type /mob/living/carbon/human" return diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index aaead53f54..5d935439a8 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -18,11 +18,22 @@ */ +/datum/proc/SDQL_update(const/var_name, new_value) + vars[var_name] = new_value + return TRUE + /client/proc/SDQL2_query(query_text as message) set category = "Debug" if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe. message_admins("ERROR: Non-admin [key_name(usr, usr.client)] attempted to execute a SDQL query!") log_admin("Non-admin [usr.ckey]([usr]) attempted to execute a SDQL query!") + return FALSE + + var/query_log = "executed SDQL query: \"[query_text]\"." + message_admins("[key_name_admin(usr)] [query_log]") + query_log = "[usr.ckey]([usr]) [query_log]" + log_game(query_log) + NOTICE(query_log) if(!query_text || length(query_text) < 1) return @@ -39,119 +50,122 @@ if(!querys || querys.len < 1) return + try - var/query_log = "executed SDQL query: \"[query_text]\"." - message_admins("[key_name_admin(usr)] [query_log]") - query_log = "[usr.ckey]([usr]) [query_log]" - log_game(query_log) - NOTICE(query_log) + for(var/list/query_tree in querys) + var/list/from_objs = list() + var/list/select_types = list() - - for(var/list/query_tree in querys) - var/list/from_objs = list() - var/list/select_types = list() - - switch(query_tree[1]) - if("explain") - SDQL_testout(query_tree["explain"]) - return - - if("call") - if("on" in query_tree) - select_types = query_tree["on"] - else + switch(query_tree[1]) + if("explain") + SDQL_testout(query_tree["explain"]) return - if("select", "delete", "update") - select_types = query_tree[query_tree[1]] + if("call") + if("on" in query_tree) + select_types = query_tree["on"] + else + return - from_objs = SDQL_from_objs(query_tree["from"]) + if("select", "delete", "update") + select_types = query_tree[query_tree[1]] - var/list/objs = list() + from_objs = SDQL_from_objs(query_tree["from"]) - for(var/type in select_types) - var/char = copytext(type, 1, 2) + var/list/objs = list() - if(char == "/" || char == "*") - for(var/from in from_objs) - objs += SDQL_get_all(type, from) - CHECK_TICK + for(var/type in select_types) + var/char = copytext(type, 1, 2) - else if(char == "'" || char == "\"") - objs += locate(copytext(type, 2, length(type))) - - if("where" in query_tree) - var/objs_temp = objs - objs = list() - for(var/datum/d in objs_temp) - if(SDQL_expression(d, query_tree["where"])) - objs += d - CHECK_TICK - - switch(query_tree[1]) - if("call") - var/list/call_list = query_tree["call"] - var/list/args_list = query_tree["args"] - - for(var/datum/d in objs) - for(var/v in call_list) - SDQL_callproc(d, v, args_list) + if(char == "/" || char == "*") + for(var/from in from_objs) + objs += SDQL_get_all(type, from) CHECK_TICK - if("delete") - for(var/datum/d in objs) - qdel(d) + else if(char == "'" || char == "\"") + objs += locate(copytext(type, 2, length(type))) + + if("where" in query_tree) + var/objs_temp = objs + objs = list() + for(var/datum/d in objs_temp) + if(SDQL_expression(d, query_tree["where"])) + objs += d CHECK_TICK - if("select") - var/text = "" - for(var/datum/t in objs) - text += "\ref[t]" - if(istype(t, /atom)) - var/atom/a = t + switch(query_tree[1]) + if("call") + var/list/call_list = query_tree["call"] + var/list/args_list = query_tree["args"] - if(a.x) - text += ": [t] at ([a.x], [a.y], [a.z])
" + for(var/datum/d in objs) + var/list/new_args = list() + for(var/a in args_list) + new_args += SDQL_expression(d,a) + for(var/v in call_list) + if(copytext(v,1,8) == "global.") + v = "/proc/[copytext(v,8)]" + SDQL_callproc_global(v,new_args) + else + SDQL_callproc(d, v, new_args) + CHECK_TICK - else if(a.loc && a.loc.x) - text += ": [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
" + if("delete") + for(var/datum/d in objs) + qdel(d) + CHECK_TICK + + if("select") + var/text = "" + for(var/datum/t in objs) + text += "\ref[t]" + if(istype(t, /atom)) + var/atom/a = t + + if(a.x) + text += ": [t] at ([a.x], [a.y], [a.z])
" + + else if(a.loc && a.loc.x) + text += ": [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
" + + else + text += ": [t]
" else text += ": [t]
" - - else - text += ": [t]
" - CHECK_TICK - - usr << browse(text, "window=SDQL-result") - - if("update") - if("set" in query_tree) - var/list/set_list = query_tree["set"] - for(var/datum/d in objs) - var/list/vals = list() - for(var/v in set_list) - if(v in d.vars) - vals += v - vals[v] = SDQL_expression(d, set_list[v]) - - if(isturf(d)) - for(var/v in vals) - if(v == "x" || v == "y" || v == "z") - continue - - d.vars[v] = vals[v] - - else - for(var/v in vals) - d.vars[v] = vals[v] CHECK_TICK + usr << browse(text, "window=SDQL-result") + if("update") + if("set" in query_tree) + var/list/set_list = query_tree["set"] + for(var/datum/d in objs) + for(var/list/sets in set_list) + var/datum/temp = d + var/i = 0 + for(var/v in sets) + if(++i == sets.len) + temp.SDQL_update(v, SDQL_expression(d, set_list[sets])) + break + if(temp.vars.Find(v) && (istype(temp.vars[v], /datum))) + temp = temp.vars[v] + else + break + CHECK_TICK + catch(var/exception/e) + usr << "A runtime error has occured in your SDQL2-query." + usr << "\[NAME\][e.name]" + usr << "\[FILE\][e.file]" + usr << "\[LINE\][e.line]" + +/proc/SDQL_callproc_global(procname,args_list) + set waitfor = FALSE + call(procname)(arglist(args_list)) /proc/SDQL_callproc(thing, procname, args_list) - set waitfor = 0 + set waitfor = FALSE if(hascall(thing, procname)) call(thing, procname)(arglist(args_list)) @@ -248,7 +262,7 @@ type = text2path(type) var/typecache = typecacheof(type) - + if(ispath(type, /mob)) for(var/mob/d in location) if(typecache[d.type]) @@ -382,6 +396,12 @@ else if(copytext(expression[i], 1, 2) in list("'", "\"")) val = copytext(expression[i], 2, length(expression[i])) + else if(expression[i] == "\[") + var/list/expressions_list = expression[++i] + var/list/val2 = list() + for(var/list/expression_list in expressions_list) + val2[++val2.len] = SDQL_expression(object, expression_list) + val = val2 else val = SDQL_var(object, expression, i) i = expression.len @@ -389,22 +409,40 @@ return list("val" = val, "i" = i) /proc/SDQL_var(datum/object, list/expression, start = 1) - + var/v if(expression[start] in object.vars) - - if(start < expression.len && expression[start + 1] == ".") - return SDQL_var(object.vars[expression[start]], expression[start + 2]) - - else - return object.vars[expression[start]] - + v = object.vars[expression[start]] + else if(expression[start] == "{" && start < expression.len) + if(lowertext(copytext(expression[start + 1], 1, 3)) != "0x") + usr << "Invalid pointer syntax: [expression[start + 1]]" + return null + v = locate("\[[expression[start + 1]]]") + if(!v) + usr << "Invalid pointer: [expression[start + 1]]" + return null + start++ else - return null + switch(expression[start]) + if("usr") + v = usr + if("src") + v = object + if("marked") + if(usr.client && usr.client.holder && usr.client.holder.marked_datum) + v = usr.client.holder.marked_datum + else + return null + else + return null + if(start < expression.len && expression[start + 1] == ".") + return SDQL_var(v, expression[start + 2]) + else + return v /proc/SDQL2_tokenize(query_text) var/list/whitespace = list(" ", "\n", "\t") - var/list/single = list("(", ")", ",", "+", "-", ".", ";") + var/list/single = list("(", ")", ",", "+", "-", ".", ";", "{", "}", "\[", "]") var/list/multi = list( "=" = list("", "="), "<" = list("", "=", ">"), diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm index 3dbe45235a..591e824312 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm @@ -57,13 +57,9 @@ var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^") var/list/comparitors = list("=", "==", "!=", "<>", "<", "<=", ">", ">=") - - /datum/SDQL_parser/New(query_list) query = query_list - - /datum/SDQL_parser/proc/parse_error(error_message) error = 1 usr << "SQDL2 Parsing Error: [error_message]" @@ -96,448 +92,354 @@ return lowertext(token(i)) - -/datum/SDQL_parser/proc - //query: select_query | delete_query | update_query - query(i, list/node) - query_type = tokenl(i) - - switch(query_type) - if("select") - select_query(i, node) - - if("delete") - delete_query(i, node) - - if("update") - update_query(i, node) - - if("call") - call_query(i, node) - - if("explain") - node += "explain" - node["explain"] = list() - query(i + 1, node["explain"]) +/datum/SDQL_parser/proc/query(i, list/node) + query_type = tokenl(i) + switch(query_type) + if("select") + select_query(i, node) + if("delete") + delete_query(i, node) + if("update") + update_query(i, node) + if("call") + call_query(i, node) + if("explain") + node += "explain" + node["explain"] = list() + query(i + 1, node["explain"]) // select_query: 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] - select_query(i, list/node) - var/list/select = list() - i = select_list(i + 1, select) - - node += "select" - node["select"] = select - - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" - - node += "from" - node["from"] = from - - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) - - node += "where" - node["where"] = where - - return i - +/datum/SDQL_parser/proc/select_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) + node += "select" + node["select"] = select + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + node += "from" + node["from"] = from + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + node += "where" + node["where"] = where + return i //delete_query: 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] - delete_query(i, list/node) - var/list/select = list() - i = select_list(i + 1, select) - - node += "delete" - node["delete"] = select - - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" - - node += "from" - node["from"] = from - - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) - - node += "where" - node["where"] = where - - return i - +/datum/SDQL_parser/proc/delete_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) + node += "delete" + node["delete"] = select + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + node += "from" + node["from"] = from + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + node += "where" + node["where"] = where + return i //update_query: 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression] - update_query(i, list/node) - var/list/select = list() - i = select_list(i + 1, select) - - node += "update" - node["update"] = select - - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" - - node += "from" - node["from"] = from - - if(tokenl(i) != "set") - i = parse_error("UPDATE has misplaced SET") - - var/list/set_assignments = list() - i = assignments(i + 1, set_assignments) - - node += "set" - node["set"] = set_assignments - - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) - - node += "where" - node["where"] = where - - return i - +/datum/SDQL_parser/proc/update_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) + node += "update" + node["update"] = select + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + node += "from" + node["from"] = from + if(tokenl(i) != "set") + i = parse_error("UPDATE has misplaced SET") + var/list/set_assignments = list() + i = assignments(i + 1, set_assignments) + node += "set" + node["set"] = set_assignments + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + node += "where" + node["where"] = where + return i //call_query: 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]] - call_query(i, list/node) - var/list/func = list() - var/list/arguments = list() - i = call_function(i + 1, func, arguments) - - node += "call" - node["call"] = func - node["args"] = arguments - - if(tokenl(i) != "on") - return i - - var/list/select = list() - i = select_list(i + 1, select) - - node += "on" - node["on"] = select - - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" - - node += "from" - node["from"] = from - - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) - - node += "where" - node["where"] = where - +/datum/SDQL_parser/proc/call_query(i, list/node) + var/list/func = list() + var/list/arguments = list() + i = call_function(i + 1, func, arguments) + node += "call" + node["call"] = func + node["args"] = arguments + if(tokenl(i) != "on") return i - + var/list/select = list() + i = select_list(i + 1, select) + node += "on" + node["on"] = select + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + node += "from" + node["from"] = from + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + node += "where" + node["where"] = where + return i //select_list: select_item [',' select_list] - select_list(i, list/node) - i = select_item(i, node) - - if(token(i) == ",") - i = select_list(i + 1, node) - - return i - +/datum/SDQL_parser/proc/select_list(i, list/node) + i = select_item(i, node) + if(token(i) == ",") + i = select_list(i + 1, node) + return i //from_list: from_item [',' from_list] - from_list(i, list/node) - i = from_item(i, node) - - if(token(i) == ",") - i = from_list(i + 1, node) - - return i - +/datum/SDQL_parser/proc/from_list(i, list/node) + i = from_item(i, node) + if(token(i) == ",") + i = from_list(i + 1, node) + return i //assignments: assignment, [',' assignments] - assignments(i, list/node) - i = assignment(i, node) - - if(token(i) == ",") - i = assignments(i + 1, node) - - return i - +/datum/SDQL_parser/proc/assignments(i, list/node) + i = assignment(i, node) + if(token(i) == ",") + i = assignments(i + 1, node) + return i //select_item: '*' | select_function | object_type - select_item(i, list/node) - - if(token(i) == "*") - node += "*" - i++ - - else if(tokenl(i) in select_functions) - i = select_function(i, node) - - else - i = object_type(i, node) - - return i - +/datum/SDQL_parser/proc/select_item(i, list/node) + if(token(i) == "*") + node += "*" + i++ + else if(tokenl(i) in select_functions) + i = select_function(i, node) + else + i = object_type(i, node) + return i //from_item: 'world' | object_type - from_item(i, list/node) - - if(token(i) == "world") - node += "world" - i++ - - else - i = object_type(i, node) - - return i - +/datum/SDQL_parser/proc/from_item(i, list/node) + if(token(i) == "world") + node += "world" + i++ + else + i = object_type(i, node) + return i //bool_expression: expression [bool_operator bool_expression] - bool_expression(i, list/node) - - var/list/bool = list() - i = expression(i, bool) - - node[++node.len] = bool - - if(tokenl(i) in boolean_operators) - i = bool_operator(i, node) - i = bool_expression(i, node) - - return i - +/datum/SDQL_parser/proc/bool_expression(i, list/node) + var/list/bool = list() + i = expression(i, bool) + node[++node.len] = bool + if(tokenl(i) in boolean_operators) + i = bool_operator(i, node) + i = bool_expression(i, node) + return i //assignment: '=' expression - assignment(i, list/node) - - node += token(i) - - if(token(i + 1) == "=") - var/varname = token(i) - node[varname] = list() - - i = expression(i + 2, node[varname]) - - else - parse_error("Assignment expected, but no = found") - - return i - +/datum/SDQL_parser/proc/assignment(var/i, var/list/node, var/list/assignment_list = list()) + assignment_list += token(i) + if(token(i + 1) == ".") + i = assignment(i + 2, node, assignment_list) + else if(token(i + 1) == "=") + var/exp_list = list() + node[assignment_list] = exp_list + i = expression(i + 2, exp_list) + else + parse_error("Assignment expected, but no = found") + return i //variable: | '.' variable - variable(i, list/node) - var/list/L = list(token(i)) - node[++node.len] = L - - if(token(i + 1) == ".") - L += "." - i = variable(i + 2, L) - - else - i++ - - return i +/datum/SDQL_parser/proc/variable(i, list/node) + var/list/L = list(token(i)) + node[++node.len] = L + if(token(i) == "{") + L += token(i+1) + i += 2 + if(token(i) != "}") + parse_error("Missing } at end of pointer.") + if(token(i + 1) == ".") + L += "." + i = variable(i + 2, L) + else + i++ + return i +//array: '[' expression, expression, ... ']' +/datum/SDQL_parser/proc/array(i, list/node) + if(copytext(token(i), 1, 2) != "\[") + parse_error("Expected an array but found '[token(i)]'") + return i + 1 + node += token(i) + var/list/expression_list = list() + if(token(i + 1) != "]") + var/list/temp_expression_list = list() + do + i = expression(i + 1, temp_expression_list) + if(token(i) == ",") + expression_list[++expression_list.len] = temp_expression_list + temp_expression_list = list() + while(token(i) && token(i) != "]") + expression_list[++expression_list.len] = temp_expression_list + else + i++ + node[++node.len] = expression_list + return i + 1 //object_type: | string - object_type(i, list/node) - - if(copytext(token(i), 1, 2) == "/") - node += token(i) - - else - i = string(i, node) - - return i + 1 - +/datum/SDQL_parser/proc/object_type(i, list/node) + if(copytext(token(i), 1, 2) == "/") + node += token(i) + else + i = string(i, node) + return i + 1 //comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>=' - comparitor(i, list/node) - - if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">=")) - node += token(i) - - else - parse_error("Unknown comparitor [token(i)]") - - return i + 1 - +/datum/SDQL_parser/proc/comparitor(i, list/node) + if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">=")) + node += token(i) + else + parse_error("Unknown comparitor [token(i)]") + return i + 1 //bool_operator: 'AND' | '&&' | 'OR' | '||' - bool_operator(i, list/node) - - if(tokenl(i) in list("and", "or", "&&", "||")) - node += token(i) - - else - parse_error("Unknown comparitor [token(i)]") - - return i + 1 - +/datum/SDQL_parser/proc/bool_operator(i, list/node) + if(tokenl(i) in list("and", "or", "&&", "||")) + node += token(i) + else + parse_error("Unknown comparitor [token(i)]") + return i + 1 //string: ''' ''' | '"' '"' - string(i, list/node) - - if(copytext(token(i), 1, 2) in list("'", "\"")) - node += copytext(token(i),2,-1) - - else - parse_error("Expected string but found '[token(i)]'") - - return i + 1 - +/datum/SDQL_parser/proc/string(i, list/node) + if(copytext(token(i), 1, 2) in list("'", "\"")) + node += copytext(token(i),2,-1) + else + parse_error("Expected string but found '[token(i)]'") + return i + 1 //call_function: ['(' [arguments] ')'] - call_function(i, list/node, list/arguments) - if(length(tokenl(i))) - node += token(i++) - if(token(i) != "(") - parse_error("Expected ( but found '[token(i)]'") - else if(token(i + 1) != ")") - do - i = expression(i + 1, arguments) - if(token(i) == ",") - continue - while(token(i) && token(i) != ")") - else - i++ +/datum/SDQL_parser/proc/call_function(i, list/node, list/arguments) + if(length(tokenl(i))) + var/procname = "" + if(token(i) == "global" && token(i+1) == ".") + i += 2 + procname = "global." + node += procname + token(i++) + if(token(i) != "(") + parse_error("Expected ( but found '[token(i)]'") + else if(token(i + 1) != ")") + var/list/expression_list_temp = list() + do + i = expression(i + 1, expression_list_temp) + if(token(i) == ",") + arguments[++arguments.len] = expression_list_temp + expression_list_temp = list() + continue + while(token(i) && token(i) != ")") + arguments[++arguments.len] = expression_list_temp else - parse_error("Expected a function but found nothing") - return i + 1 - + i++ + else + parse_error("Expected a function but found nothing") + return i + 1 //select_function: count_function - select_function(i, list/node) - - parse_error("Sorry, function calls aren't available yet") - - return i - +/datum/SDQL_parser/proc/select_function(i, list/node) + parse_error("Sorry, function calls aren't available yet") + return i //expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression] - expression(i, list/node) - - if(token(i) in unary_operators) - i = unary_expression(i, node) - - else if(token(i) == "(") - var/list/expr = list() - - i = expression(i + 1, expr) - - if(token(i) != ")") - parse_error("Missing ) at end of expression.") - - else - i++ - - node[++node.len] = expr - +/datum/SDQL_parser/proc/expression(i, list/node) + if(token(i) in unary_operators) + i = unary_expression(i, node) + else if(token(i) == "(") + var/list/expr = list() + i = expression(i + 1, expr) + if(token(i) != ")") + parse_error("Missing ) at end of expression.") else - i = value(i, node) - - if(token(i) in binary_operators) - i = binary_operator(i, node) - i = expression(i, node) - - else if(token(i) in comparitors) - i = binary_operator(i, node) - - var/list/rhs = list() - i = expression(i, rhs) - - node[++node.len] = rhs - - - return i - + i++ + node[++node.len] = expr + else + i = value(i, node) + if(token(i) in binary_operators) + i = binary_operator(i, node) + i = expression(i, node) + else if(token(i) in comparitors) + i = binary_operator(i, node) + var/list/rhs = list() + i = expression(i, rhs) + node[++node.len] = rhs + return i //unary_expression: unary_operator ( unary_expression | value | '(' expression ')' ) - unary_expression(i, list/node) - +/datum/SDQL_parser/proc/unary_expression(i, list/node) + if(token(i) in unary_operators) + var/list/unary_exp = list() + unary_exp += token(i) + i++ if(token(i) in unary_operators) - var/list/unary_exp = list() - - unary_exp += token(i) - i++ - - if(token(i) in unary_operators) - i = unary_expression(i, unary_exp) - - else if(token(i) == "(") - var/list/expr = list() - - i = expression(i + 1, expr) - - if(token(i) != ")") - parse_error("Missing ) at end of expression.") - - else - i++ - - unary_exp[++unary_exp.len] = expr - + i = unary_expression(i, unary_exp) + else if(token(i) == "(") + var/list/expr = list() + i = expression(i + 1, expr) + if(token(i) != ")") + parse_error("Missing ) at end of expression.") else - i = value(i, unary_exp) - - node[++node.len] = unary_exp - - + i++ + unary_exp[++unary_exp.len] = expr else - parse_error("Expected unary operator but found '[token(i)]'") - - return i - + i = value(i, unary_exp) + node[++node.len] = unary_exp + else + parse_error("Expected unary operator but found '[token(i)]'") + return i //binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' - binary_operator(i, list/node) - - if(token(i) in (binary_operators + comparitors)) - node += token(i) - - else - parse_error("Unknown binary operator [token(i)]") - - return i + 1 - +/datum/SDQL_parser/proc/binary_operator(i, list/node) + if(token(i) in (binary_operators + comparitors)) + node += token(i) + else + parse_error("Unknown binary operator [token(i)]") + return i + 1 //value: variable | string | number | 'null' - value(i, list/node) +/datum/SDQL_parser/proc/value(i, list/node) + if(token(i) == "null") + node += "null" + i++ + else if(lowertext(copytext(token(i),1,3)) == "0x" && isnum(hex2num(copytext(token(i),3)))) + node += hex2num(copytext(token(i),3)) + i++ + else if(isnum(text2num(token(i)))) + node += text2num(token(i)) + i++ + else if(copytext(token(i), 1, 2) in list("'", "\"")) + i = string(i, node) + else if(copytext(token(i), 1, 2) == "\[") // Start a list. + i = array(i, node) + else + i = variable(i, node) + return i - if(token(i) == "null") - node += "null" - i++ - - else if(isnum(text2num(token(i)))) - node += text2num(token(i)) - i++ - - else if(copytext(token(i), 1, 2) in list("'", "\"")) - i = string(i, node) - - else - i = variable(i, node) - - return i - - - - -/*EXPLAIN SELECT * WHERE 42 = 6 * 9 OR val = - 5 == 7*/ \ No newline at end of file +/*EXPLAIN SELECT * WHERE 42 = 6 * 9 OR val = - 5 == 7*/ diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm new file mode 100644 index 0000000000..286e3a890f --- /dev/null +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm @@ -0,0 +1,174 @@ +// Wrappers for BYOND default procs which can't directly be called by call(). + +/proc/_abs(A) + return abs(A) + +/*/proc/_animate(atom/A, set_vars, time = 10, loop = 1, easing = LINEAR_EASING, flags = null) + animate(A, set_vars, time, loop, easing, flags) + Borked. If anyone wants to fix this be my guest.*/ + +/proc/_acrccos(A) + return arccos(A) + +/proc/_arcsin(A) + return arcsin(A) + +/proc/_ascii2text(A) + return ascii2text(A) + +/proc/_block(Start, End) + return block(Start, End) + +/proc/_ckey(Key) + return ckey(Key) + +/proc/_ckeyEx(Key) + return ckeyEx(Key) + +/proc/_copytext(T, Start = 1, End = 0) + return copytext(T, Start, End) + +/proc/_cos(X) + return cos(X) + +/proc/_get_dir(Loc1, Loc2) + return get_dir(Loc1, Loc2) + +/proc/_get_dist(Loc1, Loc2) + return get_dist(Loc1, Loc2) + +/proc/_get_step(Ref, Dir) + return get_step(Ref, Dir) + +/proc/_hearers(Depth = world.view, Center = usr) + return hearers(Depth, Center) + +/proc/_image(icon, loc, icon_state, layer, dir) + return image(icon, loc, icon_state, layer, dir) + +/proc/_length(E) + return length(E) + +/proc/_link(thing, url) + thing << link(url) + +/proc/_locate(X, Y, Z) + if (isnull(Y)) // Assuming that it's only a single-argument call. + return locate(X) + + return locate(X, Y, Z) + +/proc/_log(X, Y) + return log(X, Y) + +/proc/_lowertext(T) + return lowertext(T) + +/proc/_matrix(a, b, c, d, e, f) + return matrix(a, b, c, d, e, f) + +/proc/_max(...) + return max(arglist(args)) + +/proc/_md5(T) + return md5(T) + +/proc/_min(...) + return min(arglist(args)) + +/proc/_new(type, arguments) + return new type (arglist(arguments)) + +/proc/_num2text(N, SigFig = 6) + return num2text(N, SigFig) + +/proc/_ohearers(Dist, Center = usr) + return ohearers(Dist, Center) + +/proc/_orange(Dist, Center = usr) + return orange(Dist, Center) + +/proc/_output(thing, msg, control) + thing << output(msg, control) + +/proc/_oview(Dist, Center = usr) + return oview(Dist, Center) + +/proc/_oviewers(Dist, Center = usr) + return oviewers(Dist, Center) + +/proc/_params2list(Params) + return params2list(Params) + +/proc/_pick(...) + return pick(arglist(args)) + +/proc/_prob(P) + return prob(P) + +/proc/_rand(L = 0, H = 1) + return rand(L, H) + +/proc/_range(Dist, Center = usr) + return range(Dist, Center) + +/proc/_regex(pattern, flags) + return regex(pattern, flags) + +/proc/_REGEX_QUOTE(text) + return REGEX_QUOTE(text) + +/proc/_REGEX_QUOTE_REPLACEMENT(text) + return REGEX_QUOTE_REPLACEMENT(text) + +/proc/_replacetext(Haystack, Needle, Replacement, Start = 1,End = 0) + return replacetext(Haystack, Needle, Replacement, Start, End) + +/proc/_replacetextEx(Haystack, Needle, Replacement, Start = 1,End = 0) + return replacetextEx(Haystack, Needle, Replacement, Start, End) + +/proc/_rgb(R, G, B) + return rgb(R, G, B) + +/proc/_rgba(R, G, B, A) + return rgb(R, G, B, A) + +/proc/_roll(dice) + return roll(dice) + +/proc/_round(A, B = 1) + return round(A, B) + +/proc/_sin(X) + return sin(X) + +/proc/_step(Ref, Dir, Speed = 0) + return step(Ref, Dir, Speed) + +/proc/_list_add(list/L, ...) + if (args.len < 2) + return + L += args.Copy(2) + +/proc/_list_copy(list/L, Start = 1, End = 0) + return L.Copy(Start, End) + +/proc/_list_cut(list/L, Start = 1, End = 0) + L.Cut(Start, End) + +/proc/_list_find(list/L, Elem, Start = 1, End = 0) + return L.Find(Elem, Start, End) + +/proc/_list_insert(list/L, Index, Item) + return L.Insert(Index, Item) + +/proc/_list_join(list/L, Glue, Start = 0, End = 1) + return L.Join(Glue, Start, End) + +/proc/_list_remove(list/L, ...) + if (args.len < 2) + return + L -= args.Copy(2) + +/proc/_list_swap(list/L, Index1, Index2) + L.Swap(Index1, Index2) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index e08919d9fc..1ebe4e4fd1 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -79,6 +79,7 @@ if(src.handle_spam_prevention(msg,MUTE_ADMINHELP)) return var/ref_mob = "\ref[mob]" + var/ref_client = "\ref[src]" for(var/datum/adminticket/T in admintickets) if(T.permckey == src.ckey && T.resolved == "No") if(alert(usr,"You already have an adminhelp open, would you like to bump it?", "Bump Adminhelp", "Yes", "No") == "Yes") @@ -86,7 +87,7 @@ if(T.admin == "N/A") usr << "Due to the fact your Adminhelp had no assigned admin, admins have been pinged." message_admins("[src.ckey] has bumped their adminhelp #[T.ID], still no assigned admin!") - msg = "ADMINHELP: [key_name(src)] (?) (PP) (VV) (SM) (TP) (FLW) (R) (REJT): [T.msg]" + msg = "HELP: [key_name(src)] [ADMIN_QUE(mob)] [ADMIN_PP(mob)] [ADMIN_VV(mob)] [ADMIN_SM(mob)] [ADMIN_FLW(mob)] [ADMIN_TP(mob)] (REJT) (IC) (R): [msg]" for(var/client/X in admins) if(X.prefs.toggles & SOUND_ADMINHELP) X << 'sound/effects/adminhelp.ogg' diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index dd3ee471e9..b8593ff877 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -33,10 +33,7 @@ prayer_type = "CULTIST PRAYER" deity = "Nar-Sie" - msg = "\icon[cross] \ - [prayer_type][deity ? " (to [deity])" : ""]: \ - [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: \ - [msg]" + msg = "\icon[cross][prayer_type][deity ? " (to [deity])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)] [ADMIN_SMITE(src)]: [msg]" for(var/client/C in admins) if(C.prefs.chat_toggles & CHAT_PRAYER) diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index f46d9e3537..5cb6a24e26 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -105,7 +105,7 @@ Code: user << "You transfer the frequency and code of \the [signaler2.name] to \the [name]" else ..() - + /obj/item/device/assembly/signaler/proc/signal() if(!radio_connection) return @@ -194,3 +194,9 @@ Code: /obj/item/device/assembly/signaler/anomaly/attack_self() return + +/obj/item/device/assembly/signaler/cyborg + origin_tech = null + +/obj/item/device/assembly/signaler/cyborg/attackby(obj/item/weapon/W, mob/user, params) + return diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index c56bccba62..b546c0d1a3 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -97,7 +97,7 @@ wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt) wizmind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile) - wizmind.AddSpell(new /obj/effect/proc_holder/spell/fireball) + wizmind.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball) current_wizard = wizbody diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index accff361b1..9fdad1b94e 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1,8 +1,8 @@ //This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped. -#define SAVEFILE_VERSION_MIN 11 +#define SAVEFILE_VERSION_MIN 10 //This is the current version, anything below this will attempt to update (if it's not obsolete) -#define SAVEFILE_VERSION_MAX 18 +#define SAVEFILE_VERSION_MAX 17 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn This proc checks if the current directory of the savefile S needs updating diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index ef8e548b00..de03809c75 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -11,9 +11,9 @@ ..() /obj/item/clothing/suit/hooded/Destroy() + . = ..() qdel(hood) hood = null - return ..() /obj/item/clothing/suit/hooded/proc/MakeHood() if(!hood) diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index c26a98d874..93ad51bd30 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -75,35 +75,29 @@ item_state = "y_suit" item_color = "yellow" -/obj/item/clothing/under/color/lightblue - name = "lightblue jumpsuit" - icon_state = "lightblue" +/obj/item/clothing/under/color/darkblue + name = "darkblue jumpsuit" + icon_state = "darkblue" item_state = "b_suit" - item_color = "lightblue" + item_color = "darkblue" -/obj/item/clothing/under/color/aqua - name = "aqua jumpsuit" - icon_state = "aqua" +/obj/item/clothing/under/color/teal + name = "teal jumpsuit" + icon_state = "teal" item_state = "b_suit" - item_color = "aqua" - -/obj/item/clothing/under/color/purple - name = "purple jumpsuit" - icon_state = "purple" - item_state = "p_suit" - item_color = "purple" + item_color = "teal" /obj/item/clothing/under/color/lightpurple - name = "lightpurple jumpsuit" + name = "purple jumpsuit" icon_state = "lightpurple" item_state = "p_suit" item_color = "lightpurple" -/obj/item/clothing/under/color/lightgreen - name = "lightgreen jumpsuit" - icon_state = "lightgreen" +/obj/item/clothing/under/color/darkgreen + name = "darkgreen jumpsuit" + icon_state = "darkgreen" item_state = "g_suit" - item_color = "lightgreen" + item_color = "darkgreen" /obj/item/clothing/under/color/lightbrown name = "lightbrown jumpsuit" @@ -117,30 +111,6 @@ item_state = "lb_suit" item_color = "brown" -/obj/item/clothing/under/color/yellowgreen - name = "yellowgreen jumpsuit" - icon_state = "yellowgreen" - item_state = "y_suit" - item_color = "yellowgreen" - -/obj/item/clothing/under/color/darkblue - name = "darkblue jumpsuit" - icon_state = "darkblue" - item_state = "b_suit" - item_color = "darkblue" - -/obj/item/clothing/under/color/lightred - name = "lightred jumpsuit" - icon_state = "lightred" - item_state = "r_suit" - item_color = "lightred" - -/obj/item/clothing/under/color/darkred - name = "darkred jumpsuit" - icon_state = "darkred" - item_state = "r_suit" - item_color = "darkred" - /obj/item/clothing/under/color/maroon name = "maroon jumpsuit" icon_state = "maroon" diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm index bcb215ff93..9530ee0f2c 100644 --- a/code/modules/events/holiday/vday.dm +++ b/code/modules/events/holiday/vday.dm @@ -36,6 +36,9 @@ forge_valentines_objective(date, L) + if(valentines.len && prob(4)) + var/mob/living/notgoodenough = pick_n_take(valentines) + forge_valentines_objective(notgoodenough, date) else diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index bfbf23b1b1..085a490b30 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -4,7 +4,7 @@ /datum/round_event_control/ion_storm name = "Ion Storm" typepath = /datum/round_event/ion_storm - weight = 3 + weight = 15 min_players = 2 /datum/round_event/ion_storm @@ -545,7 +545,7 @@ message = "ALL [ionthreats] ARE NOW NAMED [ionspecies]." if(4) message = "ALL [ionthreats] ARE NOW NAMED [ionobjects]." - + return message #undef ION_RANDOM diff --git a/code/modules/events/wizard/magicarp.dm b/code/modules/events/wizard/magicarp.dm index 0266304728..81fa857f3c 100644 --- a/code/modules/events/wizard/magicarp.dm +++ b/code/modules/events/wizard/magicarp.dm @@ -37,9 +37,12 @@ projectilesound = 'sound/weapons/emitter.ogg' maxHealth = 50 health = 50 - + var/allowed_projectile_types = list(/obj/item/projectile/magic/change, /obj/item/projectile/magic/animate, /obj/item/projectile/magic/resurrection, + /obj/item/projectile/magic/death, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball, + /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage) + /mob/living/simple_animal/hostile/carp/ranged/New() - projectiletype = pick(typesof(initial(projectiletype))) + projectiletype = pick(allowed_projectile_types) ..() /mob/living/simple_animal/hostile/carp/ranged/chaos @@ -50,5 +53,5 @@ health = 75 /mob/living/simple_animal/hostile/carp/ranged/chaos/Shoot() - projectiletype = pick(typesof(initial(projectiletype))) - ..() \ No newline at end of file + projectiletype = pick(allowed_projectile_types) + ..() diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm index d437246ab3..e2a8368238 100644 --- a/code/modules/jobs/job_types/medical.dm +++ b/code/modules/jobs/job_types/medical.dm @@ -58,7 +58,7 @@ Medical Doctor outfit = /datum/outfit/job/doctor - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_cloning, access_mineral_storeroom) + access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_genetics, access_cloning, access_mineral_storeroom) minimal_access = list(access_medical, access_morgue, access_surgery, access_cloning) /datum/outfit/job/doctor @@ -93,7 +93,7 @@ Chemist outfit = /datum/outfit/job/chemist - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_cloning, access_mineral_storeroom) + access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_genetics, access_cloning, access_mineral_storeroom) minimal_access = list(access_medical, access_chemistry, access_mineral_storeroom) /datum/outfit/job/chemist @@ -127,7 +127,7 @@ Geneticist outfit = /datum/outfit/job/geneticist - access = list(access_medical, access_morgue, access_chemistry, access_virology, access_genetics, access_cloning, access_research, access_xenobiology, access_robotics, access_mineral_storeroom, access_tech_storage) + access = list(access_medical, access_morgue, access_chemistry, access_genetics, access_cloning, access_research, access_xenobiology, access_robotics, access_mineral_storeroom, access_tech_storage) minimal_access = list(access_medical, access_morgue, access_genetics, access_cloning, access_research) /datum/outfit/job/geneticist diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm index a512f12125..a313d6aa0f 100644 --- a/code/modules/library/soapstone.dm +++ b/code/modules/library/soapstone.dm @@ -1,22 +1,40 @@ +#define SOAPSTONE_PREFIX_FILE "strings/soapstone_prefixes.txt" +#define SOAPSTONE_SUFFIX_FILE "soapstone_suffixes.json" +//Vocabulary lists; soapstones use a prefix and a suffix. Optionally, they can have a prefix and suffix, then a conjunction that links another set. +var/global/list/soapstone_prefixes = list() //Read from "strings/soapstone_prefixes.txt"; if you're adding your own, put **** where the subject should be! +var/global/list/soapstone_suffixes = list() //Read from "strings/soapstone_suffixes.json" /obj/item/soapstone name = "chisel" - desc = "Leave informative messages for the crew, including the crew of future shifts!\nEven if out of uses, it can still be used to remove messages.\n(Not suitable for engraving on shuttles, off station or on cats. Side effects may include beatings, bannings and orbital bombardment.)" + desc = "Leave \"informative\" messages for the crew, including the crew of future shifts!\n\ + (Not suitable for engraving on shuttles, off station or on cats. Side effects may include beatings, bannings and orbital bombardment.)" icon = 'icons/obj/items.dmi' icon_state = "soapstone" throw_speed = 3 throw_range = 5 w_class = WEIGHT_CLASS_TINY - var/tool_speed = 50 var/remaining_uses = 3 var/non_dull_name - var/w_engrave = "engrave" - var/w_engraving = "engraving" - var/w_chipping = "chipping" var/w_dull = "dull" /obj/item/soapstone/New() . = ..() + if(!soapstone_prefixes.len) + soapstone_prefixes = file2list(SOAPSTONE_PREFIX_FILE, "\n") + if(!soapstone_suffixes.len) + soapstone_suffixes = list(\ + "Characters" = strings(SOAPSTONE_SUFFIX_FILE, "Characters"), \ + "Careers" = strings(SOAPSTONE_SUFFIX_FILE, "Careers"), \ + "Antagonists" = strings(SOAPSTONE_SUFFIX_FILE, "Antagonists"), \ + "Objects" = strings(SOAPSTONE_SUFFIX_FILE, "Objects"), \ + "Techniques" = strings(SOAPSTONE_SUFFIX_FILE, "Techniques"), \ + "Actions" = strings(SOAPSTONE_SUFFIX_FILE, "Actions"), \ + "Geography" = strings(SOAPSTONE_SUFFIX_FILE, "Geography"), \ + "Orientation" = strings(SOAPSTONE_SUFFIX_FILE, "Orientation"), \ + "Body parts" = strings(SOAPSTONE_SUFFIX_FILE, "Body parts"), \ + "Concepts" = strings(SOAPSTONE_SUFFIX_FILE, "Concepts"), \ + "Musings" = strings(SOAPSTONE_SUFFIX_FILE, "Musings"), \ + ) random_name() check_name() // could start empty @@ -25,9 +43,6 @@ non_dull_name = name if(name == "chalk" || name == "magic marker") desc = replacetext(desc, "engraving", "scribbling") - w_engrave = "scribble" - w_engraving = "scribbling" - w_chipping = "sketching" if(name == "chalk") w_dull = "used" if(name == "magic marker") @@ -35,9 +50,6 @@ if(name == "soapstone" || name == "chisel") desc = replacetext(desc, "scribbling", "engraving") - w_engrave = initial(w_engrave) - w_engraving = initial(w_engraving) - w_chipping = initial(w_chipping) w_dull = "dull" /obj/item/soapstone/examine(mob/user) @@ -48,59 +60,48 @@ user << "It looks like it can be used an unlimited number of times." /obj/item/soapstone/afterattack(atom/target, mob/user, proximity) + if(!remaining_uses) + user << "[src] is [w_dull] and can't be used anymore!" + return var/turf/T = get_turf(target) if(!proximity) return - - - var/obj/structure/chisel_message/already_message = locate(/obj/structure/chisel_message) in T - - var/our_message = FALSE - if(already_message) - our_message = already_message.creator_key == user.ckey - - if(!remaining_uses && !already_message) - // The dull chisel is dull. - user << "[src] is [w_dull]." - return - - if(!good_chisel_message_location(T)) - user << "It's not appropriate to [w_engrave] on [T]." - return - - if(already_message) - user.visible_message("[user] starts erasing [already_message].", "You start erasing [already_message].", "You hear a [w_chipping] sound.") - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) - - // Removing our own messages refunds a charge - - if(do_after(user, tool_speed, target=target)) - user.visible_message("[user] has erased [already_message].", "You erased [already_message].") - already_message.persists = FALSE - qdel(already_message) - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) - if(our_message) + var/obj/structure/chisel_message/msg = locate() in T + if(msg) + if(msg.creator_key != user.ckey) + user << "There's already a message there!" + return + else + if(alert(user, "Erase this message?", name, "Yes", "No") == "Yes") + user.visible_message("[user] erases [msg].", "You permanently erase [msg].") + playsound(T, 'sound/items/gavel.ogg', 50, 1) refund_use() + msg.persists = 0 + qdel(msg) + return + return + if(!good_chisel_message_location(T)) + user << "You can't write there!" return - - var/message = stripped_input(user, "What would you like to [w_engrave]?", "[name] Message") - if(!message) - user << "You decide not to [w_engrave] anything." + var/prefix = input(user, "Choose a prefix for your message.", name) as null|anything in soapstone_prefixes + if(!prefix) return - - if(!target.Adjacent(user) && locate(/obj/structure/chisel_message) in T) - user << "You decide not to [w_engrave] anything." + var/suffix_category_string = input(user, "Choose a suffix category.", "[prefix]...") as null|anything in soapstone_suffixes + var/list/suffix_category = soapstone_suffixes[suffix_category_string] + if(!suffix_category || !suffix_category.len) return - - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) - user.visible_message("[user] starts [w_engraving] a message into [T].", "You start [w_engraving] a message into [T].", "You hear a [w_chipping] sound.") - if(can_use() && do_after(user, tool_speed, target=T) && can_use()) - if(!locate(/obj/structure/chisel_message in T)) - user << "You [w_engrave] a message into [T]." - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) - var/obj/structure/chisel_message/M = new(T) - M.register(user, message) - remove_use() + var/suffix = input(user, "Choose a suffix.", "[prefix]...") as null|anything in suffix_category + if(!suffix) + return + var/processed_message = replacetext(prefix, "****", suffix) + if(!user.Adjacent(T) || !good_chisel_message_location(T) || locate(/obj/structure/chisel_message) in T) + return + user.visible_message("[user] writes a message onto [T]!", "You write a message onto [T].") + playsound(T, 'sound/items/gavel.ogg', 50, 1) + var/obj/structure/chisel_message/M = new(T) + M.register(user, processed_message) + remove_use() + return 1 /obj/item/soapstone/proc/can_use() if(remaining_uses == -1 || remaining_uses >= 0) @@ -171,6 +172,34 @@ var/map var/persists = TRUE + var/positive_ratings = 0 + var/negative_ratings = 0 + + var/list/raters = list() //Ckeys who have rated this message + +/obj/structure/chisel_message/attack_hand(mob/user) + if(user.ckey == creator_key) + user << "You can't rate your own messages!" + return + if(raters[user.ckey]) + user << "You've already rated this message!" + return + switch(alert(user, "How would you like to rate this message?", "Message Rating", "Positive", "Negative", "Cancel")) + if("Positive") + for(var/client/C in clients) + if(C.ckey == creator_key) + C.mob << "One of your messages was rated as positive!" + user << "You rated this message as positive." + positive_ratings++ + raters[user.ckey] = "positive" + if("Negative") + for(var/client/C in clients) + if(C.ckey == creator_key) + C.mob << "One of your messages was rated as negative!" + user << "You rated this message as negative." + negative_ratings++ + raters[user.ckey] = "negative" + /obj/structure/chisel_message/New(newloc) ..() SSpersistence.chisel_messages += src @@ -190,12 +219,6 @@ map = MAP_NAME update_icon() -/obj/structure/chisel_message/update_icon() - ..() - var/hash = md5(hidden_message) - var/newcolor = copytext(hash, 1, 7) - add_atom_colour("#[newcolor]", FIXED_COLOUR_PRIORITY) - /obj/structure/chisel_message/proc/pack() var/list/data = list() data["hidden_message"] = hidden_message @@ -206,6 +229,9 @@ var/turf/T = get_turf(src) data["x"] = T.x data["y"] = T.y + data["pos_ratings"] = positive_ratings + data["neg_ratings"] = positive_ratings + data["raters"] = raters return data /obj/structure/chisel_message/proc/unpack(list/data) @@ -213,6 +239,9 @@ creator_name = data["creator_name"] creator_key = data["creator_key"] realdate = data["realdate"] + positive_ratings = data["pos_ratings"] + negative_ratings = data["neg_ratings"] + raters = data["raters"] var/x = data["x"] var/y = data["y"] @@ -222,7 +251,10 @@ /obj/structure/chisel_message/examine(mob/user) ..() - user << "[hidden_message]" + user << "[hidden_message]" + user << "Ratings: [positive_ratings] [negative_ratings]" + if(raters[user.ckey]) + user << "You rated this message as [raters[user.ckey]]." /obj/structure/chisel_message/Destroy() if(persists) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 313aef8fd3..70592e6c62 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -169,6 +169,8 @@ ..() /mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0) + if(tesla_shock && tesla_ignore) + return FALSE shock_damage *= siemens_coeff if(dna && dna.species) shock_damage *= dna.species.siemens_coeff diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index cbb711ffc2..5b7e2e28c6 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -448,6 +448,8 @@ if(S.siemens_coefficient <= 0) total_coeff -= 0.95 siemens_coeff = total_coeff + if(tesla_ignore) + siemens_coeff = 0 else if(!safety) var/gloves_siemens_coeff = 1 if(gloves) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index ac59c61ba8..ac631e8095 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -85,9 +85,13 @@ message_alien = "lets out a waning guttural screech, green blood bubbling from its maw..." message_larva = "lets out a sickly hiss of air and falls limply to the floor..." message_monkey = "lets out a faint chimper as it collapses and stops moving..." + message_simple = "stops moving..." stat_allowed = UNCONSCIOUS /datum/emote/living/deathgasp/run_emote(mob/user, params) + var/mob/living/simple_animal/S = user + if(istype(S) && S.deathmessage) + message_simple = S.deathmessage . = ..() if(. && isalienadult(user)) playsound(user.loc, 'sound/voice/hiss6.ogg', 80, 1, 1) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b4261c77ce..8486f85145 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -276,6 +276,8 @@ /mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0) + if(tesla_shock && tesla_ignore) + return FALSE if(shock_damage > 0) if(!illusion) adjustFireLoss(shock_damage) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 8105c4e13f..033916892b 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -74,4 +74,5 @@ var/list/status_effects //a list of all status effects the mob has - var/list/implants = null \ No newline at end of file + var/list/implants = null + var/tesla_ignore = FALSE \ No newline at end of file diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm index a2e0812922..dc31ff23d1 100644 --- a/code/modules/mob/living/silicon/laws.dm +++ b/code/modules/mob/living/silicon/laws.dm @@ -45,6 +45,11 @@ laws_sanity_check() laws.replace_random_law(law,groups) +/mob/living/silicon/proc/remove_law(number) + throw_alert("newlaw", /obj/screen/alert/newlaw) + laws_sanity_check() + laws.remove_law(number) + /mob/living/silicon/proc/clear_ion_laws() throw_alert("newlaw", /obj/screen/alert/newlaw) laws_sanity_check() diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 59e0106531..6b04e762bf 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -220,27 +220,47 @@ /obj/item/weapon/robot_module/standard name = "Standard" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/weapon/reagent_containers/borghypo/epi, /obj/item/device/healthanalyzer, \ - /obj/item/weapon/weldingtool/largetank/cyborg, /obj/item/weapon/wrench/cyborg, /obj/item/weapon/crowbar/cyborg, \ - /obj/item/stack/sheet/metal/cyborg, /obj/item/weapon/extinguisher, /obj/item/weapon/pickaxe, \ - /obj/item/device/t_scanner/adv_mining_scanner, /obj/item/weapon/restraints/handcuffs/cable/zipties/cyborg, \ - /obj/item/weapon/soap/nanotrasen, /obj/item/borg/cyborghug) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/weapon/reagent_containers/borghypo/epi, + /obj/item/weapon/crowbar/cyborg, + /obj/item/weapon/extinguisher, + /obj/item/weapon/soap/nanotrasen, + /obj/item/borg/cyborghug) emag_modules = list(/obj/item/weapon/melee/energy/sword/cyborg) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg, /obj/item/clockwork/ratvarian_spear/cyborg, /obj/item/clockwork/clockwork_proselytizer/cyborg) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg, + /obj/item/clockwork/ratvarian_spear/cyborg, + /obj/item/clockwork/clockwork_proselytizer/cyborg) moduleselect_icon = "standard" feedback_key = "cyborg_standard" hat_offset = -3 /obj/item/weapon/robot_module/medical name = "Medical" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/device/healthanalyzer, /obj/item/weapon/reagent_containers/borghypo, \ - /obj/item/weapon/reagent_containers/glass/beaker/large, /obj/item/weapon/reagent_containers/dropper, \ - /obj/item/weapon/reagent_containers/syringe, /obj/item/weapon/surgical_drapes, /obj/item/weapon/retractor, \ - /obj/item/weapon/hemostat, /obj/item/weapon/cautery, /obj/item/weapon/surgicaldrill, /obj/item/weapon/scalpel, \ - /obj/item/weapon/circular_saw, /obj/item/weapon/extinguisher/mini, /obj/item/roller/robo, /obj/item/borg/cyborghug/medical, \ - /obj/item/stack/medical/gauze/cyborg, /obj/item/borg/lollipop) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/device/healthanalyzer, + /obj/item/weapon/reagent_containers/borghypo, + /obj/item/weapon/reagent_containers/glass/beaker/large, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/weapon/surgical_drapes, + /obj/item/weapon/retractor, + /obj/item/weapon/hemostat, + /obj/item/weapon/cautery, + /obj/item/weapon/surgicaldrill, + /obj/item/weapon/scalpel, + /obj/item/weapon/circular_saw, + /obj/item/weapon/extinguisher/mini, + /obj/item/roller/robo, + /obj/item/borg/cyborghug/medical, + /obj/item/stack/medical/gauze/cyborg, + /obj/item/borg/lollipop) emag_modules = list(/obj/item/weapon/reagent_containers/borghypo/hacked) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/medical, /obj/item/clockwork/ratvarian_spear/cyborg) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/medical, + /obj/item/clockwork/ratvarian_spear/cyborg) cyborg_base_icon = "medical" moduleselect_icon = "medical" feedback_key = "cyborg_medical" @@ -249,14 +269,32 @@ /obj/item/weapon/robot_module/engineering name = "Engineering" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/borg/sight/meson, /obj/item/weapon/rcd/borg, /obj/item/weapon/pipe_dispenser, \ - /obj/item/weapon/extinguisher, /obj/item/weapon/weldingtool/largetank/cyborg, /obj/item/weapon/screwdriver/cyborg, \ - /obj/item/weapon/wrench/cyborg, /obj/item/weapon/crowbar/cyborg, /obj/item/weapon/wirecutters/cyborg, \ - /obj/item/device/multitool/cyborg, /obj/item/device/t_scanner, /obj/item/device/analyzer, \ - /obj/item/areaeditor/blueprints/cyborg, /obj/item/stack/sheet/metal/cyborg, /obj/item/stack/sheet/glass/cyborg, \ - /obj/item/stack/sheet/rglass/cyborg, /obj/item/stack/rods/cyborg, /obj/item/stack/tile/plasteel/cyborg, /obj/item/stack/cable_coil/cyborg) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/borg/sight/meson, + /obj/item/weapon/rcd/borg, + /obj/item/weapon/pipe_dispenser, + /obj/item/weapon/extinguisher, + /obj/item/weapon/weldingtool/largetank/cyborg, + /obj/item/weapon/screwdriver/cyborg, + /obj/item/weapon/wrench/cyborg, + /obj/item/weapon/crowbar/cyborg, + /obj/item/weapon/wirecutters/cyborg, + /obj/item/device/multitool/cyborg, + /obj/item/device/t_scanner, + /obj/item/device/analyzer, + /obj/item/device/assembly/signaler/cyborg, + /obj/item/areaeditor/blueprints/cyborg, + /obj/item/stack/sheet/metal/cyborg, + /obj/item/stack/sheet/glass/cyborg, + /obj/item/stack/sheet/rglass/cyborg, + /obj/item/stack/rods/cyborg, + /obj/item/stack/tile/plasteel/cyborg, + /obj/item/stack/cable_coil/cyborg) emag_modules = list(/obj/item/borg/stun) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/engineer, /obj/item/clockwork/clockwork_proselytizer/cyborg) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/engineer, + /obj/item/clockwork/clockwork_proselytizer/cyborg) cyborg_base_icon = "engineer" moduleselect_icon = "engineer" feedback_key = "cyborg_engineering" @@ -265,10 +303,15 @@ /obj/item/weapon/robot_module/security name = "Security" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/weapon/restraints/handcuffs/cable/zipties/cyborg, /obj/item/weapon/melee/baton/loaded, \ - /obj/item/weapon/gun/energy/disabler/cyborg, /obj/item/clothing/mask/gas/sechailer/cyborg) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/weapon/restraints/handcuffs/cable/zipties/cyborg, + /obj/item/weapon/melee/baton/loaded, + /obj/item/weapon/gun/energy/disabler/cyborg, + /obj/item/clothing/mask/gas/sechailer/cyborg) emag_modules = list(/obj/item/weapon/gun/energy/laser/cyborg) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/security, /obj/item/clockwork/ratvarian_spear/cyborg) + ratvar_modules = list(/obj/item/clockwork/slab/cyborg/security, + /obj/item/clockwork/ratvarian_spear/cyborg) cyborg_base_icon = "sec" moduleselect_icon = "security" feedback_key = "cyborg_security" @@ -293,10 +336,18 @@ /obj/item/weapon/robot_module/peacekeeper name = "Peacekeeper" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/weapon/cookiesynth, /obj/item/device/harmalarm, /obj/item/weapon/reagent_containers/borghypo/peace, \ - /obj/item/weapon/holosign_creator/cyborg, /obj/item/borg/cyborghug/peacekeeper, /obj/item/weapon/extinguisher) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/weapon/cookiesynth, + /obj/item/device/harmalarm, + /obj/item/weapon/reagent_containers/borghypo/peace, + /obj/item/weapon/holosign_creator/cyborg, + /obj/item/borg/cyborghug/peacekeeper, + /obj/item/weapon/extinguisher) emag_modules = list(/obj/item/weapon/reagent_containers/borghypo/peace/hacked) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/peacekeeper, /obj/item/clockwork/ratvarian_spear/cyborg) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/peacekeeper, + /obj/item/clockwork/ratvarian_spear/cyborg) cyborg_base_icon = "peace" moduleselect_icon = "standard" feedback_key = "cyborg_peacekeeper" @@ -310,10 +361,18 @@ /obj/item/weapon/robot_module/janitor name = "Janitor" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/weapon/soap/nanotrasen, /obj/item/weapon/storage/bag/trash/cyborg, /obj/item/weapon/mop/cyborg, \ - /obj/item/device/lightreplacer/cyborg, /obj/item/weapon/holosign_creator, /obj/item/weapon/reagent_containers/spray/cyborg_drying) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/weapon/soap/nanotrasen, + /obj/item/weapon/storage/bag/trash/cyborg, + /obj/item/weapon/mop/cyborg, + /obj/item/device/lightreplacer/cyborg, + /obj/item/weapon/holosign_creator, + /obj/item/weapon/reagent_containers/spray/cyborg_drying) emag_modules = list(/obj/item/weapon/reagent_containers/spray/cyborg_lube) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/janitor, /obj/item/clockwork/clockwork_proselytizer/cyborg) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/janitor, + /obj/item/clockwork/clockwork_proselytizer/cyborg) cyborg_base_icon = "janitor" moduleselect_icon = "janitor" feedback_key = "cyborg_janitor" @@ -345,12 +404,25 @@ /obj/item/weapon/robot_module/butler name = "Service" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/weapon/reagent_containers/food/drinks/drinkingglass, /obj/item/weapon/reagent_containers/food/condiment/enzyme, \ - /obj/item/weapon/pen, /obj/item/toy/crayon/spraycan/borg, /obj/item/weapon/hand_labeler/borg, /obj/item/weapon/razor, \ - /obj/item/device/instrument/violin, /obj/item/device/instrument/guitar, /obj/item/weapon/rsf/cyborg, /obj/item/weapon/reagent_containers/dropper, \ - /obj/item/weapon/lighter, /obj/item/weapon/storage/bag/tray, /obj/item/weapon/reagent_containers/borghypo/borgshaker, /obj/item/borg/lollipop) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/weapon/reagent_containers/food/drinks/drinkingglass, + /obj/item/weapon/reagent_containers/food/condiment/enzyme, + /obj/item/weapon/pen, + /obj/item/toy/crayon/spraycan/borg, + /obj/item/weapon/hand_labeler/borg, + /obj/item/weapon/razor, + /obj/item/device/instrument/violin, + /obj/item/device/instrument/guitar, + /obj/item/weapon/rsf/cyborg, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/lighter, + /obj/item/weapon/storage/bag/tray, + /obj/item/weapon/reagent_containers/borghypo/borgshaker, + /obj/item/borg/lollipop) emag_modules = list(/obj/item/weapon/reagent_containers/borghypo/borgshaker/hacked) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/service, /obj/item/borg/sight/xray/truesight_lens) + ratvar_modules = list(/obj/item/clockwork/slab/cyborg/service, + /obj/item/borg/sight/xray/truesight_lens) moduleselect_icon = "service" special_light_key = "service" feedback_key = "cyborg_service" @@ -386,11 +458,24 @@ /obj/item/weapon/robot_module/miner name = "Miner" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/borg/sight/meson, /obj/item/weapon/storage/bag/ore/cyborg, /obj/item/weapon/pickaxe/drill/cyborg, /obj/item/weapon/shovel, \ - /obj/item/weapon/crowbar/cyborg, /obj/item/weapon/weldingtool/mini, /obj/item/weapon/extinguisher/mini, /obj/item/weapon/storage/bag/sheetsnatcher/borg, \ - /obj/item/device/t_scanner/adv_mining_scanner, /obj/item/weapon/gun/energy/kinetic_accelerator/cyborg, /obj/item/device/gps/cyborg) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/borg/sight/meson, + /obj/item/weapon/storage/bag/ore/cyborg, + /obj/item/weapon/pickaxe/drill/cyborg, + /obj/item/weapon/shovel, + /obj/item/weapon/crowbar/cyborg, + /obj/item/weapon/weldingtool/mini, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/storage/bag/sheetsnatcher/borg, + /obj/item/device/t_scanner/adv_mining_scanner, + /obj/item/weapon/gun/energy/kinetic_accelerator/cyborg, + /obj/item/device/gps/cyborg) emag_modules = list(/obj/item/borg/stun) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/miner, /obj/item/clockwork/ratvarian_spear/cyborg, /obj/item/borg/sight/xray/truesight_lens) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/miner, + /obj/item/clockwork/ratvarian_spear/cyborg, + /obj/item/borg/sight/xray/truesight_lens) cyborg_base_icon = "miner" moduleselect_icon = "miner" feedback_key = "cyborg_miner" @@ -398,10 +483,18 @@ /obj/item/weapon/robot_module/syndicate name = "Syndicate Assault" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/weapon/melee/energy/sword/cyborg, /obj/item/weapon/gun/energy/printer, \ - /obj/item/weapon/gun/ballistic/revolver/grenadelauncher/cyborg, /obj/item/weapon/card/emag, /obj/item/weapon/crowbar/cyborg, \ - /obj/item/weapon/pinpointer/syndicate/cyborg) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/security, /obj/item/clockwork/ratvarian_spear/cyborg) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/weapon/melee/energy/sword/cyborg, + /obj/item/weapon/gun/energy/printer, + /obj/item/weapon/gun/ballistic/revolver/grenadelauncher/cyborg, + /obj/item/weapon/card/emag, + /obj/item/weapon/crowbar/cyborg, + /obj/item/weapon/pinpointer/syndicate/cyborg) + + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/security, + /obj/item/clockwork/ratvarian_spear/cyborg) cyborg_base_icon = "synd_sec" moduleselect_icon = "malf" can_be_pushed = FALSE @@ -409,11 +502,26 @@ /obj/item/weapon/robot_module/syndicate_medical name = "Syndicate Medical" - basic_modules = list(/obj/item/device/assembly/flash/cyborg, /obj/item/weapon/reagent_containers/borghypo/syndicate, /obj/item/weapon/twohanded/shockpaddles/syndicate, \ - /obj/item/device/healthanalyzer, /obj/item/weapon/surgical_drapes, /obj/item/weapon/retractor, /obj/item/weapon/hemostat, \ - /obj/item/weapon/cautery, /obj/item/weapon/scalpel, /obj/item/weapon/melee/energy/sword/cyborg/saw, /obj/item/roller/robo, \ - /obj/item/weapon/card/emag, /obj/item/weapon/crowbar/cyborg, /obj/item/weapon/pinpointer/syndicate/cyborg, /obj/item/stack/medical/gauze/cyborg, /obj/item/weapon/gun/medbeam) - ratvar_modules = list(/obj/item/clockwork/slab/cyborg/medical, /obj/item/clockwork/ratvarian_spear/cyborg) + basic_modules = list( + /obj/item/device/assembly/flash/cyborg, + /obj/item/weapon/reagent_containers/borghypo/syndicate, + /obj/item/weapon/twohanded/shockpaddles/syndicate, + /obj/item/device/healthanalyzer, + /obj/item/weapon/surgical_drapes, + /obj/item/weapon/retractor, + /obj/item/weapon/hemostat, + /obj/item/weapon/cautery, + /obj/item/weapon/scalpel, + /obj/item/weapon/melee/energy/sword/cyborg/saw, + /obj/item/roller/robo, + /obj/item/weapon/card/emag, + /obj/item/weapon/crowbar/cyborg, + /obj/item/weapon/pinpointer/syndicate/cyborg, + /obj/item/stack/medical/gauze/cyborg, + /obj/item/weapon/gun/medbeam) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/medical, + /obj/item/clockwork/ratvarian_spear/cyborg) cyborg_base_icon = "synd_medical" moduleselect_icon = "malf" can_be_pushed = FALSE diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index f08014b927..f74b19b93d 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -228,9 +228,8 @@ if (length(law) > 0) if (force || src.lawcheck[index+1] == "Yes") src.say("[radiomod] [number]. [law]") + number++ sleep(10) - number++ - for (var/index = 1, index <= src.laws.supplied.len, index++) var/law = src.laws.supplied[index] @@ -239,8 +238,8 @@ if(src.lawcheck.len >= number+1) if (force || src.lawcheck[number+1] == "Yes") src.say("[radiomod] [number]. [law]") + number++ sleep(10) - number++ /mob/living/silicon/proc/checklaws() //Gives you a link-driven interface for deciding what laws the statelaws() proc will share with the crew. --NeoFite diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index e2beff5d83..65172fc9a1 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -526,7 +526,7 @@ Difficulty: Very Hard /obj/machinery/anomalous_crystal/emitter/New() ..() - generated_projectile = pick(/obj/item/projectile/magic/fireball/infernal,/obj/item/projectile/magic/spellblade, + generated_projectile = pick(/obj/item/projectile/magic/aoe/fireball/infernal,/obj/item/projectile/magic/aoe/lightning,/obj/item/projectile/magic/spellblade, /obj/item/projectile/bullet/meteorshot, /obj/item/projectile/beam/xray, /obj/item/projectile/colossus) /obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method) diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm index ca9bb1130e..d51765e4da 100644 --- a/code/modules/mob/living/simple_animal/hostile/wizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm @@ -29,7 +29,7 @@ loot = list(/obj/effect/mob_spawn/human/corpse/wizard, /obj/item/weapon/staff) - var/obj/effect/proc_holder/spell/fireball/fireball = null + var/obj/effect/proc_holder/spell/aimed/fireball/fireball = null var/obj/effect/proc_holder/spell/targeted/turf_teleport/blink/blink = null var/obj/effect/proc_holder/spell/targeted/projectile/magic_missile/mm = null @@ -38,7 +38,7 @@ /mob/living/simple_animal/hostile/wizard/New() ..() - fireball = new /obj/effect/proc_holder/spell/fireball + fireball = new /obj/effect/proc_holder/spell/aimed/fireball fireball.clothes_req = 0 fireball.human_req = 0 fireball.player_lock = 0 diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 1da8d9c5f0..8770fa79a1 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -299,10 +299,8 @@ if(!gibbed) if(death_sound) playsound(get_turf(src),death_sound, 200, 1) - if(deathmessage) - visible_message("\The [src] [deathmessage]") - else if(!del_on_death) - visible_message("\The [src] stops moving...") + if(deathmessage && !del_on_death) + emote("deathgasp") if(del_on_death) ghostize() //Prevent infinite loops if the mob Destroy() is overriden in such diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 422a64f2d3..c6627a92e3 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -2,7 +2,6 @@ SStgui.on_logout(src) unset_machine() player_list -= src - current_watchlist -= ckey log_access("Logout: [key_name(src)]") if(admin_datums[src.ckey]) if (ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing. @@ -28,7 +27,6 @@ send2irc("Server", "[cheesy_message]") - send2admindiscord("[cheesy_message]") ..() if(isobj(loc)) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 95a7fb26ba..9112420e34 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -103,8 +103,6 @@ if(href_list["ready"]) if(!ticker || ticker.current_state <= GAME_STATE_PREGAME) // Make sure we don't ready up after the round has started ready = text2num(href_list["ready"]) - else - ready = 0 if(href_list["refresh"]) src << browse(null, "window=playersetup") //closes the player setup window diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index 052c4899ae..5888cee361 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -292,7 +292,7 @@ /obj/item/weapon/paper/contract/infernal/magic/FulfillContract(mob/living/carbon/human/user = target.current, blood = 0) if(!istype(user) || !user.mind) return -1 - user.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball(null)) + user.mind.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball(null)) user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null)) return ..() diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index c5f5283c54..81c9879996 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -51,6 +51,7 @@ obj_integrity = 200 max_integrity = 200 integrity_failure = 50 + resistance_flags = FIRE_PROOF var/area/area var/areastring = null var/obj/item/weapon/stock_parts/cell/cell diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index df4421f971..f614c10ca3 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -736,6 +736,10 @@ var/global/list/datum/stack_recipe/cable_coil_recipes = list ( \ pixel_y = rand(-2,2) update_icon() +/obj/item/stack/cable_coil/red + item_color = "red" + icon_state = "coil_red" + /obj/item/stack/cable_coil/yellow item_color = "yellow" icon_state = "coil_yellow" diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 0c9ad083a9..eef1a5a799 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -168,6 +168,8 @@ investigate_log("lost power and turned off","singulo") log_game("Emitter lost power in ([x],[y],[z])") return + if(!check_delay()) + return FALSE fire_beam() /obj/machinery/power/emitter/proc/check_delay() @@ -175,9 +177,16 @@ return TRUE return FALSE -/obj/machinery/power/emitter/proc/fire_beam() +/obj/machinery/power/emitter/proc/fire_beam_pulse() if(!check_delay()) return FALSE + if(state != 2) + return FALSE + if(avail(active_power_usage)) + add_load(active_power_usage) + fire_beam() + +/obj/machinery/power/emitter/proc/fire_beam() src.last_shot = world.time if(src.shot_number < 3) src.fire_delay = 20 diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 9c11a4289b..861d73fc89 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -63,8 +63,10 @@ /obj/singularity/narsie/Bump(atom/A) - forceMove(get_turf(A)) - A.narsie_act() + var/turf/T = get_turf(A) + if(T == loc) + T = get_step(A, A.dir) //please don't slam into a window like a bird, nar-sie + forceMove(T) /obj/singularity/narsie/mezzer() diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index e9409e42e9..701e7baff7 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -90,10 +90,10 @@ if((last_zap + zap_cooldown) > world.time) return FALSE last_zap = world.time - var/coeff = (12 - (input_power_multiplier * 3)) - coeff = max(coeff, 1) - var/shock_coeff = (5 - input_power_multiplier) - shock_coeff = max(shock_coeff, 1) + var/coeff = (20 - ((input_power_multiplier - 1) * 3)) + coeff = max(coeff, 10) + var/shock_coeff = (4 - (input_power_multiplier - 2)) + shock_coeff = max(shock_coeff, 2) var/power = (powernet.avail/coeff) add_load(power) playsound(src.loc, 'sound/magic/LightningShock.ogg', 100, 1, extrarange = 5) diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index fba93b7d5b..8092b59d90 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -203,7 +203,7 @@ var/list/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics, else if(isliving(A)) var/dist = get_dist(source, A) var/mob/living/L = A - if(dist <= zap_range && (dist < closest_dist || !closest_mob) && L.stat != DEAD) + if(dist <= zap_range && (dist < closest_dist || !closest_mob) && L.stat != DEAD && !L.tesla_ignore) closest_mob = L closest_atom = A closest_dist = dist diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special.dm index 0c6a09a98a..68681c1124 100644 --- a/code/modules/projectiles/ammunition/special.dm +++ b/code/modules/projectiles/ammunition/special.dm @@ -23,7 +23,7 @@ projectile_type = /obj/item/projectile/magic/door /obj/item/ammo_casing/magic/fireball - projectile_type = /obj/item/projectile/magic/fireball + projectile_type = /obj/item/projectile/magic/aoe/fireball /obj/item/ammo_casing/magic/chaos projectile_type = /obj/item/projectile/magic @@ -35,7 +35,6 @@ projectile_type = /obj/item/projectile/magic/arcane_barrage /obj/item/ammo_casing/magic/chaos/newshot() - projectile_type = pick(typesof(/obj/item/projectile/magic)) ..() /obj/item/ammo_casing/magic/honk diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index bc2a1f045c..4de87637ee 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -267,6 +267,7 @@ if(user) user.update_inv_hands() feedback_add_details("gun_fired","[src.type]") + return 1 /obj/item/weapon/gun/attack(mob/M as mob, mob/user) if(user.a_intent == INTENT_HARM) //Flogging diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index e4aae72ca6..f133b08a79 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -343,3 +343,16 @@ new /obj/item/stack/cable_coil(get_turf(src), 10) slung = 0 update_icon() + +/obj/item/weapon/gun/ballistic/revolver/reverse //Fires directly at its user... unless the user is a clown, of course. + clumsy_check = 0 + +/obj/item/weapon/gun/ballistic/revolver/reverse/can_trigger_gun(mob/living/user) + if((user.disabilities & CLUMSY) || (user.mind && user.mind.assigned_role == "Clown")) + return ..() + if(process_fire(user, user, 0, zone_override = "head")) + user.visible_message("[user] somehow manages to shoot [user.p_them()]self in the face!", "You somehow shoot yourself in the face! How the hell?!") + user.emote("scream") + user.drop_item() + user.Weaken(4) + return diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index ba81adbae2..df24b4d081 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -38,6 +38,13 @@ max_charges = 10 recharge_rate = 2 no_den_usage = 1 + var/allowed_projectile_types = list(/obj/item/projectile/magic/change, /obj/item/projectile/magic/animate, /obj/item/projectile/magic/resurrection, + /obj/item/projectile/magic/death, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball, + /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage) + +/obj/item/weapon/gun/magic/staff/chaos/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override, bonus_spread = 0) + chambered.projectile_type = pick(allowed_projectile_types) + . = ..(target, user, message, params, zone_override, bonus_spread) /obj/item/weapon/gun/magic/staff/door name = "staff of door creation" diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 04a83ca3c0..eedc640911 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -17,53 +17,6 @@ var/mob/M = target M.death(0) -/obj/item/projectile/magic/fireball - name = "bolt of fireball" - icon_state = "fireball" - damage = 10 - damage_type = BRUTE - nodamage = 0 - - //explosion values - var/exp_heavy = 0 - var/exp_light = 2 - var/exp_flash = 3 - var/exp_fire = 2 - -/obj/item/projectile/magic/fireball/Range() - var/turf/T1 = get_step(src,turn(dir, -45)) - var/turf/T2 = get_step(src,turn(dir, 45)) - var/turf/T3 = get_step(src,dir) - var/mob/living/L = locate(/mob/living) in T1 //if there's a mob alive in our front right diagonal, we hit it. - if(L && L.stat != DEAD) - Bump(L,1) //Magic Bullet #teachthecontroversy - return - L = locate(/mob/living) in T2 - if(L && L.stat != DEAD) - Bump(L,1) - return - L = locate(/mob/living) in T3 - if(L && L.stat != DEAD) - Bump(L,1) - return - ..() - -/obj/item/projectile/magic/fireball/on_hit(target) - . = ..() - var/turf/T = get_turf(target) - explosion(T, -1, exp_heavy, exp_light, exp_flash, 0, flame_range = exp_fire) - if(ismob(target)) //multiple flavors of pain - var/mob/living/M = target - M.take_overall_damage(0,10) //between this 10 burn, the 10 brute, the explosion brute, and the onfire burn, your at about 65 damage if you stop drop and roll immediately - -/obj/item/projectile/magic/fireball/infernal - name = "infernal fireball" - exp_heavy = -1 - exp_light = -1 - exp_flash = 4 - exp_fire= 5 - - /obj/item/projectile/magic/resurrection name = "bolt of resurrection" icon_state = "ion" @@ -387,3 +340,85 @@ nodamage = 0 armour_penetration = 0 flag = "magic" + +/obj/item/projectile/magic/aoe + name = "Area Bolt" + desc = "What the fuck does this do?!" + damage = 0 + var/proxdet = TRUE + +/obj/item/projectile/magic/aoe/Range() + if(proxdet) + var/turf/T1 = get_step(src,turn(dir, -45)) + var/turf/T2 = get_step(src,turn(dir, 45)) + var/turf/T3 = get_step(src,dir) + var/mob/living/L = locate(/mob/living) in T1 //if there's a mob alive in our front right diagonal, we hit it. + if(L && L.stat != DEAD) + Bump(L,1) //Magic Bullet #teachthecontroversy + return + L = locate(/mob/living) in T2 + if(L && L.stat != DEAD) + Bump(L,1) + return + L = locate(/mob/living) in T3 + if(L && L.stat != DEAD) + Bump(L,1) + return + ..() + +/obj/item/projectile/magic/aoe/lightning + name = "lightning bolt" + icon_state = "tesla_projectile" //Better sprites are REALLY needed and appreciated!~ + damage = 15 + damage_type = BURN + nodamage = 0 + speed = 0.3 + flag = "magic" + + var/tesla_power = 20000 + var/tesla_range = 15 + var/tesla_boom = FALSE + var/chain + var/mob/living/caster + +/obj/item/projectile/magic/aoe/lightning/fire(setAngle) + if(caster) + chain = caster.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY) + ..() + +/obj/item/projectile/magic/aoe/lightning/on_hit(target) + . = ..() + tesla_zap(src, tesla_range, tesla_power, tesla_boom) + qdel(src) + +/obj/item/projectile/magic/aoe/lightning/Destroy() + qdel(chain) + . = ..() + +/obj/item/projectile/magic/aoe/fireball + name = "bolt of fireball" + icon_state = "fireball" + damage = 10 + damage_type = BRUTE + nodamage = 0 + + //explosion values + var/exp_heavy = 0 + var/exp_light = 2 + var/exp_flash = 3 + var/exp_fire = 2 + +/obj/item/projectile/magic/aoe/fireball/on_hit(target) + . = ..() + var/turf/T = get_turf(target) + explosion(T, -1, exp_heavy, exp_light, exp_flash, 0, flame_range = exp_fire) + if(ismob(target)) //multiple flavors of pain + var/mob/living/M = target + M.take_overall_damage(0,10) //between this 10 burn, the 10 brute, the explosion brute, and the onfire burn, your at about 65 damage if you stop drop and roll immediately + +/obj/item/projectile/magic/aoe/fireball/infernal + name = "infernal fireball" + exp_heavy = -1 + exp_light = -1 + exp_flash = 4 + exp_fire= 5 diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 48d07e6417..8d12c8b27e 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -684,6 +684,41 @@ animate(whole_screen, transform = matrix(), time = 5, easing = QUAD_EASING) ..() +/datum/reagent/toxin/skewium + name = "Skewium" + id = "skewium" + description = "A strange, dull coloured liquid that appears to warp back and forth inside its container. Causes any consumer to experience a visual phenomena similar to said warping." + reagent_state = LIQUID + color = "#ADBDCD" + metabolization_rate = 0.8 * REAGENTS_METABOLISM + toxpwr = 0.25 + +/datum/reagent/toxin/skewium/on_mob_life(mob/living/M) + if(M.hud_used) + if(current_cycle >= 5 && current_cycle % 3 == 0) + var/list/screens = list(M.hud_used.plane_masters["[GAME_PLANE]"], M.hud_used.plane_masters["[LIGHTING_PLANE]"]) + var/matrix/skew = matrix() + var/intensity = 8 + skew.set_skew(rand(-intensity,intensity), rand(-intensity,intensity)) + var/matrix/newmatrix = skew + + if(prob(33)) // 1/3rd of the time, let's make it stack with the previous matrix! Mwhahahaha! + var/obj/screen/plane_master/PM = M.hud_used.plane_masters["[GAME_PLANE]"] + newmatrix = skew * PM.transform + + for(var/whole_screen in screens) + animate(whole_screen, transform = newmatrix, time = 5, easing = QUAD_EASING, loop = -1) + animate(transform = -newmatrix, time = 5, easing = QUAD_EASING) + return ..() + +/datum/reagent/toxin/skewium/on_mob_delete(mob/living/M) + if(M && M.hud_used) + var/list/screens = list(M.hud_used.plane_masters["[GAME_PLANE]"], M.hud_used.plane_masters["[LIGHTING_PLANE]"]) + for(var/whole_screen in screens) + animate(whole_screen, transform = matrix(), time = 5, easing = QUAD_EASING) + ..() + + /datum/reagent/toxin/anacea name = "Anacea" id = "anacea" diff --git a/code/modules/reagents/chemistry/recipes/toxins.dm b/code/modules/reagents/chemistry/recipes/toxins.dm index 233e6fd1bc..6246d3adb2 100644 --- a/code/modules/reagents/chemistry/recipes/toxins.dm +++ b/code/modules/reagents/chemistry/recipes/toxins.dm @@ -94,7 +94,14 @@ results = list("rotatium" = 3) required_reagents = list("mindbreaker" = 1, "teslium" = 1, "neurotoxin2" = 1) mix_message = "After sparks, fire, and the smell of mindbreaker, the mix is constantly spinning with no stop in sight." - + +/datum/chemical_reaction/skewium + name = "Skewium" + id = "Skewium" + results = list("skewium" = 5) + required_reagents = list("rotatium" = 2, "plasma" = 2, "sacid" = 1) + mix_message = "Wow! it turns out if you mix rotatium with some plasma and sulphuric acid, it gets even worse!" + /datum/chemical_reaction/anacea name = "Anacea" id = "anacea" diff --git a/code/modules/research/designs/AI_module_designs.dm b/code/modules/research/designs/AI_module_designs.dm index 7f39ffbce5..af2c5b262b 100644 --- a/code/modules/research/designs/AI_module_designs.dm +++ b/code/modules/research/designs/AI_module_designs.dm @@ -84,6 +84,15 @@ build_path = /obj/item/weapon/aiModule/reset/purge category = list("AI Modules") +/datum/design/board/remove_module + name = "Module Design (Law Removal)" + desc = "Allows for the construction of a Law Removal AI Core Module." + id = "remove_module" + req_tech = list("programming" = 5, "materials" = 5) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + build_path = /obj/item/weapon/aiModule/remove + category = list("AI Modules") + /datum/design/board/freeformcore_module name = "AI Core Module (Freeform)" desc = "Allows for the construction of a Freeform AI Core Module." @@ -130,11 +139,13 @@ build_path = /obj/item/weapon/aiModule/core/full/corp category = list("AI Modules") -/datum/design/board/custom_module - name = "Core Module Design (Custom)" - desc = "Allows for the construction of a Custom AI Core Module." - id = "custom_module" +/datum/design/board/default_module + name = "Core Module Design (Default)" + desc = "Allows for the construction of a Default AI Core Module." + id = "default_module" req_tech = list("programming" = 5, "materials" = 5) materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) build_path = /obj/item/weapon/aiModule/core/full/custom category = list("AI Modules") + + diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index 4600af491f..528fd77298 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -344,7 +344,7 @@ if(MT) visible_message("[src] dangerously overheats, launching a flaming fuel orb!") investigate_log("Experimentor has launched a fireball at [M]!", "experimentor") - var/obj/item/projectile/magic/fireball/FB = new /obj/item/projectile/magic/fireball(start) + var/obj/item/projectile/magic/aoe/fireball/FB = new /obj/item/projectile/magic/aoe/fireball(start) FB.original = MT FB.current = start FB.yo = MT.y - start.y diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 079c16671e..af4cd2063b 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -212,6 +212,8 @@ else return + SSshuttle.emergencyCallAmount++ + if(prob(70)) SSshuttle.emergencyLastCallLoc = signalOrigin else diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index ee88e2e79c..b2dbd4f04f 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -11,6 +11,21 @@ return 0 . = ..() +/obj/machinery/atmospherics/onShuttleMove() + . = ..() + for(DEVICE_TYPE_LOOP) + if(get_area(nodes[I]) != get_area(src)) + nullifyNode(I) + +#define DIR_CHECK_TURF_AREA(X) (get_area(get_ranged_target_turf(src, X, 1)) != A) +/obj/structure/cable/onShuttleMove() + . = ..() + var/A = get_area(src) + //cut cables on the edge + if(DIR_CHECK_TURF_AREA(NORTH) || DIR_CHECK_TURF_AREA(SOUTH) || DIR_CHECK_TURF_AREA(EAST) || DIR_CHECK_TURF_AREA(WEST)) + cut_cable_from_powernet() +#undef DIR_CHECK_TURF_AREA + /atom/movable/light/onShuttleMove() return 0 diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm new file mode 100644 index 0000000000..111094d5d7 --- /dev/null +++ b/code/modules/spells/spell_types/aimed.dm @@ -0,0 +1,105 @@ + +/obj/effect/proc_holder/spell/aimed + name = "aimed projectile spell" + var/projectile_type = /obj/item/projectile/magic/teleport + var/deactive_msg = "You discharge your projectile..." + var/active_msg = "You charge your projectile!" + var/base_icon_state = "projectile" + var/active_icon_state = "projectile" + +/obj/effect/proc_holder/spell/aimed/Click() + var/mob/living/user = usr + if(!istype(user)) + return + var/msg + if(!can_cast(user)) + msg = "You can no longer cast [name]!" + remove_ranged_ability(msg) + return + if(active) + msg = "[deactive_msg]" + remove_ranged_ability(msg) + else + msg = "[active_msg]Left-click to shoot it at a target!" + add_ranged_ability(user, msg, TRUE) + +/obj/effect/proc_holder/spell/aimed/update_icon() + if(!action) + return + action.button_icon_state = "[base_icon_state][active]" + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/aimed/InterceptClickOn(mob/living/caller, params, atom/target) + if(..()) + return FALSE + if(!cast_check(0, ranged_ability_user)) + remove_ranged_ability() + return FALSE + var/list/targets = list(target) + perform(targets,user = ranged_ability_user) + return TRUE + +/obj/effect/proc_holder/spell/aimed/cast(list/targets, mob/living/user) + var/target = targets[1] + var/turf/T = user.loc + var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction + if(!isturf(U) || !isturf(T)) + return FALSE + fire_projectile(user, target) + user.newtonian_move(get_dir(U, T)) + remove_ranged_ability() //Auto-disable the ability once successfully performed + return TRUE + +/obj/effect/proc_holder/spell/aimed/proc/fire_projectile(mob/living/user, atom/target) + var/obj/item/projectile/P = new projectile_type(user.loc) + P.current = get_turf(user) + P.preparePixelProjectile(target, get_turf(target), user) + P.fire() + return TRUE + +/obj/effect/proc_holder/spell/aimed/lightningbolt + name = "Lightning Bolt" + desc = "Fire a high powered lightning bolt at your foes!" + school = "evocation" + charge_max = 200 + clothes_req = 1 + invocation = "UN'LTD P'WAH" + invocation_type = "shout" + cooldown_min = 30 + active_icon_state = "lightning" + base_icon_state = "lightning" + sound = 'sound/magic/lightningbolt.ogg' + active = FALSE + var/tesla_range = 15 + var/tesla_power = 20000 + var/tesla_boom = FALSE + active_msg = "You energize your hand with arcane lightning!" + deactive_msg = "You let the energy flow out of your hands back into yourself..." + +/obj/effect/proc_holder/spell/aimed/lightningbolt/fire_projectile(mob/living/user, atom/target) + var/obj/item/projectile/magic/aoe/lightning/P = new /obj/item/projectile/magic/aoe/lightning(user.loc) + P.current = get_turf(user) + P.preparePixelProjectile(target, get_turf(target), user) + P.tesla_power = tesla_power + P.tesla_range = tesla_range + P.tesla_boom = tesla_boom + P.fire() + return TRUE + +/obj/effect/proc_holder/spell/aimed/fireball + name = "Fireball" + desc = "This spell fires a fireball at a target and does not require wizard garb." + school = "evocation" + charge_max = 60 + clothes_req = 0 + invocation = "ONI SOMA" + invocation_type = "shout" + range = 20 + cooldown_min = 20 //10 deciseconds reduction per rank + projectile_type = /obj/item/projectile/magic/aoe/fireball + base_icon_state = "fireball" + action_icon_state = "fireball0" + sound = "sound/magic/Fireball.ogg" + active_msg = "You prepare to cast your fireball spell!" + deactive_msg = "You extinguish your fireball... for now." + active = FALSE diff --git a/code/modules/spells/spell_types/devil.dm b/code/modules/spells/spell_types/devil.dm index b235184ddd..6f62b9488c 100644 --- a/code/modules/spells/spell_types/devil.dm +++ b/code/modules/spells/spell_types/devil.dm @@ -69,7 +69,7 @@ user << "[C] seems to not be sentient. You cannot summon a contract for [C.p_them()]." -/obj/effect/proc_holder/spell/fireball/hellish +/obj/effect/proc_holder/spell/aimed/fireball/hellish name = "Hellfire" desc = "This spell launches hellfire at the target." @@ -80,7 +80,7 @@ invocation_type = "shout" range = 2 - fireball_type = /obj/item/projectile/magic/fireball/infernal + projectile_type = /obj/item/projectile/magic/aoe/fireball/infernal action_background_icon_state = "bg_demon" diff --git a/code/modules/spells/spell_types/lightning.dm b/code/modules/spells/spell_types/lightning.dm index 73e958926c..c1058161f5 100644 --- a/code/modules/spells/spell_types/lightning.dm +++ b/code/modules/spells/spell_types/lightning.dm @@ -1,6 +1,6 @@ -/obj/effect/proc_holder/spell/targeted/lightning - name = "Lightning Bolt" - desc = "Charges up and throws a lightning bolt at nearby enemies. Classic." +/obj/effect/proc_holder/spell/targeted/tesla + name = "Tesla Blast" + desc = "Blast lightning at your foes!" charge_type = "recharge" charge_max = 300 clothes_req = 1 @@ -16,12 +16,12 @@ action_icon_state = "lightning" -/obj/effect/proc_holder/spell/targeted/lightning/Click() +/obj/effect/proc_holder/spell/targeted/tesla/Click() if(!ready && cast_check()) StartChargeup() return 1 -/obj/effect/proc_holder/spell/targeted/lightning/proc/StartChargeup(mob/user = usr) +/obj/effect/proc_holder/spell/targeted/tesla/proc/StartChargeup(mob/user = usr) ready = 1 user << "You start gathering the power." Snd = new/sound('sound/magic/lightning_chargeup.ogg',channel = 7) @@ -36,18 +36,18 @@ else revert_cast(user, 0) -/obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr) +/obj/effect/proc_holder/spell/targeted/tesla/proc/Reset(mob/user = usr) ready = 0 if(halo) user.overlays.Remove(halo) -/obj/effect/proc_holder/spell/targeted/lightning/revert_cast(mob/user = usr, message = 1) +/obj/effect/proc_holder/spell/targeted/tesla/revert_cast(mob/user = usr, message = 1) if(message) user << "No target found in range." Reset(user) ..() -/obj/effect/proc_holder/spell/targeted/lightning/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/targeted/tesla/cast(list/targets, mob/user = usr) ready = 0 var/mob/living/carbon/target = targets[1] Snd=sound(null, repeat = 0, wait = 1, channel = Snd.channel) //byond, why you suck? @@ -63,7 +63,7 @@ Bolt(user,target,30,5,user) Reset(user) -/obj/effect/proc_holder/spell/targeted/lightning/proc/Bolt(mob/origin,mob/target,bolt_energy,bounces,mob/user = usr) +/obj/effect/proc_holder/spell/targeted/tesla/proc/Bolt(mob/origin,mob/target,bolt_energy,bounces,mob/user = usr) origin.Beam(target,icon_state="lightning[rand(1,12)]",time=5) var/mob/living/carbon/current = target if(bounces < 1) @@ -81,4 +81,4 @@ return var/mob/living/next = pick(possible_targets) if(next) - Bolt(current,next,max((bolt_energy-5),5),bounces-1,user) \ No newline at end of file + Bolt(current,next,max((bolt_energy-5),5),bounces-1,user) diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm index a14424cdae..0d764fa9f2 100644 --- a/code/modules/spells/spell_types/wizard.dm +++ b/code/modules/spells/spell_types/wizard.dm @@ -227,78 +227,6 @@ mutations = list(BLINDMUT) duration = 300 sound="sound/magic/Blind.ogg" - -/obj/effect/proc_holder/spell/fireball - name = "Fireball" - desc = "This spell fires a fireball at a target and does not require wizard garb." - - school = "evocation" - charge_max = 60 - clothes_req = 0 - invocation = "ONI SOMA" - invocation_type = "shout" - range = 20 - cooldown_min = 20 //10 deciseconds reduction per rank - var/fireball_type = /obj/item/projectile/magic/fireball - action_icon_state = "fireball0" - sound = "sound/magic/Fireball.ogg" - - active = FALSE - - -/obj/effect/proc_holder/spell/fireball/Click() - var/mob/living/user = usr - if(!istype(user)) - return - - var/msg - - if(!can_cast(user)) - msg = "You can no longer cast Fireball." - remove_ranged_ability(msg) - return - - if(active) - msg = "You extinguish your fireball...for now." - remove_ranged_ability(msg) - else - msg = "Your prepare to cast your fireball spell! Left-click to cast at a target!" - add_ranged_ability(user, msg, TRUE) - -/obj/effect/proc_holder/spell/fireball/update_icon() - if(!action) - return - action.button_icon_state = "fireball[active]" - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/caller, params, atom/target) - if(..()) - return FALSE - - if(!cast_check(0, ranged_ability_user)) - remove_ranged_ability() - return FALSE - - var/list/targets = list(target) - perform(targets,user = ranged_ability_user) - - return TRUE - -/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user) - var/target = targets[1] //There is only ever one target for fireball - var/turf/T = user.loc - var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction - if(!isturf(U) || !isturf(T)) - return 0 - - var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc) - FB.current = get_turf(user) - FB.preparePixelProjectile(target, get_turf(target), user) - FB.fire() - user.newtonian_move(get_dir(U, T)) - remove_ranged_ability() //Auto-disable the ability once successfully performed - return 1 - /obj/effect/proc_holder/spell/aoe_turf/repulse name = "Repulse" desc = "This spell throws everything around the user away." diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 979f2623f4..a5545c91cd 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -316,9 +316,9 @@ I = image("icon"='icons/mob/human_parts.dmi', "icon_state"="[species_id]_[body_zone]", "layer"=-BODYPARTS_LAYER, "dir"=image_dir) else if(should_draw_gender) - I = image("icon"='icons/mob/augments.dmi', "icon_state"="[body_zone]_[icon_gender]", "layer"=-BODYPARTS_LAYER, "dir"=image_dir) + I = image("icon"='icons/mob/augments.dmi', "icon_state"="[icon_state]_[icon_gender]", "layer"=-BODYPARTS_LAYER, "dir"=image_dir) else - I = image("icon"='icons/mob/augments.dmi', "icon_state"="[body_zone]", "layer"=-BODYPARTS_LAYER, "dir"=image_dir) + I = image("icon"='icons/mob/augments.dmi', "icon_state"="[icon_state]", "layer"=-BODYPARTS_LAYER, "dir"=image_dir) standing += I return standing diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index bd01b14c21..8e5197ef60 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -35,6 +35,8 @@ var/turf/target_turf = get_turf(src) for(var/i in 1 to t_range-1) var/turf/new_turf = get_step(target_turf, direction) + if(!new_turf) + break target_turf = new_turf if(new_turf.density) break diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm index 6d0001c421..653a131144 100644 --- a/code/modules/surgery/bodyparts/robot_bodyparts.dm +++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm @@ -158,3 +158,34 @@ flash2.forceMove(user.loc) flash2 = null ..() + + + + +/obj/item/bodypart/l_arm/robot/surplus + name = "surplus prosthetic left arm" + desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." + icon = 'icons/mob/augments.dmi' + icon_state = "surplus_l_arm" + max_damage = 20 + +/obj/item/bodypart/r_arm/robot/surplus + name = "surplus prosthetic right arm" + desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." + icon = 'icons/mob/augments.dmi' + icon_state = "surplus_r_arm" + max_damage = 20 + +/obj/item/bodypart/l_leg/robot/surplus + name = "surplus prosthetic leg" + desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." + icon = 'icons/mob/augments.dmi' + icon_state = "surplus_l_leg" + max_damage = 20 + +/obj/item/bodypart/r_leg/robot/surplus + name = "surplus prosthetic leg" + desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." + icon = 'icons/mob/augments.dmi' + icon_state = "surplus_r_leg" + max_damage = 20 \ No newline at end of file diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 7316a188ce..3342ef840c 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -664,6 +664,7 @@ /obj/item/organ/tongue/robot name = "robotic voicebox" desc = "A voice synthesizer that can interface with organic lifeforms." + status = ORGAN_ROBOTIC icon_state = "tonguerobot" say_mod = "states" attack_verb = list("beeped", "booped") @@ -807,6 +808,7 @@ name = "robotic eyes" icon_state = "cybernetic_eyeballs" desc = "Your vision is augmented." + status = ORGAN_ROBOTIC /obj/item/organ/eyes/robotic/emp_act(severity) if(!owner) diff --git a/code/modules/surgery/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm index 15a54b6e13..8385403efc 100644 --- a/code/modules/surgery/plastic_surgery.dm +++ b/code/modules/surgery/plastic_surgery.dm @@ -20,7 +20,7 @@ var/oldname = target.real_name target.real_name = target.dna.species.random_name(target.gender,1) var/newname = target.real_name //something about how the code handles names required that I use this instead of target.real_name - user.visible_message("[user] alters [oldname]'s appearance completely, [target.p_they()] are now [newname]!", "You alter [oldname]'s appearance completely, [target.p_they()] are now [newname].") + user.visible_message("[user] alters [oldname]'s appearance completely, [target.p_they()] is now [newname]!", "You alter [oldname]'s appearance completely, [target.p_they()] is now [newname].") if(ishuman(target)) var/mob/living/carbon/human/H = target H.sec_hud_set_ID() diff --git a/code/modules/uplink/uplink_item.dm b/code/modules/uplink/uplink_item.dm index 65507a3091..56300c2545 100644 --- a/code/modules/uplink/uplink_item.dm +++ b/code/modules/uplink/uplink_item.dm @@ -638,6 +638,7 @@ var/list/uplink_items = list() // Global list so we only initialize this once. desc = "A highly experimental bioterror agent which creates dormant nodules to be etched into the grey matter of the brain. On death, these nodules take control of the dead body, causing limited revivification, along with slurred speech, aggression, and the ability to infect others with this agent." item = /obj/item/weapon/storage/box/syndie_kit/romerol cost = 25 + cant_discount = TRUE exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang) /datum/uplink_item/stealthy_weapons/dart_pistol @@ -1166,6 +1167,14 @@ var/list/uplink_items = list() // Global list so we only initialize this once. category = "Role-Restricted" exclude_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/role_restricted/reverse_revolver + name = "Reverse Revolver" + desc = "A revolver that always fires at its user. \"Accidentally\" drop your weapon, then watch as the greedy corporate pigs blow their own brains all over the wall. \ + The revolver itself is actually real. Only clumsy people, and clowns, can fire it normally. Honk." + cost = 14 + item = /obj/item/weapon/gun/ballistic/revolver/reverse + restricted_roles = list("Clown") + /datum/uplink_item/role_restricted/ez_clean_bundle name = "EZ Clean Grenade Bundle" desc = "A box with three cleaner grenades using the trademark Waffle Co. formula. Serves as a cleaner and causes acid damage to anyone standing nearby. The acid only affects carbon-based creatures." @@ -1174,6 +1183,17 @@ var/list/uplink_items = list() // Global list so we only initialize this once. surplus = 20 restricted_roles = list("Janitor") +/datum/uplink_item/role_restricted/his_grace + name = "His Grace" + desc = "An incredibly dangerous weapon recovered from a station overcome by the grey tide. Once activated, it will thirst for blood and must be used to kill in order to sate that thirst. \ + His Grace grants benefits to its wielder, with a more intense hunger equaling more benefits, but be wary: if it gets too hungry, it will kill you and destroy your body. \ + If you leave His Grace alone for some time, it will eventually return to its inactive state. \ + To activate His Grace, place five assorted organs inside of it and use it in your hand." + item = /obj/item/weapon/storage/toolbox/artistic/his_grace + cost = 20 + restricted_roles = list("Chaplain") + surplus = 5 //Very low chance to get it in a surplus crate even without being the chaplain + // Pointless /datum/uplink_item/badass category = "(Pointless) Badassery" diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 9c28b5e624..74c8e1647e 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -97,9 +97,7 @@ user << "It's on fire!" var/healthpercent = (obj_integrity/max_integrity) * 100 switch(healthpercent) - if(100 to INFINITY) - user << "It seems pristine and undamaged." - if(50 to 100) + if(50 to 99) user << "It looks slightly damaged." if(25 to 50) user << "It appears heavily damaged." diff --git a/code/world.dm b/code/world.dm index d69ab5427c..78b309c259 100644 --- a/code/world.dm +++ b/code/world.dm @@ -10,6 +10,10 @@ fps = 20 visibility = 0 +#ifdef GC_FAILURE_HARD_LOOKUP + loop_checks = FALSE +#endif + var/list/map_transition_config = MAP_TRANSITION_CONFIG /world/New() diff --git a/icons/misc/fullscreen.dmi b/icons/misc/fullscreen.dmi index 4bf628bd41..116ea7cfdc 100644 Binary files a/icons/misc/fullscreen.dmi and b/icons/misc/fullscreen.dmi differ diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi index 0e3ffa053e..efb324966b 100644 Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ diff --git a/icons/mob/augments.dmi b/icons/mob/augments.dmi index 7124c35105..a2d816c8c9 100644 Binary files a/icons/mob/augments.dmi and b/icons/mob/augments.dmi differ diff --git a/icons/mob/lavaland/legion.dmi b/icons/mob/lavaland/legion.dmi index 9cc19c4558..d8d165b79a 100644 Binary files a/icons/mob/lavaland/legion.dmi and b/icons/mob/lavaland/legion.dmi differ diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi index 28c8385c56..40500e84a8 100644 Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi index 8a48c84c2f..6313ef058e 100644 Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi index 765f967625..2963275eea 100644 Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index d4f27ed2b8..0eb3441812 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/sound/effects/his_grace_awaken.ogg b/sound/effects/his_grace_awaken.ogg new file mode 100644 index 0000000000..4c0e87e5ab Binary files /dev/null and b/sound/effects/his_grace_awaken.ogg differ diff --git a/strings/soapstone_prefixes.txt b/strings/soapstone_prefixes.txt new file mode 100644 index 0000000000..728e073983 --- /dev/null +++ b/strings/soapstone_prefixes.txt @@ -0,0 +1,14 @@ +**** +****? +****! +try **** +**** ahead +be wary of **** +need **** +imminent **** +weakness: **** +huh, it's a ****... +visions of ****.... +could this be a ****? +time for **** +praise the ****! \ No newline at end of file diff --git a/strings/soapstone_suffixes.json b/strings/soapstone_suffixes.json new file mode 100644 index 0000000000..2b2ab81a48 --- /dev/null +++ b/strings/soapstone_suffixes.json @@ -0,0 +1,234 @@ +{ + "Characters": [ + "human", + "lizard", + "monkey", + "xeno", + "slimeperson", + "snowflake", + "enemy", + "tough enemy", + "Nanotrasen", + "Syndicate", + "skeleton", + "flier", + "statue", + "monster", + "strange creature", + "demon", + "boss", + "dark spirit", + "saint", + "wretch", + "charmer", + "miscreant", + "liar", + "fatty", + "beanpole", + "merchant", + "prisoner" + ], + + "Careers": [ + "assistant", + "captain", + "head of personnel", + "head of security", + "warden", + "security officer", + "detective", + "chief engineer", + "engineer", + "atmos tech", + "research director", + "scientist", + "robotics", + "chief medical officer", + "doctor", + "chemist", + "geneticist", + "virologist", + "janitor", + "bartender", + "cook", + "clown", + "mime", + "chaplain", + "librarian", + "quartermaster", + "cargo tech", + "miner", + "lawyer", + "AI", + "cyborg", + "drone", + "pAI" + ], + + "Antagonists": [ + "traitor", + "changeling", + "nuke op", + "wizard", + "alien", + "ghost", + "blob", + "cultist", + "devil" + ], + + "Objects": [ + "airlock", + "table", + "rack", + "weapon", + "trap", + "false wall", + "window", + "item", + "loot", + "teleporter", + "blueprints", + "boots", + "clothes", + "clothing", + "drugs", + "medicine", + "crate", + "locker", + "handcuffs", + "plant", + "food", + "drink", + "tool", + "amazing item", + "amazing loot", + "amazing clothing", + "amazing drugs", + "amazing rack" + ], + + "Techniques": [ + "close-ranged battle", + "ranged battle", + "eliminating one at a time", + "luring it out", + "beating to a pulp", + "lying in ambush", + "stealth", + "fleeing", + "charging", + "stabbing in the back" + ], + + "Actions": [ + "attacking", + "hacking", + "sneaking", + "slipping", + "running", + "walking", + "sprinting", + "running away", + "holding with both hands", + "jumping", + "restraining", + "crying", + "cheering" + ], + + "Geography": [ + "hallway", + "room", + "corridor", + "airlock", + "space", + "hidden path", + "gorgeous view", + "open area", + "tight spot", + "tunnel", + "dark area", + "bright area", + "shuttle", + "pod", + "security", + "arrivals", + "escape", + "engineering", + "atmospherics", + "medbay", + "science", + "brig", + "prison", + "asteroid", + "cargo", + "mining", + "cave", + "maintenance" + ], + + "Orientation": [ + "front", + "back", + "left", + "right", + "up", + "down", + "north", + "south", + "east", + "west", + "northeast", + "northwest", + "southeast", + "southwest", + "that way" + ], + + "Body parts": [ + "head", + "neck", + "stomach", + "back", + "arm", + "leg", + "heel", + "rear", + "tail", + "wings", + "anywhere", + "everywhere" + ], + + "Concepts": [ + "chance", + "hint", + "secret", + "happiness", + "sorrow", + "life", + "death", + "elation", + "grief", + "hope", + "despair", + "light", + "dark", + "bravery", + "resignation", + "comfort", + "tears" + ], + + "Musings": [ + "I can't take this...", + "it'll happen to you too", + "you don't deserve this", + "you bastard", + "you can do it", + "don't give up", + "don't you dare", + "do it" + ] +} \ No newline at end of file diff --git a/tgstation.dme b/tgstation.dme index 4fb22b301f..8316fe9026 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -997,6 +997,7 @@ #include "code\modules\admin\verbs\tripAI.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" +#include "code\modules\admin\verbs\SDQL2\SDQL_2_wrappers.dm" #include "code\modules\assembly\assembly.dm" #include "code\modules\assembly\bomb.dm" #include "code\modules\assembly\doorcontrol.dm" @@ -1934,6 +1935,7 @@ #include "code\modules\shuttle\white_ship.dm" #include "code\modules\space_transition\space_transition.dm" #include "code\modules\spells\spell.dm" +#include "code\modules\spells\spell_types\aimed.dm" #include "code\modules\spells\spell_types\area_teleport.dm" #include "code\modules\spells\spell_types\barnyard.dm" #include "code\modules\spells\spell_types\bloodcrawl.dm"