diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 998ae80eada..1108d55aa5d 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -5221,7 +5221,7 @@ "bWu" = (/obj/effect/spawner/window/reinforced,/obj/structure/sign/examroom,/turf/simulated/floor/plating,/area/medical/sleeper) "bWv" = (/turf/simulated/wall/r_wall,/area/crew_quarters/fitness{name = "\improper Arcade"}) "bWw" = (/obj/machinery/door/firedoor,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) -"bWx" = (/obj/machinery/door/firedoor,/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) +"bWx" = (/obj/machinery/door/firedoor,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (WEST)"; icon_state = "whiteblue"; dir = 8},/area/medical/sleeper) "bWy" = (/obj/effect/spawner/window/reinforced,/turf/simulated/floor/plating,/area/medical/cryo) "bWz" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel{dir = 1; icon_state = "whitebluecorner"; tag = "icon-whitebluecorner"},/area/medical/medbay2) "bWA" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/medbay2) @@ -5282,8 +5282,8 @@ "bXD" = (/obj/effect/spawner/window/reinforced,/turf/simulated/floor/plating,/area/medical/sleeper) "bXE" = (/obj/item/roller,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/sleeper) "bXF" = (/obj/machinery/light_switch{pixel_x = -23; pixel_y = 0},/obj/machinery/sleeper{dir = 4},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTHWEST)"; icon_state = "whiteblue"; dir = 9},/area/medical/sleeper) -"bXG" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) -"bXH" = (/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) +"bXG" = (/obj/machinery/door/firedoor,/turf/simulated/floor/plasteel{dir = 4; icon_state = "whiteblue"; tag = "icon-whitehall (WEST)"},/area/medical/sleeper) +"bXH" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) "bXI" = (/obj/machinery/vending/medical,/turf/simulated/floor/plasteel{dir = 5; icon_state = "whiteblue"; tag = "icon-whitehall (WEST)"},/area/medical/sleeper) "bXJ" = (/obj/structure/table,/obj/item/reagent_containers/syringe/antiviral,/obj/item/reagent_containers/syringe/charcoal,/obj/item/reagent_containers/syringe/insulin,/obj/item/reagent_containers/glass/bottle/morphine,/obj/item/reagent_containers/glass/bottle/epinephrine,/obj/item/reagent_containers/syringe,/obj/item/stack/medical/bruise_pack/advanced{pixel_x = 6; pixel_y = 6},/obj/item/stack/medical/ointment/advanced{pixel_x = 6; pixel_y = 8},/obj/item/reagent_containers/food/pill/patch/styptic{pixel_y = 6},/obj/item/reagent_containers/food/pill/patch/styptic{pixel_x = 2; pixel_y = 8},/obj/item/reagent_containers/food/pill/patch/silver_sulf{pixel_x = -8; pixel_y = 6},/obj/item/reagent_containers/food/pill/patch/silver_sulf{pixel_x = -8; pixel_y = 8},/obj/item/storage/pill_bottle/painkillers{pixel_x = -6; pixel_y = -3},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/sleeper) "bXK" = (/obj/item/twohanded/required/kirbyplants,/obj/machinery/firealarm{dir = 8; pixel_x = -24},/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/cryo) @@ -5361,13 +5361,13 @@ "bZe" = (/obj/machinery/camera{c_tag = "Gravity Generator Foyer"; dir = 1; network = list("SS13")},/obj/machinery/alarm{dir = 4; icon_state = "alarm0"; pixel_x = -22},/obj/structure/closet/radiation,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/light,/turf/simulated/floor/plasteel,/area/engine/gravitygenerator) "bZf" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/disposalpipe/sortjunction{dir = 8; icon_state = "pipe-j2s"; name = "Genetics"; sortType = 23},/obj/machinery/atmospherics/pipe/simple/hidden{dir = 4},/obj/machinery/hologram/holopad,/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/genetics_cloning) "bZg" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"; tag = ""},/obj/structure/disposalpipe/segment{dir = 4; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/status_display{density = 0; layer = 4; pixel_x = -32; pixel_y = 0},/turf/simulated/floor/plasteel,/area/hallway/primary/central/se) -"bZh" = (/obj/structure/table,/obj/item/roller,/obj/machinery/firealarm{dir = 8; pixel_x = -24},/obj/item/soap/nanotrasen,/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTHWEST)"; icon_state = "whiteblue"; dir = 9},/area/medical/sleeper) -"bZi" = (/obj/structure/closet/crate/freezer,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/AMinus,/obj/item/reagent_containers/blood/APlus,/obj/item/reagent_containers/blood/BMinus,/obj/item/reagent_containers/blood/BPlus,/obj/item/reagent_containers/blood/OMinus,/obj/item/reagent_containers/blood/OPlus,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTHEAST)"; icon_state = "whiteblue"; dir = 5},/area/medical/sleeper) -"bZj" = (/obj/structure/sink{icon_state = "sink"; dir = 8; pixel_x = -12; pixel_y = 2},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (WEST)"; icon_state = "whiteblue"; dir = 8},/area/medical/sleeper) -"bZk" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/manifold/hidden/supply{dir = 4; initialize_directions = 11; level = 1},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/hologram/holopad,/obj/structure/disposalpipe/segment{dir = 1; icon_state = "pipe-c"},/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) -"bZl" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 4; on = 1},/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) +"bZh" = (/turf/simulated/floor/plasteel{dir = 1; icon_state = "whitebluecorner"; tag = "icon-whitebluecorner"},/area/medical/sleeper) +"bZi" = (/turf/simulated/floor/plasteel{dir = 4; icon_state = "whitebluecorner"; tag = "icon-whitebluecorner"},/area/medical/sleeper) +"bZj" = (/obj/structure/sink{dir = 8; icon_state = "sink"; pixel_x = -12; pixel_y = 0},/obj/machinery/firealarm{dir = 8; pixel_x = -24},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTHWEST)"; icon_state = "whiteblue"; dir = 9},/area/medical/sleeper) +"bZk" = (/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) +"bZl" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/manifold/hidden/supply{dir = 4; initialize_directions = 11; level = 1},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/hologram/holopad,/obj/structure/disposalpipe/segment{dir = 1; icon_state = "pipe-c"},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) "bZm" = (/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/plasteel{dir = 4; icon_state = "whiteblue"; tag = "icon-whitehall (WEST)"},/area/medical/sleeper) -"bZn" = (/obj/machinery/atmospherics/unary/vent_scrubber{on = 1; scrub_N2O = 1; scrub_Toxins = 1},/obj/structure/disposalpipe/junction{dir = 8; icon_state = "pipe-j2"; tag = "icon-pipe-j1 (WEST)"},/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) +"bZn" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 4; on = 1},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) "bZo" = (/obj/structure/disposalpipe/segment{dir = 2; icon_state = "pipe-c"},/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/cryo) "bZp" = (/obj/machinery/door/firedoor,/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/cryo) "bZq" = (/obj/machinery/atmospherics/pipe/manifold/hidden{dir = 1},/obj/machinery/hologram/holopad,/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/cryo) @@ -5433,14 +5433,14 @@ "cay" = (/obj/machinery/light{dir = 4; icon_state = "tube1"},/obj/machinery/atmospherics/pipe/simple/hidden/supply{level = 1},/obj/structure/table,/turf/simulated/floor/plasteel,/area/engine/break_room) "caz" = (/obj/structure/sign/poster/random{pixel_x = -32; pixel_y = 0},/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (WEST)"; icon_state = "whiteblue"; dir = 8},/area/medical/sleeper) "caA" = (/turf/simulated/wall,/area/medical/sleeper) -"caB" = (/obj/machinery/door/window/eastright{base_state = "left"; dir = 4; icon_state = "left"; name = "Blood Transfusion Ward"; req_access_txt = "5"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) +"caB" = (/obj/machinery/atmospherics/unary/vent_scrubber{on = 1; scrub_N2O = 1; scrub_Toxins = 1},/obj/structure/disposalpipe/junction{dir = 8; icon_state = "pipe-j2"; tag = "icon-pipe-j1 (WEST)"},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) "caC" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/unary/vent_pump{dir = 4; on = 1},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) -"caD" = (/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) +"caD" = (/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) "caE" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) -"caF" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"; tag = "90Curve"},/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"; tag = ""},/obj/structure/disposalpipe/segment{dir = 4; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply,/obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers,/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) -"caG" = (/obj/effect/landmark/start{name = "Medical Doctor"},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; tag = ""},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) +"caF" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"; tag = "90Curve"},/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"; tag = ""},/obj/structure/disposalpipe/segment{dir = 4; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply,/obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers,/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) +"caG" = (/obj/effect/landmark/start{name = "Medical Doctor"},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; tag = ""},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) "caH" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/turf/simulated/floor/plasteel{dir = 4; icon_state = "whiteblue"; tag = "icon-whitehall (WEST)"},/area/medical/sleeper) -"caI" = (/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/obj/structure/disposalpipe/segment{dir = 8; icon_state = "pipe-c"},/turf/simulated/floor/plasteel{dir = 2; icon_state = "cmo"},/area/medical/sleeper) +"caI" = (/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/obj/structure/disposalpipe/segment{dir = 8; icon_state = "pipe-c"},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/sleeper) "caJ" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/cryo) "caK" = (/obj/machinery/door/firedoor,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/obj/structure/extinguisher_cabinet{pixel_x = 0; pixel_y = -30},/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/cryo) "caL" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/obj/machinery/atmospherics/pipe/manifold4w/hidden,/turf/simulated/floor/plasteel{icon_state = "freezerfloor"},/area/medical/cryo) @@ -5480,7 +5480,7 @@ "cbt" = (/obj/machinery/navbeacon{codes_txt = "delivery"; dir = 4; location = "Medbay"},/obj/structure/plasticflaps{opacity = 1},/turf/simulated/floor/plating,/area/medical/sleeper) "cbu" = (/obj/structure/lamarr,/turf/simulated/floor/plasteel{dir = 4; icon_state = "warnwhite"; tag = "icon-warnwhite (NORTH)"},/area/crew_quarters/hor) "cbv" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/turf/simulated/floor/plating,/area/maintenance/asmaint) -"cbw" = (/obj/machinery/iv_drip,/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) +"cbw" = (/obj/structure/table,/obj/item/roller,/obj/item/soap/nanotrasen,/turf/simulated/floor/plasteel{dir = 10; icon_state = "whiteblue"; tag = "icon-whitehall (WEST)"},/area/medical/sleeper) "cbx" = (/obj/structure/stool/bed/chair/comfy/black{dir = 4},/turf/simulated/shuttle/floor{icon_state = "floor4"},/area/shuttle/administration) "cby" = (/obj/machinery/computer/shuttle/admin,/turf/simulated/shuttle/floor{icon_state = "floor4"},/area/shuttle/administration) "cbz" = (/obj/machinery/door/airlock/centcom{id_tag = "adminshuttle"; name = "Bridge"; opacity = 1; req_access_txt = "106"},/turf/simulated/shuttle/floor{icon_state = "floor4"},/area/shuttle/administration) @@ -5510,7 +5510,7 @@ "cbX" = (/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{level = 1},/obj/structure/disposalpipe/junction{tag = "icon-pipe-j1 (EAST)"; icon_state = "pipe-j1"; dir = 4},/turf/simulated/floor/plasteel{dir = 1; icon_state = "blue"},/area/hallway/primary/central/south) "cbY" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment{dir = 1; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/turf/simulated/floor/plasteel{dir = 1; icon_state = "blue"},/area/hallway/primary/central/south) "cbZ" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{req_access_txt = "0"},/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) -"cca" = (/obj/structure/closet/crate/freezer,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/AMinus,/obj/item/reagent_containers/blood/APlus,/obj/item/reagent_containers/blood/BMinus,/obj/item/reagent_containers/blood/BPlus,/obj/item/reagent_containers/blood/OMinus,/obj/item/reagent_containers/blood/OPlus,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (SOUTHEAST)"; icon_state = "whiteblue"; dir = 6},/area/medical/sleeper) +"cca" = (/obj/structure/closet/crate/freezer,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/AMinus,/obj/item/reagent_containers/blood/APlus,/obj/item/reagent_containers/blood/BMinus,/obj/item/reagent_containers/blood/BPlus,/obj/item/reagent_containers/blood/OMinus,/obj/item/reagent_containers/blood/OPlus,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/machinery/iv_drip,/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) "ccb" = (/obj/machinery/ai_status_display{pixel_y = 32},/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) "ccc" = (/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{level = 1},/obj/item/radio/intercom{broadcasting = 0; name = "station intercom (General)"; pixel_y = 25},/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) "ccd" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/effect/decal/warning_stripes/north,/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) @@ -5520,15 +5520,15 @@ "cch" = (/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{dir = 1; initialize_directions = 11; level = 1},/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) "cci" = (/obj/machinery/light{dir = 1},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) "ccj" = (/obj/machinery/camera{c_tag = "Central Primary Hallway South"; dir = 2; network = list("SS13")},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) -"cck" = (/obj/machinery/iv_drip,/obj/machinery/ai_status_display{pixel_y = -32},/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) +"cck" = (/obj/structure/closet/crate/freezer,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/empty,/obj/item/reagent_containers/blood/AMinus,/obj/item/reagent_containers/blood/APlus,/obj/item/reagent_containers/blood/BMinus,/obj/item/reagent_containers/blood/BPlus,/obj/item/reagent_containers/blood/OMinus,/obj/item/reagent_containers/blood/OPlus,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/item/reagent_containers/blood/random,/obj/machinery/iv_drip,/obj/machinery/ai_status_display{pixel_y = -32},/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) "ccl" = (/obj/machinery/door/firedoor,/turf/simulated/floor/plasteel,/area/hallway/primary/central/south) "ccm" = (/obj/machinery/door/airlock/public/glass{name = "Central Access"},/turf/simulated/floor/plasteel,/area/hallway/primary/central/se) "ccn" = (/obj/machinery/atm{pixel_y = 32},/turf/simulated/floor/plasteel,/area/hallway/primary/central/se) "cco" = (/obj/machinery/newscaster{pixel_y = 32},/turf/simulated/floor/plasteel,/area/hallway/primary/central/se) "ccp" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 6},/turf/simulated/floor/plasteel,/area/hallway/primary/central/se) -"ccq" = (/obj/machinery/camera{c_tag = "Medbay Treatment Center"; dir = 1; network = list("SS13"); pixel_x = 0},/obj/machinery/light,/obj/machinery/power/apc{dir = 2; name = "south bump"; pixel_y = -24},/obj/structure/cable,/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) +"ccq" = (/obj/machinery/camera{c_tag = "Medbay Treatment Center"; dir = 1; network = list("SS13"); pixel_x = 0},/obj/machinery/light,/obj/machinery/power/apc{dir = 2; name = "south bump"; pixel_y = -24},/obj/structure/cable,/obj/item/roller,/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) "ccr" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 9; pixel_y = 0},/turf/simulated/floor/plasteel,/area/hallway/primary/central/se) -"ccs" = (/obj/machinery/sleeper{dir = 4},/obj/item/radio/intercom{name = "station intercom (General)"; pixel_y = -28},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (SOUTHWEST)"; icon_state = "whiteblue"; dir = 10},/area/medical/sleeper) +"ccs" = (/obj/machinery/sleeper{dir = 4},/obj/item/radio/intercom{name = "station intercom (General)"; pixel_y = -28},/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) "cct" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) "ccu" = (/turf/simulated/floor/plasteel{tag = "icon-whiteblue"; icon_state = "whiteblue"},/area/medical/sleeper) "ccv" = (/obj/machinery/light{dir = 4},/obj/machinery/alarm{dir = 1; pixel_y = -22},/obj/machinery/body_scanconsole,/turf/simulated/floor/plasteel{dir = 6; icon_state = "whiteblue"; tag = "icon-whitehall (WEST)"},/area/medical/sleeper) @@ -5978,8 +5978,8 @@ "ckX" = (/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/surgery1) "ckY" = (/obj/machinery/firealarm{dir = 8; pixel_x = -24},/obj/machinery/power/apc{dir = 2; name = "south bump"; pixel_y = -24},/obj/structure/cable,/turf/simulated/floor/plasteel{dir = 10; icon_state = "darkblue"},/area/medical/ward) "ckZ" = (/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/manifold/hidden/supply{dir = 4; initialize_directions = 11; level = 1},/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{dir = 8; initialize_directions = 11; level = 1},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/medical/ward) -"cla" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 4; on = 1},/obj/machinery/alarm{dir = 1; pixel_y = -22},/turf/simulated/floor/plasteel{dir = 2; icon_state = "darkblue"},/area/medical/ward) -"clb" = (/obj/machinery/atmospherics/unary/vent_scrubber{dir = 8; on = 1},/obj/machinery/light_switch{pixel_x = 27},/turf/simulated/floor/plasteel{dir = 6; icon_state = "darkblue"},/area/medical/ward) +"cla" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 4; on = 1},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/medical/ward) +"clb" = (/obj/machinery/atmospherics/unary/vent_scrubber{dir = 8; on = 1},/obj/machinery/light_switch{pixel_x = 27},/obj/machinery/alarm{dir = 1; pixel_y = -22},/turf/simulated/floor/plasteel{dir = 6; icon_state = "darkblue"},/area/medical/ward) "clc" = (/obj/machinery/bodyscanner,/obj/machinery/firealarm{dir = 1; pixel_y = -24},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/surgery2) "cld" = (/obj/structure/closet/secure_closet/medical2,/turf/simulated/floor/plasteel{dir = 4; icon_state = "whitebluecorner"; tag = "icon-whitebluecorner"},/area/medical/surgery2) "cle" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{dir = 1},/obj/machinery/light_switch{pixel_x = 23; pixel_y = -5},/obj/machinery/holosign_switch{id = "surgery2"; pixel_x = 23; pixel_y = 4},/turf/simulated/floor/plasteel{tag = "icon-whiteblue (NORTH)"; icon_state = "whiteblue"; dir = 1},/area/medical/surgery2) @@ -6042,7 +6042,7 @@ "cmj" = (/obj/machinery/computer/security/telescreen{desc = "Used for watching the singularity chamber."; dir = 8; layer = 4; name = "Singularity Engine Telescreen"; network = list("Singularity"); pixel_x = 0; pixel_y = 30},/obj/structure/stool/bed/chair/office/dark{dir = 4},/turf/simulated/floor/plasteel,/area/engine/controlroom) "cmk" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/light/small{dir = 8},/turf/simulated/floor/plating,/area/maintenance/asmaint) "cml" = (/obj/machinery/door/firedoor,/obj/machinery/door/airlock/medical{name = "Operating Theatre Storage"; req_access_txt = "45"},/turf/simulated/floor/plasteel{icon_state = "showroomfloor"},/area/medical/surgery1) -"cmm" = (/obj/machinery/door/firedoor,/obj/structure/disposalpipe/segment,/obj/machinery/door/airlock/medical/glass{id_tag = ""; name = "Medbay Virology Lobby"; req_access_txt = "5"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/virology/lab{name = "\improper Virology Lobby"}) +"cmm" = (/obj/machinery/door/firedoor,/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/door/airlock/medical/glass{id_tag = null; name = "Virology Lobby Hallway"; req_access_txt = "0"},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/medical/virology/lab{name = "\improper Virology Lobby"}) "cmn" = (/obj/machinery/vending/coffee,/turf/simulated/floor/plasteel{tag = "icon-whitegreen (NORTHWEST)"; icon_state = "whitegreen"; dir = 9},/area/medical/virology/lab{name = "\improper Virology Lobby"}) "cmo" = (/obj/structure/sign/biohazard,/turf/simulated/wall,/area/medical/virology/lab{name = "\improper Virology Lobby"}) "cmp" = (/obj/machinery/vending/medical,/turf/simulated/floor/plasteel{tag = "icon-whitegreen (NORTHEAST)"; icon_state = "whitegreen"; dir = 5},/area/medical/virology/lab{name = "\improper Virology Lobby"}) @@ -6104,8 +6104,8 @@ "cnt" = (/obj/structure/disposalpipe/segment{dir = 8; icon_state = "pipe-c"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor/plating,/area/maintenance/apmaint) "cnu" = (/obj/structure/stool/bed/chair/office/dark,/turf/simulated/floor/carpet,/area/ntrep) "cnv" = (/obj/machinery/alarm{dir = 8; icon_state = "alarm0"; pixel_x = 24},/turf/simulated/floor/wood,/area/blueshield) -"cnw" = (/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/virology/lab{name = "\improper Virology Lobby"}) -"cnx" = (/turf/simulated/floor/plasteel{tag = "icon-whitegreen (NORTHWEST)"; icon_state = "whitegreen"; dir = 9},/area/medical/virology/lab{name = "\improper Virology Lobby"}) +"cnw" = (/obj/machinery/door/firedoor,/obj/machinery/door/airlock/medical/glass{id_tag = null; name = "Virology Lobby Hallway"; req_access_txt = "0"},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/medical/ward) +"cnx" = (/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plasteel{dir = 4; icon_state = "whitegreencorner"},/area/medical/virology/lab{name = "\improper Virology Lobby"}) "cny" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plasteel,/area/hallway/primary/aft) "cnz" = (/obj/structure/sign/biohazard{pixel_y = 32},/turf/simulated/floor/plasteel{dir = 1; icon_state = "whitegreen"; tag = "icon-whitegreen (WEST)"},/area/medical/virology/lab{name = "\improper Virology Lobby"}) "cnA" = (/turf/simulated/floor/plasteel{dir = 2; icon_state = "yellowcorner"},/area/hallway/primary/aft) @@ -7170,6 +7170,7 @@ "cHT" = (/obj/structure/disposalpipe/segment{dir = 2; icon_state = "pipe-c"},/turf/simulated/floor/plasteel,/area/engine/break_room) "cHU" = (/obj/structure/sign/securearea,/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/yellow{tag = "icon-intact (NORTHWEST)"; icon_state = "intact"; dir = 9},/turf/simulated/wall,/area/engine/break_room) "cHV" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{level = 1},/turf/simulated/wall/r_wall,/area/atmos/control) +"cHW" = (/turf/simulated/floor/plasteel{tag = "icon-whitegreen (WEST)"; icon_state = "whitegreen"; dir = 8},/area/medical/virology/lab{name = "\improper Virology Lobby"}) "cHY" = (/obj/machinery/computer/general_air_control{frequency = 1441; name = "Tank Monitor"; sensors = list("n2_sensor" = "Nitrogen", "o2_sensor" = "Oxygen", "co2_sensor" = "Carbon Dioxide", "tox_sensor" = "Toxins", "n2o_sensor" = "Nitrous Oxide", "waste_sensor" = "Gas Mix Tank")},/obj/machinery/alarm{frequency = 1439; pixel_y = 23},/turf/simulated/floor/plasteel,/area/atmos/control) "cHZ" = (/obj/machinery/kitchen_machine/grill,/turf/simulated/floor/plating,/area/maintenance/asmaint) "cIa" = (/obj/machinery/cooker/deepfryer,/obj/machinery/light/small{dir = 1},/turf/simulated/floor/plating,/area/maintenance/asmaint) @@ -8972,10 +8973,10 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabPLbPIbVebVfbVgbVhbVibRGbVkbRAbVlbRAbRAbRAbRAbRAbTlbRGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabOebVmbOcbOcbOcbVnbOebPTbKBbKBbVobKzbPPbKzbKzbKzbKzbKzbJfbKAbTsbVpbxbbVrbBnbVqbVsbBnbVwbzEbBnbBnbVtbDXbOwbTybTzbRVbRWbMGbVubVvbVybVxbVzbMHbVBbVAbVDbVCbHsbVEbVFbHsbHtbHtbVGbSibVIbVHbVKbVJbVLbVLbVNbVMbVObFObSMbSLbSObSNbSPcrAcrAcrAcrAcrAbSRbSQbSUbSSbSVbXAbSXbRtbXAbThbXAbJRcuobJSbTZbTYbUabUabUcbUbbWgbUdbUfbUdbWibWibWjbWjbWjbWjbIabIabIabIabIabWlbWnbLIbIabNVbNZbNXbWqbWqbPibOabLRctvbPkbPjcacbGGbZZaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWObWGbRAbRAbWHbWHbWHbRGbRAbRAbRGbWIbWJbWKbRAbWMbWNbRGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabOebOcbOcbOcbOcbOcbWPbTrbTqbTrbPUbWQbWRbKzbKzbKzbWQbKzbJfbKAbTsbWSbxbbWWbBnbzFbQdbBnbzJbLJbBnbBnbWUbDXbOwbTybWVbRVbWXbMGbXbbWYbWZbXabXdbMHbXcbXebXgbXfbXibXhbXjbFCaaaaaaaaabSibXpbXkbXlbXlbXmbXnbXobSibXrbXqbUhbUgbUjbUibUrbUkbUnbUmbUmbUmbUpbUobUmbUmbUqbUmbUmbUsbUnbUmbUmbUGbNubNtbUwbUvbUBbUybZfbUDbXNbUIbUIbUIbUJbWgbLRbUPbWqbWqbXYbXXbWqbXZbYbbYabYdbYcbWqbPlbYgbYfbYibYhbYlbYjbYjbYjbYjbYjcacbGjbGHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabYubWGbRAbRAbRAbRAbRAbYtbRAbRAbRGbPIbPIbPIbPIbPIbPIbYvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabOebOcbOcbOcbOcbOcbYxbYwbYybYwbYAbYzbYBbYBbYBbYBbYBbYCbYEbYDbTsbYFbxbbYGbYIbYHbYNbYJbYKbYLbYLbYLbYMbYMbYObTybYRbYQbRWbMGbYTbYSbYVbVvbYWbMHbYUbYXbZdbTJbZebQmbYYbFCaaaaaaaaabSibYZbZabZbbZcbXmbXnbXobSibZgbZJbURbUQbUTbUScyTcyTbUVbUUbUXbUWcyTbUYbUZbUZbVabUZbUZbUSbUVbUZbVcbVbbVdcyTbVPbVjbVRbVQbVTbVSbVVbVUbVXbVWbVZbVYbWcbWbbWcbWcbWebUNbZMbUNbZNbUNbZPbZObZObPCbPDbZRbZUbZTbZXbZVbGkbZWaYpbYjcacbGGbGHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaebPIcafcagcahcaibRAbRGbRAbRAbRGbRAcancakcaraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabOebOcbOcbOcbOcbOcbOebPTbKBbKBbxbbxbbKBbKBbKBbKBbKBbxbbxbbKBbKBbKBbxbcamcamcamcascambYMcaocapcaqcatbYMbOwbTycawbMEbMEbMGbMGbMGbMGbMGbMNbMHbTJbTIcaubTJcavcaxbFCbFCbFCbFCbFCbSibSjbSibSibSibSibSibSibSibNvbFObWfbSLbWkbWhbWobWmbWscaAbWubWtbWxbWwbWtbWubWabWybWabWybWabWybWabWzbWAcrAbTZbWBbWDbWCbWFbWEbXNbXsbXubXtbXvcbdcbdcbecbdcbfcbicbfcbfcbfcbhcblcbjcbjcbkcbmcbocbnbZUcbpcbrcbqbGlcbscbubYjcacbGGbZZaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabPIbPIbPIbPIbPIbPIbRGbTobRAcbzbRAcbxcbycbAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacbDcbBbOccbCbOccbEcbFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacbGaabaaaaaaaaaaaacamcbIcbHcbKcbJbYMcbNcbLcbMcbRbYMbOwbTycbOcbPcbQbXwcbVcbTcbUcbWcbYcbXbXxcbZcccccbccfccdcceccgccjcchccibXybXzcclccmbEnccnbFOccoccpccrbFObXBbQVbXCcaAcaAbXDbXDcaAbXFbXEbXHbXGbXJbXIbWabXKbXMbXLbXMbXObWabXPbWAbXRbWdbXSbXUbXTbXSbXSbWibXVbXWbXQbYkcbdccTccSccVccUccXccWcdbcbfccYccZcdacbjcbjcdccbocddcdfbPEcdhcdgbGmcdicdkbYjcdndfcbZZaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabPLbPIcdlcdmcdpcdlcdobRGbRAbRAbRGbRAcdrcdqcdsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacdvcdtcducducducdtcdwaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacamcdycdxcdAcdzbYLcdBcdDcdCcdEbYMbOwbNwcdFbTybTybYmbYncdIcdJcclcdObYobYpcdMcdNcdNcdQcdPcdRcdNcdNcdTcdNcdNcdScclccmbEncdVcdUcdXbYqcdZcdYbFOchfbYrcaAbZhbYsbYsbZibZjbXHbZlbZkbZnbZmbZpbZobZrbZqbZtbZsbZvbZubWAcuvchrbZwbZybZxbZAbZzbZBccQbXWbXQbZBcbdcezceyceBceAceDceCceGcbfceEccZceFceFcbjceHceJceIceLceKceNceMbGnceOceSbYjcaccLhbGpbGobGsaaaaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWObWGceRceRceRceRceRceTbRAbRAbRGbPIbPIbPIbPIbPIbPIbPRaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaceVceUceUceUceWaaaaaaaaaaaaaaaaaaaaaceXceZceYceZcfaaaaaaaaaacamcamcamcffcfbcdAcfcbYLcfdcbMcfecfgbYMbOwbNxbTicficflcfkbZDbZCbZFbZEbZHbZGbZIcfscftcftcfxcfvcfvcfvcfvcfvcfvcfvcJfcfycfzcfBbZLbZKbZYbZScabcaacadchfcajcaAcazcalcaCcaBcaEcaDcaGcaFcaIcaHcaKcaJcaMcaLcaOcaNbZvcaPcaQcuvbZzcaRcaTcaScaVcaUcaXcaWcaZcaYcaXcbdcgccgbcgecgdcghcgfcggcbfcgiccZceFceFcbjcgjcgmcgkcglcgncgpcgocgrcgqcgvbYjcaccgscgtcgscgucgycgucgucguaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaabaaaaaaaaaaaaaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaebPIcafcagcahcaibRAbRGbRAbRAbRGbRAcancakcaraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabOebOcbOcbOcbOcbOcbOebPTbKBbKBbxbbxbbKBbKBbKBbKBbKBbxbbxbbKBbKBbKBbxbcamcamcamcascambYMcaocapcaqcatbYMbOwbTycawbMEbMEbMGbMGbMGbMGbMGbMNbMHbTJbTIcaubTJcavcaxbFCbFCbFCbFCbFCbSibSjbSibSibSibSibSibSibSibNvbFObWfbSLbWkbWhbWobWmbWscaAbWubWtbWxbWwbXGbWubWabWybWabWybWabWybWabWzbWAcrAbTZbWBbWDbWCbWFbWEbXNbXsbXubXtbXvcbdcbdcbecbdcbfcbicbfcbfcbfcbhcblcbjcbjcbkcbmcbocbnbZUcbpcbrcbqbGlcbscbubYjcacbGGbZZaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabPIbPIbPIbPIbPIbPIbRGbTobRAcbzbRAcbxcbycbAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacbDcbBbOccbCbOccbEcbFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacbGaabaaaaaaaaaaaacamcbIcbHcbKcbJbYMcbNcbLcbMcbRbYMbOwbTycbOcbPcbQbXwcbVcbTcbUcbWcbYcbXbXxcbZcccccbccfccdcceccgccjcchccibXybXzcclccmbEnccnbFOccoccpccrbFObXBbQVbXCcaAcaAbXDbXDcaAbXFbXEbZhbXHbZibXIbWabXKbXMbXLbXMbXObWabXPbWAbXRbWdbXSbXUbXTbXSbXSbWibXVbXWbXQbYkcbdccTccSccVccUccXccWcdbcbfccYccZcdacbjcbjcdccbocddcdfbPEcdhcdgbGmcdicdkbYjcdndfcbZZaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabPLbPIcdlcdmcdpcdlcdobRGbRAbRAbRGbRAcdrcdqcdsaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacdvcdtcducducducdtcdwaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacamcdycdxcdAcdzbYLcdBcdDcdCcdEbYMbOwbNwcdFbTybTybYmbYncdIcdJcclcdObYobYpcdMcdNcdNcdQcdPcdRcdNcdNcdTcdNcdNcdScclccmbEncdVcdUcdXbYqcdZcdYbFOchfbYrcaAbZjbYsbYsbXJbZhbZkbZnbZlcaBbZmbZpbZobZrbZqbZtbZsbZvbZubWAcuvchrbZwbZybZxbZAbZzbZBccQbXWbXQbZBcbdcezceyceBceAceDceCceGcbfceEccZceFceFcbjceHceJceIceLceKceNceMbGnceOceSbYjcaccLhbGpbGobGsaaaaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabWObWGceRceRceRceRceRceTbRAbRAbRGbPIbPIbPIbPIbPIbPIbPRaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaceVceUceUceUceWaaaaaaaaaaaaaaaaaaaaaceXceZceYceZcfaaaaaaaaaacamcamcamcffcfbcdAcfcbYLcfdcbMcfecfgbYMbOwbNxbTicficflcfkbZDbZCbZFbZEbZHbZGbZIcfscftcftcfxcfvcfvcfvcfvcfvcfvcfvcJfcfycfzcfBbZLbZKbZYbZScabcaacadchfcajcaAcazcalcaCcaEcaEcaDcaGcaFcaIcaHcaKcaJcaMcaLcaOcaNbZvcaPcaQcuvbZzcaRcaTcaScaVcaUcaXcaWcaZcaYcaXcbdcgccgbcgecgdcghcgfcggcbfcgiccZceFceFcbjcgjcgmcgkcglcgncgpcgocgrcgqcgvbYjcaccgscgtcgscgucgycgucgucguaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaabaaaaaaaaaaaaaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabYubWGceRceRceRcgwcgxbRGbRAbRAbRGbRAbRAbRAcgzbRAcgAbRGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacgFcgBcgCcgDcgFbYPbYPbYPcamcgHcgGcgJcgIcgLcgKcgNcgMcgOcgOcgPbYMbTkcgQcgTcgScgScgUcbacgVcgScgWcgWcgYcbbcgZcgWcgWchacclcclchechcchcchcchccbcchcchcchfcbgchfchfchfchfchfchfchfcbvcbtcbScbwcckccaccsccqccucctccwccvbWaccxcczccyccBccAccDccCccEcuvccGccFccIccHccJchrbWibWgbWgbWgbWicbdcbdchMchQchOchPchOchRcbfchUchSchTchTcbjchVchXbTwbYjbYjbYjbYjbYjbYjbYjbYjcaccgscifchYchZciaciacibcguaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabcicciccicciccidcidcieciecieciecijaabaabaaaaaaaaaabpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaebPIcigceRceRcihciibRGbRAbRAcikbRAbRAbRAciobRAcilbRGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaceYcimcincimceYbYPcipciqcamcitcfbcircisciyciubYMcivciwcixcizbYMcgQcgQciCcgSciAciBccKciDciHcgWccLciGccMciIciKcgWciMciLciNchcccOccNccRccPcdjcdecdKcdHcdLchfceacdWceccebchfcedcbvcaAcaAcaAcaAcaAcaAcaAcefceecegcaAcDfcDfcDfcDfcDfcDfcDfcDfceicehccGcejcelcekcencemcepceocerceqclpcjFcjHcjGcjIcjGcjGcjLcjJcjKcjPcjMcjNcjOcbjcjQbUtcbnbPtbGGcjTcjScjUcjUcjScjUcjYcgscjVcjWchZciackecjXcguaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaciccjZckacjZcidciecieckbckcckbciecieaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaebPIckdceRceRckfbRGbRAbRAbRGckgbRAbRAckjckhbPIbYvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacgFckicimcimckkcklcfbcfbckmckncfbcfbcisckqckobYMbYMbYMbYMbYMbYMckpcgQciCcgSclRcesceucetckucgWckvcevcexcewcePcgWcfmceQcfnchccfpcfockHckGcfqckIchcchfdgCchfcebcebcfrcebchfcsLcbvchfcfwbUucfCcfAcfEcfDcfGcfFcfIcfHcfKcfJcfMcfLcDfcfNcfPcfOcfRcfQcfTcfScfVcfUcfXcfWcepcfYcgacfZclpclncjGcjGclmclocjGcltclpcbjclqccZclrclrcbjcbmclscbnbPtcgsclvcgscgscgscgscgscgscgscluclwcguclxclzclycguafOafOafOafOafOafOafOafOafOafOafOafOafOafOafOafOafOafOafOafOafOclBcjZcjZclAciecieckbckbclHckbckbciecieaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -8983,8 +8984,8 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaebPIbPIbPIbPIbPIbPIbPIbPIbPIbPIbYvaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabafOcgQcngcngcgQcngcngcgQaaaaaacgFcnhcnicnjcgFbYPbYPbYPcamcnmcnlcnocnocnocamcamcgQcnncnpcnrcnqcgQcnscntcgSclRcktcktcktcnvcgWckvckwckwcnuchHcgWcpicHDcnAchcchcchcchcchcchIchcchccnCdgCcsLcsLcsLchfcABchJcsLcbvchfchLchKciEchNcfEciFcfGcfFcfIcfHciOciJciQciPcDfciRciScDfciUciTchrciVciXciWchrchrcepciYcjaciZclpcjbcodcoicoicoicodcogcodcodcohcojclrclrcbjcokclscbncomcolcooconcorcopcoqcoqcotcoscovcoucoxcowcozcoycoCcoAcoBcoDcoEcoFcoHcoGcoIaabaabaabaabaabaabaabaabaaaaaaaaaaaaaaacidciecoMcoJcoJckbckbckbckbckbckbckbckbciecieaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaacgQcoKcoLcgQcoKcoLcgQaaaaaacoOceZcoNceZcoQaaaaabaaacamcamcamcamcamcamcamcoPcgQcoScoRcoUcoTclPcoVcoWcgScoYcoXcpfcktcjccgWcjdckwcjfcjecjgcgWcjicjhcjkcjjcpqcpqcpqcpqcjlcpmcppcpocjncjmcjmcjocjqcjpcjscjrcjtchfcjwcjucjycjxcjAcjzcjCcjBcjCcjDckrcjEckxcksckzckyckAcDfckBcuvckCcjvckDcmDckFckEciYckJckLcgacpRcpTcpRcpUcpWcpVcpRcpXcpYcodcqacpZcdacbjcbjcqccqbcqfcqdcqecqicqgcqhcqlcqjcqkcqncqmcqpcqocqtcqqcqrcqqcqscqrcqrcqucqwcqvcqycqxcqCaabaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaacieciecqDckbckbckbckbckbckbckbckbckbckbckbciecieaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafOaaaafOcgQcqzcoLcgQcqzcoLcgQaaaaaaaabaabaabaabaabaabaabaabaabcgQcoLcqAcqBcgQcqEcqGcgQcqHcgQcgQcqFcgQcgQcqPcgScqRcqIcqJcqKcqLcgWckMcqOcqScqQcqTcgWckOckNckPcJZcsDcsDcsDcsDcsDcrdcqZcrackQchfcucckRcsLckSckTcsLcsKchfckVckUckXckWcCWckYclackZclbcDfcldclcclfclecDfcDfcDfcDfclhclgckCchCcljclicllclkciYcmdcgacmfcpRcrNcrQcrLcrMcrLcrTcrOcrPcodcbjcrUcbjcbjcrRcrSclsbLRbPtcgscrVcgscgscrWcrYcrXcsacrZcsdcsbcsfcsecsgcsecsgcshcsgcsicgEcsncvdcsqcsraabaabaabaabaabaabaabaabaaaaaaaaaaaaaaacieckbckbckbckbckbckbcssckbcstckbckbckbckbckbciecieaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabcsjafOaabaMraMrafOcgQcgQcqFcgQcgQcqFcgQcgQcgQcngcngcngcgQcgQcgQcgQcgQcgQcgQcoLcskcslcgQcsmcgQcgQcsucgQcsocspcoLcgQcsvcgScsycsxcszclRcmgcgWcswckvcsEcsCcmhcgWcsIcsHcmicsDcsDcsNcsOcsGcmjcsQcsJcsKcsLcsUchfcmkcsLckSckTchfchfchfcCWcCWcmlcCWcCWcxhcxhcmmcxhcDfcDfcDfcDfcDfcDfcmncmpcmocmrcmqcmtcmscmvcmucmxcmwciYcgacgaaqEcpRcttctxctwctyctwctActzcrLctuctvctBbLRctCbLRcrSclsbLRbIictFctHctGcgscgscgscgscgsctIcqkctKctDcsccqkctEcqkclucqkctLcgEctOctQctPcgEaabaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaciectJctJctJctJctJctJctSckbctTctJcstckbckbckbckbcieaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabafOafOcngcoLcoLcoLcoLctMctNcoLcoLcoSctUctUctVctUctUctUctUctUctUctUctUctUctUctUctUcoRcgQcskcqzctWcgQcsvctRctRctRctRctRctRcgWcgWcgWcgWcgWcgWcgWcuactXcmBcmzcmEcmCcmFcmFcmGcuicsJcmHcsLcubcuccmIcsLckScmJchicsLcsLcsKcCWcmLcmKcmNcCWcnxcnwcnBcnzcnEcnDcnGcnFcnIcnHcnKcnJcnMcnLckCcnNcnPcnOcnRcnQciYbQXcgacgacpRcuGcnTcnScnUcrLcuKcuJcuOcuLcuScuRcuVbLRcuXcrSclscuMbIibPAcuNcuYcuPcuQbGGbGGcgscvccvecuTcuUcvfcuWcvgcvicuZcvacvbcgEcvjdoKdoFcvraaaaaaaaaaaaabpaaaaaaaaaaaaaaaaaaaaaaaacvscjZcjZcjZcjZcjZcjZcjZcvhcvtckbckbckbckbcvvckccieaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabcsjafOaabaMraMrafOcgQcgQcqFcgQcgQcqFcgQcgQcgQcngcngcngcgQcgQcgQcgQcgQcgQcgQcoLcskcslcgQcsmcgQcgQcsucgQcsocspcoLcgQcsvcgScsycsxcszclRcmgcgWcswckvcsEcsCcmhcgWcsIcsHcmicsDcsDcsNcsOcsGcmjcsQcsJcsKcsLcsUchfcmkcsLckSckTchfchfchfcCWcCWcmlcCWcCWcxhcnwcmmcxhcDfcDfcDfcDfcDfcDfcmncmpcmocmrcmqcmtcmscmvcmucmxcmwciYcgacgaaqEcpRcttctxctwctyctwctActzcrLctuctvctBbLRctCbLRcrSclsbLRbIictFctHctGcgscgscgscgscgsctIcqkctKctDcsccqkctEcqkclucqkctLcgEctOctQctPcgEaabaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaciectJctJctJctJctJctJctSckbctTctJcstckbckbckbckbcieaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabafOafOcngcoLcoLcoLcoLctMctNcoLcoLcoSctUctUctVctUctUctUctUctUctUctUctUctUctUctUctUcoRcgQcskcqzctWcgQcsvctRctRctRctRctRctRcgWcgWcgWcgWcgWcgWcgWcuactXcmBcmzcmEcmCcmFcmFcmGcuicsJcmHcsLcubcuccmIcsLckScmJchicsLcsLcsKcCWcmLcmKcmNcCWcHWcnxcnBcnzcnEcnDcnGcnFcnIcnHcnKcnJcnMcnLckCcnNcnPcnOcnRcnQciYbQXcgacgacpRcuGcnTcnScnUcrLcuKcuJcuOcuLcuScuRcuVbLRcuXcrSclscuMbIibPAcuNcuYcuPcuQbGGbGGcgscvccvecuTcuUcvfcuWcvgcvicuZcvacvbcgEcvjdoKdoFcvraaaaaaaaaaaaabpaaaaaaaaaaaaaaaaaaaaaaaacvscjZcjZcjZcjZcjZcjZcjZcvhcvtckbckbckbckbcvvckccieaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabafOaabaabafOcngcvkcvlcvmcoLcoLcvncoLcoLcsucgQcgQcgQcgQcgQcgQcqFcgQcgQcgQcgQcvocvocvocvocvocvocvocvocvocgQcsvcvpaaaaaaaaaaaacvqcvwcvzcvycvucvAcvDcvxcvJcvGcvKcvBcvCcvLcvEcvEcvFcvMcvHcvIcvIcvIcvIcvNcvIckScmJchfcnVcKOdercCWcnXcnWcnZcnYcobcoacoccoccofcoecpacoZcpccpbcpecpdcphcpgciYciYciYciYciYciYciYciYcpkcpjcpRcwjcplcwqcptcpncpvcpucpRcpRbIicwpbIibIibIicwvcwJcwwbIibGGcuNcwKcwucuQcuQcuQcgscgscgscgscgscgscgscgscgscgscgscgscgEcgEcgEcgEcgEaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaciecoJcoJcoJcoJcoJcoJcwLckbcoMcoJcwMckbckbckbckbcieaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabcgQcwxcwxcwxcwxcwxcwxcwycwxcsucwzcgQcoLcwAcwBcwCcoLcwCcwDcwEcwBcvocwFcwFcvocwGcvocwHcwIcvocoLcqPcngaaacwPcwUcwQcwVcwNcwOcwWcwXcwRcwScwTcwZcwYcxbcxacxdcxccxfcxecxjcxgcvHcxkcxmcxmcxmcxocvIckScmJchfdgmcQkcQkcQkcQkcpwcpxcQkcpycpycpycpycQkcpzcpBcpAcQkcQncQncpCcpDcuocukbQXbQXcpEbQXbQXbQXbQXbQXcpFcpRcpRcpRcpRcpGcpRcpRcpRcxIcpHcpIcxKcxUcxMcxNcxOcxWcxQcxNbGGcuNcylbGGbGGcxScxZcyactFbGGbGGbGGbGGbGGbGGbGGcxVcymbZZaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacieckbckbckbckbckbckbcssckbcwMckbckbckbckbckbciecieaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacxXcxYcxYcxYcxYcxYcxYcyocyncybcyccydcyecyfcwycsucygcgQcoLcoLcwBcoLcoLcoLcwDcqBcwBcvocwFcyicwIcwIcyjcwIcykcvocypcqPcngaaacyrcytcyscyvcyqcwOcywcyAcyycyucvxcyCcsHcyEcvBcyxctYcyFctYcyzcyGcyBcyHcyDcyDcyDcyIcvIckScmJchfcQkcQkcpKcpJcQkcQkcQkcQkcpMcpLcpOcpNcQkcQkcpPcQkcQkcTBdgmbQVcpScuvcqMcqMcqMcqMcqMcqMcqNbQXcqWcqUcqXcqXcqXcqYcrccrbcrbcrfcrbcrbcrbcrgczmczmcricrhcrjczwczzczyczBczAczCczCczDczCczCczCczCczGczIczCczCczCczOczLbGGbZZaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacieciecqDckbckbckbckbckbckbckbckbckbckbckbciecieaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm index c6ffc61495e..3f360d5bad7 100644 --- a/code/__DEFINES/math.dm +++ b/code/__DEFINES/math.dm @@ -32,6 +32,8 @@ #define ToDegrees(radians) ((radians) * 57.2957795) // 180 / Pi #define ToRadians(degrees) ((degrees) * 0.0174532925) // Pi / 180 +#define SHORT_REAL_LIMIT 16777216 + //"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks //percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio) //collapsed to percent_of_tick_used * tick_lag diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 4b97b67e27e..0db57b27eae 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -31,6 +31,9 @@ /proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b) return a.priority - b.priority +/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b) + return a.timeToRun - b.timeToRun + /proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B) . = B.hard_delete_time - A.hard_delete_time if(!.) diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm index 8171dd66427..136e573a25e 100644 --- a/code/__HELPERS/qdel.dm +++ b/code/__HELPERS/qdel.dm @@ -1,5 +1,10 @@ -#define QDEL_IN(item, time) addtimer(GLOBAL_PROC, "qdel", time, FALSE, item) +#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) +#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) #define QDEL_NULL(item) if(item) { qdel(item); item = null } #define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } +#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE) #define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); } #define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } + +/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly. + QDEL_LIST(L) \ No newline at end of file diff --git a/code/controllers/Processes/nano_mob_hunter.dm b/code/controllers/Processes/nano_mob_hunter.dm index fb57b6e0bbf..dc5dcba805b 100644 --- a/code/controllers/Processes/nano_mob_hunter.dm +++ b/code/controllers/Processes/nano_mob_hunter.dm @@ -44,7 +44,7 @@ DECLARE_GLOBAL_CONTROLLER(mob_hunt, mob_hunt_server) recover_time = 3000 if(recover_time > 0) //when provided with a negative or zero valued recover_time argument, the server won't auto-restart but can be manually rebooted still //set a timer to automatically recover after recover_time has passed (can be manually restarted if you get impatient too) - addtimer(src, "auto_recover", recover_time, TRUE) + addtimer(CALLBACK(src, .proc/auto_recover), recover_time, TIMER_UNIQUE) /datum/controller/process/mob_hunt/proc/client_mob_update() var/list/ex_players = list() diff --git a/code/controllers/Processes/timer.dm b/code/controllers/Processes/timer.dm deleted file mode 100644 index 4b42d69221d..00000000000 --- a/code/controllers/Processes/timer.dm +++ /dev/null @@ -1,100 +0,0 @@ -var/global/datum/controller/process/timer/timer_master - -/datum/controller/process/timer - var/list/processing_timers = list() - var/list/hashes = list() - -/datum/controller/process/timer/setup() - name = "timer" - schedule_interval = 1 //every 0.1 seconds--2 server ticks - log_startup_progress("Timer process starting up.") - -/datum/controller/process/timer/statProcess() - ..() - stat(null, "[processing_timers.len] timers") - -/datum/controller/process/timer/doWork() - if(!processing_timers.len) - disabled = 1 //nothing to do, lets stop firing. - return - for(last_object in processing_timers) - var/datum/timedevent/event = last_object - if(!event.thingToCall || check_datum_qdeleted(event.thingToCall)) - qdel(event) - if(event.timeToRun <= world.time) - runevent(event) - qdel(event) - SCHECK - -DECLARE_GLOBAL_CONTROLLER(timer, timer_master) - -/datum/controller/process/timer/proc/runevent(datum/timedevent/event) - set waitfor = 0 - if(event.thingToCall) - if(event.thingToCall == GLOBAL_PROC && istext(event.procToCall)) - call("/proc/[event.procToCall]")(arglist(event.argList)) - else - call(event.thingToCall, event.procToCall)(arglist(event.argList)) - -/datum/timedevent - var/thingToCall - var/procToCall - var/timeToRun - var/argList - var/id - var/hash - var/static/nextid = 1 - -/datum/timedevent/New() - id = nextid++ - -/datum/timedevent/Destroy() - timer_master.processing_timers -= src - timer_master.hashes -= hash - return QDEL_HINT_IWILLGC - -/proc/addtimer(thingToCall, procToCall, wait, unique = FALSE, ...) - if(!timer_master) //can't run timers before the mc has been created - return - if(!thingToCall || !procToCall) - return - if(timer_master.disabled) - timer_master.disabled = 0 - - var/datum/timedevent/event = new() - event.thingToCall = thingToCall - event.procToCall = procToCall - event.timeToRun = world.time + wait - var/hashlist = args.Copy() - - hashlist[1] = "[thingToCall](\ref[thingToCall])" - event.hash = jointext(hashlist, null) - if(args.len > 4) - event.argList = args.Copy(5) - - // Check for dupes if unique = 1. - if(unique) - var/datum/timedevent/hash_event = timer_master.hashes[event.hash] - if(hash_event) - return hash_event.id - timer_master.hashes[event.hash] = event - if(wait <= 0) - timer_master.runevent(event) - timer_master.hashes -= event.hash - return - // If we are unique (or we're not checking that), add the timer and return the id. - timer_master.processing_timers += event - - return event.id - -/proc/deltimer(id) - if(id == 0) - // No event will correspond to an id of 0 - the timer does not exist - // Save us a possibly expensive iteration through the timer list - // This would probably be more efficient in general if we used an associative list instead - return 0 - for(var/datum/timedevent/event in timer_master.processing_timers) - if(event.id == id) - qdel(event) - return 1 - return 0 diff --git a/code/controllers/Processes/weather.dm b/code/controllers/Processes/weather.dm index 1706b2232c0..919352313c6 100644 --- a/code/controllers/Processes/weather.dm +++ b/code/controllers/Processes/weather.dm @@ -37,7 +37,7 @@ var/global/datum/controller/process/weather/weather_master var/datum/weather/W = pickweight(possible_weather_for_this_z) run_weather(W.name) eligible_zlevels -= Z - addtimer(src, "make_z_eligible", rand(3000, 6000) + W.weather_duration_upper, TRUE, Z) //Around 5-10 minutes between weathers + addtimer(CALLBACK(src, .proc/make_z_eligible, Z), rand(3000, 6000) + W.weather_duration_upper, TIMER_UNIQUE) //Around 5-10 minutes between weathers DECLARE_GLOBAL_CONTROLLER(weather, weather_master) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index b71531ee20f..b35bc300434 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -202,6 +202,9 @@ var/randomize_shift_time = FALSE var/enable_night_shifts = FALSE + // Developer + var/developer_express_start = 0 + /datum/configuration/New() for(var/T in subtypesof(/datum/game_mode)) var/datum/game_mode/M = T @@ -612,6 +615,8 @@ config.high_pop_mc_mode_amount = text2num(value) if("disable_high_pop_mc_mode_amount") config.disable_high_pop_mc_mode_amount = text2num(value) + if("developer_express_start") + config.developer_express_start = 1 else log_config("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 518d27d3c0a..9cb84f26aa5 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -54,7 +54,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/restart_clear = 0 var/static/restart_timeout = 0 var/static/restart_count = 0 - + var/static/random_seed //current tick limit, assigned before running a subsystem. @@ -70,7 +70,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(!random_seed) random_seed = rand(1, 1e9) rand_seed(random_seed) - + var/list/_subsystems = list() subsystems = _subsystems if(Master != src) @@ -194,6 +194,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new to_chat(world, "[msg]") log_world(msg) + if(config.developer_express_start & ticker.current_state == GAME_STATE_PREGAME) + ticker.current_state = GAME_STATE_SETTING_UP + if(!current_runlevel) SetRunLevel(1) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm new file mode 100644 index 00000000000..21fea2ee419 --- /dev/null +++ b/code/controllers/subsystem/timer.dm @@ -0,0 +1,518 @@ +#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth) +#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN) + 1) +#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1))) +#define TIMER_ID_MAX (2**24) //max float with integer precision + +SUBSYSTEM_DEF(timer) + name = "Timer" + wait = 1 //SS_TICKER subsystem, so wait is in ticks + init_order = INIT_ORDER_TIMER + + flags = SS_TICKER|SS_NO_INIT + + var/list/second_queue = list() //awe, yes, you've had first queue, but what about second queue? Contains: /datum/timedevent + var/list/hashes = list() + + var/head_offset = 0 //world.time of the first entry in the the bucket. + var/practical_offset = 1 //index of the first non-empty item in the bucket. + var/bucket_resolution = 0 //world.tick_lag the bucket was designed for + var/bucket_count = 0 //how many timers are in the buckets + + var/list/bucket_list = list() //list of buckets, each bucket holds every timer that has to run that byond tick. + + var/list/timer_id_dict = list() //list of all active timers assoicated to their timer id (for easy lookup) + + var/list/clienttime_timers = list() //special snowflake timers that run on fancy pansy "client time" + + var/last_invoke_tick = 0 + var/static/last_invoke_warning = 0 + var/static/bucket_auto_reset = TRUE + +/datum/controller/subsystem/timer/PreInit() + bucket_list.len = BUCKET_LEN + head_offset = world.time + bucket_resolution = world.tick_lag + +/datum/controller/subsystem/timer/stat_entry(msg) + ..("B:[bucket_count] P:[length(second_queue)] H:[length(hashes)] C:[length(clienttime_timers)] S:[length(timer_id_dict)]") + +/datum/controller/subsystem/timer/fire(resumed = FALSE) + var/lit = last_invoke_tick + var/last_check = world.time - TIMER_NO_INVOKE_WARNING + var/list/bucket_list = src.bucket_list + + if(!bucket_count) + last_invoke_tick = world.time + + if(lit && lit < last_check && last_invoke_warning < last_check) + last_invoke_warning = world.time + var/msg = "No regular timers processed in the last [TIMER_NO_INVOKE_WARNING] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!" + message_admins(msg) + WARNING(msg) + if(bucket_auto_reset) + bucket_resolution = 0 + + log_world("Timer bucket reset. world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + for(var/i in 1 to length(bucket_list)) + var/datum/timedevent/bucket_head = bucket_list[i] + if(!bucket_head) + continue + + log_world("Active timers at index [i]:") + + var/datum/timedevent/bucket_node = bucket_head + var/anti_loop_check = 1000 + do + log_world(get_timer_debug_string(bucket_node)) + bucket_node = bucket_node.next + anti_loop_check-- + while(bucket_node && bucket_node != bucket_head && anti_loop_check) + log_world("Active timers in the second_queue queue:") + for(var/I in second_queue) + log_world(get_timer_debug_string(I)) + + var/next_clienttime_timer_index = 0 + var/len = length(clienttime_timers) + + for(next_clienttime_timer_index in 1 to len) + if(MC_TICK_CHECK) + next_clienttime_timer_index-- + break + var/datum/timedevent/ctime_timer = clienttime_timers[next_clienttime_timer_index] + if(ctime_timer.timeToRun > REALTIMEOFDAY) + next_clienttime_timer_index-- + break + + var/datum/callback/callBack = ctime_timer.callBack + if(!callBack) + clienttime_timers.Cut(next_clienttime_timer_index,next_clienttime_timer_index+1) + CRASH("Invalid timer: [get_timer_debug_string(ctime_timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset], REALTIMEOFDAY: [REALTIMEOFDAY]") + + ctime_timer.spent = REALTIMEOFDAY + callBack.InvokeAsync() + qdel(ctime_timer) + + + if(next_clienttime_timer_index) + clienttime_timers.Cut(1,next_clienttime_timer_index+1) + + if(MC_TICK_CHECK) + return + + var/static/list/spent = list() + var/static/datum/timedevent/timer + if(practical_offset > BUCKET_LEN) + head_offset += TICKS2DS(BUCKET_LEN) + practical_offset = 1 + resumed = FALSE + + if((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution)) + reset_buckets() + bucket_list = src.bucket_list + resumed = FALSE + + + if(!resumed) + timer = null + + while(practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time) + var/datum/timedevent/head = bucket_list[practical_offset] + if(!timer || !head || timer == head) + head = bucket_list[practical_offset] + timer = head + while(timer) + var/datum/callback/callBack = timer.callBack + if(!callBack) + bucket_resolution = null //force bucket recreation + CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + + if(!timer.spent) + spent += timer + timer.spent = world.time + callBack.InvokeAsync() + last_invoke_tick = world.time + + if(MC_TICK_CHECK) + return + + timer = timer.next + if(timer == head) + break + + + bucket_list[practical_offset++] = null + + //we freed up a bucket, lets see if anything in second_queue needs to be shifted to that bucket. + var/i = 0 + var/L = length(second_queue) + for(i in 1 to L) + timer = second_queue[i] + if(timer.timeToRun >= TIMER_MAX) + i-- + break + + if(timer.timeToRun < head_offset) + bucket_resolution = null //force bucket recreation + CRASH("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + + if(timer.callBack && !timer.spent) + timer.callBack.InvokeAsync() + spent += timer + bucket_count++ + else if(!QDELETED(timer)) + qdel(timer) + continue + + if(timer.timeToRun < head_offset + TICKS2DS(practical_offset)) + bucket_resolution = null //force bucket recreation + CRASH("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + if(timer.callBack && !timer.spent) + timer.callBack.InvokeAsync() + spent += timer + bucket_count++ + else if(!QDELETED(timer)) + qdel(timer) + continue + + bucket_count++ + var/bucket_pos = max(1, BUCKET_POS(timer)) + + var/datum/timedevent/bucket_head = bucket_list[bucket_pos] + if(!bucket_head) + bucket_list[bucket_pos] = timer + timer.next = null + timer.prev = null + continue + + if(!bucket_head.prev) + bucket_head.prev = bucket_head + timer.next = bucket_head + timer.prev = bucket_head.prev + timer.next.prev = timer + timer.prev.next = timer + if(i) + second_queue.Cut(1, i+1) + + timer = null + + bucket_count -= length(spent) + + for(var/spent_timer in spent) + qdel(spent_timer) + + spent.len = 0 + +//formated this way to be runtime resistant +/datum/controller/subsystem/timer/proc/get_timer_debug_string(datum/timedevent/TE) + . = "Timer: [TE]" + . += "Prev: [TE.prev ? TE.prev : "NULL"], Next: [TE.next ? TE.next : "NULL"]" + if(TE.spent) + . += ", SPENT([TE.spent])" + if(QDELETED(TE)) + . += ", QDELETED" + if(!TE.callBack) + . += ", NO CALLBACK" + +/datum/controller/subsystem/timer/proc/reset_buckets() + var/list/bucket_list = src.bucket_list + var/list/alltimers = list() + //collect the timers currently in the bucket + for(var/bucket_head in bucket_list) + if(!bucket_head) + continue + var/datum/timedevent/bucket_node = bucket_head + do + alltimers += bucket_node + bucket_node = bucket_node.next + while(bucket_node && bucket_node != bucket_head) + + bucket_list.len = 0 + bucket_list.len = BUCKET_LEN + + practical_offset = 1 + bucket_count = 0 + head_offset = world.time + bucket_resolution = world.tick_lag + + alltimers += second_queue + if(!length(alltimers)) + return + + sortTim(alltimers, .proc/cmp_timer) + + var/datum/timedevent/head = alltimers[1] + + if(head.timeToRun < head_offset) + head_offset = head.timeToRun + + var/new_bucket_count + var/i = 1 + for(i in 1 to length(alltimers)) + var/datum/timedevent/timer = alltimers[1] + if(!timer) + continue + + var/bucket_pos = BUCKET_POS(timer) + if(timer.timeToRun >= TIMER_MAX) + i-- + break + + + if(!timer.callBack || timer.spent) + WARNING("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") + if(timer.callBack) + qdel(timer) + continue + + new_bucket_count++ + var/datum/timedevent/bucket_head = bucket_list[bucket_pos] + if(!bucket_head) + bucket_list[bucket_pos] = timer + timer.next = null + timer.prev = null + continue + + if(!bucket_head.prev) + bucket_head.prev = bucket_head + timer.next = bucket_head + timer.prev = bucket_head.prev + timer.next.prev = timer + timer.prev.next = timer + if(i) + alltimers.Cut(1, i+1) + second_queue = alltimers + bucket_count = new_bucket_count + + +/datum/controller/subsystem/timer/Recover() + second_queue |= SStimer.second_queue + hashes |= SStimer.hashes + timer_id_dict |= SStimer.timer_id_dict + bucket_list |= SStimer.bucket_list + +/datum/timedevent + var/id + var/datum/callback/callBack + var/timeToRun + var/hash + var/list/flags + var/spent = 0 //time we ran the timer. + var/name //for easy debugging. + //cicular doublely linked list + var/datum/timedevent/next + var/datum/timedevent/prev + +/datum/timedevent/New(datum/callback/callBack, timeToRun, flags, hash) + var/static/nextid = 1 + id = TIMER_ID_NULL + src.callBack = callBack + src.timeToRun = timeToRun + src.flags = flags + src.hash = hash + + if(flags & TIMER_UNIQUE) + SStimer.hashes[hash] = src + + if(flags & TIMER_STOPPABLE) + id = num2text(nextid, 100) + if(nextid >= SHORT_REAL_LIMIT) + nextid += min(1, 2**round(nextid/SHORT_REAL_LIMIT)) + else + nextid++ + SStimer.timer_id_dict[id] = src + + name = "Timer: [id] (\ref[src]), TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])" + + if((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME)) + CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]") + + if(callBack.object != GLOBAL_PROC) + LAZYADD(callBack.object.active_timers, src) + + + var/list/L + + if(flags & TIMER_CLIENT_TIME) + L = SStimer.clienttime_timers + else if(timeToRun >= TIMER_MAX) + L = SStimer.second_queue + + + if(L) + //binary search sorted insert + var/cttl = length(L) + if(cttl) + var/left = 1 + var/right = cttl + var/mid = (left+right) >> 1 //rounded divide by two for hedgehogs + + var/datum/timedevent/item + while(left < right) + item = L[mid] + if(item.timeToRun <= timeToRun) + left = mid+1 + else + right = mid + mid = (left+right) >> 1 + + item = L[mid] + mid = item.timeToRun > timeToRun ? mid : mid+1 + L.Insert(mid, src) + + else + L += src + return + + //get the list of buckets + var/list/bucket_list = SStimer.bucket_list + + //calculate our place in the bucket list + var/bucket_pos = BUCKET_POS(src) + + //get the bucket for our tick + var/datum/timedevent/bucket_head = bucket_list[bucket_pos] + SStimer.bucket_count++ + //empty bucket, we will just add ourselves + if(!bucket_head) + bucket_list[bucket_pos] = src + return + //other wise, lets do a simplified linked list add. + if(!bucket_head.prev) + bucket_head.prev = bucket_head + next = bucket_head + prev = bucket_head.prev + next.prev = src + prev.next = src + +/datum/timedevent/Destroy() + ..() + if(flags & TIMER_UNIQUE && hash) + SStimer.hashes -= hash + + if(callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers) + callBack.object.active_timers -= src + UNSETEMPTY(callBack.object.active_timers) + + callBack = null + + if(flags & TIMER_STOPPABLE) + SStimer.timer_id_dict -= id + + if(flags & TIMER_CLIENT_TIME) + if(!spent) + spent = world.time + SStimer.clienttime_timers -= src + return QDEL_HINT_IWILLGC + + if(!spent) + spent = world.time + var/bucketpos = BUCKET_POS(src) + var/datum/timedevent/buckethead + var/list/bucket_list = SStimer.bucket_list + if(bucketpos > 0) + buckethead = bucket_list[bucketpos] + + if(buckethead == src) + bucket_list[bucketpos] = next + SStimer.bucket_count-- + else if(timeToRun < TIMER_MAX || next || prev) + SStimer.bucket_count-- + else + var/l = length(SStimer.second_queue) + SStimer.second_queue -= src + if(l == length(SStimer.second_queue)) + SStimer.bucket_count-- + + if(prev == next && next) + next.prev = null + prev.next = null + else + if(prev) + prev.next = next + if(next) + next.prev = prev + else + if(prev && prev.next == src) + prev.next = next + if(next && next.prev == src) + next.prev = prev + next = null + prev = null + return QDEL_HINT_IWILLGC + +/datum/timedevent/proc/getcallingtype() + . = "ERROR" + if(callBack.object == GLOBAL_PROC) + . = "GLOBAL_PROC" + else + . = "[callBack.object.type]" + +/proc/addtimer(datum/callback/callback, wait = 0, flags = 0) + if(!callback) + CRASH("addtimer called without a callback") + + if(wait < 0) + stack_trace("addtimer called with a negative wait. Converting to 0") + + //alot of things add short timers on themselves in their destroy, we ignore those cases + if(wait >= 1 && callback && callback.object && callback.object != GLOBAL_PROC && QDELETED(callback.object)) + stack_trace("addtimer called with a callback assigned to a qdeleted object") + + wait = max(wait, 0) + + if(wait >= INFINITY) + CRASH("Attempted to create timer with INFINITY delay") + + var/hash + + if(flags & TIMER_UNIQUE) + var/list/hashlist + if(flags & TIMER_NO_HASH_WAIT) + hashlist = list(callback.object, "([callback.object.UID()])", callback.delegate, flags & TIMER_CLIENT_TIME) + else + hashlist = list(callback.object, "([callback.object.UID()])", callback.delegate, wait, flags & TIMER_CLIENT_TIME) + hashlist += callback.arguments + hash = hashlist.Join("|||||||") + + var/datum/timedevent/hash_timer = SStimer.hashes[hash] + if(hash_timer) + if(hash_timer.spent) //it's pending deletion, pretend it doesn't exist. + hash_timer.hash = null //but keep it from accidentally deleting us + else + if(flags & TIMER_OVERRIDE) + hash_timer.hash = null //no need having it delete it's hash if we are going to replace it + qdel(hash_timer) + else + if(hash_timer.flags & TIMER_STOPPABLE) + . = hash_timer.id + return + + + var/timeToRun = world.time + wait + if(flags & TIMER_CLIENT_TIME) + timeToRun = REALTIMEOFDAY + wait + + var/datum/timedevent/timer = new(callback, timeToRun, flags, hash) + return timer.id + +/proc/deltimer(id) + if(!id) + return FALSE + if(id == TIMER_ID_NULL) + CRASH("Tried to delete a null timerid. Use TIMER_STOPPABLE flag") + if(!istext(id)) + if(istype(id, /datum/timedevent)) + qdel(id) + return TRUE + //id is string + var/datum/timedevent/timer = SStimer.timer_id_dict[id] + if(timer && !timer.spent) + qdel(timer) + return TRUE + return FALSE + + +#undef BUCKET_LEN +#undef BUCKET_POS +#undef TIMER_MAX +#undef TIMER_ID_MAX diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 7fcce1238a5..3315cafb399 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -91,7 +91,7 @@ debug_variables(shuttle_master) feedback_add_details("admin_verb","DShuttle") if("Timer") - debug_variables(timer_master) + debug_variables(SStimer) feedback_add_details("admin_verb","DTimer") if("Weather") debug_variables(weather_master) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 3e8c11cc1e8..ae9a6df5b4f 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -1,5 +1,6 @@ /datum var/gc_destroyed //Time when this object was destroyed. + var/list/active_timers //for SStimer var/list/datum_components //for /datum/components var/var_edited = FALSE //Warranty void if seal is broken @@ -16,6 +17,14 @@ /datum/proc/Destroy(force = FALSE, ...) tag = null + var/list/timers = active_timers + active_timers = null + for(var/thing in timers) + var/datum/timedevent/timer = thing + if(timer.spent) + continue + qdel(timer) + var/list/dc = datum_components if(dc) var/all_components = dc[/datum/component] diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm new file mode 100644 index 00000000000..2cf79ad318b --- /dev/null +++ b/code/datums/looping_sounds/looping_sound.dm @@ -0,0 +1,100 @@ +/* + output_atoms (list of atoms) The destination(s) for the sounds + + mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end. + mid_length (num) The length to wait between playing mid_sounds + + start_sound (soundfile) Played before starting the mid_sounds loop + start_length (num) How long to wait before starting the main loop after playing start_sound + + end_sound (soundfile) The sound played after the main loop has concluded + + chance (num) Chance per loop to play a mid_sound + volume (num) Sound output volume + muted (bool) Private. Used to stop the sound loop. + max_loops (num) The max amount of loops to run for. + direct (bool) If true plays directly to provided atoms instead of from them +*/ +/datum/looping_sound + var/list/atom/output_atoms + var/mid_sounds + var/mid_length + var/start_sound + var/start_length + var/end_sound + var/chance + var/volume = 100 + var/muted = TRUE + var/max_loops + var/direct + +/datum/looping_sound/New(list/_output_atoms = list(), start_immediately = FALSE, _direct = FALSE) + if(!mid_sounds) + WARNING("A looping sound datum was created without sounds to play.") + return + + output_atoms = _output_atoms + direct = _direct + + if(start_immediately) + start() + +/datum/looping_sound/Destroy() + stop() + output_atoms = null + return ..() + +/datum/looping_sound/proc/start(atom/add_thing) + if(add_thing) + output_atoms |= add_thing + if(!muted) + return + muted = FALSE + on_start() + +/datum/looping_sound/proc/stop(atom/remove_thing) + if(remove_thing) + output_atoms -= remove_thing + if(muted) + return + muted = TRUE + +/datum/looping_sound/proc/sound_loop(looped = 0) + if(muted || (max_loops && looped > max_loops)) + on_stop(looped) + return + if(!chance || prob(chance)) + play(get_sound(looped)) + addtimer(CALLBACK(src, .proc/sound_loop, ++looped), mid_length) + +/datum/looping_sound/proc/play(soundfile) + var/list/atoms_cache = output_atoms + var/sound/S = sound(soundfile) + if(direct) + S.channel = open_sound_channel() + S.volume = volume + for(var/i in 1 to atoms_cache.len) + var/atom/thing = atoms_cache[i] + if(direct) + SEND_SOUND(thing, S) + else + playsound(thing, S, volume) + +/datum/looping_sound/proc/get_sound(looped, _mid_sounds) + if(!_mid_sounds) + . = mid_sounds + else + . = _mid_sounds + while(!isfile(.) && !isnull(.)) + . = pickweight(.) + +/datum/looping_sound/proc/on_start() + var/start_wait = 0 + if(start_sound) + play(start_sound) + start_wait = start_length + addtimer(CALLBACK(src, .proc/sound_loop), start_wait) + +/datum/looping_sound/proc/on_stop(looped) + if(end_sound) + play(end_sound) \ No newline at end of file diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm new file mode 100644 index 00000000000..b1f5bdc6908 --- /dev/null +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -0,0 +1,7 @@ +/datum/looping_sound/showering + start_sound = 'sound/machines/shower/shower_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/shower/shower_mid1.ogg' = 1,'sound/machines/shower/shower_mid2.ogg' = 1,'sound/machines/shower/shower_mid3.ogg' = 1) + mid_length = 10 + end_sound = 'sound/machines/shower/shower_end.ogg' + volume = 20 \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 5fee7994a83..be35cf8cca8 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1538,8 +1538,7 @@ H.update_inv_w_uniform(0,0) add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes") - addtimer(src, "remove_zealot", convert_duration, FALSE, jumpsuit) //deconverts after the timer expires - + addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires return 1 /datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null) diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 525880ee314..74cc6235bb6 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -145,6 +145,7 @@ belt = /obj/item/gun/projectile/automatic/pistol/deagle/camo l_ear = /obj/item/radio/headset/syndicate/alt l_pocket = /obj/item/pinpointer/advpinpointer + r_pocket = null // stop them getting a radio uplink, they get an implant instead backpack_contents = list( /obj/item/storage/box/engineer = 1, @@ -158,7 +159,6 @@ id_icon = "commander" id_access = "Syndicate Operative Leader" - uplink_uses = 500 /datum/outfit/admin/syndicate/officer/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm index 43c3d3a61cd..6e3ead3849e 100644 --- a/code/datums/spells/ethereal_jaunt.dm +++ b/code/datums/spells/ethereal_jaunt.dm @@ -26,7 +26,7 @@ if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains to_chat(target, "You are somehow too bound to your current location to abandon it.") continue - addtimer(src, "do_jaunt", 0, FALSE, target) + INVOKE_ASYNC(src, .proc/do_jaunt, target) /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) target.notransform = 1 diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm index c87b2b455e7..320cc137806 100644 --- a/code/datums/weather/weather.dm +++ b/code/datums/weather/weather.dm @@ -31,7 +31,7 @@ var/list/impacted_areas = list() //Areas to be affected by the weather, calculated when the weather begins var/target_z = MAIN_STATION //The z-level to affect var/list/protected_areas = list()//Areas that are protected and excluded from the affected areas. - + var/overlay_layer = 10 //Since it's above everything else, this is the layer used by default. 2 is below mobs and walls if you need to use that. var/aesthetic = FALSE //If the weather has no purpose other than looks var/immunity_type = "storm" //Used by mobs to prevent them from being affected by the weather @@ -70,7 +70,7 @@ to_chat(M, telegraph_message) if(telegraph_sound) M << sound(telegraph_sound) - addtimer(src, "start", telegraph_duration) + addtimer(CALLBACK(src, .proc/start), telegraph_duration) /datum/weather/proc/start() if(stage >= MAIN_STAGE) @@ -85,7 +85,7 @@ if(weather_sound) M << sound(weather_sound) weather_master.processing_weather |= src - addtimer(src, "wind_down", weather_duration) + addtimer(CALLBACK(src, .proc/wind_down), weather_duration) /datum/weather/proc/wind_down() if(stage >= WIND_DOWN_STAGE) @@ -100,7 +100,7 @@ if(end_sound) M << sound(end_sound) weather_master.processing_weather -= src - addtimer(src, "end", end_duration) + addtimer(CALLBACK(src, .proc/end), end_duration) /datum/weather/proc/end() if(stage == END_STAGE) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 5f09e2d5349..19af319aa20 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -298,7 +298,7 @@ /atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked) if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - addtimer(src, "hitby_react", 2, TRUE, AM) + addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) /atom/proc/hitby_react(atom/movable/AM) if(AM && isturf(AM.loc)) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 714edc0fbfc..c7e6401e204 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -166,11 +166,11 @@ /mob/living/forceMove(atom/destination) if(buckled) - addtimer(src, "check_buckled", 1, TRUE) + addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) if(buckled_mob) - addtimer(buckled_mob, "check_buckled", 1, TRUE) + addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) if(pulling) - addtimer(src, "check_pull", 1, TRUE) + addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) . = ..() if(client) reset_perspective(destination) diff --git a/code/game/gamemodes/changeling/powers/biodegrade.dm b/code/game/gamemodes/changeling/powers/biodegrade.dm index 5e4b0e7fa12..3103416b9e4 100644 --- a/code/game/gamemodes/changeling/powers/biodegrade.dm +++ b/code/game/gamemodes/changeling/powers/biodegrade.dm @@ -18,7 +18,7 @@ return FALSE user.visible_message("[user] vomits a glob of acid on \his [O]!", \ "We vomit acidic ooze onto our restraints!") - addtimer(src, "dissolve_handcuffs", 30, FALSE, user, O) + addtimer(CALLBACK(src, .proc/dissolve_handcuffs, user, O), 30) used = TRUE if(user.wear_suit && user.wear_suit.breakouttime && !used) @@ -27,7 +27,7 @@ return FALSE user.visible_message("[user] vomits a glob of acid across the front of \his [S]!", \ "We vomit acidic ooze onto our straight jacket!") - addtimer(src, "dissolve_straightjacket", 30, FALSE, user, S) + addtimer(CALLBACK(src, .proc/dissolve_straightjacket, user, S), 30) used = TRUE @@ -37,7 +37,7 @@ return FALSE C.visible_message("[C]'s hinges suddenly begin to melt and run!") to_chat(user, "We vomit acidic goop onto the interior of [C]!") - addtimer(src, "open_closet", 70, FALSE, user, C) + addtimer(CALLBACK(src, .proc/open_closet, user, C), 70) used = TRUE if(istype(user.loc, /obj/structure/spider/cocoon) && !used) @@ -46,7 +46,7 @@ return FALSE C.visible_message("[src] shifts and starts to fall apart!") to_chat(user, "We secrete acidic enzymes from our skin and begin melting our cocoon...") - addtimer(src, "dissolve_cocoon", 25, FALSE, user, C) //Very short because it's just webs + addtimer(CALLBACK(src, .proc/dissolve_cocoon, user, C), 25) //Very short because it's just webs used = TRUE if(used) diff --git a/code/game/gamemodes/changeling/powers/fakedeath.dm b/code/game/gamemodes/changeling/powers/fakedeath.dm index 156e8ffb896..a166fd74502 100644 --- a/code/game/gamemodes/changeling/powers/fakedeath.dm +++ b/code/game/gamemodes/changeling/powers/fakedeath.dm @@ -17,7 +17,7 @@ user.status_flags |= FAKEDEATH //play dead user.update_canmove() - addtimer(src, "ready_to_regenerate", LING_FAKEDEATH_TIME, FALSE, user) + addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME) feedback_add_details("changeling_powers","FD") return 1 diff --git a/code/game/gamemodes/changeling/powers/fleshmend.dm b/code/game/gamemodes/changeling/powers/fleshmend.dm index 08786b6d6db..56417ac4335 100644 --- a/code/game/gamemodes/changeling/powers/fleshmend.dm +++ b/code/game/gamemodes/changeling/powers/fleshmend.dm @@ -31,7 +31,7 @@ by quick repeated use!") recent_uses++ - addtimer(src, "fleshmend", 0, FALSE, user) + INVOKE_ASYNC(src, .proc/fleshmend, user) feedback_add_details("changeling_powers","RR") return TRUE diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 57ce00c92af..8f4b5dab95e 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -159,9 +159,8 @@ for(var/obj/machinery/door/D in airlocks) if(!is_station_level(D.z)) continue - spawn(0) - D.hostile_lockdown(src) - addtimer(D, "disable_lockdown", 900) + INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, src) + addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) post_status("alert", "lockdown") diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index 96b83c9a8da..00bf335c576 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -43,7 +43,8 @@ to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") var/delay = (rand(350,450) + B.host.brainloss) - addtimer(src, "return_control", delay, FALSE, B) + addtimer(CALLBACK(src, .proc/return_control, B), delay) + /mob/living/captive_brain/proc/return_control(mob/living/simple_animal/borer/B) if(!B || !B.controlling) @@ -534,7 +535,7 @@ leaving = TRUE - addtimer(src, "let_go", 200) + addtimer(CALLBACK(src, .proc/let_go), 200) /mob/living/simple_animal/borer/proc/let_go() @@ -611,7 +612,7 @@ bonding = TRUE var/delay = 300+(host.getBrainLoss()*5) - addtimer(src, "assume_control", delay) + addtimer(CALLBACK(src, .proc/assume_control), delay) /mob/living/simple_animal/borer/proc/assume_control() if(!host || !src || controlling) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 907e037f72c..501f510348b 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -118,7 +118,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/Initialize() . = ..() if(closeOtherId != null) - addtimer(src, "update_other_id", 5) + addtimer(CALLBACK(src, .proc/update_other_id), 5) if(glass) airlock_material = "glass" if(security_level > AIRLOCK_SECURITY_METAL) @@ -226,13 +226,11 @@ About the new airlock wires panel: /obj/machinery/door/airlock/proc/loseMainPower() main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) if(main_power_lost_until > 0) - main_power_timer = addtimer(src, "regainMainPower", SecondsToTicks(60), 1) - + main_power_timer = addtimer(CALLBACK(src, .proc/regainMainPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE) // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running if(backup_power_lost_until == -1 && !backupPowerCablesCut()) backup_power_lost_until = world.time + SecondsToTicks(10) - backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(10), 1) - + backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(10), TIMER_UNIQUE | TIMER_STOPPABLE) // Disable electricity if required if(electrified_until && isAllPowerLoss()) electrify(0) @@ -240,7 +238,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/proc/loseBackupPower() backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) if(backup_power_lost_until > 0) - backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(60), 1) + backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE) // Disable electricity if required if(electrified_until && isAllPowerLoss()) @@ -288,8 +286,7 @@ About the new airlock wires panel: message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) if(duration != -1) - electrified_timer = addtimer(src, "electrify", SecondsToTicks(duration), 1, 0) - + electrified_timer = addtimer(CALLBACK(src, .proc/electrify, 0), SecondsToTicks(duration), TIMER_UNIQUE | TIMER_STOPPABLE) if(feedback && message) to_chat(usr, message) @@ -1080,8 +1077,7 @@ About the new airlock wires panel: // The `addtimer` system has the advantage of being cancelable if(autoclose) - autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1) - + autoclose_timer = addtimer(CALLBACK(src, .proc/autoclose), normalspeed ? auto_close_time : auto_close_time_dangerous, TIMER_UNIQUE | TIMER_STOPPABLE) return TRUE /obj/machinery/door/airlock/close(forced=0, override = 0) @@ -1098,7 +1094,7 @@ About the new airlock wires panel: for(var/turf/turf in locs) for(var/atom/movable/M in turf) if(M.density && M != src) //something is blocking the door - addtimer(src, "autoclose", 60) + addtimer(CALLBACK(src, .proc/autoclose), 60) use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people if(forced) diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index bff4bda660d..db78e8c82ef 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -150,13 +150,15 @@ /obj/machinery/door/airlock/uranium/New() ..() - addtimer(src, "radiate", event_step) + addtimer(CALLBACK(src, .proc/radiate), event_step) + /obj/machinery/door/airlock/uranium/proc/radiate() if(prob(50)) for(var/mob/living/L in range (3,src)) L.apply_effect(15,IRRADIATE,0) - addtimer(src, "radiate", event_step) + addtimer(CALLBACK(src, .proc/radiate), event_step) + /obj/machinery/door/airlock/uranium/glass opacity = 0 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 4652cde6224..78949fa0e45 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -279,7 +279,7 @@ // The `addtimer` system has the advantage of being cancelable if(autoclose) - autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1) + autoclose_timer = addtimer(CALLBACK(src, .proc/autoclose), normalspeed ? auto_close_time : auto_close_time_dangerous, TIMER_UNIQUE | TIMER_STOPPABLE) return TRUE @@ -292,7 +292,7 @@ for(var/atom/movable/M in get_turf(src)) if(M.density && M != src) //something is blocking the door if(autoclose) - addtimer(src, "autoclose", 60) + addtimer(CALLBACK(src, .proc/autoclose), 60) return operating = TRUE diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 13a2ba49ddd..d80a628927b 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -143,7 +143,7 @@ var/const/SAFETY_COOLDOWN = 100 safety_mode = 1 update_icon() L.loc = loc - addtimer(src, "reboot", SAFETY_COOLDOWN) + addtimer(CALLBACK(src, .proc/reboot), SAFETY_COOLDOWN) /obj/machinery/recycler/proc/reboot() playsound(loc, 'sound/machines/ping.ogg', 50, 0) diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index 48637f9e67f..a4c73cc8fac 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -438,7 +438,7 @@ chem_splash(get_turf(src), spread_range, list(reactants), temp_boost) // Detonate it again in one second, until it's out of juice. - addtimer(src, "detonate", 10) + addtimer(CALLBACK(src, .proc/detonate), 10) // If it's not a time release bomb, do normal explosion diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 98e7b6f342d..b990029423c 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -90,7 +90,7 @@ /obj/item/mecha_parts/mecha_equipment/proc/start_cooldown() set_ready_state(0) chassis.use_power(energy_drain) - addtimer(src, "set_ready_state", equip_cooldown, FALSE, 1) + addtimer(CALLBACK(src, .proc/set_ready_state, 1), equip_cooldown) /obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(atom/target) if(!chassis) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 02f89f6f0f6..f832b446cd5 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -44,7 +44,7 @@ var/global/list/image/splatter_cache=list() if(B.blood_DNA) blood_DNA |= B.blood_DNA.Copy() qdel(B) - dry_timer = addtimer(src, "dry", DRYING_TIME * (amount+1)) + dry_timer = addtimer(CALLBACK(src, .proc/dry), DRYING_TIME * (amount+1), TIMER_STOPPABLE) /obj/effect/decal/cleanable/blood/Destroy() if(GAMEMODE_IS_CULT) diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm index fa65008d307..7fb397297d7 100644 --- a/code/game/objects/effects/effect_system/effect_system.dm +++ b/code/game/objects/effects/effect_system/effect_system.dm @@ -51,7 +51,7 @@ would spawn and follow the beaker, even if it is carried or thrown. for(var/i in 1 to number) if(total_effects > 20) return - addtimer(src, "generate_effect", 0) + INVOKE_ASYNC(src, .proc/generate_effect) /datum/effect_system/proc/generate_effect() if(holder) @@ -67,7 +67,7 @@ would spawn and follow the beaker, even if it is carried or thrown. for(var/j in 1 to steps_amt) sleep(5) step(E,direction) - addtimer(src, "decrement_total_effect", 20) + addtimer(CALLBACK(src, .proc/decrement_total_effect), 20) /datum/effect_system/proc/decrement_total_effect() total_effects-- diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm index aa3ecd8376b..5ad00157a02 100644 --- a/code/game/objects/effects/effect_system/effects_explosion.dm +++ b/code/game/objects/effects/effect_system/effects_explosion.dm @@ -58,4 +58,4 @@ /datum/effect_system/explosion/smoke/start() ..() - addtimer(src, "create_smoke", 5) + addtimer(CALLBACK(src, .proc/create_smoke), 5) diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm index 64421b35fe9..385067e0611 100644 --- a/code/game/objects/effects/effect_system/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_smoke.dm @@ -39,7 +39,7 @@ /obj/effect/particle_effect/smoke/proc/kill_smoke() processing_objects.Remove(src) - addtimer(src, "fade_out", 0) + INVOKE_ASYNC(src, .proc/fade_out) QDEL_IN(src, 10) /obj/effect/particle_effect/smoke/process() @@ -67,7 +67,7 @@ if(C.smoke_delay) return FALSE C.smoke_delay++ - addtimer(src, "remove_smoke_delay", 10, FALSE, C) + addtimer(CALLBACK(src, .proc/remove_smoke_delay, C), 10) return TRUE /obj/effect/particle_effect/smoke/proc/remove_smoke_delay(mob/living/carbon/C) diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm index 9ecafee37f0..4cf5e79cc77 100644 --- a/code/game/objects/effects/glowshroom.dm +++ b/code/game/objects/effects/glowshroom.dm @@ -72,7 +72,7 @@ else //if on the floor, glowshroom on-floor sprite icon_state = "[base_icon_state]f" - addtimer(src, "Spread", delay, FALSE) + addtimer(CALLBACK(src, .proc/Spread), delay) /obj/structure/glowshroom/proc/Spread() var/turf/ownturf = get_turf(src) @@ -117,7 +117,7 @@ shrooms_planted++ //if we failed due to generation, don't try to plant one later if(shrooms_planted < myseed.yield) //if we didn't get all possible shrooms planted, try again later myseed.yield -= shrooms_planted - addtimer(src, "Spread", delay, FALSE) + addtimer(CALLBACK(src, .proc/Spread), delay) /obj/structure/glowshroom/proc/CalcDir(turf/location = loc) var/direction = 16 diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 3f56932371d..0e2e3cc3785 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -208,7 +208,7 @@ to_chat(I.owner, "Your photon projector implant overheats and deactivates!") I.Retract() overheat = FALSE - addtimer(src, "cooldown", flashcd * 2) + addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) /obj/item/flash/armimplant/try_use_flash(mob/user = null) if(overheat) @@ -216,7 +216,7 @@ to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") return FALSE overheat = TRUE - addtimer(src, "cooldown", flashcd) + addtimer(CALLBACK(src, .proc/cooldown), flashcd) playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) update_icon(1) return TRUE diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 003248cdfbc..530156fbd99 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -271,7 +271,7 @@ var/global/list/default_medbay_channels = list( universal_speak = 1 /mob/living/automatedannouncer/New() - lifetime_timer = addtimer(src, "autocleanup", SecondsToTicks(10)) + lifetime_timer = addtimer(CALLBACK(src, .proc/autocleanup), SecondsToTicks(10), TIMER_STOPPABLE) ..() /mob/living/automatedannouncer/Destroy() @@ -620,7 +620,7 @@ var/global/list/default_medbay_channels = list( /obj/item/radio/emp_act(severity) on = 0 disable_timer++ - addtimer(src, "enable_radio", rand(100, 200)) + addtimer(CALLBACK(src, .proc/enable_radio), rand(100, 200)) if(listening) visible_message("[src] buzzes violently!") diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 3382292bc3a..b321721b639 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -303,7 +303,7 @@ /obj/effect/decal/cleanable/ash/snappop_phoenix/New() . = ..() - addtimer(src, "respawn", respawn_time) + addtimer(CALLBACK(src, .proc/respawn), respawn_time) /obj/effect/decal/cleanable/ash/snappop_phoenix/proc/respawn() new /obj/item/toy/snappop/phoenix(get_turf(src)) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index b52a92112eb..fe132c107b0 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -385,7 +385,7 @@ RCD buzz loudly!","[src] begins \ vibrating violently!") // 5 seconds to get rid of it - addtimer(src, "detonate_pulse_explode", 50) + addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50) /obj/item/rcd/proc/detonate_pulse_explode() explosion(src, 0, 0, 3, 1, flame_range = 1) diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index 381e6177bb2..6164ed01047 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -85,7 +85,7 @@ target.overlays += image_overlay if(!nadeassembly) to_chat(user, "You plant the bomb. Timer counting down from [det_time].") - addtimer(src, "prime", det_time*10) + addtimer(CALLBACK(src, .proc/prime), det_time*10) /obj/item/grenade/plastic/suicide_act(mob/user) message_admins("[key_name_admin(user)](?) (FLW) suicided with [src.name] at ([user.x],[user.y],[user.z] - JMP)",0,1) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index c6899d71c22..de85ec64a58 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -422,7 +422,7 @@ message_admins("grenade primed by an assembly, attached by [key_name_admin(M)](?) (FLW) and last touched by [key_name_admin(last)](?) (FLW) ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).") log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])") else - addtimer(src, "prime", det_time) + addtimer(CALLBACK(src, .proc/prime), det_time) var/turf/DT = get_turf(src) var/area/DA = get_area(DT) log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])") diff --git a/code/game/objects/items/weapons/grenades/ghettobomb.dm b/code/game/objects/items/weapons/grenades/ghettobomb.dm index d24f93736e5..d0dc2c8fe06 100644 --- a/code/game/objects/items/weapons/grenades/ghettobomb.dm +++ b/code/game/objects/items/weapons/grenades/ghettobomb.dm @@ -53,7 +53,7 @@ if(iscarbon(user)) var/mob/living/carbon/C = user C.throw_mode_on() - addtimer(src, "prime", det_time) + addtimer(CALLBACK(src, .proc/prime), det_time) /obj/item/grenade/iedcasing/prime() //Blowing that can up update_mob() diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm index b238899676b..e6ba57ff8da 100644 --- a/code/game/objects/items/weapons/legcuffs.dm +++ b/code/game/objects/items/weapons/legcuffs.dm @@ -132,7 +132,7 @@ /obj/item/restraints/legcuffs/beartrap/energy/New() ..() - addtimer(src, "dissipate", 100) + addtimer(CALLBACK(src, .proc/dissipate), 100) /obj/item/restraints/legcuffs/beartrap/energy/proc/dissipate() if(!ismob(loc)) diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 0738d57e8fb..fcd233318d5 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -162,7 +162,7 @@ being_shocked = TRUE var/power_bounced = power * 0.5 tesla_zap(src, 3, power_bounced) - addtimer(src, "reset_shocked", 10) + addtimer(CALLBACK(src, .proc/reset_shocked), 10) /obj/proc/reset_shocked() being_shocked = FALSE diff --git a/code/game/objects/structures/barsign.dm b/code/game/objects/structures/barsign.dm index 6c627bb40ce..d392deeec81 100644 --- a/code/game/objects/structures/barsign.dm +++ b/code/game/objects/structures/barsign.dm @@ -105,7 +105,7 @@ to_chat(user, "Nothing interesting happens!") return to_chat(user, "You emag the barsign. Takeover in progress...") - addtimer(src, "post_emag", 100) + addtimer(CALLBACK(src, .proc/post_emag), 100) /obj/structure/sign/barsign/proc/post_emag() if(broken || emagged) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 284001de2bf..d5dccaf0d03 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -146,6 +146,7 @@ return ..() /obj/structure/grille/proc/build_window(obj/item/stack/sheet/S, mob/user) + var/dir_to_set = NORTH if(!istype(S) || !user) return if(broken) @@ -160,59 +161,40 @@ if(!getRelativeDirection(src, user) && (user.loc != loc)) //essentially a cardinal direction adjacent or sharing same loc check to_chat(user, "You can't reach.") return - if(/obj/structure/window/full in loc) //check for a full window already present (blocks the whole tile) - to_chat(user, "There is already a full window there.") - return - var/selection = alert(user, "What type of window would you like to place?", "Window Construction", "One Direction", "Full", "Cancel") - if(selection == "Cancel") - return - if(selection == "Full") - if(S.get_amount() < 2) - to_chat(user, "You need at least two sheets of glass for that!") + if(loc == user.loc) + dir_to_set = user.dir + else + if(x == user.x) + if(y > user.y) + dir_to_set = SOUTH + else + dir_to_set = NORTH + else if(y == user.y) + if(x > user.x) + dir_to_set = WEST + else + dir_to_set = EAST + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set) + to_chat(user, "There is already a window facing this way there.") return - if(do_after(user, 20, target = src)) //glass doesn't have a toolspeed, so no multiplier - if(broken || !anchored || !src) //make sure the grille is still intact, anchored, and exists! - return - if(S.get_amount() < 2) //make sure we still have enough for this! - return - if(!getRelativeDirection(src, user) && (user.loc != loc)) //make sure we can still do this from our location - return - var/obj/structure/window/W = new S.full_window(get_turf(src)) - S.use(2) - W.anchored = 0 - W.state = 0 - to_chat(user, "You place [W] on [src].") - W.update_icon() - return - if(selection == "One Direction") - var/dir_selection = input("Which direction will this window face?", "Direction") as null|anything in list("north", "east", "south", "west") - if(!dir_selection) + to_chat(user, "You start placing the window...") + if(do_after(user, 20, target = src)) + if(!loc || !anchored) //Grille destroyed or unanchored while waiting return - var/temp_dir = text2dir(dir_selection) - for(var/obj/structure/window/W in loc) - if(istype(W, /obj/structure/window/full)) //double checking in case a full window was created while selecting direction - to_chat(user, "There is already a full window there.") + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. + to_chat(user, "There is already a window facing this way there.") return - if(W.dir == temp_dir) //to avoid building a window on top of an existing window - to_chat(user, "There is already a window facing this direction there.") - return - if(do_after(user, 20, target = src)) - if(broken || !anchored || !src) //make sure the grille is still intact, anchored, and exists! - return - if(S.get_amount() < 1) //make sure we still have enough fir this! - to_chat(user, "You need at least one sheet of glass for that!") - return - if(!getRelativeDirection(src, user) && (user.loc != loc)) //make sure we can still do this from our location - return - var/obj/structure/window/W = new S.created_window(get_turf(src)) - S.use(1) - W.setDir(temp_dir) - W.ini_dir = temp_dir - W.anchored = 0 - W.state = 0 - to_chat(user, "You place [W] on [src].") - W.update_icon() - return + var/obj/structure/window/W = new S.created_window(get_turf(src)) + S.use(1) + W.setDir(dir_to_set) + W.ini_dir = dir_to_set + W.anchored = 0 + W.state = 0 + to_chat(user, "You place the [W] on [src].") + W.update_icon() + return /obj/structure/grille/attacked_by(obj/item/I, mob/living/user) user.changeNext_move(CLICK_CD_MELEE) diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm new file mode 100644 index 00000000000..0de1a09a74c --- /dev/null +++ b/code/game/objects/structures/guillotine.dm @@ -0,0 +1,235 @@ +#define GUILLOTINE_BLADE_MAX_SHARP 10 // This is maxiumum sharpness that will decapitate without failure +#define GUILLOTINE_DECAP_MIN_SHARP 7 // Minimum amount of sharpness for decapitation. Any less and it will just deal brute damage +#define GUILLOTINE_ANIMATION_LENGTH 9 // How many deciseconds the animation is +#define GUILLOTINE_BLADE_RAISED 1 +#define GUILLOTINE_BLADE_MOVING 2 +#define GUILLOTINE_BLADE_DROPPED 3 +#define GUILLOTINE_BLADE_SHARPENING 4 +#define GUILLOTINE_HEAD_OFFSET 16 // How much we need to move the player to center their head +#define GUILLOTINE_LAYER_DIFF 1.2 // How much to increase/decrease a head when it's buckled/unbuckled +#define GUILLOTINE_ACTIVATE_DELAY 30 // Delay for executing someone +#define GUILLOTINE_WRENCH_DELAY 10 +#define GUILLOTINE_ACTION_INUSE 5 +#define GUILLOTINE_ACTION_WRENCH 6 + +/obj/structure/guillotine + name = "guillotine" + desc = "A large structure used to remove the heads of traitors and treasonists." + icon = 'icons/obj/guillotine.dmi' + icon_state = "guillotine_raised" + can_buckle = TRUE + anchored = TRUE + density = FALSE + buckle_lying = FALSE + layer = ABOVE_MOB_LAYER + var/blade_status = GUILLOTINE_BLADE_RAISED + var/blade_sharpness = GUILLOTINE_BLADE_MAX_SHARP // How sharp the blade is + var/kill_count = 0 + var/force_clap = FALSE //You WILL clap if I want you to + var/current_action = 0 // What's currently happening to the guillotine + +/obj/structure/guillotine/examine(mob/user) + ..() + + var/msg = "" + + msg += "It is [anchored ? "wrenched to the floor." : "unsecured. A wrench should fix that."]
" + + if(blade_status == GUILLOTINE_BLADE_RAISED) + msg += "The blade is raised, ready to fall, and" + + if(blade_sharpness >= GUILLOTINE_DECAP_MIN_SHARP) + msg += " looks sharp enough to decapitate without any resistance." + else + msg += " doesn't look particularly sharp. Perhaps a whetstone can be used to sharpen it." + else + msg += "The blade is hidden inside the stocks." + + if(has_buckled_mobs()) + msg += "
" + msg += "Someone appears to be strapped in. You can help them out, or you can harm them by activating the guillotine." + + to_chat(user, msg) + + return msg + +/obj/structure/guillotine/attack_hand(mob/user) + add_fingerprint(user) + + // Currently being used by something + if(current_action) + return + + switch(blade_status) + if(GUILLOTINE_BLADE_MOVING) + return + if(GUILLOTINE_BLADE_DROPPED) + blade_status = GUILLOTINE_BLADE_MOVING + icon_state = "guillotine_raise" + addtimer(CALLBACK(src, .proc/raise_blade), GUILLOTINE_ANIMATION_LENGTH) + return + if(GUILLOTINE_BLADE_RAISED) + if(has_buckled_mobs()) + if(user.a_intent == INTENT_HARM) + user.visible_message("[user] begins to pull the lever!", + "You begin to the pull the lever.") + current_action = GUILLOTINE_ACTION_INUSE + + if(do_after(user, GUILLOTINE_ACTIVATE_DELAY, target = src) && blade_status == GUILLOTINE_BLADE_RAISED) + current_action = 0 + blade_status = GUILLOTINE_BLADE_MOVING + icon_state = "guillotine_drop" + playsound(src, 'sound/items/unsheath.ogg', 100, 1) + addtimer(CALLBACK(src, .proc/drop_blade, user), GUILLOTINE_ANIMATION_LENGTH - 2) // Minus two so we play the sound and decap faster + else + current_action = 0 + else + unbuckle_mob() + else + blade_status = GUILLOTINE_BLADE_MOVING + icon_state = "guillotine_drop" + playsound(src, 'sound/items/unsheath.ogg', 100, 1) + addtimer(CALLBACK(src, .proc/drop_blade), GUILLOTINE_ANIMATION_LENGTH) + +/obj/structure/guillotine/proc/raise_blade() + blade_status = GUILLOTINE_BLADE_RAISED + icon_state = "guillotine_raised" + +/obj/structure/guillotine/proc/drop_blade(mob/user) + if(has_buckled_mobs() && blade_sharpness) + var/mob/living/carbon/human/H = buckled_mob + + if(!H) + blade_status = GUILLOTINE_BLADE_DROPPED + icon_state = "guillotine" + return + + var/obj/item/organ/external/head/head = H.get_organ("head") + + if(QDELETED(head)) + blade_status = GUILLOTINE_BLADE_DROPPED + icon_state = "guillotine" + return + + playsound(src, 'sound/weapons/bladeslice.ogg', 100, 1) + if(blade_sharpness >= GUILLOTINE_DECAP_MIN_SHARP || head.brute_dam >= 100) + head.droplimb() + add_attack_logs(user, H, "beheaded with [src]") + H.regenerate_icons() + unbuckle_mob() + kill_count += 1 + + var/blood_overlay = "bloody" + + if(kill_count == 2) + blood_overlay = "bloodier" + else if(kill_count > 2) + blood_overlay = "bloodiest" + + blood_overlay = "guillotine_" + blood_overlay + "_overlay" + overlays.Cut() + overlays += mutable_appearance(icon, blood_overlay) + + if(force_clap) + // The crowd is pleased + // The delay is to make large crowds have a longer lasting applause + var/delay_offset = 0 + for(var/mob/living/carbon/human/HM in viewers(src, 7)) + addtimer(CALLBACK(HM, /mob/.proc/emote, "clap"), delay_offset * 0.3) + delay_offset++ + else + H.apply_damage(15 * blade_sharpness, BRUTE, head) + add_attack_logs(user, H, "non-fatally dropped the blade on with [src]") + H.emote("scream") + + if(blade_sharpness > 1) + blade_sharpness -= 1 + + blade_status = GUILLOTINE_BLADE_DROPPED + icon_state = "guillotine" + +/obj/structure/guillotine/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/whetstone)) + add_fingerprint(user) + if(blade_status == GUILLOTINE_BLADE_SHARPENING) + return + + if(blade_status == GUILLOTINE_BLADE_RAISED) + if(blade_sharpness < GUILLOTINE_BLADE_MAX_SHARP) + blade_status = GUILLOTINE_BLADE_SHARPENING + if(do_after(user, 7, target = src)) + blade_status = GUILLOTINE_BLADE_RAISED + user.visible_message("[user] sharpens the large blade of the guillotine.", + "You sharpen the large blade of the guillotine.") + blade_sharpness += 1 + playsound(src, 'sound/items/Screwdriver.ogg', 100, 1) + return + else + blade_status = GUILLOTINE_BLADE_RAISED + return + else + to_chat(user, "The blade is sharp enough!") + return + else + to_chat(user, "You need to raise the blade in order to sharpen it!") + return + if(iswrench(W)) + if(current_action) + return + + current_action = GUILLOTINE_ACTION_WRENCH + + if(do_after(user, GUILLOTINE_WRENCH_DELAY, target = src)) + current_action = 0 + if(has_buckled_mobs()) + to_chat(user, "Can't unfasten, someone's strapped in!") + return + + if(current_action) + return + to_chat(user, "You [anchored ? "un" : ""]secure [src].") + anchored = !anchored + playsound(src, 'sound/items/deconstruct.ogg', 50, 1) + dir = SOUTH + return TRUE + else + current_action = 0 + else + return ..() + +/obj/structure/guillotine/buckle_mob(mob/living/M, force = 0) + if(!anchored) + to_chat(usr, "The [src] needs to be wrenched to the floor!") + return FALSE + + if(!ishuman(M)) + to_chat(usr, "It doesn't look like they can fit into this properly!") + return FALSE // Can't decapitate non-humans + + if(blade_status != GUILLOTINE_BLADE_RAISED) + to_chat(usr, "You need to raise the blade before buckling someone in!") + return FALSE + + if(..()) + M.pixel_y -= GUILLOTINE_HEAD_OFFSET // Offset their body so it looks like they're in the guillotine + M.layer += GUILLOTINE_LAYER_DIFF + +/obj/structure/guillotine/unbuckle_mob(force = 0) + if(buckled_mob) + buckled_mob.pixel_y += GUILLOTINE_HEAD_OFFSET // Move their body back + buckled_mob.layer -= GUILLOTINE_LAYER_DIFF + . = ..() + +#undef GUILLOTINE_BLADE_MAX_SHARP +#undef GUILLOTINE_DECAP_MIN_SHARP +#undef GUILLOTINE_ANIMATION_LENGTH +#undef GUILLOTINE_BLADE_RAISED +#undef GUILLOTINE_BLADE_MOVING +#undef GUILLOTINE_BLADE_DROPPED +#undef GUILLOTINE_BLADE_SHARPENING +#undef GUILLOTINE_HEAD_OFFSET +#undef GUILLOTINE_LAYER_DIFF +#undef GUILLOTINE_ACTIVATE_DELAY +#undef GUILLOTINE_WRENCH_DELAY +#undef GUILLOTINE_ACTION_INUSE +#undef GUILLOTINE_ACTION_WRENCH \ No newline at end of file diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 002bbb566d6..367be27b6dc 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -376,7 +376,7 @@ return // Don't break if they're just flying past if(AM.throwing) - addtimer(src, "throw_check", 5, FALSE, AM) + addtimer(CALLBACK(src, .proc/throw_check, AM), 5) else check_break(AM) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 7cc9c97dfdd..3ea23108662 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -249,9 +249,11 @@ var/ismist = 0 //needs a var so we can make it linger~ var/watertemp = "normal" //freezing, normal, or boiling var/mobpresent = 0 //true if there is a mob on the shower's loc, this is to ease process() + var/datum/looping_sound/showering/soundloop /obj/machinery/shower/New(turf/T, newdir = SOUTH, building = FALSE) ..() + soundloop = new(list(src), FALSE) if(building) dir = newdir pixel_x = 0 @@ -264,8 +266,8 @@ layer = FLY_LAYER /obj/machinery/shower/Destroy() - if(mymist) - QDEL_NULL(mymist) + QDEL_NULL(mymist) + QDEL_NULL(soundloop) return ..() //add heat controls? when emagged, you can freeze to death in it? @@ -282,6 +284,7 @@ on = !on update_icon() if(on) + soundloop.start() if(M.loc == loc) wash(M) check_heat(M) @@ -289,6 +292,8 @@ for(var/atom/movable/G in src.loc) G.clean_blood() G.water_act(100, convertHeat(), src) + else + soundloop.stop() /obj/machinery/shower/attackby(obj/item/I as obj, mob/user as mob, params) if(I.type == /obj/item/analyzer) @@ -338,9 +343,9 @@ mist_time = 70 //7 seconds on freezing temperature to disperse existing mist if(watertemp == "boiling") mist_time = 20 //2 seconds on boiling temperature to build up mist - addtimer(src, "update_mist", mist_time) + addtimer(CALLBACK(src, .proc/update_mist), mist_time) else - addtimer(src, "update_mist", 250) //25 seconds for mist to disperse after being turned off + addtimer(CALLBACK(src, .proc/update_mist), 250) //25 seconds for mist to disperse after being turned off /obj/machinery/shower/proc/update_mist() if(on) diff --git a/code/game/sound.dm b/code/game/sound.dm index 39bf16bc697..66746ece0ae 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, frequency = null, channel = 0, pressure_affected = TRUE) +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE) if(isarea(source)) error("[source] is an area and is trying to make the sound: [soundin]") return @@ -11,7 +11,10 @@ // Looping through the player list has the added bonus of working for mobs inside containers var/sound/S = sound(get_sfx(soundin)) var/maxdistance = (world.view + extrarange) * 3 - for(var/P in player_list) + var/list/listeners = player_list + if(!ignore_walls) //these sounds don't carry through walls + listeners = listeners & hearers(maxdistance, turf_source) + for(var/P in listeners) var/mob/M = P if(!M || !M.client) continue @@ -78,13 +81,15 @@ S.y = 1 S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) - src << S + SEND_SOUND(src, S) -/client/proc/playtitlemusic() - if(!ticker || !ticker.login_music || config.disable_lobby_music) - return - if(prefs.sound & SOUND_LOBBY) - src << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBYMUSIC) // MAD JAMS +/proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, falloff = FALSE, channel = 0, pressure_affected = FALSE, sound/S) + if(!S) + S = sound(get_sfx(soundin)) + for(var/m in player_list) + if(ismob(m) && !isnewplayer(m)) + var/mob/M = m + M.playsound_local(M, null, volume, vary, frequency, falloff, channel, pressure_affected, S) /proc/open_sound_channel() var/static/next_channel = 1 //loop through the available 1024 - (the ones we reserve) channels and pray that its not still being used @@ -93,7 +98,13 @@ next_channel = 1 /mob/proc/stop_sound_channel(chan) - src << sound(null, repeat = 0, wait = 0, channel = chan) + SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = chan)) + +/client/proc/playtitlemusic() + if(!ticker || !ticker.login_music || config.disable_lobby_music) + return + if(prefs.sound & SOUND_LOBBY) + SEND_SOUND(src, sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBYMUSIC)) // MAD JAMS /proc/get_rand_frequency() return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs. diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm index 42342e0693b..e4ea98a44a6 100644 --- a/code/modules/arcade/mob_hunt/mob_avatar.dm +++ b/code/modules/arcade/mob_hunt/mob_avatar.dm @@ -21,7 +21,7 @@ update_self() forceMove(mob_info.spawn_point) if(!mob_info.is_trap) - addtimer(src, "despawn", mob_info.lifetime) + addtimer(CALLBACK(src, .proc/despawn), mob_info.lifetime) /obj/effect/nanomob/proc/update_self() if(!mob_info) diff --git a/code/modules/awaymissions/mission_code/beach.dm b/code/modules/awaymissions/mission_code/beach.dm index f285d4434da..4d2384ceb67 100644 --- a/code/modules/awaymissions/mission_code/beach.dm +++ b/code/modules/awaymissions/mission_code/beach.dm @@ -12,7 +12,7 @@ var/water_timer = 0 /obj/effect/waterfall/New() - water_timer = addtimer(src, "drip", water_frequency) + water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) /obj/effect/waterfall/Destroy() if(water_timer) @@ -25,4 +25,4 @@ W.dir = dir spawn(1) W.loc = get_step(W, dir) - water_timer = addtimer(src, "drip", water_frequency) \ No newline at end of file + water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) \ No newline at end of file diff --git a/code/modules/awaymissions/mission_code/spacehotel.dm b/code/modules/awaymissions/mission_code/spacehotel.dm index 2810320aaf7..4e4f2628736 100644 --- a/code/modules/awaymissions/mission_code/spacehotel.dm +++ b/code/modules/awaymissions/mission_code/spacehotel.dm @@ -238,7 +238,7 @@ return null D.occupant = occupant - D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid) + D.roomtimer = addtimer(CALLBACK(src, .proc/process_room, roomid), PAY_INTERVAL, TIMER_STOPPABLE) vacant_rooms -= D guests[occupant] = roomid @@ -252,7 +252,7 @@ return if(D.account.charge(100, transaction_purpose = "10 minutes", dest_name = name)) - D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid) + D.roomtimer = addtimer(CALLBACK(src, .proc/process_room, roomid), PAY_INTERVAL, TIMER_STOPPABLE) else force_checkout(roomid) diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm index d81f1f7e02c..7d6275e09e4 100644 --- a/code/modules/client/preference/loadout/loadout_hat.dm +++ b/code/modules/client/preference/loadout/loadout_hat.dm @@ -152,3 +152,7 @@ /datum/gear/hat/flowerpin display_name = "hair flower" path = /obj/item/clothing/head/hairflower + +/datum/gear/hat/kitty + display_name = "kitty headband" + path = /obj/item/clothing/head/kitty diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 84226bd1f3b..f1739750a0a 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -354,3 +354,13 @@ reqs = list(/obj/item/grown/log = 5) result = /obj/structure/bonfire category = CAT_PRIMAL + +/datum/crafting_recipe/guillotine + name = "Guillotine" + result = /obj/structure/guillotine + time = 150 // Building a functioning guillotine takes time + reqs = list(/obj/item/stack/sheet/plasteel = 3, + /obj/item/stack/sheet/wood = 20, + /obj/item/stack/cable_coil = 10) + tools = list(/obj/item/screwdriver, /obj/item/wrench, /obj/item/weldingtool) + category = CAT_MISC \ No newline at end of file diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index f7135acd270..a075dbdfba3 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -696,6 +696,12 @@ user.update_inv_head() return 1 +/obj/item/clothing/head/beret/fluff/elo //V-Force_Bomber: E.L.O. + name = "E.L.O.'s medical beret" + desc = "E.L.O.s personal medical beret, issued by Nanotrassen and awarded along with her medal." + icon = 'icons/obj/custom_items.dmi' + icon_state = "elo-beret" + //////////// Suits //////////// /obj/item/clothing/suit/fluff icon = 'icons/obj/custom_items.dmi' diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm index d01b5595d92..6a1d28eaf87 100644 --- a/code/modules/flufftext/Dreaming.dm +++ b/code/modules/flufftext/Dreaming.dm @@ -11,7 +11,7 @@ dream_images += pick_n_take(dreams) dreaming++ for(var/i in 1 to dream_images.len) - addtimer(src, "experience_dream", ((i - 1) * rand(30,60)), FALSE, dream_images[i], FALSE) + addtimer(CALLBACK(src, .proc/experience_dream, dream_images[i], FALSE), ((i - 1) * rand(30,60))) return TRUE @@ -33,7 +33,7 @@ dream_images += pick_n_take(nightmares) nightmare++ for(var/i in 1 to dream_images.len) - addtimer(src, "experience_dream", ((i - 1) * rand(30,60)), FALSE, nightmares[i], TRUE) + addtimer(CALLBACK(src, .proc/experience_dream, nightmares[i], TRUE), ((i - 1) * rand(30,60))) return TRUE /mob/living/carbon/proc/handle_dreams() diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 621b8ec37ea..021db59f8a4 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -339,7 +339,7 @@ Gunshots/explosions/opening doors/less rare audio (done) s.loc = get_step(get_turf(s), get_dir(s, target)) s.Show() s.Eat() - addtimer(src, "wake_and_restore", rand(50, 100)) + addtimer(CALLBACK(src, .proc/wake_and_restore), rand(50, 100)) qdel(s) /obj/effect/hallucination/simple/singularity @@ -365,9 +365,9 @@ Gunshots/explosions/opening doors/less rare audio (done) for(var/i=0,iERROR: APC access disabled, hack attempt canceled.") - malfhacking = 0 - malfhack = null + deltimer(malfhacking) + // This proc handles cleanup of screen notifications and + // messenging the client + malfhacked(malfhack) if(aiRestorePowerRoutine) adjustOxyLoss(1) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index c040fbe1ed2..986795ccd1e 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -193,6 +193,9 @@ bots_list -= src QDEL_NULL(Radio) QDEL_NULL(access_card) + if(reset_access_timer_id) + deltimer(reset_access_timer_id) + reset_access_timer_id = null if(radio_controller && bot_filter) radio_controller.remove_object(bot_core, control_freq) QDEL_NULL(bot_core) @@ -527,7 +530,7 @@ Pass a positive integer as an argument to override a bot's default speed. turn_on() //Saves the AI the hassle of having to activate a bot manually. access_card = all_access //Give the bot all-access while under the AI's command. if(client) - reset_access_timer_id = addtimer(src, "bot_reset", 600) //if the bot is player controlled, they get the extra access for a limited time + reset_access_timer_id = addtimer(CALLBACK (src, .proc/bot_reset), 600, TIMER_OVERRIDE|TIMER_STOPPABLE) //if the bot is player controlled, they get the extra access for a limited time to_chat(src, "Priority waypoint set by [calling_ai] [caller]. Proceed to [end_area.name].
[path.len-1] meters to destination. You have been granted additional door access for 60 seconds.
") if(message) to_chat(calling_ai, "[bicon(src)] [name] called to [end_area.name]. [path.len-1] meters to destination.") diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index 3f39ce233bf..3063c5a8ce1 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -62,7 +62,7 @@ /obj/item/organ/internal/hivelord_core/New() ..() - addtimer(src, "inert_check", 2400) + addtimer(CALLBACK(src, .proc/inert_check), 2400) /obj/item/organ/internal/hivelord_core/proc/inert_check() if(!owner && !preserved) @@ -151,7 +151,7 @@ /mob/living/simple_animal/hostile/asteroid/hivelordbrood/New() ..() - addtimer(src, "death", 100) + addtimer(CALLBACK(src, .proc/death), 100) /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood name = "blood brood" diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 0b174ed5fee..ef456ddcb0e 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -270,8 +270,8 @@ var/global/list/ts_spiderling_list = list() else ts_count_alive_station++ // after 30 seconds, assuming nobody took control of it yet, offer it to ghosts. - addtimer(src, "CheckFaction", 150) - addtimer(src, "announcetoghosts", 300) + addtimer(CALLBACK(src, .proc/CheckFaction), 150) + addtimer(CALLBACK(src, .proc/announcetoghosts), 300) var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm index 9e6396dbde0..57412eb5a0d 100644 --- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm +++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm @@ -22,7 +22,7 @@ for(var/turf/T in anchors) var/datum/beam/B = Beam(T, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine) B.sleep_time = 10 //these shouldn't move, so let's slow down updates to 1 second (any slower and the deletion of the vines would be too slow) - addtimer(src, "bear_fruit", growth_time) + addtimer(CALLBACK(src, .proc/bear_fruit), growth_time) /obj/structure/alien/resin/flower_bud_enemy/proc/bear_fruit() visible_message("the plant has borne fruit!") diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 23c6e2a1f15..9c3d64ef4fc 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1004,8 +1004,7 @@ return to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") malf.malfhack = src - malf.malfhacking = addtimer(malf, "malfhacked", 600, FALSE, src) - + malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) var/obj/screen/alert/hackingapc/A A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc) A.target = src diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 8bca07ccd45..aa25105cba6 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -66,7 +66,7 @@ flick("coilhit", src) playsound(src.loc, 'sound/magic/LightningShock.ogg', 100, 1, extrarange = 5) tesla_zap(src, 5, power_produced) - addtimer(src, "reset_shocked", 10) + addtimer(CALLBACK(src, .proc/reset_shocked), 10) else ..() diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index f2ffbb9b857..9cfd526585e 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -83,7 +83,7 @@ energy_to_raise = energy_to_raise * 1.25 playsound(src.loc, 'sound/magic/lightning_chargeup.ogg', 100, 1, extrarange = 30) - addtimer(src, "new_mini_ball", 100) + addtimer(CALLBACK(src, .proc/new_mini_ball), 100) else if(energy < energy_to_lower && orbiting_balls.len) energy_to_raise = energy_to_raise / 1.25 diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm new file mode 100644 index 00000000000..f6881208a32 --- /dev/null +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -0,0 +1,130 @@ +/obj/item/gun/blastcannon + name = "pipe gun" + desc = "A pipe welded onto a gun stock, with a mechanical trigger. The pipe has an opening near the top, and there seems to be a spring loaded wheel in the hole." + icon_state = "empty_blastcannon" + var/icon_state_loaded = "loaded_blastcannon" + item_state = "blastcannon_empty" + w_class = WEIGHT_CLASS_NORMAL + force = 10 + fire_sound = 'sound/weapons/blastcannon.ogg' + needs_permit = FALSE + clumsy_check = FALSE + randomspread = FALSE + + var/obj/item/transfer_valve/bomb + +/obj/item/gun/blastcannon/Destroy() + QDEL_NULL(bomb) + return ..() + +/obj/item/gun/blastcannon/attack_self(mob/user) + if(bomb) + bomb.forceMove(user.loc) + user.put_in_hands(bomb) + user.visible_message("[user] detaches [bomb] from [src].") + bomb = null + update_icon() + return ..() + +/obj/item/gun/blastcannon/update_icon() + if(bomb) + icon_state = icon_state_loaded + name = "blast cannon" + desc = "A makeshift device used to concentrate a bomb's blast energy to a narrow wave." + else + icon_state = initial(icon_state) + name = initial(name) + desc = initial(desc) + +/obj/item/gun/blastcannon/attackby(obj/O, mob/user) + if(istype(O, /obj/item/transfer_valve)) + var/obj/item/transfer_valve/T = O + if(!T.tank_one || !T.tank_two) + to_chat(user, "What good would an incomplete bomb do?") + return FALSE + if(!user.drop_item()) + to_chat(user, "[T] seems to be stuck to your hand!") + return FALSE + user.visible_message("[user] attaches [T] to [src]!") + T.forceMove(src) + bomb = T + update_icon() + return TRUE + return ..() + +/obj/item/gun/blastcannon/proc/calculate_bomb() + if(!istype(bomb)||!istype(bomb.tank_one)||!istype(bomb.tank_two)) + return 0 + var/datum/gas_mixture/temp = new() //directional buff. + temp.volume = 60 + temp.merge(bomb.tank_one.air_contents.remove_ratio(1)) + temp.merge(bomb.tank_two.air_contents.remove_ratio(2)) + for(var/i in 1 to 6) + temp.react() + var/pressure = temp.return_pressure() + qdel(temp) + if(pressure < TANK_FRAGMENT_PRESSURE) + return 0 + return (pressure / TANK_FRAGMENT_SCALE) + +/obj/item/gun/blastcannon/afterattack(atom/target, mob/user, flag, params) + if((!bomb) || (!target) || (get_dist(get_turf(target), get_turf(user)) <= 2)) + return ..() + var/power = calculate_bomb() + QDEL_NULL(bomb) + update_icon() + var/heavy = power * 0.2 + var/medium = power * 0.5 + var/light = power + user.visible_message("[user] opens [bomb] on \his [name] and fires a blast wave at [target]!","You open [bomb] on your [name] and fire a blast wave at [target]!") + playsound(user, "explosion", 100, 1) + var/turf/starting = get_turf(user) + var/turf/targturf = get_turf(target) + message_admins("Blast wave fired from [ADMIN_COORDJMP(starting)] ([get_area_name(user, TRUE)]) at [ADMIN_COORDJMP(targturf)] ([target.name]) by [key_name_admin(user)] with power [heavy]/[medium]/[light].") + log_game("Blast wave fired from ([starting.x], [starting.y], [starting.z]) ([get_area_name(user, TRUE)]) at ([target.x], [target.y], [target.z]) ([target]) by [key_name(user)] with power [heavy]/[medium]/[light].") + var/obj/item/projectile/blastwave/BW = new(loc, heavy, medium, light) + BW.preparePixelProjectile(target, get_turf(target), user, params, 0) + BW.fire() + +/obj/item/projectile/blastwave + name = "blast wave" + icon_state = "blastwave" + damage = 0 + nodamage = FALSE + forcedodge = TRUE + range = 150 + var/heavyr = 0 + var/mediumr = 0 + var/lightr = 0 + +/obj/item/projectile/blastwave/New(loc, _h, _m, _l) + ..() + heavyr = _h + mediumr = _m + lightr = _l + +/obj/item/projectile/blastwave/Range() + ..() + var/amount_destruction = 0 + if(heavyr) + amount_destruction = EXPLODE_DEVASTATE + else if(mediumr) + amount_destruction = EXPLODE_HEAVY + else if(lightr) + amount_destruction = EXPLODE_LIGHT + if(amount_destruction && isturf(loc)) + var/turf/T = loc + for(var/thing in T.contents) + var/atom/AM = thing + if(AM && AM.simulated) + AM.ex_act(amount_destruction) + CHECK_TICK + T.ex_act(amount_destruction) + else + qdel(src) + heavyr = max(heavyr - 1, 0) + mediumr = max(mediumr - 1, 0) + lightr = max(lightr - 1, 0) + +/obj/item/projectile/blastwave/ex_act() + return \ No newline at end of file diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 22fd75b5e21..39c2e8c78fb 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -101,7 +101,7 @@ min_temp = 374 result_amount = 1 -/datum/chemical_reaction/plastication/on_reaction(datum/reagents/holder) +/datum/chemical_reaction/plastic_polymers/on_reaction(datum/reagents/holder, created_volume) var/obj/item/stack/sheet/plastic/P = new /obj/item/stack/sheet/plastic P.amount = 10 P.forceMove(get_turf(holder.my_atom)) diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index b39229ebc29..8d18df1ee07 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -887,7 +887,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, //helper proc that guarantees the wait message will not freeze the UI /obj/machinery/computer/rdconsole/proc/add_wait_message(message, delay) wait_message = message - wait_message_timer = addtimer(src, "clear_wait_message", delay, TRUE) + wait_message_timer = addtimer(CALLBACK(src, .proc/clear_wait_message), delay, TIMER_UNIQUE | TIMER_STOPPABLE) // This is here to guarantee that we never lock the console, so long as the timer // process is running diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 342931a065c..88551f49096 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -17,18 +17,18 @@ return 0 /obj/machinery/door/airlock/onShuttleMove() - . = ..() - if(!.) - return - addtimer(src, "close", 0, TRUE, 0, 1) - // Close any nearby airlocks as well - for(var/obj/machinery/door/airlock/D in orange(1, src)) - addtimer(D, "close", 0, TRUE, 0, 1) + . = ..() + if(!.) + return + INVOKE_ASYNC(src, .proc/close, 0, 1) + // Close any nearby airlocks as well + for(var/obj/machinery/door/airlock/D in orange(1, src)) + INVOKE_ASYNC(D, .proc/close, 0, 1) /obj/machinery/door/airlock/onShuttleMove() - . = ..() - if(id_tag == "s_docking_airlock") - addtimer(src, "lock", 0, TRUE) + . = ..() + if(id_tag == "s_docking_airlock") + INVOKE_ASYNC(src, .proc/lock) /mob/onShuttleMove() if(!move_on_shuttle) @@ -57,7 +57,7 @@ /obj/machinery/door/airlock/postDock(obj/docking_port/stationary/S1) . = ..() if(!S1.lock_shuttle_doors && id_tag == "s_docking_airlock") - addtimer(src, "unlock", 0, TRUE) + INVOKE_ASYNC(src, .proc/unlock) // Shuttle Rotation // /atom/proc/shuttleRotate(rotation) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 1290212a249..d98e40700df 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -290,7 +290,7 @@ if(H.nutrition >= NUTRITION_LEVEL_WELL_FED) to_chat(user, "You are already fully charged!") else - addtimer(src, "powerdraw_loop", 0, TRUE, A, H) + INVOKE_ASYNC(src, .proc/powerdraw_loop, A, H) else to_chat(user, "There is no charge to draw from that APC.") else diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 88e8341d37c..9f732fdef4d 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -7,7 +7,7 @@ return else bleedsuppress = TRUE - addtimer(src, "resume_bleeding", amount) + addtimer(CALLBACK(src, .proc/resume_bleeding), amount) /mob/living/carbon/human/proc/resume_bleeding() bleedsuppress = FALSE diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index c8ed52ec787..467f92d095e 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -26,7 +26,7 @@ var/list/GPS_list = list() emped = 1 overlays -= "working" overlays += "emp" - addtimer(src, "reboot", 300) + addtimer(CALLBACK(src, .proc/reboot), 300) /obj/item/gps/proc/reboot() emped = FALSE diff --git a/config/example/config.txt b/config/example/config.txt index d0eb704ea07..46d03c48aaa 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -368,3 +368,6 @@ HIGH_POP_MC_MODE_AMOUNT 65 ##Disengage high pop mode if player count drops below this DISABLE_HIGH_POP_MC_MODE_AMOUNT 60 + +##Uncomment to enable developer start. Auto starts the server after initialization +##DEVELOPER_EXPRESS_START \ No newline at end of file diff --git a/html/changelog.html b/html/changelog.html index 054cee07cbc..7f3af71489e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,25 @@ -->
+

30 April 2018

+

Fox McCloud updated:

+ +

FreeStylaLT updated:

+ + +

28 April 2018

+

MINIMAN10000 updated:

+ +

26 April 2018

Fox McCloud updated: