diff --git a/.gitignore b/.gitignore index a8ccd115662f..dc7913007271 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ stddef.dm # ignore midi2piano build cache /tools/midi2piano/midi2piano/obj/* + +*/node_modules/* diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 3b44b3c4c21a..a7f414a63e1c 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -582,3 +582,23 @@ CREATE TABLE `round` ( `station_name` VARCHAR(80) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Table structure for table `ckey_whitelist` +-- +DROP TABLE IF EXISTS `ckey_whitelist`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ckey_whitelist` +( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `date` DATETIME DEFAULT now() NOT NULL, + `ckey` VARCHAR(32) NOT NULL, + `adminwho` VARCHAR(32) NOT NULL, + `port` INT(5) UNSIGNED NOT NULL, + `date_start` DATETIME DEFAULT now() NOT NULL, + `date_end` DATETIME NULL, + `is_valid` BOOLEAN DEFAULT true NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql index 7d2b37211eef..49857adfa788 100644 --- a/SQL/paradise_schema_prefixed.sql +++ b/SQL/paradise_schema_prefixed.sql @@ -579,3 +579,24 @@ CREATE TABLE `SS13_round` ( `station_name` VARCHAR(80) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Table structure for table `ckey_whitelist` +-- +DROP TABLE IF EXISTS `SS13_ckey_whitelist`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `SS13_ckey_whitelist` +( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `date` DATETIME DEFAULT now() NOT NULL, + `ckey` VARCHAR(32) NOT NULL, + `adminwho` VARCHAR(32) NOT NULL, + `port` INT(5) UNSIGNED NOT NULL, + `date_start` DATETIME DEFAULT now() NOT NULL, + `date_end` DATETIME NULL, + `is_valid` BOOLEAN DEFAULT true NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + diff --git a/SQL/updates/ckey_whitelist.sql b/SQL/updates/ckey_whitelist.sql new file mode 100644 index 000000000000..e891b10ed79c --- /dev/null +++ b/SQL/updates/ckey_whitelist.sql @@ -0,0 +1,18 @@ +-- +-- Table structure for table `ckey_whitelist` +-- +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ckey_whitelist` +( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `date` DATETIME DEFAULT now() NOT NULL, + `ckey` VARCHAR(32) NOT NULL, + `adminwho` VARCHAR(32) NOT NULL, + `port` INT(5) UNSIGNED NOT NULL, + `date_start` DATETIME DEFAULT now() NOT NULL, + `date_end` DATETIME NULL, + `is_valid` BOOLEAN DEFAULT true NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index e9fb8749349e..eb057b69a91b 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -557,20 +557,24 @@ /turf/simulated/floor/plating, /area/security/main) "acD" = ( -/obj/structure/cable{ - d2 = 8; - icon_state = "0-8" - }, /obj/structure/window/reinforced/polarized{ - dir = 1; - id = "hos" + id = "hos_room" }, /obj/structure/window/reinforced/polarized{ dir = 4; - id = "hos" + id = "hos_room" }, /obj/structure/window/reinforced/polarized{ - id = "hos" + dir = 1; + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 8; + id = "hos_room" + }, +/obj/structure/cable{ + d2 = 4; + icon_state = "0-4" }, /obj/structure/grille, /turf/simulated/floor/plating, @@ -844,11 +848,24 @@ }, /area/security/prisonershuttle) "adb" = ( -/turf/simulated/floor/plasteel{ +/obj/structure/window/reinforced/polarized{ + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 1; + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ dir = 8; - icon_state = "vault" + id = "hos_room" }, -/area/security/securearmoury) +/obj/structure/cable{ + d2 = 4; + icon_state = "0-4" + }, +/obj/structure/grille, +/turf/simulated/floor/plating, +/area/security/hos) "adc" = ( /obj/machinery/door/airlock/external{ frequency = 1331; @@ -893,22 +910,50 @@ }, /area/shuttle/syndicate) "adg" = ( +/obj/structure/window/reinforced/polarized{ + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 1; + id = "hos_room" + }, /obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2"; + d1 = 2; + d2 = 8; + icon_state = "2-8"; tag = "" }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/structure/cable{ + d1 = 2; + d2 = 4; + icon_state = "2-4" }, -/area/security/securearmoury) +/obj/structure/cable{ + d2 = 2; + icon_state = "0-2" + }, +/obj/structure/grille, +/turf/simulated/floor/plating, +/area/security/hos) "adh" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/structure/window/reinforced/polarized{ + id = "hos_room" }, -/area/security/securearmoury) +/obj/structure/window/reinforced/polarized{ + dir = 1; + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 4; + id = "hos_room" + }, +/obj/structure/cable{ + d2 = 8; + icon_state = "0-8" + }, +/obj/structure/grille, +/turf/simulated/floor/plating, +/area/security/hos) "adi" = ( /obj/structure/cable{ d2 = 2; @@ -960,10 +1005,9 @@ /area/security/main) "adm" = ( /obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2"; - tag = "" + d1 = 2; + d2 = 4; + icon_state = "2-4" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -998,30 +1042,13 @@ /turf/simulated/floor/plating, /area/security/main) "adq" = ( -/obj/structure/cable{ - d2 = 2; - icon_state = "0-2"; - pixel_y = 1 - }, -/obj/structure/cable{ - d1 = 2; - d2 = 4; - icon_state = "2-4"; - tag = "" - }, -/obj/structure/window/reinforced/polarized{ +/obj/structure/table/wood, +/obj/item/flashlight/lamp, +/obj/machinery/light{ dir = 1; - id = "hos" - }, -/obj/structure/window/reinforced/polarized{ - dir = 8; - id = "hos" - }, -/obj/structure/window/reinforced/polarized{ - id = "hos" + on = 1 }, -/obj/structure/grille, -/turf/simulated/floor/plating, +/turf/simulated/floor/wood, /area/security/hos) "adr" = ( /obj/structure/cable{ @@ -1065,16 +1092,17 @@ /turf/simulated/floor/plasteel, /area/security/prisonershuttle) "adv" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmoury) +/obj/machinery/atmospherics/unary/vent_pump/on, +/turf/simulated/floor/wood, +/area/security/hos) "adw" = ( /obj/machinery/firealarm{ dir = 4; pixel_x = 24 }, +/obj/structure/table/wood, +/obj/item/paper_bin/nanotrasen, +/obj/item/pen/multi, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -1169,11 +1197,14 @@ }, /area/shuttle/syndicate) "adD" = ( -/obj/effect/decal/warning_stripes/red/partial{ - dir = 8 +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + tag = "" }, -/turf/simulated/floor/plasteel, -/area/security/main) +/turf/simulated/floor/wood, +/area/security/hos) "adE" = ( /obj/structure/cable{ d2 = 8; @@ -1195,13 +1226,11 @@ /turf/simulated/floor/plasteel, /area/security/main) "adH" = ( -/obj/structure/table/wood, -/obj/item/pen/multi, -/obj/machinery/light{ - dir = 1; - on = 1 +/obj/machinery/button/windowtint{ + id = "hos_room"; + pixel_x = -24; + pixel_y = 25 }, -/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -1211,11 +1240,9 @@ /obj/machinery/recharger{ pixel_y = 4 }, -/obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2"; - tag = "" +/obj/machinery/light{ + dir = 1; + on = 1 }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -1287,11 +1314,15 @@ /turf/simulated/floor/plasteel, /area/security/prisonershuttle) "adP" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" +/obj/effect/decal/warning_stripes/east, +/obj/machinery/atmospherics/unary/vent_scrubber{ + name = "standard air scrubber"; + on = 1; + scrub_N2O = 1; + scrub_Toxins = 1 }, -/area/security/securearmoury) +/turf/simulated/floor/wood, +/area/security/hos) "adQ" = ( /obj/structure/closet/secure_closet/security, /obj/structure/window/reinforced{ @@ -1991,9 +2022,6 @@ tag = "" }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 10 - }, /obj/machinery/door_control{ desc = "A remote control-switch to lock down the prison wing's blast doors"; id = "Prison Gate"; @@ -2017,6 +2045,11 @@ pixel_y = 17; req_access_txt = "58" }, +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 4; + initialize_directions = 11; + level = 1 + }, /turf/simulated/floor/carpet, /area/security/hos) "aeX" = ( @@ -2097,11 +2130,17 @@ }, /area/security/medbay) "afe" = ( +/obj/structure/closet/secure_closet/hos, +/obj/item/reagent_containers/food/drinks/flask/barflask, +/obj/item/megaphone, +/obj/machinery/light{ + dir = 1; + on = 1 + }, /turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" + icon_state = "dark" }, -/area/security/securearmoury) +/area/security/hos) "aff" = ( /obj/machinery/atmospherics/unary/vent_scrubber{ dir = 4; @@ -2865,17 +2904,36 @@ }, /area/security/brig) "agt" = ( -/obj/machinery/flasher/portable, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/structure/window/reinforced/polarized{ + dir = 1; + id = "hos_room" }, -/area/security/securearmoury) +/obj/structure/window/reinforced/polarized{ + dir = 4; + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 8; + id = "hos_room" + }, +/obj/structure/cable{ + d2 = 2; + icon_state = "0-2" + }, +/obj/structure/grille, +/turf/simulated/floor/plating, +/area/security/hos) "agu" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/structure/bed, +/obj/item/bedsheet/hos, +/obj/effect/landmark/start{ + name = "Head of Security" }, -/area/security/securearmoury) +/obj/machinery/newscaster/security_unit{ + pixel_x = -30 + }, +/turf/simulated/floor/wood, +/area/security/hos) "agv" = ( /obj/structure/cable{ d1 = 1; @@ -3156,11 +3214,12 @@ }, /area/security/securearmoury) "agS" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 5; + level = 1 }, -/area/security/securearmoury) +/turf/simulated/floor/wood, +/area/security/hos) "agT" = ( /obj/structure/cable{ d2 = 2; @@ -3540,10 +3599,25 @@ /turf/space, /area/shuttle/gamma/station) "ahG" = ( -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + tag = "" }, -/area/security/securearmoury) +/obj/structure/cable{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 10 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 6 + }, +/turf/simulated/floor/wood, +/area/security/hos) "ahH" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -3663,11 +3737,18 @@ }, /area/security/main) "ahS" = ( -/obj/machinery/flasher/portable, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/effect/decal/warning_stripes/east, +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8"; + pixel_x = 0 }, -/area/security/securearmoury) +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 9 + }, +/turf/simulated/floor/wood, +/area/security/hos) "ahT" = ( /obj/machinery/flasher/portable, /turf/simulated/floor/plasteel{ @@ -3675,11 +3756,17 @@ }, /area/security/securearmoury) "ahU" = ( -/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/suit_storage_unit/security, +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8"; + pixel_x = 0 + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, -/area/security/securearmoury) +/area/security/hos) "ahV" = ( /obj/machinery/photocopier, /turf/simulated/floor/plasteel{ @@ -4434,16 +4521,32 @@ }, /area/security/permabrig) "ajj" = ( +/obj/structure/window/reinforced/polarized{ + dir = 4; + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 8; + id = "hos_room" + }, /obj/structure/cable{ d1 = 1; - d2 = 2; - icon_state = "1-2"; + d2 = 8; + icon_state = "1-8" + }, +/obj/structure/cable{ + d1 = 2; + d2 = 8; + icon_state = "2-8"; tag = "" }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/structure/cable{ + d2 = 8; + icon_state = "0-8" }, -/area/security/securearmoury) +/obj/structure/grille, +/turf/simulated/floor/plating, +/area/security/hos) "ajk" = ( /obj/structure/rack{ dir = 8; @@ -4482,11 +4585,9 @@ }, /area/security/brig) "ajn" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmoury) +/obj/structure/dresser, +/turf/simulated/floor/wood, +/area/security/hos) "ajo" = ( /obj/structure/rack{ dir = 8; @@ -4526,11 +4627,13 @@ }, /area/security/brig) "ajq" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" +/obj/machinery/button/windowtint{ + id = "hos_room"; + pixel_x = -24; + pixel_y = -24 }, -/area/security/securearmoury) +/turf/simulated/floor/wood, +/area/security/hos) "ajr" = ( /obj/structure/cable{ d1 = 1; @@ -4689,11 +4792,16 @@ }, /area/security/brig) "ajK" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + tag = "" }, -/area/security/securearmoury) +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/turf/simulated/floor/wood, +/area/security/hos) "ajM" = ( /obj/structure/cable{ d1 = 4; @@ -5002,11 +5110,9 @@ }, /area/security/brig) "akf" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmoury) +/obj/effect/decal/warning_stripes/east, +/turf/simulated/floor/wood, +/area/security/hos) "akh" = ( /obj/structure/cable{ d1 = 4; @@ -5090,11 +5196,28 @@ }, /area/security/brig) "akl" = ( -/obj/effect/decal/warning_stripes/red/hollow, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "HoS personal weapons"; + req_access_txt = "58" + }, +/obj/item/ammo_box/a357{ + pixel_x = -9; + pixel_y = 9 + }, +/obj/item/ammo_box/a357{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/ammo_box/a357, +/obj/item/gun/projectile/revolver/mateba{ + pixel_x = 3; + pixel_y = -3 + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, -/area/security/securearmoury) +/area/security/hos) "akm" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 @@ -5165,11 +5288,21 @@ }, /area/security/brig) "akt" = ( -/turf/simulated/floor/plasteel{ +/obj/structure/window/reinforced/polarized{ + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 4; + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ dir = 8; - icon_state = "vault" + id = "hos_room" }, -/area/security/securearmoury) +/obj/structure/cable, +/obj/structure/grille, +/turf/simulated/floor/plating, +/area/security/hos) "aku" = ( /obj/machinery/door_control{ id = "SecureArmory"; @@ -5191,11 +5324,36 @@ /turf/simulated/floor/plasteel, /area/security/main) "akw" = ( -/obj/machinery/suit_storage_unit/security/secure, +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + tag = "" + }, +/obj/structure/cable{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, +/obj/structure/cable{ + d1 = 2; + d2 = 8; + icon_state = "2-8"; + tag = "" + }, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/door/airlock/command/glass{ + id = "hos_room"; + id_tag = null; + name = "Head of Security Room"; + req_access_txt = "58" + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, -/area/security/securearmoury) +/area/security/hos) "akx" = ( /obj/structure/cable{ d1 = 2; @@ -5351,14 +5509,27 @@ /turf/simulated/floor/plasteel, /area/security/permabrig) "akN" = ( -/obj/machinery/newscaster{ - pixel_y = 30 +/obj/structure/window/reinforced/polarized{ + id = "hos_room" }, -/obj/structure/closet/secure_closet/hos, -/obj/item/reagent_containers/food/drinks/flask/barflask, -/turf/simulated/floor/plasteel{ - icon_state = "dark" +/obj/structure/window/reinforced/polarized{ + dir = 1; + id = "hos_room" }, +/obj/structure/window/reinforced/polarized{ + dir = 4; + id = "hos_room" + }, +/obj/structure/window/reinforced/polarized{ + dir = 8; + id = "hos_room" + }, +/obj/structure/cable{ + d2 = 8; + icon_state = "0-8" + }, +/obj/structure/grille, +/turf/simulated/floor/plating, /area/security/hos) "akO" = ( /obj/machinery/door/airlock/public/glass, @@ -5662,16 +5833,9 @@ }, /area/security/securearmoury) "alj" = ( -/obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2"; - tag = "" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmoury) +/obj/structure/lattice, +/turf/simulated/wall/r_wall, +/area/security/hos) "alk" = ( /obj/structure/rack{ dir = 8; @@ -5736,11 +5900,16 @@ }, /area/security/securearmoury) "alm" = ( +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8"; + pixel_x = 0 + }, /turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" + icon_state = "dark" }, -/area/security/securearmoury) +/area/security/hos) "aln" = ( /obj/structure/window/reinforced, /obj/structure/closet/secure_closet/guncabinet{ @@ -5877,11 +6046,17 @@ /turf/simulated/shuttle/floor4/vox, /area/shuttle/vox) "alB" = ( -/obj/effect/decal/warning_stripes/red/partial{ - dir = 8 +/obj/structure/cable{ + d1 = 1; + d2 = 8; + icon_state = "1-8" }, -/turf/simulated/floor/plasteel, -/area/security/main) +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/hos) "alC" = ( /obj/structure/cable{ d1 = 1; @@ -6369,9 +6544,10 @@ }, /area/security/hos) "amu" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 10; - initialize_directions = 10; +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 4; + initialize_directions = 11; level = 1 }, /turf/simulated/floor/carpet, @@ -6768,6 +6944,7 @@ pixel_x = -26; pixel_y = 15 }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/carpet, /area/security/hos) "ane" = ( @@ -8859,13 +9036,13 @@ tag = "" }, /obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/disposalpipe/segment, /obj/machinery/door/airlock/command{ name = "Head of Security"; req_access_txt = "58" }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -40946,7 +41123,7 @@ /obj/machinery/door/firedoor, /obj/machinery/door/airlock/security/glass{ name = "Escape Shuttle Cell"; - req_access_txt = "2" + req_access_txt = "1" }, /obj/structure/cable{ d1 = 1; @@ -41013,7 +41190,7 @@ /obj/machinery/door/firedoor, /obj/machinery/door/airlock/security/glass{ name = "Escape Shuttle Cell"; - req_access_txt = "2" + req_access_txt = "1" }, /obj/structure/cable{ d1 = 1; @@ -95732,7 +95909,7 @@ "pdr" = ( /obj/machinery/door/airlock/security/glass{ name = "Escape Shuttle Cell"; - req_access_txt = "2" + req_access_txt = "1" }, /turf/simulated/shuttle/floor4, /area/shuttle/escape) @@ -130747,179 +130924,164 @@ aaa aaa aaa aaa -aab -aaa -acC -alF -alF -alF -amQ -alF -afn -apO -anx -aqX -aiW -akC -avF -aol -arP -arA -ayt -aFL -azM -aAK -aBR -avq -avq -aCh -avq -aDP -aGM -aIn -aJu -aFI -aFI -aFI -aFI -aQx -aSJ -aUI -aWK -aZy -bbn -bda -bfd -aUS -bjf -baK -beI -aUS -bfs -aYe -boc -bkP -bmr -brV -btg -bqi -bmq -buN -bmq -aVG -aUS -cMK -bHy -bAk -bEp -aTV -bDf -bGd -bGi -bJJ -bTL -bPf -bQQ -bSr -bSP -bZG -cuo -bWo -caA -bZj -caz -bWX -caA -cfw -chk -chL -cjw -ckV -cCW -csK -der -cQk -cpK -cxH -cAr -cuf -cuI -dcC -cxp -cxE -cyS -cYi -cYi -cKg -cAI -cOW -cQh -cQT -cSm -cTn -cRk -cVO -cWY -cYa -cZc -cZY -daP -daP -dcv -daP -dec -deR -dfI -dgt -dgP -dhF -dip -diZ -djB -dks -dkT -dkT -cQZ -doE -aab -aab -dmX -aab -aab -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa -aaa +aab +aaa +acC +alF +alF +alF +amQ +alF +afn +apO +anx +aqX +aiW +akC +avF +aol +arP +arA +ayt +aFL +azM +aAK +aBR +avq +avq +aCh +avq +aDP +aGM +aIn +aJu +aFI +aFI +aFI +aFI +aQx +aSJ +aUI +aWK +aZy +bbn +bda +bfd +aUS +bjf +baK +beI +aUS +bfs +aYe +boc +bkP +bmr +brV +btg +bqi +bmq +buN +bmq +aVG +aUS +cMK +bHy +bAk +bEp +aTV +bDf +bGd +bGi +bJJ +bTL +bPf +bQQ +bSr +bSP +bZG +cuo +bWo +caA +bZj +caz +bWX +caA +cfw +chk +chL +cjw +ckV +cCW +csK +der +cQk +cpK +cxH +cAr +cuf +cuI +dcC +cxp +cxE +cyS +cYi +cYi +cKg +cAI +cOW +cQh +cQT +cSm +cTn +cRk +cVO +cWY +cYa +cZc +cZY +daP +daP +dcv +daP +dec +deR +dfI +dgt +dgP +dhF +dip +diZ +djB +dks +dkT +dkT +cQZ +doE +aab +aab +dmX +aab +aab +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa +aaa aaa aaa aaa @@ -130938,15 +131100,12 @@ aaa aaa aaa aaa -"} -(135,1,1) = {" aaa aaa aaa aaa aaa aaa -aab aaa aaa aaa @@ -130956,6 +131115,18 @@ aaa aaa aaa aaa +"} +(135,1,1) = {" +aaa +aaa +aaa +aaa +aaa +aaa +aab +aaa +aaa +aaa aaa aaa aaa @@ -131004,6 +131175,12 @@ aaa aaa aaa aaa +aaa +aaa +amW +aab +amW +aab amW aab anH @@ -131257,9 +131434,9 @@ aaa aaa aaa aaa +aab aaa -aaa -aaa +aab aaa aab aaa @@ -131514,12 +131691,12 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -amW aab +aaa +aih +aih +aih +aih aih akK aiY @@ -131771,13 +131948,13 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa +amW aab -aaa +aih adq +agu +ajn +aih adI adm aev @@ -132028,15 +132205,15 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa aab aaa +adb +adv +agS +ajq acD adH -aiY +alm afM aeC ahm @@ -132285,15 +132462,15 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -amW aab -aih -akN -aiY +aaa +adg +adD +ahG +ajK +akw +aoT +alB amu and aeW @@ -132542,13 +132719,13 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa aab aaa -aih +adh +adP +ahS +akf +akN acP adw amt @@ -132799,13 +132976,13 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa amW aab aih +afe +ahU +akl +aih aih aih aih @@ -133056,14 +133233,14 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aab -aaa aab aaa +aih +agt +ajj +akt +alj +aih ajb ajs aeE @@ -133313,14 +133490,14 @@ aaa aaa aaa aaa +aab aaa +aab aaa aaa aaa -amW -aab -amW aab +aaa ajb ajt ajw @@ -133570,14 +133747,14 @@ aaa abp aaa aaa -aaa -aaa -aaa -aaa -aaa -aaa +amW +aab +amW +aab +aab +aab +amW aab -aaa ajb ajv ajw @@ -134861,7 +135038,7 @@ aaa aaa aaa aaa -alw +alv aab alJ aaa diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 66ee12d74f7b..fb0cac5c0da8 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -1,22 +1,25 @@ //Timing subsystem //Don't run if there is an identical unique timer active //if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer, and returns the id of the existing timer -#define TIMER_UNIQUE 1 +#define TIMER_UNIQUE (1<<0) //For unique timers: Replace the old timer rather then not start this one -#define TIMER_OVERRIDE 2 +#define TIMER_OVERRIDE (1<<1) //Timing should be based on how timing progresses on clients, not the sever. // tracking this is more expensive, // should only be used in conjuction with things that have to progress client side, such as animate() or sound() -#define TIMER_CLIENT_TIME 4 +#define TIMER_CLIENT_TIME (1<<2) //Timer can be stopped using deltimer() -#define TIMER_STOPPABLE 8 +#define TIMER_STOPPABLE (1<<3) //To be used with TIMER_UNIQUE //prevents distinguishing identical timers with the wait variable -#define TIMER_NO_HASH_WAIT 16 +#define TIMER_NO_HASH_WAIT (1<<4) //Loops the timer repeatedly until qdeleted //In most cases you want a subsystem instead -#define TIMER_LOOP 32 +#define TIMER_LOOP (1<<5) + +///Delete the timer on parent datum Destroy() and when deltimer'd +#define TIMER_DELETE_ME (1<<6) #define TIMER_ID_NULL -1 diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index aab920f55fd4..f7021e4d55bb 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -37,6 +37,46 @@ LIST.Insert(__BIN_MID, IN);\ } +/// Passed into BINARY_INSERT to compare keys +#define COMPARE_KEY __BIN_LIST[__BIN_MID] +/// Passed into BINARY_INSERT to compare values +#define COMPARE_VALUE __BIN_LIST[__BIN_LIST[__BIN_MID]] + +/**** + * Binary search sorted insert from TG + * INPUT: Object to be inserted + * LIST: List to insert object into + * TYPECONT: The typepath of the contents of the list + * COMPARE: The object to compare against, usualy the same as INPUT + * COMPARISON: The variable on the objects to compare + * COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE. + */ +#define BINARY_INSERT_TG(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \ + do {\ + var/list/__BIN_LIST = LIST;\ + var/__BIN_CTTL = length(__BIN_LIST);\ + if(!__BIN_CTTL) {\ + __BIN_LIST += INPUT;\ + } else {\ + var/__BIN_LEFT = 1;\ + var/__BIN_RIGHT = __BIN_CTTL;\ + var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + var ##TYPECONT/__BIN_ITEM;\ + while(__BIN_LEFT < __BIN_RIGHT) {\ + __BIN_ITEM = COMPTYPE;\ + if(__BIN_ITEM.##COMPARISON <= COMPARE.##COMPARISON) {\ + __BIN_LEFT = __BIN_MID + 1;\ + } else {\ + __BIN_RIGHT = __BIN_MID;\ + };\ + __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + };\ + __BIN_ITEM = COMPTYPE;\ + __BIN_MID = __BIN_ITEM.##COMPARISON > COMPARE.##COMPARISON ? __BIN_MID : __BIN_MID + 1;\ + __BIN_LIST.Insert(__BIN_MID, INPUT);\ + };\ + } while(FALSE) + //Returns a list in plain english as a string /proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index b03f03301322..37d44129eb13 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -61,6 +61,7 @@ var/guest_jobban = 1 var/panic_bunker_threshold = 150 // above this player count threshold, never-before-seen players are blocked from connecting var/usewhitelist = 0 + var/usewhitelist_database = 0 var/mods_are_mentors = 0 var/load_jobs_from_txt = 0 var/automute_on = 0 //enables automuting/spam prevention @@ -286,6 +287,8 @@ var/full_day_logs = FALSE + var/allow_head_of_departaments_assign_civilian = FALSE + /datum/configuration/New() for(var/T in subtypesof(/datum/game_mode)) var/datum/game_mode/M = T @@ -551,6 +554,9 @@ if("usewhitelist") config.usewhitelist = 1 + if("usewhitelist_database") + config.usewhitelist_database = 1 + if("feature_object_spell_system") config.feature_object_spell_system = 1 @@ -817,6 +823,9 @@ if ("full_day_logs") config.full_day_logs = TRUE + if ("allow_head_of_departaments_assign_civilian") + config.allow_head_of_departaments_assign_civilian = TRUE + else log_config("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index 69bd3e44d804..19d412db9519 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -441,23 +441,23 @@ SUBSYSTEM_DEF(jobs) to_chat(H, "Вы [alt_title ? alt_title : rank].") to_chat(H, "На этой должности вы отвечаете непосредственно перед [replacetext(job.supervisors,"the ","")]. Особые обстоятельства могут это изменить.") - to_chat(H, "Для получения дополнительной информации о работе на станции, см. Стандартные Рабочие Процедуры (СРП)") + to_chat(H, "Для получения дополнительной информации о работе на станции, см. Стандартные Рабочие Процедуры (СРП)") if(job.is_service) - to_chat(H, "Будучи работником отдела Обслуживания, убедитесь что прочли СРП своего отдела") + to_chat(H, "Будучи работником отдела Обслуживания, убедитесь что прочли СРП своего отдела") if(job.is_supply) - to_chat(H, "Будучи работником отдела Снабжения, убедитесь что прочли СРП своего отдела") + to_chat(H, "Будучи работником отдела Снабжения, убедитесь что прочли СРП своего отдела") if(job.is_command) - to_chat(H, "Будучи важным членом Командования, убедитесь что прочли СРП своего отдела") + to_chat(H, "Будучи важным членом Командования, убедитесь что прочли СРП своего отдела") if(job.is_legal) - to_chat(H, "Ваша должность требует полного знания Космического Закона и Правовых Стандартных Рабочих Процедур") + to_chat(H, "Ваша должность требует полного знания Космического Закона и Правовых Стандартных Рабочих Процедур") if(job.is_engineering) - to_chat(H, "Будучи работником Инженерного отдела, убедитесь что прочли СРП своего отдела") + to_chat(H, "Будучи работником Инженерного отдела, убедитесь что прочли СРП своего отдела") if(job.is_medical) - to_chat(H, "Будучи работником Медицинского отдела, убедитесь что прочли СРП своего отдела") + to_chat(H, "Будучи работником Медицинского отдела, убедитесь что прочли СРП своего отдела") if(job.is_science) - to_chat(H, "Будучи работником Научного отдела, убедитесь что прочли СРП своего отдела") + to_chat(H, "Будучи работником Научного отдела, убедитесь что прочли СРП своего отдела") if(job.is_security) - to_chat(H, "Будучи работником Службы Безопасности, вам необходимо знание Космического Закона, Правовых СРП, а также СРП своего отдела") + to_chat(H, "Будучи работником Службы Безопасности, вам необходимо знание Космического Закона, Правовых СРП, а также СРП своего отдела") if(job.req_admin_notify) to_chat(H, "Вы играете на важной для игрового процесса должности. Если вам необходимо покинуть игру, пожалуйста, используйте крио и проинформируйте командование. Если вы не можете это сделать, пожалуйста, проинформируйте админов через админхэлп.") diff --git a/code/controllers/subsystem/runechat.dm b/code/controllers/subsystem/runechat.dm index 350523724ac2..da53432d55d1 100644 --- a/code/controllers/subsystem/runechat.dm +++ b/code/controllers/subsystem/runechat.dm @@ -1,5 +1,5 @@ /// Controls how many buckets should be kept, each representing a tick. (30 seconds worth) -#define BUCKET_LEN (world.fps * 1 * 30) +#define BUCKET_LEN (world.fps * 1 * 60) /// Helper for getting the correct bucket for a given chatmessage #define BUCKET_POS(scheduled_destruction) (((round((scheduled_destruction - SSrunechat.head_offset) / world.tick_lag) + 1) % BUCKET_LEN) || BUCKET_LEN) /// Gets the maximum time at which messages will be handled in buckets, used for deferring to secondary queue @@ -63,16 +63,11 @@ SUBSYSTEM_DEF(runechat) practical_offset = 1 resumed = FALSE - if((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution)) - bucket_list.len = 0 - bucket_list.len = BUCKET_LEN - practical_offset = 1 - bucket_count = 0 - head_offset = world.time - bucket_resolution = world.tick_lag + // Check for when we have to reset buckets, typically from auto-reset + if ((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution)) + reset_buckets() bucket_list = src.bucket_list resumed = FALSE - // Store a reference to the 'working' chatmessage so that we can resume if the MC // has us stop mid-way through processing var/static/datum/chatmessage/cm @@ -135,6 +130,11 @@ SUBSYSTEM_DEF(runechat) bucket_list |= SSrunechat.bucket_list second_queue |= SSrunechat.second_queue +/datum/controller/subsystem/runechat/proc/reset_buckets() + bucket_list.len = BUCKET_LEN + head_offset = world.time + bucket_resolution = world.tick_lag + /** * Enters the runechat subsystem with this chatmessage, inserting it into the end-of-life queue * @@ -174,7 +174,7 @@ SUBSYSTEM_DEF(runechat) // Handle insertion into the secondary queue if the required time is outside our tracked amounts if (scheduled_destruction >= BUCKET_LIMIT) - BINARY_INSERT(src, SSrunechat.second_queue, datum/chatmessage, scheduled_destruction) + BINARY_INSERT_TG(src, SSrunechat.second_queue, /datum/chatmessage, src, scheduled_destruction, COMPARE_KEY) return // Get bucket position and a local reference to the datum var, it's faster to access this way diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 53d77e8fad60..91a66a99bf6f 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -1,32 +1,54 @@ -#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth) +/// Controls how many buckets should be kept, each representing a tick. (1 minutes worth) +#define BUCKET_LEN (world.fps*1*60) +/// Helper for getting the correct bucket for a given timer #define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN) +/// Gets the maximum time at which timers will be invoked from buckets, used for deferring to secondary queue #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 - +/// Max float with integer precision +#define TIMER_ID_MAX (2**24) + +/** + * # Timer Subsystem + * + * Handles creation, callbacks, and destruction of timed events. + * + * It is important to understand the buckets used in the timer subsystem are just a series of circular doubly-linked + * lists. The object at a given index in bucket_list is a /datum/timedevent, the head of a circular list, which has prev + * and next references for the respective elements in that bucket's circular list. + */ SUBSYSTEM_DEF(timer) name = "Timer" - wait = 1 //SS_TICKER subsystem, so wait is in ticks + wait = 1 // SS_TICKER subsystem, so wait is in ticks init_order = INIT_ORDER_TIMER flags = SS_TICKER|SS_NO_INIT offline_implications = "The game will no longer process timers. Immediate server restart recommended." - var/list/second_queue = list() //awe, yes, you've had first queue, but what about second queue? + /// Queue used for storing timers that do not fit into the current buckets + var/list/datum/timedevent/second_queue = list() + /// A hashlist dictionary used for storing unique timers 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" - + /// world.time of the first entry in the bucket list, effectively the 'start time' of the current buckets + var/head_offset = 0 + /// Index of the wrap around pivot for buckets. buckets before this are later running buckets wrapped around from the end of the bucket list. + var/practical_offset = 1 + /// world.tick_lag the bucket was designed for + var/bucket_resolution = 0 + /// How many timers are in the buckets + var/bucket_count = 0 + /// List of buckets, each bucket holds every timer that has to run that byond tick + var/list/bucket_list = list() + /// List of all active timers associated to their timer ID (for easy lookup) + var/list/timer_id_dict = list() + /// Special timers that run in real-time, not BYOND time; these are more expensive to run and maintain + var/list/clienttime_timers = list() + /// Contains the last time that a timer's callback was invoked, or the last tick the SS fired if no timers are being processed var/last_invoke_tick = 0 + /// Keeps track of the next index to work on for client timers + var/next_clienttime_timer_index = 0 + /// Contains the last time that a warning was issued for not invoking callbacks var/static/last_invoke_warning = 0 + /// Boolean operator controlling if the timer SS will automatically reset buckets if it fails to invoke callbacks for an extended period of time var/static/bucket_auto_reset = TRUE /datum/controller/subsystem/timer/PreInit() @@ -38,56 +60,64 @@ SUBSYSTEM_DEF(timer) ..("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) + // Store local references to datum vars as it is faster to access them var/lit = last_invoke_tick - var/last_check = world.time - TICKS2DS(BUCKET_LEN*1.5) var/list/bucket_list = src.bucket_list + var/last_check = world.time - TICKS2DS(BUCKET_LEN * 1.5) + // If there are no timers being tracked, then consider now to be the last invoked time if(!bucket_count) last_invoke_tick = world.time + // Check that we have invoked a callback in the last 1.5 minutes of BYOND time, + // and throw a warning and reset buckets if this is true if(lit && lit < last_check && head_offset < last_check && last_invoke_warning < last_check) last_invoke_warning = world.time - var/msg = "No regular timers processed in the last [BUCKET_LEN*1.5] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!" + var/msg = "No regular timers processed in the last [BUCKET_LEN * 1.5] 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/list/to_log = list("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) + if (!bucket_head) continue - log_world("Active timers at index [i]:") - + to_log += "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)) + to_log += 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:") + + to_log += "Active timers in the second_queue queue:" for(var/I in second_queue) - log_world(get_timer_debug_string(I)) + to_log += get_timer_debug_string(I) - var/next_clienttime_timer_index = 0 - var/len = length(clienttime_timers) + // Dump all the logged data to the world log + log_world(to_log.Join("\n")) - for(next_clienttime_timer_index in 1 to len) - if(MC_TICK_CHECK) + // Process client-time timers + if (next_clienttime_timer_index) + clienttime_timers.Cut(1, next_clienttime_timer_index+1) + next_clienttime_timer_index = 0 + for (next_clienttime_timer_index in 1 to length(clienttime_timers)) + 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) + 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]") + if (!callBack) + 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() @@ -95,135 +125,90 @@ SUBSYSTEM_DEF(timer) if(ctime_timer.flags & TIMER_LOOP) ctime_timer.spent = 0 ctime_timer.timeToRun = REALTIMEOFDAY + ctime_timer.wait - BINARY_INSERT(ctime_timer, clienttime_timers, datum/timedevent, timeToRun) + BINARY_INSERT_TG(ctime_timer, clienttime_timers, /datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY) else qdel(ctime_timer) - - if(next_clienttime_timer_index) + // Remove invoked client-time timers + if (next_clienttime_timer_index) clienttime_timers.Cut(1, next_clienttime_timer_index+1) + next_clienttime_timer_index = 0 - if(MC_TICK_CHECK) - return - - var/static/list/spent = list() - var/static/datum/timedevent/timer - if(practical_offset > BUCKET_LEN) + // Check for when we need to loop the buckets, this occurs when + // the head_offset is approaching BUCKET_LEN ticks in the past + 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)) + // Check for when we have to reset buckets, typically from auto-reset + 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-1)*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) + // Iterate through each bucket starting from the practical offset + while (practical_offset <= BUCKET_LEN && head_offset + ((practical_offset - 1) * world.tick_lag) <= world.time) + var/datum/timedevent/timer + while ((timer = bucket_list[practical_offset])) 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 (!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]") + + timer.bucketEject() //pop the timer off of the bucket list. - if(!timer.spent) - spent += timer + // Invoke callback if possible + if (!timer.spent) 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 + if (timer.flags & TIMER_LOOP) // Prepare looping timers to re-enter the queue + timer.spent = 0 + timer.timeToRun = world.time + timer.wait + timer.bucketJoin() + else + qdel(timer) - //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-- + if (MC_TICK_CHECK) break - if(timer.timeToRun < head_offset) - bucket_resolution = null //force bucket recreation - stack_trace("[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-1)) - bucket_resolution = null //force bucket recreation - stack_trace("[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/i in spent) - var/datum/timedevent/qtimer = i - if(QDELETED(qtimer)) - bucket_count++ - continue - if(!(qtimer.flags & TIMER_LOOP)) - qdel(qtimer) - else - bucket_count++ - qtimer.spent = 0 - qtimer.bucketEject() - if(qtimer.flags & TIMER_CLIENT_TIME) - qtimer.timeToRun = REALTIMEOFDAY + qtimer.wait - else - qtimer.timeToRun = world.time + qtimer.wait - qtimer.bucketJoin() - - spent.len = 0 + if (!bucket_list[practical_offset]) + // Empty the bucket, check if anything in the secondary queue should be shifted to this bucket + bucket_list[practical_offset++] = null + var/i = 0 + for (i in 1 to length(second_queue)) + timer = second_queue[i] + if (timer.timeToRun >= TIMER_MAX) + i-- + break + + // Check for timers that are scheduled to run in the past + if (timer.timeToRun < head_offset) + bucket_resolution = null // force bucket recreation + stack_trace("[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]") + break + + // Check for timers that are not capable of being scheduled to run without rebuilding buckets + if (timer.timeToRun < head_offset + TICKS2DS(practical_offset - 1)) + bucket_resolution = null // force bucket recreation + stack_trace("[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]") + break + + timer.bucketJoin() + if (i) + second_queue.Cut(1, i+1) + if (MC_TICK_CHECK) + break -//formated this way to be runtime resistant +/** + * Generates a string with details about the timed event for debugging purposes + */ /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"]" @@ -234,12 +219,16 @@ SUBSYSTEM_DEF(timer) if(!TE.callBack) . += ", NO CALLBACK" +/** + * Destroys the existing buckets and creates new buckets from the existing timed events + */ /datum/controller/subsystem/timer/proc/reset_buckets() - var/list/bucket_list = src.bucket_list + var/list/bucket_list = src.bucket_list // Store local reference to datum var, this is faster var/list/alltimers = list() - //collect the timers currently in the bucket - for(var/bucket_head in bucket_list) - if(!bucket_head) + + // Get all timers currently in the buckets + for (var/bucket_head in bucket_list) + if (!bucket_head) // if bucket is empty for this tick continue var/datum/timedevent/bucket_node = bucket_head do @@ -247,60 +236,77 @@ SUBSYSTEM_DEF(timer) bucket_node = bucket_node.next while(bucket_node && bucket_node != bucket_head) + // Empty the list by zeroing and re-assigning the length bucket_list.len = 0 bucket_list.len = BUCKET_LEN + // Reset values for the subsystem to their initial values practical_offset = 1 bucket_count = 0 head_offset = world.time bucket_resolution = world.tick_lag + // Add all timed events from the secondary queue as well alltimers += second_queue - if(!length(alltimers)) + + // If there are no timers being tracked by the subsystem, + // there is no need to do any further rebuilding + if (!length(alltimers)) return + // Sort all timers by time to run sortTim(alltimers, .proc/cmp_timer) + // Get the earliest timer, and if the TTR is earlier than the current world.time, + // then set the head offset appropriately to be the earliest time tracked by the + // current set of buckets var/datum/timedevent/head = alltimers[1] - - if(head.timeToRun < head_offset) + if (head.timeToRun < head_offset) head_offset = head.timeToRun + // Iterate through each timed event and insert it into an appropriate bucket, + // up unto the point that we can no longer insert into buckets as the TTR + // is outside the range we are tracking, then insert the remainder into the + // secondary queue var/new_bucket_count var/i = 1 - for(i in 1 to length(alltimers)) + for (i in 1 to length(alltimers)) var/datum/timedevent/timer = alltimers[i] - if(!timer) + if (!timer) continue - var/bucket_pos = BUCKET_POS(timer) - if(timer.timeToRun >= TIMER_MAX) + // Check that the TTR is within the range covered by buckets, when exceeded we've finished + 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) + // Check that timer has a valid callback and hasn't been invoked + 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 + // Insert the timer into the bucket, and perform necessary circular doubly-linked list operations new_bucket_count++ + var/bucket_pos = BUCKET_POS(timer) var/datum/timedevent/bucket_head = bucket_list[bucket_pos] - if(!bucket_head) + if (!bucket_head) bucket_list[bucket_pos] = timer timer.next = null timer.prev = null continue - - if(!bucket_head.prev) + 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) + + // Cut the timers that are tracked by the buckets from the secondary queue + if (i) + alltimers.Cut(1, i + 1) second_queue = alltimers bucket_count = new_bucket_count @@ -311,102 +317,137 @@ SUBSYSTEM_DEF(timer) timer_id_dict |= SStimer.timer_id_dict bucket_list |= SStimer.bucket_list +/** + * # Timed Event + * + * This is the actual timer, it contains the callback and necessary data to maintain + * the timer. + * + * See the documentation for the timer subsystem for an explanation of the buckets referenced + * below in next and prev + */ /datum/timedevent + /// ID used for timers when the TIMER_STOPPABLE flag is present var/id + /// The callback to invoke after the timer completes var/datum/callback/callBack + /// The time at which the callback should be invoked at var/timeToRun + /// The length of the timer var/wait + /// Unique hash generated when TIMER_UNIQUE flag is present var/hash + /// The source of the timedevent, whatever called addtimer + var/source + /// Flags associated with the timer, see _DEFINES/subsystems.dm var/list/flags - var/spent = 0 //time we ran the timer. - var/name //for easy debugging. - //cicular doublely linked list + /// Time at which the timer was invoked or destroyed + var/spent = 0 + /// An informative name generated for the timer as its representation in strings, useful for debugging + var/name + /// Next timed event in the bucket var/datum/timedevent/next + /// Previous timed event in the bucket var/datum/timedevent/prev -/datum/timedevent/New(datum/callback/callBack, wait, flags, hash) +/datum/timedevent/New(datum/callback/callBack, wait, flags, hash, source) var/static/nextid = 1 id = TIMER_ID_NULL src.callBack = callBack src.wait = wait src.flags = flags src.hash = hash + src.source = source - if(flags & TIMER_CLIENT_TIME) - timeToRun = REALTIMEOFDAY + wait - else - timeToRun = world.time + wait + // Determine time at which the timer's callback should be invoked + timeToRun = (flags & TIMER_CLIENT_TIME ? REALTIMEOFDAY : world.time) + wait - if(flags & TIMER_UNIQUE) + // Include the timer in the hash table if the timer is unique + if (flags & TIMER_UNIQUE) SStimer.hashes[hash] = src - if(flags & TIMER_STOPPABLE) + // Generate ID for the timer if the timer is stoppable, include in the timer id dictionary + if (flags & TIMER_STOPPABLE) id = num2text(nextid, 100) - if(nextid >= SHORT_REAL_LIMIT) - nextid += min(1, 2**round(nextid/SHORT_REAL_LIMIT)) + 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", "TIMER_LOOP")), ", ")], 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)) + 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 && !QDESTROYING(callBack.object)) + if (callBack.object != GLOBAL_PROC && !QDESTROYING(callBack.object)) LAZYADD(callBack.object.active_timers, src) bucketJoin() /datum/timedevent/Destroy() ..() - if(flags & TIMER_UNIQUE && hash) + if (flags & TIMER_UNIQUE && hash) SStimer.hashes -= hash - if(callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers) + 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) + if (flags & TIMER_STOPPABLE) SStimer.timer_id_dict -= id - if(flags & TIMER_CLIENT_TIME) - if(!spent) + if (flags & TIMER_CLIENT_TIME) + if (!spent) spent = world.time SStimer.clienttime_timers -= src return QDEL_HINT_IWILLGC - if(!spent) + if (!spent) spent = world.time bucketEject() else - if(prev && prev.next == src) + if (prev && prev.next == src) prev.next = next - if(next && next.prev == src) + if (next && next.prev == src) next.prev = prev next = null prev = null return QDEL_HINT_IWILLGC +/** + * Removes this timed event from any relevant buckets, or the secondary queue + */ /datum/timedevent/proc/bucketEject() + // Attempt to find bucket that contains this timed event var/bucketpos = BUCKET_POS(src) + + // Store local references for the bucket list and secondary queue + // This is faster than referencing them from the datum itself var/list/bucket_list = SStimer.bucket_list var/list/second_queue = SStimer.second_queue + + // Attempt to get the head of the bucket var/datum/timedevent/buckethead if(bucketpos > 0) buckethead = bucket_list[bucketpos] + + // Decrement the number of timers in buckets if the timed event is + // the head of the bucket, or has a TTR less than TIMER_MAX implying it fits + // into an existing bucket, or is otherwise not present in the secondary queue if(buckethead == src) bucket_list[bucketpos] = next SStimer.bucket_count-- - else if(timeToRun < TIMER_MAX || next || prev) + else if(timeToRun < TIMER_MAX) SStimer.bucket_count-- else var/l = length(second_queue) second_queue -= src if(l == length(second_queue)) SStimer.bucket_count-- + + // Remove the timed event from the bucket, ensuring to maintain + // the integrity of the bucket's list if relevant if(prev != next) prev.next = next next.prev = prev @@ -415,82 +456,108 @@ SUBSYSTEM_DEF(timer) next?.prev = null prev = next = null +/** + * Attempts to add this timed event to a bucket, will enter the secondary queue + * if there are no appropriate buckets at this time. + * + * Secondary queueing of timed events will occur when the timespan covered by the existing + * buckets is exceeded by the time at which this timed event is scheduled to be invoked. + * If the timed event is tracking client time, it will be added to a special bucket. + */ /datum/timedevent/proc/bucketJoin() - var/list/L + // Generate debug-friendly name for timer + var/static/list/bitfield_flags = list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT", "TIMER_LOOP") + name = "Timer: [id] (\ref[src]), TTR: [timeToRun], wait:[wait] Flags: [jointext(bitfield2list(flags, bitfield_flags), ", ")], \ + callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), \ + callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""]), source: [source]" - if(flags & TIMER_CLIENT_TIME) + // Check if this timed event should be diverted to the client time bucket, or the secondary queue + var/list/L + if (flags & TIMER_CLIENT_TIME) L = SStimer.clienttime_timers - else if(timeToRun >= TIMER_MAX) + else if (timeToRun >= TIMER_MAX) L = SStimer.second_queue - if(L) - BINARY_INSERT(src, L, datum/timedevent, timeToRun) + BINARY_INSERT_TG(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY) return - //get the list of buckets + // Get a local reference to the bucket list, this is faster than referencing the datum var/list/bucket_list = SStimer.bucket_list - //calculate our place in the bucket list + // Find the correct bucket for this timed event 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) + + // If there is no timed event at this position, then the bucket is 'empty' + // and we can just set this event to that position + if (!bucket_head) bucket_list[bucket_pos] = src return - //other wise, lets do a simplified linked list add. - if(!bucket_head.prev) + + // Otherwise, we merely add this timed event into the bucket, which is a + // circularly doubly-linked list + if (!bucket_head.prev) bucket_head.prev = bucket_head next = bucket_head prev = bucket_head.prev next.prev = src prev.next = src +/** + * Returns a string of the type of the callback for this timer + */ /datum/timedevent/proc/getcallingtype() . = "ERROR" - if(callBack.object == GLOBAL_PROC) + if (callBack.object == GLOBAL_PROC) . = "GLOBAL_PROC" else . = "[callBack.object.type]" +/** + * Create a new timer and insert it in the queue. + * You should not call this directly, and should instead use the addtimer macro, which includes source information. + * + * Arguments: + * * callback the callback to call on timer finish + * * wait deciseconds to run the timer for + * * flags flags for this timer, see: code\__DEFINES\subsystems.dm + */ /proc/addtimer(datum/callback/callback, wait = 0, flags = 0) - if(!callback) + if (!callback) CRASH("addtimer called without a callback") - if(wait < 0) + if (wait < 0) stack_trace("addtimer called with a negative wait. Converting to [world.tick_lag]") - if(callback.object != GLOBAL_PROC && QDELETED(callback.object) && !QDESTROYING(callback.object)) - stack_trace("addtimer called with a callback assigned to a qdeleted object. In the future such timers will not be supported and may refuse to run or run with a 0 wait") + if (callback.object != GLOBAL_PROC && QDELETED(callback.object) && !QDESTROYING(callback.object)) + stack_trace("addtimer called with a callback assigned to a qdeleted object. In the future such timers will not \ + be supported and may refuse to run or run with a 0 wait") wait = max(CEILING(wait, world.tick_lag), world.tick_lag) if(wait >= INFINITY) CRASH("Attempted to create timer with INFINITY delay") + // Generate hash if relevant for timed events with the TIMER_UNIQUE flag var/hash - - if(flags & TIMER_UNIQUE) - var/list/hashlist - if(flags & TIMER_NO_HASH_WAIT) - hashlist = list(callback.object, "(\ref[callback.object])", callback.delegate, flags & TIMER_CLIENT_TIME) - else - hashlist = list(callback.object, "(\ref[callback.object])", callback.delegate, wait, flags & TIMER_CLIENT_TIME) + if (flags & TIMER_UNIQUE) + var/list/hashlist = list(callback.object, "(\ref[callback.object])", callback.delegate, flags & TIMER_CLIENT_TIME) + if(!(flags & TIMER_NO_HASH_WAIT)) + hashlist += wait 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 + 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 + 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) + if (hash_timer.flags & TIMER_STOPPABLE) . = hash_timer.id return else if(flags & TIMER_OVERRIDE) @@ -499,18 +566,23 @@ SUBSYSTEM_DEF(timer) var/datum/timedevent/timer = new(callback, wait, flags, hash) return timer.id +/** + * Delete a timer + * + * Arguments: + * * id a timerid or a /datum/timedevent + */ /proc/deltimer(id) - if(!id) + if (!id) return FALSE - if(id == TIMER_ID_NULL) + 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 + 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) + if (timer && (!timer.spent || timer.flags & TIMER_DELETE_ME)) qdel(timer) return TRUE return FALSE diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 8d3ec92cf406..e5022240a87c 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -26,7 +26,7 @@ active_timers = null for(var/thing in timers) var/datum/timedevent/timer = thing - if(timer.spent) + if(timer.spent && !(timer.flags & TIMER_DELETE_ME)) continue qdel(timer) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 2abecc57e9f0..b13080f31585 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -591,7 +591,7 @@ var/new_target if(length(possible_targets) > 0) if(alert(usr, "Do you want to pick the objective yourself? No will randomise it", "Pick objective", "Yes", "No") == "Yes") - possible_targets += "Free objective" + possible_targets += "Free Objective." new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets else new_target = pick(possible_targets) @@ -600,14 +600,14 @@ return else to_chat(usr, "No possible target found. Defaulting to a Free objective.") - new_target = "Free objective" + new_target = "Free Objective." var/objective_path = text2path("/datum/objective/[new_obj_type]") - if(new_target == "Free objective") + if(new_target == "Free Objective.") new_objective = new objective_path new_objective.owner = src new_objective:target = null - new_objective.explanation_text = "Free objective" + new_objective.explanation_text = "Free Objective." else new_objective = new objective_path new_objective.owner = src @@ -691,7 +691,7 @@ if((possible_target != src) && ishuman(possible_target.current)) possible_targets += possible_target possible_targets = sortAtom(possible_targets) - possible_targets += "Free objective" + possible_targets += "Free Objective." var/new_target = input("Select target:", "Objective target") as null|anything in possible_targets if(!new_target) return diff --git a/code/datums/revision.dm b/code/datums/revision.dm index 50c616fc94f2..4d809a279cb0 100644 --- a/code/datums/revision.dm +++ b/code/datums/revision.dm @@ -13,9 +13,12 @@ GLOBAL_PROTECT(revision_info) // Dont mess with this var/commit_date /datum/code_revision/New() - commit_hash = rustg_git_revparse("HEAD") - if(commit_hash) - commit_date = rustg_git_commit_date(commit_hash) + commit_hash = "-" + commit_date = "-" + return + // commit_hash = rustg_git_revparse("HEAD") + // if(commit_hash) + // commit_date = rustg_git_commit_date(commit_hash) /** * Code Revision Logging Helper diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 8c1e767ef990..1eda706eb986 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -170,7 +170,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A grenade that explodes into HONK! brand banana peels that are genetically modified to be extra slippery and extrude caustic acid when stepped on" reference = "BG" item = /obj/item/grenade/clown_grenade - cost = 5 + cost = 2 job = list("Clown") /datum/uplink_item/jobspecific/clownmagboots @@ -227,7 +227,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A mean looking meat cleaver that does damage comparable to an Energy Sword but with the added benefit of chopping your victim into hunks of meat after they've died." reference = "MC" item = /obj/item/kitchen/knife/butcher/meatcleaver - cost = 10 + cost = 4 job = list("Chef") /datum/uplink_item/jobspecific/syndidonk @@ -245,7 +245,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A doll created by Syndicate Witch Doctors. Ingredients: Something of the Thread, Something of the Head, Something of the Body, Something of the Dead, Secret Voodoo herbs, and Monosodium glutamate." reference = "VD" item = /obj/item/voodoo - cost = 13 + cost = 4 job = list("Chaplain") /datum/uplink_item/jobspecific/missionary_kit @@ -342,7 +342,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A seemingly innocent briefcase full of not-so-innocent Syndicate-bred bees. Inject the case with blood to train the bees to ignore the donor(s). It also wirelessly taps into station intercomms to broadcast a message of TERROR." reference = "BEE" item = /obj/item/bee_briefcase - cost = 10 + cost = 6 job = list("Botanist") //Engineer @@ -399,7 +399,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A highly flexible jumpsuit that will help you navigate the ventilation loops of the station internally. Comes with pockets and ID slot, but can't be used without stripping off most gear, including backpack, belt, helmet, and exosuit. Free hands are also necessary to crawl around inside." reference = "AIRJ" item = /obj/item/clothing/under/contortionist - cost = 6 + cost = 10 job = list("Life Support Specialist") /datum/uplink_item/jobspecific/energizedfireaxe @@ -407,7 +407,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A fire axe with a massive energy charge built into it. Upon striking someone while charged it will throw them backwards while stunning them briefly, but will take some time to charge up again. It is also much sharper than a regular axe and can pierce light armor." reference = "EFA" item = /obj/item/twohanded/fireaxe/energized - cost = 10 + cost = 4 job = list("Life Support Specialist") //Stimulants @@ -417,7 +417,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A highly illegal compound contained within a compact auto-injector; when injected it makes the user extremely resistant to incapacitation and greatly enhances the body's ability to repair itself." reference = "ST" item = /obj/item/reagent_containers/hypospray/autoinjector/stimulants - cost = 7 + cost = 4 job = list("Scientist", "Research Director", "Geneticist", "Chief Medical Officer", "Medical Doctor", "Psychiatrist", "Chemist", "Paramedic", "Coroner", "Virologist") //Tator Poison Bottles @@ -459,7 +459,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) reference = "SR" desc = "A brutally simple syndicate revolver that fires .357 Magnum cartridges and has 7 chambers." item = /obj/item/gun/projectile/revolver - cost = 13 + cost = 10 surplus = 50 /datum/uplink_item/dangerous/smg @@ -494,7 +494,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "These gloves let the user punch people very fast. Does not improve weapon attack speed." reference = "RPGD" item = /obj/item/clothing/gloves/fingerless/rapid - cost = 8 + cost = 4 /datum/uplink_item/dangerous/sniper name = "Sniper Rifle" @@ -510,7 +510,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A compact, unscoped version of the operative sniper rifle. Packs a powerful punch, but ammo is limited." reference = "CSR" item = /obj/item/gun/projectile/automatic/sniper_rifle/compact - cost = 16 + cost = 8 surplus = 0 cant_discount = TRUE excludefrom = list(/datum/game_mode/nuclear) @@ -548,14 +548,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) deal extra damage and hit targets further. Use a screwdriver to take out any attached tanks." reference = "PF" item = /obj/item/melee/powerfist - cost = 8 + cost = 4 /datum/uplink_item/dangerous/chainsaw name = "Chainsaw" desc = "A high powered chainsaw for cutting up ...you know...." reference = "CH" item = /obj/item/twohanded/chainsaw - cost = 13 + cost = 12 /datum/uplink_item/dangerous/batterer name = "Mind Batterer" @@ -831,7 +831,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A speed loader that contains seven additional .357 Magnum rounds for the syndicate revolver. For when you really need a lot of things dead." reference = "357" item = /obj/item/ammo_box/a357 - cost = 3 + cost = 1 // STEALTHY WEAPONS @@ -863,7 +863,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A manual that teaches a single user tactical Close-Quarters Combat before self-destructing. Does not restrict weapon usage, but cannot be used alongside Gloves of the North Star." reference = "CQC" item = /obj/item/CQC_manual - cost = 13 + cost = 12 /datum/uplink_item/stealthy_weapons/cameraflash name = "Camera Flash" @@ -1038,7 +1038,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A pizza box with a bomb taped inside of it. The timer needs to be set by opening the box; afterwards, opening the box again will trigger the detonation." reference = "PB" item = /obj/item/pizza_bomb - cost = 5 + cost = 2 surplus = 80 /datum/uplink_item/explosives/grenadier @@ -1063,7 +1063,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A box of two (2) grenades that spread knockout gas over a large area. Equip internals before using one of these." reference = "ANG" item = /obj/item/storage/box/syndie_kit/atmosn2ogrenades - cost = 8 + cost = 4 /datum/uplink_item/explosives/atmosfiregrenades name = "Plasma Fire Grenades" @@ -1179,7 +1179,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "Projects an image across a user, disguising them as an object scanned with it, as long as they don't move the projector from their hand. The disguised user cannot run and projectiles pass over them." reference = "CP" item = /obj/item/chameleon - cost = 7 + cost = 5 /datum/uplink_item/stealthy_tools/camera_bug name = "Camera Bug" @@ -1210,7 +1210,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Useful for disrupting headsets, cameras, and borgs during stealth operations." reference = "EMPL" item = /obj/item/flashlight/emp - cost = 2 + cost = 4 surplus = 30 /datum/uplink_item/stealthy_tools/syndigaloshes @@ -1264,7 +1264,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "The cryptographic sequencer, also known as an emag, is a small card that unlocks hidden functions in electronic devices, subverts intended functions and characteristically breaks security mechanisms." reference = "EMAG" item = /obj/item/card/emag - cost = 6 + cost = 10 // No brainrot allowed /datum/uplink_item/device_tools/access_tuner name = "Access Tuner" @@ -1402,7 +1402,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. Be careful with their wording, as artificial intelligences may look for loopholes to exploit." reference = "HAI" item = /obj/item/aiModule/syndicate - cost = 10 + cost = 8 /datum/uplink_item/device_tools/magboots name = "Blood-Red Magboots" @@ -1461,7 +1461,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "This device will disrupt any nearby outgoing radio communication when activated." reference = "RJ" item = /obj/item/jammer - cost = 5 + cost = 2 /datum/uplink_item/device_tools/teleporter name = "Teleporter Circuit Board" @@ -1516,7 +1516,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "An implant injected into the body, and later activated manually to open an uplink with 10 telecrystals. The ability for an agent to open an uplink after their possessions have been stripped from them makes this implant excellent for escaping confinement." reference = "UI" item = /obj/item/implanter/uplink - cost = 14 + cost = 12 surplus = 0 cant_discount = TRUE @@ -1525,7 +1525,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "An implant injected into the body, and later activated at the user's will. It will open a small subspace pocket capable of storing two items." reference = "ESI" item = /obj/item/implanter/storage - cost = 8 + cost = 6 /datum/uplink_item/implants/mindslave name = "Mindslave Implant" diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm index d14069b9d24d..cc5418754434 100644 --- a/code/game/gamemodes/blob/blob_finish.dm +++ b/code/game/gamemodes/blob/blob_finish.dm @@ -11,20 +11,14 @@ /datum/game_mode/blob/declare_completion() if(blobwincount <= GLOB.blobs.len) SSticker.mode_result = "blob win - blob took over" - to_chat(world, "The blob has taken over the station!") - to_chat(world, "The entire station was eaten by the Blob") log_game("Blob mode completed with a blob victory.") else if(station_was_nuked) SSticker.mode_result = "blob halfwin - nuke" - to_chat(world, "Partial Win: The station has been destroyed!") - to_chat(world, "Directive 7-12 has been successfully carried out preventing the Blob from spreading.") log_game("Blob mode completed with a tie (station destroyed).") else if(!GLOB.blob_cores.len) SSticker.mode_result = "blob loss - blob eliminated" - to_chat(world, "The staff has won!") - to_chat(world, "The alien organism has been eradicated from the station") log_game("Blob mode completed with a crew victory.") to_chat(world, "Rebooting in 30s") ..() diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index c33b5eb6d8c2..35c4e70000d4 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -207,19 +207,17 @@ GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","E var/count = 1 for(var/datum/objective/objective in changeling.objectives) if(objective.check_completion()) - text += "
Objective #[count]: [objective.explanation_text] Success!" + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "changeling_objective", 1, list("[objective.type]", "SUCCESS")) else - text += "
Objective #[count]: [objective.explanation_text] Fail." + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "changeling_objective", 1, list("[objective.type]", "FAIL")) changelingwin = 0 count++ if(changelingwin) - text += "
The changeling was successful!" SSblackbox.record_feedback("tally", "changeling_success", 1, "SUCCESS") else - text += "
The changeling has failed." SSblackbox.record_feedback("tally", "changeling_success", 1, "FAIL") to_chat(world, text) diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index e4d94ecb69f5..4962829595e5 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -299,13 +299,10 @@ GLOBAL_LIST_EMPTY(all_cults) /datum/game_mode/cult/declare_completion() if(cult_objs.cult_status == NARSIE_HAS_RISEN) SSticker.mode_result = "cult win - cult win" - to_chat(world, " The cult wins! It has succeeded in summoning [SSticker.cultdat.entity_name]!") else if(cult_objs.cult_status == NARSIE_HAS_FALLEN) SSticker.mode_result = "cult draw - narsie died, nobody wins" - to_chat(world, " Nobody wins! [SSticker.cultdat.entity_name] was summoned, but banished!") else SSticker.mode_result = "cult loss - staff stopped the cult" - to_chat(world, " The staff managed to stop the cult!") var/endtext endtext += "
The cultists' objectives were:" diff --git a/code/game/gamemodes/devil/objectives.dm b/code/game/gamemodes/devil/objectives.dm index 04d0217b5998..a94359bcf902 100644 --- a/code/game/gamemodes/devil/objectives.dm +++ b/code/game/gamemodes/devil/objectives.dm @@ -83,7 +83,7 @@ if(target) explanation_text = "Purchase and retain the soul of [target.name], the [target.assigned_role]." else - explanation_text = "Free objective." + explanation_text = "Free Objective.." /datum/objective/devil/buy_target/check_completion() return target.soulOwner == owner diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 27b1ddcb94ff..76c9716d5c4e 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -478,10 +478,7 @@ var/list/objective_parts = list() var/count = 1 for(var/datum/objective/objective in ply.objectives) - if(objective.check_completion()) - objective_parts += "Objective #[count]: [objective.explanation_text] Success!" - else - objective_parts += "Objective #[count]: [objective.explanation_text] Fail." + objective_parts += "Objective #[count]: [objective.explanation_text]" count++ return objective_parts.Join("
") diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm index 54faf90fef89..85b888cfd1d8 100644 --- a/code/game/gamemodes/heist/heist.dm +++ b/code/game/gamemodes/heist/heist.dm @@ -234,17 +234,15 @@ GLOBAL_LIST_EMPTY(cortical_stacks) //Stacks for 'leave nobody behind' objective. else win_msg += "The Vox Raiders were repelled!" - to_chat(world, "[win_type] [win_group] victory!") - to_chat(world, "[win_msg]") SSticker.mode_result = "heist - [win_type] [win_group]" var/count = 1 for(var/datum/objective/objective in raid_objectives) if(objective.check_completion()) - to_chat(world, "
Objective #[count]: [objective.explanation_text] Success!") + to_chat(world, "
Objective #[count]: [objective.explanation_text]") SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "SUCCESS")) else - to_chat(world, "
Objective #[count]: [objective.explanation_text] Fail.") + to_chat(world, "
Objective #[count]: [objective.explanation_text]") SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "FAIL")) count++ diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index 8aced721dca6..b34eb27bb5cb 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -31,28 +31,17 @@ sendmeteors() /datum/game_mode/meteor/declare_completion() - var/text - var/survivors = 0 for(var/mob/living/player in GLOB.player_list) if(player.stat != DEAD) var/turf/location = get_turf(player.loc) if(!location) continue if(location.loc.type == SSshuttle.emergency.areaInstance.type) //didn't work in the switch for some reason - text += "
[player.real_name] escaped on the emergency shuttle" else switch(location.loc.type) if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) - text += "
[player.real_name] escaped in a life pod." else - text += "
[player.real_name] survived but is stranded without any hope of rescue." - survivors++ - - if(survivors) - to_chat(world, "The following survived the meteor storm:[text]") - else - to_chat(world, "Nobody survived the meteor storm!") SSticker.mode_result = "meteor end - evacuation" diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index dff473d63bcb..fe912e9a7b9d 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -337,48 +337,30 @@ if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) SSticker.mode_result = "nuclear win - syndicate nuke" - to_chat(world, "Syndicate Major Victory!") - to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!") else if(!disk_rescued && station_was_nuked && syndies_didnt_escape) SSticker.mode_result = "nuclear halfwin - syndicate nuke - did not evacuate in time" - to_chat(world, "Total Annihilation") - to_chat(world, "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") else if(!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) SSticker.mode_result = "nuclear halfwin - blew wrong station" - to_chat(world, "Crew Minor Victory") - to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") else if(!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) SSticker.mode_result = "nuclear halfwin - blew wrong station - did not evacuate in time" - to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!") - to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") else if(disk_rescued && is_operatives_are_dead()) SSticker.mode_result = "nuclear loss - evacuation - disk secured - syndi team dead" - to_chat(world, "Crew Major Victory!") - to_chat(world, "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") else if(disk_rescued) SSticker.mode_result = "nuclear loss - evacuation - disk secured" - to_chat(world, "Crew Major Victory") - to_chat(world, "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") else if(!disk_rescued && is_operatives_are_dead()) SSticker.mode_result = "nuclear loss - evacuation - disk not secured" - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") else if(!disk_rescued && crew_evacuated) SSticker.mode_result = "nuclear halfwin - detonation averted" - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") else if(!disk_rescued && !crew_evacuated) SSticker.mode_result = "nuclear halfwin - interrupted" - to_chat(world, "Neutral Victory") - to_chat(world, "Round was mysteriously interrupted!") ..() return diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 39691d231cb3..bb832436615e 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -83,7 +83,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(target && target.current) explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." return target /datum/objective/assassinate/check_completion() @@ -106,7 +106,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(target && target.current) explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." return target /datum/objective/mutiny/check_completion() @@ -132,7 +132,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(target && target.current) explanation_text = "Prevent [target.current.real_name], the [target.assigned_role] from escaping alive." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." return target /datum/objective/maroon/check_completion() @@ -160,7 +160,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(target && target.current) explanation_text = "Steal the brain of [target.current.real_name] the [target.assigned_role]." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." return target @@ -187,7 +187,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(target && target.current) explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." return target /datum/objective/protect/check_completion() @@ -325,7 +325,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) target_real_name = target.current.real_name explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role] while wearing [target.p_their()] identification card." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." /datum/objective/escape/escape_with_identity/check_completion() if(!target_real_name) @@ -398,7 +398,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(islist(O.protected_jobs) && O.protected_jobs.len) explanation_text += "It may also be in the possession of the [jointext(O.protected_jobs, ", ")]." return - explanation_text = "Free Objective." + explanation_text = "Free Objective.." /datum/objective/steal/proc/select_target() @@ -423,7 +423,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) /datum/objective/steal/check_completion() if(!steal_target) - return 1 // Free Objective + return 1 // Free Objective. if(!owner.current) return FALSE @@ -525,7 +525,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) target_real_name = target.current.real_name explanation_text = "Destroy [target_real_name], the AI." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." return target /datum/objective/destroy/check_completion() @@ -632,7 +632,7 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) if(target && target.current) explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take [target.current.p_them()] alive." else - explanation_text = "Free Objective" + explanation_text = "Free Objective." return target /datum/objective/heist/kidnap/check_completion() diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index a920aa97905c..aaf1377edb50 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -368,10 +368,8 @@ /datum/game_mode/revolution/declare_completion() if(finished == 1) SSticker.mode_result = "revolution win - heads killed" - to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!") else if(finished == 2) SSticker.mode_result = "revolution loss - rev heads killed" - to_chat(world, "The heads of staff managed to stop the revolution!") ..() return TRUE diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm index 8eb5c2af8380..516d90a6ad28 100644 --- a/code/game/gamemodes/shadowling/shadowling.dm +++ b/code/game/gamemodes/shadowling/shadowling.dm @@ -267,20 +267,12 @@ Made by Xhuis /datum/game_mode/shadowling/declare_completion() if(check_shadow_victory() && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //Doesn't end instantly - this is hacky and I don't know of a better way ~X SSticker.mode_result = "shadowling win - shadowling ascension" - to_chat(world, "Shadowling Victory") - to_chat(world, "The shadowlings have ascended and taken over the station!") else if(shadowling_dead && !check_shadow_victory()) //If the shadowlings have ascended, they can not lose the round SSticker.mode_result = "shadowling loss - shadowling killed" - to_chat(world, "Crew Major Victory") - to_chat(world, "The shadowlings have been killed by the crew!") else if(!check_shadow_victory() && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) SSticker.mode_result = "shadowling loss - crew escaped" - to_chat(world, "Crew Minor Victory") - to_chat(world, "The crew escaped the station before the shadowlings could ascend!") else SSticker.mode_result = "shadowling loss - generic failure" - to_chat(world, "Crew Major Victory") - to_chat(world, "The shadowlings have failed!") ..() return 1 diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index 4c6d6139b6d4..d8828b20b8d8 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -117,20 +117,14 @@ var/count = 1 for(var/datum/objective/objective in traitor.objectives) if(objective.check_completion()) - text += "
Objective #[count]: [objective.explanation_text] Success!" + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "SUCCESS")) else - text += "
Objective #[count]: [objective.explanation_text] Fail." + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "FAIL")) traitorwin = 0 count++ - var/special_role_text - if(traitor.special_role) - special_role_text = lowertext(traitor.special_role) - else - special_role_text = "antagonist" - var/datum/antagonist/traitor/contractor/contractor = traitor.has_antag_datum(/datum/antagonist/traitor/contractor) if(istype(contractor) && contractor.contractor_uplink) var/count = 1 @@ -154,10 +148,8 @@ text += "
[earned_tc] TC were earned from the contracts." if(traitorwin) - text += "
The [special_role_text] was successful!
" SSblackbox.record_feedback("tally", "traitor_success", 1, "SUCCESS") else - text += "
The [special_role_text] has failed!
" SSblackbox.record_feedback("tally", "traitor_success", 1, "FAIL") if(length(SSticker.mode.implanted)) diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm index 9db30a4f4c69..4b45d9c84277 100644 --- a/code/game/gamemodes/vampire/vampire.dm +++ b/code/game/gamemodes/vampire/vampire.dm @@ -98,25 +98,17 @@ var/count = 1 for(var/datum/objective/objective in vampire.objectives) if(objective.check_completion()) - text += "
Objective #[count]: [objective.explanation_text] Success!" + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "SUCCESS")) else - text += "
Objective #[count]: [objective.explanation_text] Fail." + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "FAIL")) traitorwin = 0 count++ - var/special_role_text - if(vampire.special_role) - special_role_text = lowertext(vampire.special_role) - else - special_role_text = "antagonist" - if(traitorwin) - text += "
The [special_role_text] was successful!" SSblackbox.record_feedback("tally", "traitor_success", 1, "SUCCESS") else - text += "
The [special_role_text] has failed!" SSblackbox.record_feedback("tally", "traitor_success", 1, "FAIL") to_chat(world, text) return 1 diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm index 5c5960a1fe32..953c5af00049 100644 --- a/code/game/gamemodes/wizard/raginmages.dm +++ b/code/game/gamemodes/wizard/raginmages.dm @@ -158,5 +158,4 @@ /datum/game_mode/wizard/raginmages/declare_completion() if(finished) SSticker.mode_result = "raging wizard loss - wizard killed" - to_chat(world, " The crew has managed to hold off the Wizard attack! The Space Wizard Federation has been taught a lesson they will not soon forget!") ..(1) diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 2917e37c8d1c..49c9e34f1754 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -201,7 +201,6 @@ /datum/game_mode/wizard/declare_completion(var/ragin = 0) if(finished && !ragin) SSticker.mode_result = "wizard loss - wizard killed" - to_chat(world, " The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") ..() return 1 @@ -227,19 +226,17 @@ var/wizardwin = 1 for(var/datum/objective/objective in wizard.objectives) if(objective.check_completion()) - text += "
Objective #[count]: [objective.explanation_text] Success!" + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "wizard_objective", 1, list("[objective.type]", "SUCCESS")) else - text += "
Objective #[count]: [objective.explanation_text] Fail." + text += "
Objective #[count]: [objective.explanation_text]" SSblackbox.record_feedback("nested tally", "wizard_objective", 1, list("[objective.type]", "FAIL")) wizardwin = 0 count++ if(wizard.current && wizard.current.stat!=DEAD && wizardwin) - text += "
The wizard was successful!" SSblackbox.record_feedback("tally", "wizard_success", 1, "SUCCESS") else - text += "
The wizard has failed!" SSblackbox.record_feedback("tally", "wizard_success", 1, "FAIL") if(wizard.spell_list) text += "
[wizard.name] used the following spells: " diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index fef9c2302b39..ecb9798c1c7b 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -9,7 +9,7 @@ department_head = list("Captain") req_admin_notify = 1 minimal_player_age = 30 - exp_requirements = 300 + exp_requirements = 3000 exp_type = EXP_TYPE_SILICON /datum/job/ai/equip(mob/living/carbon/human/H) diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 03e1023461e8..2fe962c7c586 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -452,7 +452,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) visible_message("[src]: Reassigning a demoted individual requires a full ID computer.") return FALSE - if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE)) + if(!job_in_department(SSjobs.GetJob(modify.rank), config.allow_head_of_departaments_assign_civilian)) playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) visible_message("[src]: Reassigning someone outside your department requires a full ID computer.") return FALSE diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index 01562d2c60d5..6f01f42156a0 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -57,7 +57,7 @@ return if(istype(M, /mob/living/carbon/human)) var/mob/living/carbon/human/N = M - var/objective = "Free Objective" + var/objective = "Free Objective." switch(rand(1,100)) if(1 to 50) objective = "Steal [pick("a hand teleporter", "the Captain's antique laser gun", "a jetpack", "the Captain's ID", "the Captain's jumpsuit")]." diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index 6cdd3b0de5fa..d26ff3aa58da 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -19,7 +19,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -55,7 +55,7 @@ - + @@ -73,7 +73,7 @@ - + @@ -91,7 +91,7 @@ - + @@ -690,7 +690,7 @@ - + @@ -713,7 +713,7 @@ - + @@ -731,7 +731,7 @@ - + @@ -748,7 +748,7 @@ - + @@ -1072,7 +1072,7 @@ - + @@ -1090,7 +1090,7 @@ - + @@ -1108,7 +1108,7 @@ - + @@ -1126,7 +1126,7 @@ - + @@ -1144,7 +1144,7 @@ - + @@ -1162,7 +1162,7 @@ - + @@ -1180,7 +1180,7 @@ - + @@ -1198,7 +1198,7 @@ - + @@ -1216,7 +1216,7 @@ - + diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 9f0daed2fbeb..3a778c92c55c 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -24,7 +24,7 @@ // Please dont override this unless you absolutely have to /obj/structure/closet/Initialize(mapload) . = ..() - if(!opened) + if(mapload && !opened) // Youre probably asking, why is this a 0 seconds timer AA? // Well, I will tell you. One day, all /obj/effect/spawner will use Initialize // This includes maint loot spawners. The problem with that is if a closet loads before a spawner, diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 6b8800449872..931628050fe2 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -277,6 +277,7 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) return anchored = !anchored + air_update_turf(TRUE) update_nearby_icons() to_chat(user, "You [anchored ? "fasten the frame to":"unfasten the frame from"] the floor.") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 890a1e8c6921..b85ebe4ded0e 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1860,16 +1860,14 @@ if(PDA.owner == old_name) PDA.owner = new_name PDA.name = "PDA-[new_name] ([PDA.ownjob])" - //rename general records with mob old name - for(var/datum/data/record/R in GLOB.data_core.general) - if(R.fields["name"] == old_name) - R.fields["name"] = new_name - break - //rename security records with mob old name - for(var/datum/data/record/E in GLOB.data_core.security) - if(E.fields["name"] == old_name) - E.fields["name"] = new_name - break + //update the datacore records! This is goig to be a bit costly. + for(var/list/L in list(GLOB.data_core.general, GLOB.data_core.medical, GLOB.data_core.security, GLOB.data_core.locked)) + for(var/datum/data/record/R in L) + if(R.fields["name"] == old_name) + R.fields["name"] = new_name + if(length(R.fields["id"]) == 32) + R.fields["id"] = md5("[new_name][M.mind.assigned_role]") + break log_and_message_admins(message + "[new_name].") diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 41333760f2d6..49a8aefa13a6 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -113,19 +113,12 @@ GLOBAL_LIST_EMPTY(antagonists) report += printplayer(owner) - var/objectives_complete = TRUE if(owner.objectives.len) report += printobjectives(owner) for(var/datum/objective/objective in owner.objectives) if(!objective.check_completion()) - objectives_complete = FALSE break - if(owner.objectives.len == 0 || objectives_complete) - report += "The [name] was successful!" - else - report += "The [name] has failed!" - return report.Join("
") //Displayed at the start of roundend_category section, default to roundend_category header diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 974ad9fd26fd..894192bdc395 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -379,8 +379,12 @@ body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS resistance_flags = NONE -/obj/item/clothing/under/gladiator/ash_walker - desc = "This gladiator uniform appears to be covered in ash and fairly dated." +/obj/item/clothing/under/ash_walker + name = "ash-walker uniform" + desc = "" + icon_state = "ash" + item_state = "ash" + item_color = "ash" has_sensor = FALSE //dress diff --git a/code/modules/food_and_drinks/food.dm b/code/modules/food_and_drinks/food.dm index 15e28cc6c62d..a534cf1ad25d 100644 --- a/code/modules/food_and_drinks/food.dm +++ b/code/modules/food_and_drinks/food.dm @@ -13,7 +13,7 @@ var/transfer_efficiency = 1.0 var/instant_application = 0 //if we want to bypass the forcedfeed delay var/can_taste = TRUE//whether you can taste eating from this - var/antable = TRUE // Will ants come near it? + var/antable = FALSE // Will ants come near it? var/ant_location = null var/ant_timer = null resistance_flags = FLAMMABLE diff --git a/code/modules/martial_arts/combos/synthojitsu/lock.dm b/code/modules/martial_arts/combos/synthojitsu/lock.dm new file mode 100644 index 000000000000..9209c392a75f --- /dev/null +++ b/code/modules/martial_arts/combos/synthojitsu/lock.dm @@ -0,0 +1,13 @@ +/datum/martial_combo/synthojitsu/lock + name = "Lock" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_GRAB) + explaination_text = "Allows user to grab opponent quickly" + +/datum/martial_combo/synthojitsu/lock/perform_combo(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/MA) + var/obj/item/grab/G = target.grabbedby(user, 1) + if(G) + G.state = GRAB_AGGRESSIVE //Instant aggressive grab + add_attack_logs(user, target, "Melee attacked with martial-art [src] : aggressively grabbed", ATKLOG_ALL) + user.adjust_nutrition(-25) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_FAIL diff --git a/code/modules/martial_arts/combos/synthojitsu/overload.dm b/code/modules/martial_arts/combos/synthojitsu/overload.dm new file mode 100644 index 000000000000..3a5c47d099b5 --- /dev/null +++ b/code/modules/martial_arts/combos/synthojitsu/overload.dm @@ -0,0 +1,16 @@ +/datum/martial_combo/synthojitsu/overload + name = "Overload" + steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Shocks opponent using spare energy." + +/datum/martial_combo/synthojitsu/overload/perform_combo(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/MA) + . = MARTIAL_COMBO_FAIL + target.visible_message("[user] shocked [target]!", \ + "[user] shocked you!") + target.apply_damage(10, BRUTE) + target.Weaken(1) + target.apply_damage(20, BURN) + user.adjust_nutrition(-125) + playsound(get_turf(target), 'sound/magic/lightningbolt.ogg', 50, 1) + add_attack_logs(user, target, "Melee attacked with martial-art [src]", ATKLOG_ALL) + return MARTIAL_COMBO_DONE diff --git a/code/modules/martial_arts/combos/synthojitsu/reanimate.dm b/code/modules/martial_arts/combos/synthojitsu/reanimate.dm new file mode 100644 index 000000000000..e2d5076aea2e --- /dev/null +++ b/code/modules/martial_arts/combos/synthojitsu/reanimate.dm @@ -0,0 +1,24 @@ +/datum/martial_combo/synthojitsu/reanimate + name = "Reanimate" + steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HELP) + explaination_text = "Restarts heart with shock. Use with caution." + +/datum/martial_combo/synthojitsu/reanimate/perform_combo(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/MA) + if(target.stat == DEAD) + to_chat(user, "[target] doesn't respond at all!") + return MARTIAL_COMBO_FAIL + if(target.undergoing_cardiac_arrest()) + to_chat(user, "[target] inhales deeply!") + target.adjustOxyLoss(-100) + target.set_heartattack(FALSE) + user.adjust_nutrition(-75) + target.shock_internal_organs(100) + target.visible_message("[user] shocked [target]!", \ + "[user] shoked you!") + playsound(get_turf(user), 'sound/weapons/egloves.ogg', 50, 1, -1) + target.apply_damage(10, BURN) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : defib", ATKLOG_ALL) + . = MARTIAL_COMBO_DONE + else + to_chat(user, "[target] does not require shock.Aborting...") + return MARTIAL_COMBO_FAIL \ No newline at end of file diff --git a/code/modules/martial_arts/synthojitsu.dm b/code/modules/martial_arts/synthojitsu.dm new file mode 100644 index 000000000000..7d4922457786 --- /dev/null +++ b/code/modules/martial_arts/synthojitsu.dm @@ -0,0 +1,63 @@ +/datum/martial_art/synthojitsu + name = "Synthojitsu" + block_chance = 0 + has_explaination_verb = TRUE + combos = list(/datum/martial_combo/synthojitsu/lock, /datum/martial_combo/synthojitsu/overload, /datum/martial_combo/synthojitsu/reanimate) + +/datum/martial_art/synthojitsu/can_use(mob/living/carbon/human/H) + if(!ismachineperson(H) || H.nutrition == 0) + return FALSE + return ..() + +/datum/martial_art/synthojitsu/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + MARTIAL_ARTS_ACT_CHECK + add_attack_logs(A, D, "Melee attacked with martial-art [src]", ATKLOG_ALL) + A.do_attack_animation(D) + D.apply_damage(5, BURN) + D.apply_damage(5, BRUTE) + A.adjust_nutrition(-10) + playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + D.visible_message("[A] electrocuted [D]!", \ + "[A] elecrtrocuted you!") + add_attack_logs(A, D, "Melee attacked with martial-art [src]", ATKLOG_ALL) + return TRUE + +/datum/martial_art/synthojitsu/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + MARTIAL_ARTS_ACT_CHECK + A.do_attack_animation(D) + add_attack_logs(A, D, "Melee attacked with martial-art [src]", ATKLOG_ALL) + D.apply_damage(30, STAMINA) + A.adjust_nutrition(-10) + playsound(get_turf(D), 'sound/weapons/contractorbatonhit.ogg', 50, 1, -1) + D.visible_message("[A] tapped [D]!", \ + "[A] tapped you!") + return TRUE + +/obj/item/ipc_combat_upgrade + name = "IPC combat upgrade" + desc = "Advanced data storage designed to be compatible with positronic systems.This one include melee algorithms along with overwritten microbattery safety protocols." + icon = 'icons/obj/ipc_module.dmi' + icon_state ="viable" + var/is_used = FALSE + +/obj/item/ipc_combat_upgrade/attack_self(mob/user as mob) + if(!ismachineperson(user) || is_used == TRUE) + return + to_chat(user, "Installing data. It will take some time...") + if(do_after(user, 100)) + var/mob/living/carbon/human/H = user + var/datum/martial_art/synthojitsu/F = new/datum/martial_art/synthojitsu(null) + F.teach(H) + H.adjustBrainLoss(50) + H.Weaken(5) + to_chat(H, "Melee algorithms installed. Safety disabled.") + is_used = TRUE + desc = "Advanced data storage designed to be compatible with positronic systems.This one include melee algorithms along with overwritten microbattery safety protocols.It's hardlocked" + name = "IPC combat upgrade" + icon_state = "unviable" + +/datum/martial_art/synthojitsu/explaination_header(user) + to_chat(user, "You reapload some of the basics of synthojitsu.") + +/datum/martial_art/synthojitsu/explaination_footer(user) + to_chat(user, "In addition, your attacks will deal additional burn damage. Your disarm attempts will exhaust opponent. All attacks and combos will draw your internal battery.") diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index 0c7b836b3226..b0ce9757c2a3 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -165,7 +165,11 @@ desc = "Wall-mounted Medical Equipment dispenser. This one seems just a tiny bit smaller." req_access = list() - products = list(/obj/item/stack/medical/splint = 2) + products = list(/obj/item/stack/medical/splint = 2, + /obj/item/reagent_containers/food/pill/patch/silver_sulf = 2, + /obj/item/reagent_containers/food/pill/patch/styptic = 2, + /obj/item/reagent_containers/hypospray/autoinjector = 1, + /obj/item/healthanalyzer = 1) contraband = list() //Computer diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm index 906077a4915c..e5013b2ccb43 100644 --- a/code/modules/mob/living/carbon/human/species/machine.dm +++ b/code/modules/mob/living/carbon/human/species/machine.dm @@ -15,8 +15,8 @@ skinned_type = /obj/item/stack/sheet/metal // Let's grind up IPCs for station resources! eyes = "blank_eyes" - brute_mod = 2.28 // 100% * 2.28 * 0.66 (robolimbs) ~= 150% - burn_mod = 2.28 // So they take 50% extra damage from brute/burn overall + brute_mod = 1 // 100% * 2.28 * 0.66 (robolimbs) ~= 150% // nope + burn_mod = 1 // So they take 50% extra damage from brute/burn overall // nope tox_mod = 0 clone_mod = 0 death_message = "gives a short series of shrill beeps, their chassis shuddering before falling limp, nonfunctional." 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 a5248e98bb6d..df7a78b13e86 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -357,7 +357,7 @@ l_pocket = pickweight(list(/obj/item/stack/spacecash/c1000 = 7, /obj/item/reagent_containers/hypospray/autoinjector/survival = 2, /obj/item/borg/upgrade/modkit/cooldown = 1 )) if("Ashwalker") mob_species = /datum/species/unathi/ashwalker - uniform = /obj/item/clothing/under/gladiator/ash_walker + uniform = /obj/item/clothing/under/ash_walker if(prob(95)) head = /obj/item/clothing/head/helmet/gladiator else diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index de80691db16e..d99dd6e9d39b 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -547,6 +547,8 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM for(var/datum/data/record/R in L) if(R.fields["name"] == oldname) R.fields["name"] = newname + if(length(R.fields["id"]) == 32) + R.fields["id"] = md5("[newname][mind.assigned_role]") break //update our pda and id if we have them on our person diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index 299f208444e3..63c47ea29924 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -1,5 +1,15 @@ /mob/new_player/Login() update_Login_details() //handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying + + //Overflow rerouting, if set, forces players to be moved to a different server once a player cap is reached. Less rough than a pure kick. + if(config.player_overflow_cap && config.overflow_server_url) + if(!whitelist_check()) + var/tally = 0 + for(var/client/C in GLOB.clients) + tally++ + if(tally > config.player_overflow_cap) + src << link(config.overflow_server_url) + if(GLOB.join_motd) to_chat(src, "
[GLOB.join_motd]
") @@ -32,11 +42,24 @@ if(client) client.playtitlemusic() - if(config.player_overflow_cap && config.overflow_server_url) //Overflow rerouting, if set, forces players to be moved to a different server once a player cap is reached. Less rough than a pure kick. - if(src.client.holder) return //admins are immune to overflow rerouting - if(config.overflow_whitelist.Find(lowertext(src.ckey))) return //Whitelisted people are immune to overflow rerouting. - var/tally = 0 - for(var/client/C in GLOB.clients) - tally++ - if(tally > config.player_overflow_cap) - src << link(config.overflow_server_url) +/mob/new_player/proc/whitelist_check() + // Admins are immune to overflow rerouting + if(check_rights(rights_required = 0, show_msg = 0)) + return TRUE + + //Whitelisted people are immune to overflow rerouting. + if(config.usewhitelist_database && SSdbcore.IsConnected()) + var/datum/db_query/find_ticket = SSdbcore.NewQuery( + "SELECT ckey FROM [format_table_name("ckey_whitelist")] WHERE ckey=:ckey AND is_valid=true AND port=:port AND date_start<=NOW() AND (NOW()