diff --git a/SQL/database_changelog.txt b/SQL/database_changelog.txt
index c2625e2663..7063273378 100644
--- a/SQL/database_changelog.txt
+++ b/SQL/database_changelog.txt
@@ -1,13 +1,52 @@
Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255.
-The latest database version is 3.1; The query to update the schema revision table is:
+The latest database version is 3.4; The query to update the schema revision table is:
-INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 1);
+INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 4);
or
-INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (3, 1);
+INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (3, 4);
+
+In any query remember to add a prefix to the table names if you use one.
----------------------------------------------------
+28 August 2017, by MrStonedOne
+Modified table 'messages', adding a deleted column and editing all indexes to include it
+
+ALTER TABLE `messages`
+ADD COLUMN `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' AFTER `edits`,
+DROP INDEX `idx_msg_ckey_time`,
+DROP INDEX `idx_msg_type_ckeys_time`,
+DROP INDEX `idx_msg_type_ckey_time_odr`,
+ADD INDEX `idx_msg_ckey_time` (`targetckey`,`timestamp`, `deleted`),
+ADD INDEX `idx_msg_type_ckeys_time` (`type`,`targetckey`,`adminckey`,`timestamp`, `deleted`),
+ADD INDEX `idx_msg_type_ckey_time_odr` (`type`,`targetckey`,`timestamp`, `deleted`);
+
+----------------------------------------------------
+
+25 August 2017, by Jordie0608
+
+Modified tables 'connection_log', 'legacy_population', 'library', 'messages' and 'player' to add additional 'round_id' tracking in various forms and 'server_ip' and 'server_port' to the table 'messages'.
+
+ALTER TABLE `connection_log` ADD COLUMN `round_id` INT(11) UNSIGNED NOT NULL AFTER `server_port`;
+ALTER TABLE `legacy_population` ADD COLUMN `round_id` INT(11) UNSIGNED NOT NULL AFTER `server_port`;
+ALTER TABLE `library` ADD COLUMN `round_id_created` INT(11) UNSIGNED NOT NULL AFTER `deleted`;
+ALTER TABLE `messages` ADD COLUMN `server_ip` INT(10) UNSIGNED NOT NULL AFTER `server`, ADD COLUMN `server_port` SMALLINT(5) UNSIGNED NOT NULL AFTER `server_ip`, ADD COLUMN `round_id` INT(11) UNSIGNED NOT NULL AFTER `server_port`;
+ALTER TABLE `player` ADD COLUMN `firstseen_round_id` INT(11) UNSIGNED NOT NULL AFTER `firstseen`, ADD COLUMN `lastseen_round_id` INT(11) UNSIGNED NOT NULL AFTER `lastseen`;
+
+----------------------------------------------------
+
+18 August 2017, by Cyberboss and nfreader
+
+Modified table 'death', adding the columns `last_words` and 'suicide'.
+
+ALTER TABLE `death`
+ADD COLUMN `last_words` varchar(255) DEFAULT NULL AFTER `staminaloss`,
+ADD COLUMN `suicide` tinyint(0) NOT NULL DEFAULT '0' AFTER `last_words`;
+
+Remember to add a prefix to the table name if you use them.
+
+----------------------------------------------------
20th July 2017, by Shadowlight213
Added role_time table to track time spent playing departments.
@@ -17,21 +56,10 @@ CREATE TABLE `role_time` ( `ckey` VARCHAR(32) NOT NULL , `job` VARCHAR(128) NOT
ALTER TABLE `player` ADD `flags` INT NOT NULL default '0' AFTER `accountjoindate`;
-
-UPDATE `schema_revision` SET minor = 1;
Remember to add a prefix to the table name if you use them.
----------------------------------------------------
-Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255.
-
-The latest database version is 3.0; The query to update the schema revision table is:
-
-INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 0);
-or
-INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (3, 0);
-
-----------------------------------------------------
28 June 2017, by oranges
Added schema_revision to store the current db revision, why start at 3.0?
@@ -336,4 +364,4 @@ UPDATE erro_library SET deleted = 1 WHERE id = someid
(Replace someid with the id of the book you want to soft delete.)
-----------------------------------------------------
+----------------------------------------------------
\ No newline at end of file
diff --git a/SQL/errofreedatabase.sql b/SQL/errofreedatabase.sql
index 7f23fcc861..7d6ea4561c 100644
--- a/SQL/errofreedatabase.sql
+++ b/SQL/errofreedatabase.sql
@@ -12,4 +12,4 @@ ALTER TABLE erro_poll_option RENAME TO SS13_poll_option;
ALTER TABLE erro_poll_question RENAME TO SS13_poll_question;
ALTER TABLE erro_poll_textreply RENAME TO SS13_poll_textreply;
ALTER TABLE erro_poll_vote RENAME TO SS13_poll_vote;
-ALTER TABLE erro_watch RENAME TO SS13_watch;
+ALTER TABLE erro_watch RENAME TO SS13_watch;
\ No newline at end of file
diff --git a/SQL/optimisations_2017-02-19.sql b/SQL/optimisations_2017-02-19.sql
index 674cbcf9c6..b9017497ed 100644
--- a/SQL/optimisations_2017-02-19.sql
+++ b/SQL/optimisations_2017-02-19.sql
@@ -38,7 +38,7 @@ Take note some columns have been renamed, removed or changed type. Any services
----------------------------------------------------*/
START TRANSACTION;
-ALTER TABLE `feedback`.`ban`
+ALTER TABLE `ban`
DROP COLUMN `rounds`
, CHANGE COLUMN `bantype` `bantype` ENUM('PERMABAN', 'TEMPBAN', 'JOB_PERMABAN', 'JOB_TEMPBAN', 'ADMIN_PERMABAN', 'ADMIN_TEMPBAN') NOT NULL
, CHANGE COLUMN `reason` `reason` VARCHAR(2048) NOT NULL
@@ -51,14 +51,14 @@ ALTER TABLE `feedback`.`ban`
, ADD COLUMN `a_ipTEMP` INT UNSIGNED NOT NULL AFTER `a_ip`
, ADD COLUMN `unbanned_ipTEMP` INT UNSIGNED NULL DEFAULT NULL AFTER `unbanned_ip`;
SET SQL_SAFE_UPDATES = 0;
-UPDATE `feedback`.`ban`
+UPDATE `ban`
SET `server_ip` = INET_ATON(SUBSTRING_INDEX(IF(`serverip` = '', '0', IF(SUBSTRING_INDEX(`serverip`, ':', 1) LIKE '%_._%', `serverip`, '0')), ':', 1))
, `server_port` = IF(`serverip` LIKE '%:_%', CAST(SUBSTRING_INDEX(`serverip`, ':', -1) AS UNSIGNED), '0')
, `ipTEMP` = INET_ATON(IF(`ip` LIKE '%_._%', `ip`, '0'))
, `a_ipTEMP` = INET_ATON(IF(`a_ip` LIKE '%_._%', `a_ip`, '0'))
, `unbanned_ipTEMP` = INET_ATON(IF(`unbanned_ip` LIKE '%_._%', `unbanned_ip`, '0'));
SET SQL_SAFE_UPDATES = 1;
-ALTER TABLE `feedback`.`ban`
+ALTER TABLE `ban`
DROP COLUMN `unbanned_ip`
, DROP COLUMN `a_ip`
, DROP COLUMN `ip`
@@ -69,17 +69,17 @@ ALTER TABLE `feedback`.`ban`
COMMIT;
START TRANSACTION;
-ALTER TABLE `feedback`.`connection_log`
+ALTER TABLE `connection_log`
ADD COLUMN `server_ip` INT UNSIGNED NOT NULL AFTER `serverip`
, ADD COLUMN `server_port` SMALLINT UNSIGNED NOT NULL AFTER `server_ip`
, ADD COLUMN `ipTEMP` INT UNSIGNED NOT NULL AFTER `ip`;
SET SQL_SAFE_UPDATES = 0;
-UPDATE `feedback`.`connection_log`
+UPDATE `connection_log`
SET `server_ip` = INET_ATON(SUBSTRING_INDEX(IF(`serverip` = '', '0', IF(SUBSTRING_INDEX(`serverip`, ':', 1) LIKE '%_._%', `serverip`, '0')), ':', 1))
, `server_port` = IF(`serverip` LIKE '%:_%', CAST(SUBSTRING_INDEX(`serverip`, ':', -1) AS UNSIGNED), '0')
, `ipTEMP` = INET_ATON(IF(`ip` LIKE '%_._%', `ip`, '0'));
SET SQL_SAFE_UPDATES = 1;
-ALTER TABLE `feedback`.`connection_log`
+ALTER TABLE `connection_log`
DROP COLUMN `ip`
, DROP COLUMN `serverip`
, CHANGE COLUMN `ipTEMP` `ip` INT(10) UNSIGNED NOT NULL;
@@ -87,12 +87,12 @@ COMMIT;
START TRANSACTION;
SET SQL_SAFE_UPDATES = 0;
-UPDATE `feedback`.`death`
+UPDATE `death`
SET `bruteloss` = LEAST(`bruteloss`, 65535)
, `brainloss` = LEAST(`brainloss`, 65535)
, `fireloss` = LEAST(`fireloss`, 65535)
, `oxyloss` = LEAST(`oxyloss`, 65535);
-ALTER TABLE `feedback`.`death`
+ALTER TABLE `death`
CHANGE COLUMN `pod` `pod` VARCHAR(50) NOT NULL
, CHANGE COLUMN `coord` `coord` VARCHAR(32) NOT NULL
, CHANGE COLUMN `mapname` `mapname` VARCHAR(32) NOT NULL
@@ -109,39 +109,39 @@ ALTER TABLE `feedback`.`death`
, CHANGE COLUMN `oxyloss` `oxyloss` SMALLINT UNSIGNED NOT NULL
, ADD COLUMN `server_ip` INT UNSIGNED NOT NULL AFTER `server`
, ADD COLUMN `server_port` SMALLINT UNSIGNED NOT NULL AFTER `server_ip`;
-UPDATE `feedback`.`death`
+UPDATE `death`
SET `server_ip` = INET_ATON(SUBSTRING_INDEX(IF(`server` = '', '0', IF(SUBSTRING_INDEX(`server`, ':', 1) LIKE '%_._%', `server`, '0')), ':', 1))
, `server_port` = IF(`server` LIKE '%:_%', CAST(SUBSTRING_INDEX(`server`, ':', -1) AS UNSIGNED), '0');
SET SQL_SAFE_UPDATES = 1;
-ALTER TABLE `feedback`.`death`
+ALTER TABLE `death`
DROP COLUMN `server`;
COMMIT;
-ALTER TABLE `feedback`.`library`
+ALTER TABLE `library`
CHANGE COLUMN `category` `category` ENUM('Any', 'Fiction', 'Non-Fiction', 'Adult', 'Reference', 'Religion') NOT NULL
, CHANGE COLUMN `ckey` `ckey` VARCHAR(32) NOT NULL DEFAULT 'LEGACY'
, CHANGE COLUMN `datetime` `datetime` DATETIME NOT NULL
, CHANGE COLUMN `deleted` `deleted` TINYINT(1) UNSIGNED NULL DEFAULT NULL;
-ALTER TABLE `feedback`.`messages`
+ALTER TABLE `messages`
CHANGE COLUMN `type` `type` ENUM('memo', 'message', 'message sent', 'note', 'watchlist entry') NOT NULL
, CHANGE COLUMN `text` `text` VARCHAR(2048) NOT NULL
, CHANGE COLUMN `secret` `secret` TINYINT(1) UNSIGNED NOT NULL;
START TRANSACTION;
-ALTER TABLE `feedback`.`player`
+ALTER TABLE `player`
ADD COLUMN `ipTEMP` INT UNSIGNED NOT NULL AFTER `ip`;
SET SQL_SAFE_UPDATES = 0;
-UPDATE `feedback`.`player`
+UPDATE `player`
SET `ipTEMP` = INET_ATON(IF(`ip` LIKE '%_._%', `ip`, '0'));
SET SQL_SAFE_UPDATES = 1;
-ALTER TABLE `feedback`.`player`
+ALTER TABLE `player`
DROP COLUMN `ip`
, CHANGE COLUMN `ipTEMP` `ip` INT(10) UNSIGNED NOT NULL;
COMMIT;
START TRANSACTION;
-ALTER TABLE `feedback`.`poll_question`
+ALTER TABLE `poll_question`
CHANGE COLUMN `polltype` `polltype` ENUM('OPTION', 'TEXT', 'NUMVAL', 'MULTICHOICE', 'IRV') NOT NULL
, CHANGE COLUMN `adminonly` `adminonly` TINYINT(1) UNSIGNED NOT NULL
, CHANGE COLUMN `createdby_ckey` `createdby_ckey` VARCHAR(32) NULL DEFAULT NULL
@@ -149,36 +149,36 @@ ALTER TABLE `feedback`.`poll_question`
, ADD COLUMN `createdby_ipTEMP` INT UNSIGNED NOT NULL AFTER `createdby_ip`
, DROP COLUMN `for_trialmin`;
SET SQL_SAFE_UPDATES = 0;
-UPDATE `feedback`.`poll_question`
+UPDATE `poll_question`
SET `createdby_ipTEMP` = INET_ATON(IF(`createdby_ip` LIKE '%_._%', `createdby_ip`, '0'));
SET SQL_SAFE_UPDATES = 1;
-ALTER TABLE `feedback`.`poll_question`
+ALTER TABLE `poll_question`
DROP COLUMN `createdby_ip`
, CHANGE COLUMN `createdby_ipTEMP` `createdby_ip` INT(10) UNSIGNED NOT NULL;
COMMIT;
START TRANSACTION;
-ALTER TABLE `feedback`.`poll_textreply`
+ALTER TABLE `poll_textreply`
CHANGE COLUMN `replytext` `replytext` VARCHAR(2048) NOT NULL
, ADD COLUMN `ipTEMP` INT UNSIGNED NOT NULL AFTER `ip`;
SET SQL_SAFE_UPDATES = 0;
-UPDATE `feedback`.`poll_textreply`
+UPDATE `poll_textreply`
SET `ipTEMP` = INET_ATON(IF(`ip` LIKE '%_._%', `ip`, '0'));
SET SQL_SAFE_UPDATES = 1;
-ALTER TABLE `feedback`.`poll_textreply`
+ALTER TABLE `poll_textreply`
DROP COLUMN `ip`
, CHANGE COLUMN `ipTEMP` `ip` INT(10) UNSIGNED NOT NULL;
COMMIT;
START TRANSACTION;
-ALTER TABLE `feedback`.`poll_vote`
+ALTER TABLE `poll_vote`
CHANGE COLUMN `ckey` `ckey` VARCHAR(32) NOT NULL
, ADD COLUMN `ipTEMP` INT UNSIGNED NOT NULL AFTER `ip`;
SET SQL_SAFE_UPDATES = 0;
-UPDATE `feedback`.`poll_vote`
+UPDATE `poll_vote`
SET `ipTEMP` = INET_ATON(IF(`ip` LIKE '%_._%', `ip`, '0'));
SET SQL_SAFE_UPDATES = 1;
-ALTER TABLE `feedback`.`poll_vote`
+ALTER TABLE `poll_vote`
DROP COLUMN `ip`
, CHANGE COLUMN `ipTEMP` `ip` INT(10) UNSIGNED NOT NULL;
COMMIT;
@@ -191,39 +191,39 @@ You may find it helpful to modify or create your own indexes if you utilise addi
----------------------------------------------------*/
-ALTER TABLE `feedback`.`ban`
+ALTER TABLE `ban`
ADD INDEX `idx_ban_checkban` (`ckey` ASC, `bantype` ASC, `expiration_time` ASC, `unbanned` ASC, `job` ASC)
, ADD INDEX `idx_ban_isbanned` (`ckey` ASC, `ip` ASC, `computerid` ASC, `bantype` ASC, `expiration_time` ASC, `unbanned` ASC)
, ADD INDEX `idx_ban_count` (`id` ASC, `a_ckey` ASC, `bantype` ASC, `expiration_time` ASC, `unbanned` ASC);
-ALTER TABLE `feedback`.`ipintel`
+ALTER TABLE `ipintel`
ADD INDEX `idx_ipintel` (`ip` ASC, `intel` ASC, `date` ASC);
-ALTER TABLE `feedback`.`library`
+ALTER TABLE `library`
ADD INDEX `idx_lib_id_del` (`id` ASC, `deleted` ASC)
, ADD INDEX `idx_lib_del_title` (`deleted` ASC, `title` ASC)
, ADD INDEX `idx_lib_search` (`deleted` ASC, `author` ASC, `title` ASC, `category` ASC);
-ALTER TABLE `feedback`.`messages`
+ALTER TABLE `messages`
ADD INDEX `idx_msg_ckey_time` (`targetckey` ASC, `timestamp` ASC)
, ADD INDEX `idx_msg_type_ckeys_time` (`type` ASC, `targetckey` ASC, `adminckey` ASC, `timestamp` ASC)
, ADD INDEX `idx_msg_type_ckey_time_odr` (`type` ASC, `targetckey` ASC, `timestamp` ASC);
-ALTER TABLE `feedback`.`player`
+ALTER TABLE `player`
ADD INDEX `idx_player_cid_ckey` (`computerid` ASC, `ckey` ASC)
, ADD INDEX `idx_player_ip_ckey` (`ip` ASC, `ckey` ASC);
-ALTER TABLE `feedback`.`poll_option`
+ALTER TABLE `poll_option`
ADD INDEX `idx_pop_pollid` (`pollid` ASC);
-ALTER TABLE `feedback`.`poll_question`
+ALTER TABLE `poll_question`
ADD INDEX `idx_pquest_question_time_ckey` (`question` ASC, `starttime` ASC, `endtime` ASC, `createdby_ckey` ASC, `createdby_ip` ASC)
, ADD INDEX `idx_pquest_time_admin` (`starttime` ASC, `endtime` ASC, `adminonly` ASC)
, ADD INDEX `idx_pquest_id_time_type_admin` (`id` ASC, `starttime` ASC, `endtime` ASC, `polltype` ASC, `adminonly` ASC);
-ALTER TABLE `feedback`.`poll_vote`
+ALTER TABLE `poll_vote`
ADD INDEX `idx_pvote_pollid_ckey` (`pollid` ASC, `ckey` ASC)
, ADD INDEX `idx_pvote_optionid_ckey` (`optionid` ASC, `ckey` ASC);
-ALTER TABLE `feedback`.`poll_textreply`
- ADD INDEX `idx_ptext_pollid_ckey` (`pollid` ASC, `ckey` ASC);
+ALTER TABLE `poll_textreply`
+ ADD INDEX `idx_ptext_pollid_ckey` (`pollid` ASC, `ckey` ASC);
\ No newline at end of file
diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql
index 12b0b93c6a..1edd5ad12b 100644
--- a/SQL/tgstation_schema.sql
+++ b/SQL/tgstation_schema.sql
@@ -254,10 +254,11 @@ CREATE TABLE `messages` (
`secret` tinyint(1) unsigned NOT NULL,
`lasteditor` varchar(32) DEFAULT NULL,
`edits` text,
+ `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
- KEY `idx_msg_ckey_time` (`targetckey`,`timestamp`),
- KEY `idx_msg_type_ckeys_time` (`type`,`targetckey`,`adminckey`,`timestamp`),
- KEY `idx_msg_type_ckey_time_odr` (`type`,`targetckey`,`timestamp`)
+ KEY `idx_msg_ckey_time` (`targetckey`,`timestamp`, `deleted`),
+ KEY `idx_msg_type_ckeys_time` (`type`,`targetckey`,`adminckey`,`timestamp`, `deleted`),
+ KEY `idx_msg_type_ckey_time_odr` (`type`,`targetckey`,`timestamp`, `deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -430,4 +431,4 @@ CREATE TABLE `schema_revision` (
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
\ No newline at end of file
diff --git a/SQL/tgstation_schema.sql.rej b/SQL/tgstation_schema.sql.rej
deleted file mode 100644
index 51068bed4e..0000000000
--- a/SQL/tgstation_schema.sql.rej
+++ /dev/null
@@ -1,9 +0,0 @@
-diff a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql (rejected hunks)
-@@ -268,7 +283,6 @@ CREATE TABLE `player` (
- `ip` int(10) unsigned NOT NULL,
- `computerid` varchar(32) NOT NULL,
- `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player',
-- `exp` mediumtext,
- PRIMARY KEY (`id`),
- UNIQUE KEY `ckey` (`ckey`),
- KEY `idx_player_cid_ckey` (`computerid`,`ckey`),
diff --git a/SQL/tgstation_schema_prefixed.sql b/SQL/tgstation_schema_prefixed.sql
index 6c8ea19b0f..72045e50fb 100644
--- a/SQL/tgstation_schema_prefixed.sql
+++ b/SQL/tgstation_schema_prefixed.sql
@@ -31,7 +31,7 @@ CREATE TABLE `SS13_admin` (
-- Table structure for table `SS13_admin_log`
--
-DROP TABLE IF EXISTS `SS13_dmin_log`;
+DROP TABLE IF EXISTS `SS13_admin_log`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `SS13_admin_log` (
@@ -254,10 +254,11 @@ CREATE TABLE `SS13_messages` (
`secret` tinyint(1) unsigned NOT NULL,
`lasteditor` varchar(32) DEFAULT NULL,
`edits` text,
+ `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
- KEY `idx_msg_ckey_time` (`targetckey`,`timestamp`),
- KEY `idx_msg_type_ckeys_time` (`type`,`targetckey`,`adminckey`,`timestamp`),
- KEY `idx_msg_type_ckey_time_odr` (`type`,`targetckey`,`timestamp`)
+ KEY `idx_msg_ckey_time` (`targetckey`,`timestamp`, `deleted`),
+ KEY `idx_msg_type_ckeys_time` (`type`,`targetckey`,`adminckey`,`timestamp`, `deleted`),
+ KEY `idx_msg_type_ckey_time_odr` (`type`,`targetckey`,`timestamp`, `deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -430,4 +431,4 @@ CREATE TABLE `SS13_schema_revision` (
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
-/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
\ No newline at end of file
diff --git a/SQL/tgstation_schema_prefixed.sql.rej b/SQL/tgstation_schema_prefixed.sql.rej
deleted file mode 100644
index dd4bc6a7f5..0000000000
--- a/SQL/tgstation_schema_prefixed.sql.rej
+++ /dev/null
@@ -1,9 +0,0 @@
-diff a/SQL/tgstation_schema_prefixed.sql b/SQL/tgstation_schema_prefixed.sql (rejected hunks)
-@@ -268,7 +297,6 @@ CREATE TABLE `SS13_player` (
- `ip` int(10) unsigned NOT NULL,
- `computerid` varchar(32) NOT NULL,
- `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player',
-- `exp` mediumtext,
- PRIMARY KEY (`id`),
- UNIQUE KEY `ckey` (`ckey`),
- KEY `idx_player_cid_ckey` (`computerid`,`ckey`),
diff --git a/_maps/RandomRuins/SpaceRuins/gondolaasteroid.dmm b/_maps/RandomRuins/SpaceRuins/gondolaasteroid.dmm
new file mode 100644
index 0000000000..b7a5910b1e
--- /dev/null
+++ b/_maps/RandomRuins/SpaceRuins/gondolaasteroid.dmm
@@ -0,0 +1,1382 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/template_noop,
+/area/template_noop)
+"b" = (
+/turf/closed/mineral/random,
+/area/ruin/space/has_grav)
+"c" = (
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"d" = (
+/obj/structure/marker_beacon{
+ light_color = "#FFE8AA";
+ light_range = 20
+ },
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"e" = (
+/obj/structure/flora/ausbushes/fullgrass,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"f" = (
+/obj/structure/flora/ausbushes/ywflowers,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"g" = (
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav{
+ dynamic_lighting = 1
+ })
+"h" = (
+/mob/living/simple_animal/pet/gondola,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"i" = (
+/obj/structure/flora/ausbushes/sparsegrass,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"j" = (
+/obj/effect/overlay/coconut,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"k" = (
+/obj/effect/overlay/palmtree_l,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"l" = (
+/obj/structure/flora/ausbushes/stalkybush,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"m" = (
+/obj/structure/flora/ausbushes/grassybush,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"n" = (
+/obj/structure/flora/ausbushes/reedbush,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"o" = (
+/obj/structure/flora/ausbushes/lavendergrass,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"p" = (
+/obj/structure/flora/ausbushes/brflowers,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"q" = (
+/obj/structure/flora/ausbushes/fernybush,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"r" = (
+/obj/effect/overlay/palmtree_r,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"s" = (
+/obj/structure/flora/junglebush/large,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"t" = (
+/obj/structure/flora/ausbushes/sunnybush,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+"u" = (
+/obj/machinery/door/airlock/survival_pod/vertical,
+/turf/open/floor/plating/asteroid/airless,
+/area/ruin/space/has_grav)
+
+(1,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+a
+a
+a
+"}
+(2,1,1) = {"
+a
+a
+a
+a
+b
+b
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+a
+a
+"}
+(3,1,1) = {"
+a
+a
+b
+b
+b
+b
+b
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+"}
+(4,1,1) = {"
+a
+b
+b
+b
+b
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+b
+b
+b
+b
+b
+"}
+(5,1,1) = {"
+a
+b
+b
+b
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+c
+c
+c
+b
+b
+b
+"}
+(6,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+f
+c
+c
+c
+h
+c
+b
+b
+"}
+(7,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+o
+c
+r
+c
+b
+b
+b
+b
+b
+c
+"}
+(8,1,1) = {"
+a
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+b
+c
+c
+c
+c
+c
+c
+c
+c
+j
+c
+c
+c
+c
+b
+b
+b
+c
+"}
+(9,1,1) = {"
+a
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+c
+k
+c
+c
+q
+c
+c
+j
+c
+c
+k
+c
+c
+c
+c
+m
+c
+c
+b
+b
+b
+c
+"}
+(10,1,1) = {"
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+c
+s
+c
+c
+c
+c
+c
+c
+i
+c
+c
+c
+b
+b
+b
+c
+"}
+(11,1,1) = {"
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+i
+n
+f
+c
+c
+d
+c
+c
+j
+c
+h
+c
+l
+c
+d
+c
+b
+b
+b
+c
+"}
+(12,1,1) = {"
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+o
+o
+c
+h
+c
+c
+c
+c
+c
+c
+c
+i
+o
+c
+c
+b
+b
+b
+c
+"}
+(13,1,1) = {"
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+i
+c
+q
+c
+c
+c
+c
+c
+c
+s
+c
+c
+c
+c
+b
+b
+b
+b
+c
+"}
+(14,1,1) = {"
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+h
+c
+c
+c
+c
+l
+c
+c
+c
+m
+i
+c
+c
+c
+c
+c
+c
+c
+b
+b
+b
+b
+b
+c
+"}
+(15,1,1) = {"
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+c
+i
+o
+c
+c
+c
+c
+c
+c
+c
+c
+b
+b
+b
+b
+c
+"}
+(16,1,1) = {"
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+o
+p
+c
+c
+c
+c
+r
+c
+c
+c
+c
+b
+b
+b
+c
+"}
+(17,1,1) = {"
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+e
+n
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+b
+b
+c
+"}
+(18,1,1) = {"
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+e
+c
+b
+b
+b
+b
+b
+c
+c
+c
+c
+c
+c
+i
+t
+c
+c
+c
+i
+c
+c
+b
+b
+b
+"}
+(19,1,1) = {"
+a
+b
+b
+b
+b
+b
+b
+b
+b
+g
+c
+c
+c
+c
+c
+b
+b
+b
+b
+c
+r
+j
+c
+c
+c
+f
+c
+c
+c
+c
+c
+c
+u
+c
+u
+"}
+(20,1,1) = {"
+a
+c
+b
+b
+b
+b
+b
+b
+b
+h
+c
+c
+d
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+b
+b
+b
+b
+"}
+(21,1,1) = {"
+a
+c
+c
+b
+b
+b
+b
+b
+b
+c
+c
+c
+k
+c
+i
+i
+c
+c
+q
+c
+c
+c
+c
+c
+d
+c
+h
+c
+c
+c
+c
+c
+b
+b
+b
+"}
+(22,1,1) = {"
+a
+c
+c
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+c
+c
+l
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+r
+c
+c
+b
+b
+b
+"}
+(23,1,1) = {"
+a
+c
+c
+b
+b
+b
+b
+b
+b
+c
+c
+j
+c
+c
+c
+c
+c
+c
+c
+c
+c
+h
+s
+c
+c
+c
+s
+c
+c
+c
+c
+c
+b
+b
+b
+"}
+(24,1,1) = {"
+a
+a
+c
+c
+b
+b
+b
+b
+e
+c
+c
+c
+c
+c
+h
+c
+c
+c
+c
+c
+c
+c
+c
+c
+m
+c
+c
+c
+c
+c
+c
+c
+b
+b
+b
+"}
+(25,1,1) = {"
+a
+a
+c
+c
+b
+b
+b
+c
+c
+c
+c
+c
+c
+c
+c
+c
+i
+l
+c
+c
+c
+c
+c
+c
+n
+m
+c
+c
+c
+c
+c
+b
+b
+b
+a
+"}
+(26,1,1) = {"
+a
+a
+c
+c
+b
+b
+b
+c
+c
+c
+c
+c
+c
+c
+c
+m
+p
+i
+c
+f
+c
+c
+c
+c
+c
+c
+k
+c
+c
+j
+c
+b
+b
+b
+a
+"}
+(27,1,1) = {"
+a
+a
+c
+c
+b
+b
+b
+b
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+d
+m
+i
+c
+c
+c
+c
+c
+c
+c
+c
+b
+b
+b
+a
+a
+"}
+(28,1,1) = {"
+a
+a
+a
+c
+c
+b
+b
+b
+c
+c
+c
+c
+c
+e
+c
+c
+c
+c
+c
+c
+i
+l
+p
+c
+c
+c
+c
+c
+c
+b
+b
+b
+b
+a
+a
+"}
+(29,1,1) = {"
+a
+a
+a
+c
+c
+b
+b
+c
+c
+c
+e
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+c
+i
+c
+c
+c
+b
+b
+b
+c
+c
+a
+"}
+(30,1,1) = {"
+a
+a
+a
+c
+b
+b
+c
+d
+f
+c
+i
+c
+c
+c
+b
+c
+c
+c
+c
+c
+j
+c
+c
+c
+c
+c
+c
+c
+b
+b
+b
+c
+c
+c
+a
+"}
+(31,1,1) = {"
+a
+a
+a
+b
+b
+b
+b
+c
+c
+c
+c
+c
+c
+b
+b
+b
+c
+c
+c
+r
+c
+c
+c
+c
+c
+c
+b
+b
+b
+b
+c
+c
+c
+b
+b
+"}
+(32,1,1) = {"
+a
+a
+a
+b
+b
+b
+c
+c
+c
+c
+c
+c
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+b
+b
+b
+"}
+(33,1,1) = {"
+a
+a
+a
+b
+b
+c
+c
+c
+c
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+a
+a
+b
+b
+b
+a
+"}
+(34,1,1) = {"
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+b
+c
+c
+c
+c
+c
+b
+b
+b
+b
+b
+a
+a
+a
+a
+a
+a
+a
+b
+b
+b
+a
+"}
+(35,1,1) = {"
+a
+a
+a
+a
+b
+b
+b
+b
+b
+b
+b
+b
+b
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+a
+"}
diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 95f1cb283c..27f366f7f8 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -24458,7 +24458,7 @@
/area/crew_quarters/heads/captain)
"bfE" = (
/obj/structure/table/wood,
-/obj/item/pinpointer,
+/obj/item/pinpointer/nuke,
/obj/item/disk/nuclear,
/obj/item/storage/secure/safe{
pixel_x = 35;
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 5e0ded4416..0484d95b79 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -53210,7 +53210,7 @@
/area/tcommsat/server)
"bYo" = (
/obj/structure/table/wood,
-/obj/item/pinpointer,
+/obj/item/pinpointer/nuke,
/obj/item/disk/nuclear,
/obj/item/device/radio/intercom{
name = "Station Intercom";
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index ec76a564d5..a60e43dd83 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -29108,7 +29108,7 @@
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/table/wood,
-/obj/item/pinpointer,
+/obj/item/pinpointer/nuke,
/obj/item/disk/nuclear,
/turf/open/floor/carpet,
/area/crew_quarters/heads/captain/private)
diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm
index 207a64fe5d..419737682f 100644
--- a/_maps/map_files/OmegaStation/OmegaStation.dmm
+++ b/_maps/map_files/OmegaStation/OmegaStation.dmm
@@ -1187,7 +1187,7 @@
pixel_x = 32;
pixel_y = 24
},
-/obj/item/pinpointer,
+/obj/item/pinpointer/nuke,
/obj/item/disk/nuclear,
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 10
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index 502f7ba293..66a9216882 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -13917,7 +13917,7 @@
/area/storage/primary)
"aEx" = (
/obj/structure/table/wood,
-/obj/item/pinpointer,
+/obj/item/pinpointer/nuke,
/obj/item/disk/nuclear,
/obj/machinery/light{
dir = 8
diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index 26a1535e33..c5c79b9610 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -1,74 +1,148 @@
-//A set of constants used to determine which type of mute an admin wishes to apply:
-//Please read and understand the muting/automuting stuff before changing these. MUTE_IC_AUTO etc = (MUTE_IC << 1)
-//Therefore there needs to be a gap between the flags_1 for the automute flags_1
-#define MUTE_IC 1
-#define MUTE_OOC 2
-#define MUTE_PRAY 4
-#define MUTE_ADMINHELP 8
-#define MUTE_DEADCHAT 16
-#define MUTE_ALL 31
-
-//Some constants for DB_Ban
-#define BANTYPE_PERMA 1
-#define BANTYPE_TEMP 2
-#define BANTYPE_JOB_PERMA 3
-#define BANTYPE_JOB_TEMP 4
-#define BANTYPE_ANY_FULLBAN 5 //used to locate stuff to unban.
-
-#define BANTYPE_ADMIN_PERMA 7
-#define BANTYPE_ADMIN_TEMP 8
-#define BANTYPE_ANY_JOB 9 //used to remove jobbans
-
-//Please don't edit these values without speaking to Errorage first ~Carn
-//Admin Permissions
-#define R_BUILDMODE 1
-#define R_ADMIN 2
-#define R_BAN 4
-#define R_FUN 8
-#define R_SERVER 16
-#define R_DEBUG 32
-#define R_POSSESS 64
-#define R_PERMISSIONS 128
-#define R_STEALTH 256
-#define R_POLL 512
-#define R_VAREDIT 1024
-#define R_SOUNDS 2048
-#define R_SPAWN 4096
-
-#if DM_VERSION > 512
-#error Remove the flag below , its been long enough
-#endif
-//legacy , remove post 512, it was replaced by R_POLL
-#define R_REJUVINATE 2
-
-#define R_MAXPERMISSION 4096 //This holds the maximum value for a permission. It is used in iteration, so keep it updated.
-
-#define ADMIN_QUE(user) "(?)"
-#define ADMIN_FLW(user) "(FLW)"
-#define ADMIN_PP(user) "(PP)"
-#define ADMIN_VV(atom) "(VV)"
-#define ADMIN_SM(user) "(SM)"
-#define ADMIN_TP(user) "(TP)"
-#define ADMIN_KICK(user) "(KICK)"
-#define ADMIN_CENTCOM_REPLY(user) "(RPLY)"
-#define ADMIN_SYNDICATE_REPLY(user) "(RPLY)"
-#define ADMIN_SC(user) "(SC)"
-#define ADMIN_SMITE(user) "(SMITE)"
-#define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]"
-#define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]"
-#define ADMIN_SET_SD_CODE "(SETCODE)"
-#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_TP(user)] [ADMIN_INDIVIDUALLOG(user)] [ADMIN_SMITE(user)]"
-#define ADMIN_FULLMONTY(user) "[key_name_admin(user)] [ADMIN_FULLMONTY_NONAME(user)]"
-#define ADMIN_JMP(src) "(JMP)"
-#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
-#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
-#define ADMIN_INDIVIDUALLOG(user) "(LOGS)"
-
-#define ADMIN_PUNISHMENT_LIGHTNING "Lightning bolt"
-#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage"
-#define ADMIN_PUNISHMENT_GIB "Gib"
-#define ADMIN_PUNISHMENT_BSA "Bluespace Artillery Device"
-
-#define AHELP_ACTIVE 1
-#define AHELP_CLOSED 2
-#define AHELP_RESOLVED 3
+//A set of constants used to determine which type of mute an admin wishes to apply:
+//Please read and understand the muting/automuting stuff before changing these. MUTE_IC_AUTO etc = (MUTE_IC << 1)
+//Therefore there needs to be a gap between the flags_1 for the automute flags_1
+#define MUTE_IC 1
+#define MUTE_OOC 2
+#define MUTE_PRAY 4
+#define MUTE_ADMINHELP 8
+#define MUTE_DEADCHAT 16
+#define MUTE_ALL 31
+
+//Some constants for DB_Ban
+#define BANTYPE_PERMA 1
+#define BANTYPE_TEMP 2
+#define BANTYPE_JOB_PERMA 3
+#define BANTYPE_JOB_TEMP 4
+#define BANTYPE_ANY_FULLBAN 5 //used to locate stuff to unban.
+
+#define BANTYPE_ADMIN_PERMA 7
+#define BANTYPE_ADMIN_TEMP 8
+#define BANTYPE_ANY_JOB 9 //used to remove jobbans
+
+//Please don't edit these values without speaking to Errorage first ~Carn
+//Admin Permissions
+#define R_BUILDMODE 1
+#define R_ADMIN 2
+#define R_BAN 4
+#define R_FUN 8
+#define R_SERVER 16
+#define R_DEBUG 32
+#define R_POSSESS 64
+#define R_PERMISSIONS 128
+#define R_STEALTH 256
+#define R_POLL 512
+#define R_VAREDIT 1024
+#define R_SOUNDS 2048
+#define R_SPAWN 4096
+
+#if DM_VERSION > 512
+#error Remove the flag below , its been long enough
+#endif
+//legacy , remove post 512, it was replaced by R_POLL
+#define R_REJUVINATE 2
+
+#define R_MAXPERMISSION 4096 //This holds the maximum value for a permission. It is used in iteration, so keep it updated.
+
+#define ADMIN_QUE(user) "(?)"
+#define ADMIN_FLW(user) "(FLW)"
+#define ADMIN_PP(user) "(PP)"
+#define ADMIN_VV(atom) "(VV)"
+#define ADMIN_SM(user) "(SM)"
+#define ADMIN_TP(user) "(TP)"
+#define ADMIN_KICK(user) "(KICK)"
+#define ADMIN_CENTCOM_REPLY(user) "(RPLY)"
+#define ADMIN_SYNDICATE_REPLY(user) "(RPLY)"
+#define ADMIN_SC(user) "(SC)"
+#define ADMIN_SMITE(user) "(SMITE)"
+#define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]"
+#define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]"
+#define ADMIN_SET_SD_CODE "(SETCODE)"
+#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_TP(user)] [ADMIN_INDIVIDUALLOG(user)] [ADMIN_SMITE(user)]"
+#define ADMIN_FULLMONTY(user) "[key_name_admin(user)] [ADMIN_FULLMONTY_NONAME(user)]"
+#define ADMIN_JMP(src) "(JMP)"
+#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
+#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
+#define ADMIN_INDIVIDUALLOG(user) "(LOGS)"
+
+#define ADMIN_PUNISHMENT_LIGHTNING "Lightning bolt"
+#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage"
+#define ADMIN_PUNISHMENT_GIB "Gib"
+#define ADMIN_PUNISHMENT_BSA "Bluespace Artillery Device"
+
+#define AHELP_ACTIVE 1
+#define AHELP_CLOSED 2
+#define AHELP_RESOLVED 3
+//A set of constants used to determine which type of mute an admin wishes to apply:
+//Please read and understand the muting/automuting stuff before changing these. MUTE_IC_AUTO etc = (MUTE_IC << 1)
+//Therefore there needs to be a gap between the flags for the automute flags
+#define MUTE_IC 1
+#define MUTE_OOC 2
+#define MUTE_PRAY 4
+#define MUTE_ADMINHELP 8
+#define MUTE_DEADCHAT 16
+#define MUTE_ALL 31
+
+//Some constants for DB_Ban
+#define BANTYPE_PERMA 1
+#define BANTYPE_TEMP 2
+#define BANTYPE_JOB_PERMA 3
+#define BANTYPE_JOB_TEMP 4
+#define BANTYPE_ANY_FULLBAN 5 //used to locate stuff to unban.
+
+#define BANTYPE_ADMIN_PERMA 7
+#define BANTYPE_ADMIN_TEMP 8
+#define BANTYPE_ANY_JOB 9 //used to remove jobbans
+
+//Please don't edit these values without speaking to Errorage first ~Carn
+//Admin Permissions
+#define R_BUILDMODE 1
+#define R_ADMIN 2
+#define R_BAN 4
+#define R_FUN 8
+#define R_SERVER 16
+#define R_DEBUG 32
+#define R_POSSESS 64
+#define R_PERMISSIONS 128
+#define R_STEALTH 256
+#define R_POLL 512
+#define R_VAREDIT 1024
+#define R_SOUNDS 2048
+#define R_SPAWN 4096
+
+#if DM_VERSION > 512
+#error Remove the flag below , its been long enough
+#endif
+//legacy , remove post 512, it was replaced by R_POLL
+#define R_REJUVINATE 2
+
+#define R_MAXPERMISSION 4096 //This holds the maximum value for a permission. It is used in iteration, so keep it updated.
+
+#define ADMIN_QUE(user) "(?)"
+#define ADMIN_FLW(user) "(FLW)"
+#define ADMIN_PP(user) "(PP)"
+#define ADMIN_VV(atom) "(VV)"
+#define ADMIN_SM(user) "(SM)"
+#define ADMIN_TP(user) "(TP)"
+#define ADMIN_KICK(user) "(KICK)"
+#define ADMIN_CENTCOM_REPLY(user) "(RPLY)"
+#define ADMIN_SYNDICATE_REPLY(user) "(RPLY)"
+#define ADMIN_SC(user) "(SC)"
+#define ADMIN_SMITE(user) "(SMITE)"
+#define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]"
+#define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]"
+#define ADMIN_SET_SD_CODE "(SETCODE)"
+#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_TP(user)] [ADMIN_INDIVIDUALLOG(user)] [ADMIN_SMITE(user)]"
+#define ADMIN_FULLMONTY(user) "[key_name_admin(user)] [ADMIN_FULLMONTY_NONAME(user)]"
+#define ADMIN_JMP(src) "(JMP)"
+#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
+#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
+#define ADMIN_INDIVIDUALLOG(user) "(LOGS)"
+
+#define ADMIN_PUNISHMENT_LIGHTNING "Lightning bolt"
+#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage"
+#define ADMIN_PUNISHMENT_GIB "Gib"
+#define ADMIN_PUNISHMENT_BSA "Bluespace Artillery Device"
+
+#define AHELP_ACTIVE 1
+#define AHELP_CLOSED 2
+#define AHELP_RESOLVED 3
diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm
index 46589408a2..ae3f9f6fe9 100644
--- a/code/__DEFINES/construction.dm
+++ b/code/__DEFINES/construction.dm
@@ -101,6 +101,7 @@
#define CAT_ROBOT "Robots"
#define CAT_MISC "Misc"
#define CAT_PRIMAL "Tribal"
+#define CAT_CLOTHING "Clothing"
#define CAT_FOOD "Foods"
#define CAT_BREAD "Breads"
#define CAT_BURGER "Burgers"
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 2c18e51f01..3f85c93a00 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -1,5 +1,7 @@
// simple is_type and similar inline helpers
+#define isdatum(D) (istype(D, /datum))
+
#define islist(L) (istype(L, /list))
#define in_range(source, user) (get_dist(source, user) <= 1)
diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm
index 084cc7d1ab..5e7ee3a468 100644
--- a/code/__DEFINES/maps.dm
+++ b/code/__DEFINES/maps.dm
@@ -35,7 +35,7 @@ Last space-z level = empty
//zlevel defines, can be overridden for different maps in the appropriate _maps file.
#define ZLEVEL_CENTCOM 1
-#define ZLEVEL_STATION 2
+#define ZLEVEL_STATION_PRIMARY 2
#define ZLEVEL_MINING 5
#define ZLEVEL_LAVALAND 5
#define ZLEVEL_EMPTY_SPACE 12
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 209a33e0c5..b34af475d3 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -399,8 +399,6 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
#define NUKE_SYNDICATE_BASE 3
#define STATION_DESTROYED_NUKE 4
#define STATION_EVACUATED 5
-#define GANG_LOSS 6
-#define GANG_TAKEOVER 7
#define BLOB_WIN 8
#define BLOB_NUKE 9
#define BLOB_DESTROYED 10
@@ -437,10 +435,6 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
#define GIBTONITE_DETONATE 3
//for obj explosion block calculation
#define EXPLOSION_BLOCK_PROC -1
-//Gangster starting influences
-
-#define GANGSTER_SOLDIER_STARTING_INFLUENCE 5
-#define GANGSTER_BOSS_STARTING_INFLUENCE 20
//for determining which type of heartbeat sound is playing
#define BEAT_FAST 1
@@ -458,3 +452,8 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
#define SHELLEO_ERRORLEVEL 1
#define SHELLEO_STDOUT 2
#define SHELLEO_STDERR 3
+
+//server security mode
+#define SECURITY_SAFE 1
+#define SECURITY_ULTRASAFE 2
+#define SECURITY_TRUSTED 3
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 52e324bd76..c2d3256e59 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -154,3 +154,5 @@
#define JUDGE_IGNOREMONKEYS 16
#define MEGAFAUNA_DEFAULT_RECOVERY_TIME 5
+
+#define SHADOW_SPECIES_LIGHT_THRESHOLD 0.2
\ No newline at end of file
diff --git a/code/__DEFINES/pinpointers.dm b/code/__DEFINES/pinpointers.dm
index 80403e54de..75f0452ea9 100644
--- a/code/__DEFINES/pinpointers.dm
+++ b/code/__DEFINES/pinpointers.dm
@@ -2,6 +2,3 @@
#define TRACK_NUKE_DISK 1 //We track the nuclear authentication disk, either to protect it or steal it
#define TRACK_MALF_AI 2 //We track the malfunctioning AI, so we can prevent it from blowing us all up
#define TRACK_INFILTRATOR 3 //We track the Syndicate infiltrator, so we can get back to ship when the nuke's armed
-#define TRACK_OPERATIVES 4 //We track the closest operative, so we can regroup when we need to
-#define TRACK_ATOM 5 //We track a specified atom, so admins can make us function for events
-#define TRACK_COORDINATES 6 //We point towards the specified coordinates on our z-level, so we can navigate
diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm
index 041a6283e7..8749218847 100644
--- a/code/__DEFINES/qdel.dm
+++ b/code/__DEFINES/qdel.dm
@@ -10,6 +10,11 @@
//if TESTING is enabled, qdel will call this object's find_references() verb.
//defines for the gc_destroyed var
+#define GC_QUEUE_PREQUEUE 1
+#define GC_QUEUE_CHECK 2
+#define GC_QUEUE_HARDDELETE 3
+#define GC_QUEUE_COUNT 3 //increase this when adding more steps.
+
#define GC_QUEUED_FOR_QUEUING -1
#define GC_QUEUED_FOR_HARD_DEL -2
#define GC_CURRENTLY_BEING_QDELETED -3
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index cb4d552e5d..fbefab15bb 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -18,7 +18,6 @@
#define ROLE_BLOB "blob"
#define ROLE_NINJA "space ninja"
#define ROLE_MONKEY "monkey"
-#define ROLE_GANG "gangster"
#define ROLE_ABDUCTOR "abductor"
#define ROLE_REVENANT "revenant"
#define ROLE_DEVIL "devil"
@@ -41,7 +40,6 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_BLOB = /datum/game_mode/blob,
ROLE_NINJA,
ROLE_MONKEY = /datum/game_mode/monkey,
- ROLE_GANG = /datum/game_mode/gang,
ROLE_REVENANT,
ROLE_ABDUCTOR = /datum/game_mode/abduction,
ROLE_DEVIL = /datum/game_mode/devil,
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index f7322abe6c..9ec26b7506 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -56,7 +56,6 @@
#define INIT_ORDER_TIMER 1
#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_AIR -1
-#define INIT_ORDER_SHUTTLE -2
#define INIT_ORDER_MINIMAP -3
#define INIT_ORDER_ASSETS -4
#define INIT_ORDER_ICON_SMOOTHING -5
@@ -64,6 +63,7 @@
#define INIT_ORDER_XKEYSCORE -10
#define INIT_ORDER_STICKY_BAN -10
#define INIT_ORDER_LIGHTING -20
+#define INIT_ORDER_SHUTTLE -21
#define INIT_ORDER_SQUEAK -40
#define INIT_ORDER_PERSISTENCE -100
diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm
index 956935258b..28017de85c 100644
--- a/code/__DEFINES/time.dm
+++ b/code/__DEFINES/time.dm
@@ -13,3 +13,11 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using
#define FRIDAY "Fri"
#define SATURDAY "Sat"
#define SUNDAY "Sun"
+
+#define SECONDS *10
+
+#define MINUTES SECONDS*60
+
+#define HOURS MINUTES*60
+
+#define TICKS *world.tick_lag
\ No newline at end of file
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index cde5e0b4e7..4b5342031f 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -96,6 +96,9 @@
if (config.log_pda)
WRITE_FILE(GLOB.world_game_log, "\[[time_stamp()]]CHAT: [text]")
+/proc/log_qdel(text)
+ WRITE_FILE(GLOB.world_qdel_log, "\[[time_stamp()]]QDEL: [text]")
+
/proc/log_sql(text)
WRITE_FILE(GLOB.sql_error_log, "\[[time_stamp()]]SQL: [text]")
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 171d776d0d..1c9c33f21a 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -49,3 +49,12 @@ GLOBAL_VAR_INIT(cmp_field, "name")
/proc/cmp_ruincost_priority(datum/map_template/ruin/A, datum/map_template/ruin/B)
return initial(A.cost) - initial(B.cost)
+
+/proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B)
+ . = B.hard_delete_time - A.hard_delete_time
+ if (!.)
+ . = B.destroy_time - A.destroy_time
+ if (!.)
+ . = B.failures - A.failures
+ if (!.)
+ . = B.qdels - A.qdels
diff --git a/code/__HELPERS/priority_announce.dm b/code/__HELPERS/priority_announce.dm
index d3b9580f37..0ffd8d8580 100644
--- a/code/__HELPERS/priority_announce.dm
+++ b/code/__HELPERS/priority_announce.dm
@@ -39,7 +39,7 @@
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg')
for(var/obj/machinery/computer/communications/C in GLOB.machines)
- if(!(C.stat & (BROKEN|NOPOWER)) && C.z == ZLEVEL_STATION)
+ if(!(C.stat & (BROKEN|NOPOWER)) && (C.z in GLOB.station_z_levels))
var/obj/item/paper/P = new /obj/item/paper(C.loc)
P.name = "paper - '[title]'"
P.info = text
diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm
index 1581c904b9..a2d30b1528 100644
--- a/code/_globalvars/lists/mapping.dm
+++ b/code/_globalvars/lists/mapping.dm
@@ -1,55 +1,57 @@
-#define Z_NORTH 1
-#define Z_EAST 2
-#define Z_SOUTH 3
-#define Z_WEST 4
-
+#define Z_NORTH 1
+#define Z_EAST 2
+#define Z_SOUTH 3
+#define Z_WEST 4
+
GLOBAL_LIST_INIT(cardinals, list(NORTH, SOUTH, EAST, WEST))
-GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
-GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
-
-//This list contains the z-level numbers which can be accessed via space travel and the percentile chances to get there.
-//(Exceptions: extended, sandbox and nuke) -Errorage
-//Was list("3" = 30, "4" = 70).
-//Spacing should be a reliable method of getting rid of a body -- Urist.
-//Go away Urist, I'm restoring this to the longer list. ~Errorage
-GLOBAL_LIST_INIT(accessable_z_levels, list(1,3,4,5,6,7)) //Keep this to six maps, repeating z-levels is ok if needed
-
-GLOBAL_LIST(global_map)
- //list/global_map = list(list(1,5),list(4,3))//an array of map Z levels.
- //Resulting sector map looks like
- //|_1_|_4_|
- //|_5_|_3_|
- //
- //1 - SS13
- //4 - Derelict
- //3 - AI satellite
- //5 - empty space
-
-GLOBAL_LIST_EMPTY(landmarks_list) //list of all landmarks created
-GLOBAL_LIST_EMPTY(start_landmarks_list) //list of all spawn points created
-GLOBAL_LIST_EMPTY(department_security_spawns) //list of all department security spawns
-GLOBAL_LIST_EMPTY(generic_event_spawns) //list of all spawns for events
-
-GLOBAL_LIST_EMPTY(wizardstart)
-GLOBAL_LIST_EMPTY(newplayer_start)
-GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these
-GLOBAL_LIST_EMPTY(holdingfacility) //captured people go here
-GLOBAL_LIST_EMPTY(xeno_spawn)//Aliens spawn at these.
-GLOBAL_LIST_EMPTY(tdome1)
-GLOBAL_LIST_EMPTY(tdome2)
-GLOBAL_LIST_EMPTY(tdomeobserve)
-GLOBAL_LIST_EMPTY(tdomeadmin)
-GLOBAL_LIST_EMPTY(prisonwarped) //list of players already warped
-GLOBAL_LIST_EMPTY(blobstart)
-GLOBAL_LIST_EMPTY(secequipment)
-GLOBAL_LIST_EMPTY(deathsquadspawn)
-GLOBAL_LIST_EMPTY(emergencyresponseteamspawn)
-GLOBAL_LIST_EMPTY(ruin_landmarks)
-
- //away missions
-GLOBAL_LIST_EMPTY(awaydestinations) //a list of landmarks that the warpgate can take you to
-
- //used by jump-to-area etc. Updated by area/updateName()
-GLOBAL_LIST_EMPTY(sortedAreas)
-
-GLOBAL_LIST_EMPTY(all_abstract_markers)
\ No newline at end of file
+GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
+GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
+
+//This list contains the z-level numbers which can be accessed via space travel and the percentile chances to get there.
+//(Exceptions: extended, sandbox and nuke) -Errorage
+//Was list("3" = 30, "4" = 70).
+//Spacing should be a reliable method of getting rid of a body -- Urist.
+//Go away Urist, I'm restoring this to the longer list. ~Errorage
+GLOBAL_LIST_INIT(accessable_z_levels, list(1,3,4,5,6,7)) //Keep this to six maps, repeating z-levels is ok if needed
+
+GLOBAL_LIST_INIT(station_z_levels, list(ZLEVEL_STATION_PRIMARY))
+
+GLOBAL_LIST(global_map)
+ //list/global_map = list(list(1,5),list(4,3))//an array of map Z levels.
+ //Resulting sector map looks like
+ //|_1_|_4_|
+ //|_5_|_3_|
+ //
+ //1 - SS13
+ //4 - Derelict
+ //3 - AI satellite
+ //5 - empty space
+
+GLOBAL_LIST_EMPTY(landmarks_list) //list of all landmarks created
+GLOBAL_LIST_EMPTY(start_landmarks_list) //list of all spawn points created
+GLOBAL_LIST_EMPTY(department_security_spawns) //list of all department security spawns
+GLOBAL_LIST_EMPTY(generic_event_spawns) //list of all spawns for events
+
+GLOBAL_LIST_EMPTY(wizardstart)
+GLOBAL_LIST_EMPTY(newplayer_start)
+GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these
+GLOBAL_LIST_EMPTY(holdingfacility) //captured people go here
+GLOBAL_LIST_EMPTY(xeno_spawn)//Aliens spawn at these.
+GLOBAL_LIST_EMPTY(tdome1)
+GLOBAL_LIST_EMPTY(tdome2)
+GLOBAL_LIST_EMPTY(tdomeobserve)
+GLOBAL_LIST_EMPTY(tdomeadmin)
+GLOBAL_LIST_EMPTY(prisonwarped) //list of players already warped
+GLOBAL_LIST_EMPTY(blobstart)
+GLOBAL_LIST_EMPTY(secequipment)
+GLOBAL_LIST_EMPTY(deathsquadspawn)
+GLOBAL_LIST_EMPTY(emergencyresponseteamspawn)
+GLOBAL_LIST_EMPTY(ruin_landmarks)
+
+ //away missions
+GLOBAL_LIST_EMPTY(awaydestinations) //a list of landmarks that the warpgate can take you to
+
+ //used by jump-to-area etc. Updated by area/updateName()
+GLOBAL_LIST_EMPTY(sortedAreas)
+
+GLOBAL_LIST_EMPTY(all_abstract_markers)
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 5be6a8dbaa..e600619d45 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -4,6 +4,8 @@ GLOBAL_VAR(world_game_log)
GLOBAL_PROTECT(world_game_log)
GLOBAL_VAR(world_runtime_log)
GLOBAL_PROTECT(world_runtime_log)
+GLOBAL_VAR(world_qdel_log)
+GLOBAL_PROTECT(world_qdel_log)
GLOBAL_VAR(world_attack_log)
GLOBAL_PROTECT(world_attack_log)
GLOBAL_VAR(world_href_log)
diff --git a/code/citadel/custom_loadout/custom_items.dm b/code/citadel/custom_loadout/custom_items.dm
index 6495d38a0f..da81dfb37c 100644
--- a/code/citadel/custom_loadout/custom_items.dm
+++ b/code/citadel/custom_loadout/custom_items.dm
@@ -10,6 +10,9 @@
w_class = WEIGHT_CLASS_TINY
flags_1 = NOBLUDGEON_1
+
+/*Inferno707*/
+
/obj/item/clothing/neck/cloak/inferno
name = "Kiara's Cloak"
desc = "The design on this seems a little too familiar."
@@ -19,6 +22,16 @@
w_class = WEIGHT_CLASS_SMALL
body_parts_covered = CHEST|GROIN|LEGS|ARMS
+/obj/item/clothing/neck/petcollar/inferno
+ name = "Kiara's Collar"
+ desc = "A soft black collar that seems to stretch to fit whoever wears it."
+ icon_state = "infcollar"
+ item_state = "infcollar"
+ item_color = null
+ tagname = null
+
+/*DirtyOldHarry*/
+
/obj/item/lighter/gold
name = "\improper Engraved Zippo"
desc = "A shiny and relatively expensive zippo lighter. There's a small etched in verse on the bottom that reads, 'No Gods, No Masters, Only Man.'"
@@ -30,4 +43,14 @@
slot_flags = SLOT_BELT
heat = 1500
resistance_flags = FIRE_PROOF
- light_color = LIGHT_COLOR_FIRE
\ No newline at end of file
+ light_color = LIGHT_COLOR_FIRE
+
+
+/*Zombierobin*/
+
+/obj/item/clothing/neck/scarf/zomb //Default white color, same functionality as beanies.
+ name = "A special scarf"
+ icon_state = "zombscarf"
+ desc = "A fashionable collar"
+ item_color = "zombscarf"
+ dog_fashion = /datum/dog_fashion/head
\ No newline at end of file
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index bba82e6c8a..180d14853e 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -108,6 +108,8 @@ GLOBAL_PROTECT(config_dir)
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
var/use_account_age_for_jobs = 0 //Uses the time they made the account for the job restriction stuff. New player joining alerts should be unaffected.
var/see_own_notes = 0 //Can players see their own admin notes (read-only)? Config option in config.txt
+ var/note_fresh_days
+ var/note_stale_days
var/use_exp_tracking = FALSE
var/use_exp_restrictions_heads = FALSE
@@ -280,6 +282,8 @@ GLOBAL_PROTECT(config_dir)
var/list/policies = list()
+ var/debug_admin_hrefs = FALSE //turns off admin href token protection for debugging purposes
+
/datum/configuration/New()
gamemode_cache = typecacheof(/datum/game_mode,TRUE)
for(var/T in gamemode_cache)
@@ -484,6 +488,10 @@ GLOBAL_PROTECT(config_dir)
showircname = 1
if("see_own_notes")
see_own_notes = 1
+ if("note_fresh_days")
+ note_fresh_days = text2num(value)
+ if("note_stale_days")
+ note_stale_days = text2num(value)
if("soft_popcap")
soft_popcap = text2num(value)
if("hard_popcap")
@@ -563,6 +571,8 @@ GLOBAL_PROTECT(config_dir)
error_msg_delay = text2num(value)
if("irc_announce_new_game")
irc_announce_new_game = TRUE
+ if("debug_admin_hrefs")
+ debug_admin_hrefs = TRUE
else
#if DM_VERSION > 511
#error Replace the line below with WRITE_FILE(GLOB.config_error_log, "Unknown setting in configuration: '[name]'")
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index d5298f7dde..2ca208642c 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -1,102 +1,102 @@
- /**
- * Failsafe
- *
- * Pretty much pokes the MC to make sure it's still alive.
- **/
-
-GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
-
-/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
- name = "Failsafe"
-
- // The length of time to check on the MC (in deciseconds).
- // Set to 0 to disable.
- var/processing_interval = 20
- // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
- var/defcon = 5
- //the world.time of the last check, so the mc can restart US if we hang.
- // (Real friends look out for *eachother*)
- var/lasttick = 0
-
- // Track the MC iteration to make sure its still on track.
- var/master_iteration = 0
- var/running = TRUE
-
-/datum/controller/failsafe/New()
- // Highlander-style: there can only be one! Kill off the old and replace it with the new.
- if(Failsafe != src)
- if(istype(Failsafe))
- qdel(Failsafe)
- Failsafe = src
- Initialize()
-
-/datum/controller/failsafe/Initialize()
- set waitfor = 0
- Failsafe.Loop()
- if(!QDELETED(src))
- qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
-
-/datum/controller/failsafe/Destroy()
- running = FALSE
- ..()
- return QDEL_HINT_HARDDEL_NOW
-
-/datum/controller/failsafe/proc/Loop()
- while(running)
- lasttick = world.time
- if(!Master)
- // Replace the missing Master! This should never, ever happen.
- new /datum/controller/master()
- // Only poke it if overrides are not in effect.
- if(processing_interval > 0)
- if(Master.processing && Master.iteration)
- // Check if processing is done yet.
- if(Master.iteration == master_iteration)
- switch(defcon)
- if(4,5)
- --defcon
- if(3)
- to_chat(GLOB.admins, "Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.")
- --defcon
- if(2)
- to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.")
- --defcon
- if(1)
-
- to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...")
- --defcon
- var/rtn = Recreate_MC()
- if(rtn > 0)
- defcon = 4
- master_iteration = 0
- to_chat(GLOB.admins, "MC restarted successfully")
- else if(rtn < 0)
- log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
- to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.")
- //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
- //no need to handle that specially when defcon 0 can handle it
- if(0) //DEFCON 0! (mc failed to restart)
- var/rtn = Recreate_MC()
- if(rtn > 0)
- defcon = 4
- master_iteration = 0
- to_chat(GLOB.admins, "MC restarted successfully")
- else
- defcon = min(defcon + 1,5)
- master_iteration = Master.iteration
- if (defcon <= 1)
- sleep(processing_interval*2)
- else
- sleep(processing_interval)
- else
- defcon = 5
- sleep(initial(processing_interval))
-
-/datum/controller/failsafe/proc/defcon_pretty()
- return defcon
-
-/datum/controller/failsafe/stat_entry()
- if(!statclick)
- statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
-
- stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
+ /**
+ * Failsafe
+ *
+ * Pretty much pokes the MC to make sure it's still alive.
+ **/
+
+GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
+
+/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
+ name = "Failsafe"
+
+ // The length of time to check on the MC (in deciseconds).
+ // Set to 0 to disable.
+ var/processing_interval = 20
+ // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
+ var/defcon = 5
+ //the world.time of the last check, so the mc can restart US if we hang.
+ // (Real friends look out for *eachother*)
+ var/lasttick = 0
+
+ // Track the MC iteration to make sure its still on track.
+ var/master_iteration = 0
+ var/running = TRUE
+
+/datum/controller/failsafe/New()
+ // Highlander-style: there can only be one! Kill off the old and replace it with the new.
+ if(Failsafe != src)
+ if(istype(Failsafe))
+ qdel(Failsafe)
+ Failsafe = src
+ Initialize()
+
+/datum/controller/failsafe/Initialize()
+ set waitfor = 0
+ Failsafe.Loop()
+ if(!QDELETED(src))
+ qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
+
+/datum/controller/failsafe/Destroy()
+ running = FALSE
+ ..()
+ return QDEL_HINT_HARDDEL_NOW
+
+/datum/controller/failsafe/proc/Loop()
+ while(running)
+ lasttick = world.time
+ if(!Master)
+ // Replace the missing Master! This should never, ever happen.
+ new /datum/controller/master()
+ // Only poke it if overrides are not in effect.
+ if(processing_interval > 0)
+ if(Master.processing && Master.iteration)
+ // Check if processing is done yet.
+ if(Master.iteration == master_iteration)
+ switch(defcon)
+ if(4,5)
+ --defcon
+ if(3)
+ message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.")
+ --defcon
+ if(2)
+ to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.")
+ --defcon
+ if(1)
+
+ to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...")
+ --defcon
+ var/rtn = Recreate_MC()
+ if(rtn > 0)
+ defcon = 4
+ master_iteration = 0
+ to_chat(GLOB.admins, "MC restarted successfully")
+ else if(rtn < 0)
+ log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
+ to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.")
+ //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
+ //no need to handle that specially when defcon 0 can handle it
+ if(0) //DEFCON 0! (mc failed to restart)
+ var/rtn = Recreate_MC()
+ if(rtn > 0)
+ defcon = 4
+ master_iteration = 0
+ to_chat(GLOB.admins, "MC restarted successfully")
+ else
+ defcon = min(defcon + 1,5)
+ master_iteration = Master.iteration
+ if (defcon <= 1)
+ sleep(processing_interval*2)
+ else
+ sleep(processing_interval)
+ else
+ defcon = 5
+ sleep(initial(processing_interval))
+
+/datum/controller/failsafe/proc/defcon_pretty()
+ return defcon
+
+/datum/controller/failsafe/stat_entry()
+ if(!statclick)
+ statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
+
+ stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm
index c41f422575..7816a6bdf8 100644
--- a/code/controllers/subsystem/events.dm
+++ b/code/controllers/subsystem/events.dm
@@ -116,6 +116,8 @@ SUBSYSTEM_DEF(events)
//allows a client to trigger an event
//aka Badmin Central
+// > Not in modules/admin
+// REEEEEEEEE
/client/proc/forceEvent()
set name = "Trigger Event"
set category = "Fun"
@@ -131,7 +133,7 @@ SUBSYSTEM_DEF(events)
var/magic = ""
var/holiday = ""
for(var/datum/round_event_control/E in SSevents.control)
- dat = "
[E]"
+ dat = "
[E]"
if(E.holidayID)
holiday += dat
else if(E.wizardevent)
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index 8502280aaf..d202c08d39 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -1,40 +1,44 @@
SUBSYSTEM_DEF(garbage)
name = "Garbage"
priority = 15
- wait = 20
+ wait = 2 SECONDS
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
- var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
- var/delslasttick = 0 // number of del()'s we've done this tick
- var/gcedlasttick = 0 // number of things that gc'ed last tick
+ var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
+
+ //Stat tracking
+ var/delslasttick = 0 // number of del()'s we've done this tick
+ var/gcedlasttick = 0 // number of things that gc'ed last tick
var/totaldels = 0
var/totalgcs = 0
var/highest_del_time = 0
var/highest_del_tickusage = 0
- var/list/queue = list() // list of refID's of things that should be garbage collected
- // refID's are associated with the time at which they time out and need to be manually del()
- // we do this so we aren't constantly locating them and preventing them from being gc'd
+ var/list/pass_counts
+ var/list/fail_counts
- var/list/tobequeued = list() //We store the references of things to be added to the queue separately so we can spread out GC overhead over a few ticks
+ var/list/items = list() // Holds our qdel_item statistics datums
- var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
- // the types are stored as strings
- var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
+ //Queue
+ var/list/queues
- var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT
- // all types that did not respect qdel(A, force=TRUE) and returned one
- // of the immortality qdel hints
- var/list/noforcerespect = list()
-#ifdef TESTING
- var/list/qdel_list = list() // list of all types that have been qdel()eted
-#endif
+/datum/controller/subsystem/garbage/PreInit()
+ queues = new(GC_QUEUE_COUNT)
+ pass_counts = new(GC_QUEUE_COUNT)
+ fail_counts = new(GC_QUEUE_COUNT)
+ for(var/i in 1 to GC_QUEUE_COUNT)
+ queues[i] = list()
+ pass_counts[i] = 0
+ fail_counts[i] = 0
/datum/controller/subsystem/garbage/stat_entry(msg)
- msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|"
+ var/list/counts = list()
+ for (var/list/L in queues)
+ counts += length(L)
+ msg += "Q:[counts.Join(",")]|D:[delslasttick]|G:[gcedlasttick]|"
msg += "GR:"
if (!(delslasttick+gcedlasttick))
msg += "n/a|"
@@ -46,116 +50,179 @@ SUBSYSTEM_DEF(garbage)
msg += "n/a|"
else
msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
+ msg += " P:[pass_counts.Join(",")]"
+ msg += "|F:[fail_counts.Join(",")]"
..(msg)
/datum/controller/subsystem/garbage/Shutdown()
- //Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
- if(didntgc.len || sleptDestroy.len)
- var/list/dellog = list()
- for(var/path in didntgc)
- dellog += "Path : [path] \n"
- dellog += "Failures : [didntgc[path]] \n"
- if(path in sleptDestroy)
- dellog += "Sleeps : [sleptDestroy[path]] \n"
- sleptDestroy -= path
- for(var/path in sleptDestroy)
- dellog += "Path : [path] \n"
- dellog += "Sleeps : [sleptDestroy[path]] \n"
- text2file(dellog.Join(), "[GLOB.log_directory]/qdel.log")
+ //Adds the del() log to the qdel log file
+ var/list/dellog = list()
+
+ //sort by how long it's wasted hard deleting
+ sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
+ for(var/path in items)
+ var/datum/qdel_item/I = items[path]
+ dellog += "Path: [path]"
+ if (I.failures)
+ dellog += "\tFailures: [I.failures]"
+ dellog += "\tqdel() Count: [I.qdels]"
+ dellog += "\tDestroy() Cost: [I.destroy_time]ms"
+ if (I.hard_deletes)
+ dellog += "\tTotal Hard Deletes [I.hard_deletes]"
+ dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms"
+ if (I.slept_destroy)
+ dellog += "\tSleeps: [I.slept_destroy]"
+ if (I.no_respect_force)
+ dellog += "\tIgnored force: [I.no_respect_force] times"
+ if (I.no_hint)
+ dellog += "\tNo hint: [I.no_hint] times"
+ log_qdel(dellog.Join("\n"))
/datum/controller/subsystem/garbage/fire()
- HandleToBeQueued()
- if(state == SS_RUNNING)
- HandleQueue()
-
+ //the fact that this resets its processing each fire (rather then resume where it left off) is intentional.
+ var/queue = GC_QUEUE_PREQUEUE
+
+ while (state == SS_RUNNING)
+ switch (queue)
+ if (GC_QUEUE_PREQUEUE)
+ HandlePreQueue()
+ queue = GC_QUEUE_PREQUEUE+1
+ if (GC_QUEUE_CHECK)
+ HandleQueue(GC_QUEUE_CHECK)
+ queue = GC_QUEUE_CHECK+1
+ if (GC_QUEUE_HARDDELETE)
+ HandleQueue(GC_QUEUE_HARDDELETE)
+ break
+
if (state == SS_PAUSED) //make us wait again before the next run.
- state = SS_RUNNING
+ state = SS_RUNNING
//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond.
//Don't attempt to optimize, not worth the effort.
-/datum/controller/subsystem/garbage/proc/HandleToBeQueued()
- var/list/tobequeued = src.tobequeued
- var/starttime = world.time
- var/starttimeofday = world.timeofday
- while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday)
- if (MC_TICK_CHECK)
- break
- var/ref = tobequeued[1]
- Queue(ref)
- tobequeued.Cut(1, 2)
+/datum/controller/subsystem/garbage/proc/HandlePreQueue()
+ var/list/tobequeued = queues[GC_QUEUE_PREQUEUE]
+ var/static/count = 0
+ if (count)
+ var/c = count
+ count = 0 //so if we runtime on the Cut, we don't try again.
+ tobequeued.Cut(1,c+1)
-/datum/controller/subsystem/garbage/proc/HandleQueue()
- delslasttick = 0
- gcedlasttick = 0
- var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
- var/list/queue = src.queue
- var/starttime = world.time
- var/starttimeofday = world.timeofday
- while(queue.len && starttime == world.time && starttimeofday == world.timeofday)
+ for (var/ref in tobequeued)
+ count++
+ Queue(ref, GC_QUEUE_PREQUEUE+1)
if (MC_TICK_CHECK)
break
- var/refID = queue[1]
+ if (count)
+ tobequeued.Cut(1,count+1)
+ count = 0
+
+/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK)
+ if (level == GC_QUEUE_CHECK)
+ delslasttick = 0
+ gcedlasttick = 0
+ var/cut_off_time = world.time - collection_timeout[level] //ignore entries newer then this
+ var/list/queue = queues[level]
+ var/static/lastlevel
+ var/static/count = 0
+ if (count) //runtime last run before we could do this.
+ var/c = count
+ count = 0 //so if we runtime on the Cut, we don't try again.
+ var/list/lastqueue = queues[lastlevel]
+ lastqueue.Cut(1, c+1)
+
+ lastlevel = level
+
+ for (var/refID in queue)
if (!refID)
- queue.Cut(1, 2)
+ count++
+ if (MC_TICK_CHECK)
+ break
continue
var/GCd_at_time = queue[refID]
- if(GCd_at_time > time_to_kill)
+ if(GCd_at_time > cut_off_time)
break // Everything else is newer, skip them
- queue.Cut(1, 2)
- var/datum/A
- A = locate(refID)
- if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
- #ifdef GC_FAILURE_HARD_LOOKUP
- A.find_references()
- #endif
+ count++
- // Something's still referring to the qdel'd object. Kill it.
- var/type = A.type
- testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --")
- didntgc["[type]"]++
-
- HardDelete(A)
+ var/datum/D
+ D = locate(refID)
- ++delslasttick
- ++totaldels
- else
+ if (!D || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
++gcedlasttick
++totalgcs
+ pass_counts[level]++
+ if (MC_TICK_CHECK)
+ break
+ continue
-/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A)
- if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
- tobequeued += A
- A.gc_destroyed = GC_QUEUED_FOR_QUEUING
+ // Something's still referring to the qdel'd object.
+ fail_counts[level]++
+ switch (level)
+ if (GC_QUEUE_CHECK)
+ #ifdef GC_FAILURE_HARD_LOOKUP
+ D.find_references()
+ #endif
+ var/type = D.type
+ var/datum/qdel_item/I = items[type]
+ testing("GC: -- \ref[D] | [type] was unable to be GC'd --")
+ I.failures++
+ if (GC_QUEUE_HARDDELETE)
+ HardDelete(D)
+ if (MC_TICK_CHECK)
+ break
+ continue
-/datum/controller/subsystem/garbage/proc/Queue(datum/A)
- if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0))
+ Queue(D, level+1)
+
+ if (MC_TICK_CHECK)
+ break
+ if (count)
+ queue.Cut(1,count+1)
+ count = 0
+
+/datum/controller/subsystem/garbage/proc/PreQueue(datum/D)
+ if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+ queues[GC_QUEUE_PREQUEUE] += D
+ D.gc_destroyed = GC_QUEUED_FOR_QUEUING
+
+/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK)
+ if (isnull(D))
return
- if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
- HardDelete(A)
+ if (D.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
+ level = GC_QUEUE_HARDDELETE
+ if (level > GC_QUEUE_COUNT)
+ HardDelete(D)
return
var/gctime = world.time
- var/refid = "\ref[A]"
-
- A.gc_destroyed = gctime
+ var/refid = "\ref[D]"
+ D.gc_destroyed = gctime
+ var/list/queue = queues[level]
if (queue[refid])
queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
queue[refid] = gctime
-//this is purely to separate things profile wise.
-/datum/controller/subsystem/garbage/proc/HardDelete(datum/A)
+//this is mainly to separate things profile wise.
+/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
var/time = world.timeofday
var/tick = TICK_USAGE
var/ticktime = world.time
-
- var/type = A.type
- var/refID = "\ref[A]"
-
- del(A)
-
+ ++delslasttick
+ ++totaldels
+ var/type = D.type
+ var/refID = "\ref[D]"
+
+ del(D)
+
tick = (TICK_USAGE-tick+((world.time-ticktime)/world.tick_lag*100))
+
+ var/datum/qdel_item/I = items[type]
+
+ I.hard_deletes++
+ I.hard_delete_time += TICK_DELTA_TO_MS(tick)
+
+
if (tick > highest_del_tickusage)
highest_del_tickusage = tick
time = world.timeofday - time
@@ -166,18 +233,33 @@ SUBSYSTEM_DEF(garbage)
if (time > 10)
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
- postpone(time/5)
-
-/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
- if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
- tobequeued += A
- A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
+ postpone(time)
+
+/datum/controller/subsystem/garbage/proc/HardQueue(datum/D)
+ if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+ queues[GC_QUEUE_PREQUEUE] += D
+ D.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
/datum/controller/subsystem/garbage/Recover()
- if (istype(SSgarbage.queue))
- queue |= SSgarbage.queue
- if (istype(SSgarbage.tobequeued))
- tobequeued |= SSgarbage.tobequeued
+ if (istype(SSgarbage.queues))
+ for (var/i in 1 to SSgarbage.queues.len)
+ queues[i] |= SSgarbage.queues[i]
+
+
+/datum/qdel_item
+ var/name = ""
+ var/qdels = 0 //Total number of times it's passed thru qdel.
+ var/destroy_time = 0 //Total amount of milliseconds spent processing this type's Destroy()
+ var/failures = 0 //Times it was queued for soft deletion but failed to soft delete.
+ var/hard_deletes = 0 //Different from failures because it also includes QDEL_HINT_HARDDEL deletions
+ var/hard_delete_time = 0//Total amount of milliseconds spent hard deleting this type.
+ var/no_respect_force = 0//Number of times it's not respected force=TRUE
+ var/no_hint = 0 //Number of times it's not even bother to give a qdel hint
+ var/slept_destroy = 0 //Number of times it's slept in its destroy
+
+/datum/qdel_item/New(mytype)
+ name = "[mytype]"
+
// Should be treated as a replacement for the 'del' keyword.
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
@@ -185,21 +267,27 @@ SUBSYSTEM_DEF(garbage)
if(!istype(D))
del(D)
return
-#ifdef TESTING
- SSgarbage.qdel_list += "[D.type]"
-#endif
+ var/datum/qdel_item/I = SSgarbage.items[D.type]
+ if (!I)
+ I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type)
+ I.qdels++
+
+
if(isnull(D.gc_destroyed))
D.SendSignal(COMSIG_PARENT_QDELETED)
D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
var/start_time = world.time
+ var/start_tick = world.tick_usage
var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
if(world.time != start_time)
- SSgarbage.sleptDestroy["[D.type]"]++
+ I.slept_destroy++
+ else
+ I.destroy_time += TICK_USAGE_TO_MS(start_tick)
if(!D)
return
switch(hint)
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
- SSgarbage.QueueForQueuing(D)
+ SSgarbage.PreQueue(D)
if (QDEL_HINT_IWILLGC)
D.gc_destroyed = world.time
return
@@ -209,28 +297,33 @@ SUBSYSTEM_DEF(garbage)
return
// Returning LETMELIVE after being told to force destroy
// indicates the objects Destroy() does not respect force
- if(!SSgarbage.noforcerespect["[D.type]"])
- SSgarbage.noforcerespect["[D.type]"] = "[D.type]"
+ #ifdef TESTING
+ if(!I.no_respect_force)
testing("WARNING: [D.type] has been force deleted, but is \
returning an immortal QDEL_HINT, indicating it does \
not respect the force flag for qdel(). It has been \
placed in the queue, further instances of this type \
will also be queued.")
- SSgarbage.QueueForQueuing(D)
+ #endif
+ I.no_respect_force++
+
+ SSgarbage.PreQueue(D)
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate()
SSgarbage.HardQueue(D)
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
SSgarbage.HardDelete(D)
if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion.
- SSgarbage.QueueForQueuing(D)
+ SSgarbage.PreQueue(D)
#ifdef TESTING
D.find_references()
#endif
else
- if(!SSgarbage.noqdelhint["[D.type]"])
- SSgarbage.noqdelhint["[D.type]"] = "[D.type]"
+ #ifdef TESTING
+ if(!I.no_hint)
testing("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
- SSgarbage.QueueForQueuing(D)
+ #endif
+ I.no_hint++
+ SSgarbage.PreQueue(D)
else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
@@ -281,15 +374,6 @@ SUBSYSTEM_DEF(garbage)
SSgarbage.can_fire = 1
SSgarbage.next_fire = world.time + world.tick_lag
-/client/verb/purge_all_destroyed_objects()
- set category = "Debug"
- while(SSgarbage.queue.len)
- var/datum/o = locate(SSgarbage.queue[1])
- if(istype(o) && o.gc_destroyed)
- del(o)
- SSgarbage.totaldels++
- SSgarbage.queue.Cut(1, 2)
-
/datum/verb/qdel_then_find_references()
set category = "Debug"
set name = "qdel() then Find References"
@@ -300,24 +384,6 @@ SUBSYSTEM_DEF(garbage)
if(!running_find_references)
find_references(TRUE)
-/client/verb/show_qdeleted()
- set category = "Debug"
- set name = "Show qdel() Log"
- set desc = "Render the qdel() log and display it"
-
- var/dat = "List of things that have been qdel()eted this round
"
-
- var/tmplist = list()
- for(var/elem in SSgarbage.qdel_list)
- if(!(elem in tmplist))
- tmplist[elem] = 0
- tmplist[elem]++
-
- for(var/path in tmplist)
- dat += "[path] - [tmplist[path]] times
"
-
- usr << browse(dat, "window=qdeletedlog")
-
/datum/proc/DoSearchVar(X, Xname)
if(usr && usr.client && !usr.client.running_find_references) return
if(istype(X, /datum))
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 2c36e10d31..f2bb300471 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -117,7 +117,7 @@ SUBSYSTEM_DEF(mapping)
var/start_time = REALTIMEOFDAY
INIT_ANNOUNCE("Loading [config.map_name]...")
- TryLoadZ(config.GetFullMapPath(), FailedZs, ZLEVEL_STATION)
+ TryLoadZ(config.GetFullMapPath(), FailedZs, ZLEVEL_STATION_PRIMARY)
INIT_ANNOUNCE("Loaded station in [(REALTIMEOFDAY - start_time)/10]s!")
if(SSdbcore.Connect())
var/datum/DBQuery/query_round_map_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET map_name = '[config.map_name]' WHERE id = [GLOB.round_id]")
diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm
index 10c6f8e5c4..4e58212b44 100644
--- a/code/controllers/subsystem/minimap.dm
+++ b/code/controllers/subsystem/minimap.dm
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(minimap)
var/const/MINIMAP_SIZE = 2048
var/const/TILE_SIZE = 8
- var/list/z_levels = list(ZLEVEL_STATION)
+ var/list/z_levels = list(ZLEVEL_STATION_PRIMARY)
/datum/controller/subsystem/minimap/Initialize(timeofday)
var/hash = md5(SSmapping.config.GetFullMapPath())
diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm
index 8b8bc81ad2..13762b83f9 100644
--- a/code/controllers/subsystem/persistence.dm
+++ b/code/controllers/subsystem/persistence.dm
@@ -2,15 +2,13 @@ SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE
flags = SS_NO_FIRE
- var/secret_satchels
var/list/satchel_blacklist = list() //this is a typecache
var/list/new_secret_satchels = list() //these are objects
- var/list/old_secret_satchels = ""
+ var/list/old_secret_satchels = list()
var/list/obj/structure/chisel_message/chisel_messages = list()
var/list/saved_messages = list()
- var/trophy_sav
var/list/saved_trophies = list()
/datum/controller/subsystem/persistence/Initialize()
@@ -21,32 +19,47 @@ SUBSYSTEM_DEF(persistence)
..()
/datum/controller/subsystem/persistence/proc/LoadSatchels()
- secret_satchels = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
- if(!fexists(secret_satchels))
- return
- satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar))
- var/list/json = list()
- json = json_decode(file2text(secret_satchels))
- old_secret_satchels = json["data"]
var/placed_satchel = 0
- if(old_secret_satchels.len)
- if(old_secret_satchels.len >= 20) //guards against low drop pools assuring that one player cannot reliably find his own gear.
- var/pos = rand(1, old_secret_satchels.len)
- old_secret_satchels.Cut(pos, pos+1)
- var/obj/item/storage/backpack/satchel/flat/F = new()
- F.x = old_secret_satchels[pos]["x"]
- F.y = old_secret_satchels[pos]["y"]
- F.z = ZLEVEL_STATION
- var/path = text2path(old_secret_satchels[pos]["saved_obj"])
- if(!ispath(path))
- return
- if(isfloorturf(F.loc) && !istype(F.loc, /turf/open/floor/plating/))
- F.hide(1)
- new path(F)
- placed_satchel++
-
+ var/path
+ var/obj/item/storage/backpack/satchel/flat/F = new()
+ if(fexists("data/npc_saves/SecretSatchels.sav")) //legacy compatability to convert old format to new
+ var/savefile/secret_satchels = new /savefile("data/npc_saves/SecretSatchels.sav")
+ var/sav_text
+ secret_satchels[SSmapping.config.map_name] >> sav_text
+ fdel("data/npc_saves/SecretSatchels.sav")
+ if(sav_text)
+ old_secret_satchels = splittext(sav_text,"#")
+ if(old_secret_satchels.len >= 20)
+ var/satchel_string = pick_n_take(old_secret_satchels)
+ var/list/chosen_satchel = splittext(satchel_string,"|")
+ if(chosen_satchel.len == 3)
+ F.x = text2num(chosen_satchel[1])
+ F.y = text2num(chosen_satchel[2])
+ F.z = ZLEVEL_STATION_PRIMARY
+ path = text2path(chosen_satchel[3])
+ else
+ var/json_file = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
+ if(!fexists(json_file))
+ return
+ var/list/json = list()
+ json = json_decode(file2text(json_file))
+ old_secret_satchels = json["data"]
+ if(old_secret_satchels.len)
+ if(old_secret_satchels.len >= 20) //guards against low drop pools assuring that one player cannot reliably find his own gear.
+ var/pos = rand(1, old_secret_satchels.len)
+ old_secret_satchels.Cut(pos, pos+1)
+ F.x = old_secret_satchels[pos]["x"]
+ F.y = old_secret_satchels[pos]["y"]
+ F.z = ZLEVEL_STATION_PRIMARY
+ path = text2path(old_secret_satchels[pos]["saved_obj"])
+ if(!ispath(path))
+ return
+ if(isfloorturf(F.loc) && !istype(F.loc, /turf/open/floor/plating/))
+ F.hide(1)
+ new path(F)
+ placed_satchel++
var/list/free_satchels = list()
- for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,ZLEVEL_STATION), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,ZLEVEL_STATION)))) //Nontrivially expensive but it's roundstart only
+ for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,ZLEVEL_STATION_PRIMARY), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,ZLEVEL_STATION_PRIMARY)))) //Nontrivially expensive but it's roundstart only
if(isfloorturf(T) && !istype(T, /turf/open/floor/plating/))
free_satchels += new /obj/item/storage/backpack/satchel/flat/secret(T)
if(!isemptylist(free_satchels) && ((free_satchels.len + placed_satchel) >= (50 - old_secret_satchels.len) * 0.1)) //up to six tiles, more than enough to kill anything that moves
@@ -58,16 +71,25 @@ SUBSYSTEM_DEF(persistence)
break //Who's been duping the bird?!
/datum/controller/subsystem/persistence/proc/LoadChiselMessages()
- var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
- if(!fexists(json_file))
- return
- var/list/json
- json = json_decode(file2text(json_file))
+ var/list/saved_messages = list()
+ if(fexists("data/npc_saves/ChiselMessages.sav")) //legacy compatability to convert old format to new
+ var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav")
+ var/saved_json
+ chisel_messages_sav[SSmapping.config.map_name] >> saved_json
+ if(!saved_json)
+ return
+ saved_messages = json_decode(saved_json)
+ fdel("data/npc_saves/ChiselMessages.sav")
+ else
+ var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
+ if(!fexists(json_file))
+ return
+ var/list/json
+ json = json_decode(file2text(json_file))
- if(!json)
- return
-
- var/list/saved_messages = json["data"]
+ if(!json)
+ return
+ saved_messages = json["data"]
for(var/item in saved_messages)
if(!islist(item))
@@ -95,17 +117,23 @@ SUBSYSTEM_DEF(persistence)
log_world("Loaded [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
/datum/controller/subsystem/persistence/proc/LoadTrophies()
- trophy_sav = file("data/npc_saves/TrophyItems.json")
- if(!fexists(trophy_sav))
- return
- var/list/json = list()
- json = json_decode(file2text(trophy_sav))
-
- if(!json)
- return
-
- saved_trophies = json["data"]
-
+ if(fexists("data/npc_saves/TrophyItems.sav")) //legacy compatability to convert old format to new
+ var/savefile/S = new /savefile("data/npc_saves/TrophyItems.sav")
+ var/saved_json
+ S >> saved_json
+ if(!saved_json)
+ return
+ saved_trophies = json_decode(saved_json)
+ fdel("data/npc_saves/TrophyItems.sav")
+ else
+ var/json_file = file("data/npc_saves/TrophyItems.json")
+ if(!fexists(json_file))
+ return
+ var/list/json = list()
+ json = json_decode(file2text(json_file))
+ if(!json)
+ return
+ saved_trophies = json["data"]
SetUpTrophies(saved_trophies.Copy())
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
@@ -140,9 +168,10 @@ SUBSYSTEM_DEF(persistence)
/datum/controller/subsystem/persistence/proc/CollectSecretSatchels()
var/list/satchels = list()
+ satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar))
for(var/A in new_secret_satchels)
var/obj/item/storage/backpack/satchel/flat/F = A
- if(QDELETED(F) || F.z != ZLEVEL_STATION || F.invisibility != INVISIBILITY_MAXIMUM)
+ if(QDELETED(F) || F.z != ZLEVEL_STATION_PRIMARY || F.invisibility != INVISIBILITY_MAXIMUM)
continue
var/list/savable_obj = list()
for(var/obj/O in F)
@@ -159,10 +188,11 @@ SUBSYSTEM_DEF(persistence)
data["y"] = F.y
data["saved_obj"] = pick(savable_obj)
satchels += list(data)
+ var/json_file = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
var/list/file_data = list()
file_data["data"] = satchels
- fdel(secret_satchels)
- WRITE_FILE(secret_satchels, json_encode(file_data))
+ fdel(json_file)
+ WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectChiselMessages()
var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
@@ -181,10 +211,11 @@ SUBSYSTEM_DEF(persistence)
/datum/controller/subsystem/persistence/proc/CollectTrophies()
+ var/json_file = file("data/npc_saves/TrophyItems.json")
var/list/file_data = list()
file_data["data"] = saved_trophies
- fdel(trophy_sav)
- WRITE_FILE(trophy_sav, json_encode(file_data))
+ fdel(json_file)
+ WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T)
if(!T.added_roundstart && T.showpiece)
@@ -192,4 +223,4 @@ SUBSYSTEM_DEF(persistence)
data["path"] = T.showpiece.type
data["message"] = T.trophy_message
data["placer_key"] = T.placer_key
- saved_trophies += list(data)
\ No newline at end of file
+ saved_trophies += list(data)
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index f9774f8cb5..9f6f6f5fb3 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -290,7 +290,7 @@ SUBSYSTEM_DEF(shuttle)
continue
var/turf/T = get_turf(thing)
- if(T && T.z == ZLEVEL_STATION)
+ if(T && (T.z in GLOB.station_z_levels))
callShuttle = 0
break
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index e51ef0c255..04677f593f 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -315,11 +315,6 @@ SUBSYSTEM_DEF(ticker)
flick("station_corrupted",cinematic)
SEND_SOUND(world, sound('sound/effects/ghost.ogg'))
actually_blew_up = FALSE
- if("gang war") //Gang Domination (just show the override screen)
- cinematic.icon_state = "intro_malf_still"
- flick("intro_malf",cinematic)
- actually_blew_up = FALSE
- sleep(70)
if("fake") //The round isn't over, we're just freaking people out for fun
flick("intro_nuke",cinematic)
sleep(35)
@@ -392,7 +387,7 @@ SUBSYSTEM_DEF(ticker)
if(bomb && bomb.loc)
bombloc = bomb.z
else if(!station_missed)
- bombloc = ZLEVEL_STATION
+ bombloc = ZLEVEL_STATION_PRIMARY
if(mode)
mode.explosion_in_progress = 0
@@ -739,10 +734,6 @@ SUBSYSTEM_DEF(ticker)
news_message = "We would like to reassure all employees that the reports of a Syndicate backed nuclear attack on [station_name()] are, in fact, a hoax. Have a secure day!"
if(STATION_EVACUATED)
news_message = "The crew of [station_name()] has been evacuated amid unconfirmed reports of enemy activity."
- if(GANG_LOSS)
- news_message = "Organized crime aboard [station_name()] has been stamped out by members of our ever vigilant security team. Remember to thank your assigned officers today!"
- if(GANG_TAKEOVER)
- news_message = "Contact with [station_name()] has been lost after a sophisticated hacking attack by organized criminal elements. Stay vigilant!"
if(BLOB_WIN)
news_message = "[station_name()] was overcome by an unknown biological outbreak, killing all crew on board. Don't let it happen to you! Remember, a clean work station is a safe work station."
if(BLOB_NUKE)
diff --git a/code/datums/antagonists/datum_cult.dm b/code/datums/antagonists/datum_cult.dm
index d17b799ca2..8c05c54b21 100644
--- a/code/datums/antagonists/datum_cult.dm
+++ b/code/datums/antagonists/datum_cult.dm
@@ -40,7 +40,7 @@
if(!GLOB.summon_spots.len)
while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES)
var/area/summon = pick(GLOB.sortedAreas - GLOB.summon_spots)
- if(summon && (summon.z == ZLEVEL_STATION) && summon.valid_territory)
+ if(summon && (summon.z in GLOB.station_z_levels) && summon.valid_territory)
GLOB.summon_spots += summon
SSticker.mode.cult_objectives += "eldergod"
diff --git a/code/datums/antagonists/devil.dm b/code/datums/antagonists/devil.dm
index 9839dfe4ac..14c92f3263 100644
--- a/code/datums/antagonists/devil.dm
+++ b/code/datums/antagonists/devil.dm
@@ -164,7 +164,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
update_hud()
switch(SOULVALUE)
if(0)
- to_chat(owner.current, "Your hellish powers have been restored.")
+ to_chat(owner.current, "Your hellish powers have been restored.")
give_appropriate_spells()
if(BLOOD_THRESHOLD)
increase_blood_lizard()
@@ -189,10 +189,10 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
regress_humanoid()
if(SOULVALUE < 0)
give_appropriate_spells()
- to_chat(owner.current, "As punishment for your failures, all of your powers except contract creation have been revoked.")
+ to_chat(owner.current, "As punishment for your failures, all of your powers except contract creation have been revoked.")
/datum/antagonist/devil/proc/regress_humanoid()
- to_chat(owner.current, "Your powers weaken, have more contracts be signed to regain power.")
+ to_chat(owner.current, "Your powers weaken, have more contracts be signed to regain power.")
if(ishuman(owner.current))
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/human, 1)
@@ -204,7 +204,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
/datum/antagonist/devil/proc/regress_blood_lizard()
var/mob/living/carbon/true_devil/D = owner.current
- to_chat(D, "Your powers weaken, have more contracts be signed to regain power.")
+ to_chat(D, "Your powers weaken, have more contracts be signed to regain power.")
D.oldform.loc = D.loc
owner.transfer_to(D.oldform)
give_appropriate_spells()
@@ -214,7 +214,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
/datum/antagonist/devil/proc/increase_blood_lizard()
- to_chat(owner.current, "You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.")
+ to_chat(owner.current, "You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.")
sleep(50)
if(ishuman(owner.current))
var/mob/living/carbon/human/H = owner.current
@@ -232,7 +232,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
/datum/antagonist/devil/proc/increase_true_devil()
- to_chat(owner.current, "You feel as though your current form is about to shed. You will soon turn into a true devil.")
+ to_chat(owner.current, "You feel as though your current form is about to shed. You will soon turn into a true devil.")
sleep(50)
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(owner.current.loc)
A.faction |= "hell"
@@ -248,7 +248,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
if(!ascendable)
return
var/mob/living/carbon/true_devil/D = owner.current
- to_chat(D, "You feel as though your form is about to ascend.")
+ to_chat(D, "You feel as though your form is about to ascend.")
sleep(50)
if(!D)
return
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 84cb42cf47..d55a32609a 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -25,10 +25,10 @@
/datum/proc/vv_get_dropdown()
. = list()
. += "---"
- .["Call Proc"] = "?_src_=vars;proc_call=\ref[src]"
- .["Mark Object"] = "?_src_=vars;mark_object=\ref[src]"
- .["Delete"] = "?_src_=vars;delete=\ref[src]"
- .["Show VV To Player"] = "?_src_=vars;expose=\ref[src]"
+ .["Call Proc"] = "?_src_=vars;[HrefToken()];proc_call=\ref[src]"
+ .["Mark Object"] = "?_src_=vars;[HrefToken()];mark_object=\ref[src]"
+ .["Delete"] = "?_src_=vars;[HrefToken()];delete=\ref[src]"
+ .["Show VV To Player"] = "?_src_=vars;[HrefToken(TRUE)];expose=\ref[src]"
/datum/proc/on_reagent_change()
@@ -81,26 +81,26 @@
if(istype(D, /atom))
var/atom/A = D
if(isliving(A))
- atomsnowflake += "[D]"
+ atomsnowflake += "[D]"
if(A.dir)
- atomsnowflake += "
<< [dir2text(A.dir)] >>"
+ atomsnowflake += "
<< [dir2text(A.dir)] >>"
var/mob/living/M = A
atomsnowflake += {"
-
[M.ckey ? M.ckey : "No ckey"] / [M.real_name ? M.real_name : "No real name"]
+
[M.ckey ? M.ckey : "No ckey"] / [M.real_name ? M.real_name : "No real name"]
- BRUTE:[M.getBruteLoss()]
- FIRE:[M.getFireLoss()]
- TOXIN:[M.getToxLoss()]
- OXY:[M.getOxyLoss()]
- CLONE:[M.getCloneLoss()]
- BRAIN:[M.getBrainLoss()]
- STAMINA:[M.getStaminaLoss()]
+ BRUTE:[M.getBruteLoss()]
+ FIRE:[M.getFireLoss()]
+ TOXIN:[M.getToxLoss()]
+ OXY:[M.getOxyLoss()]
+ CLONE:[M.getCloneLoss()]
+ BRAIN:[M.getBrainLoss()]
+ STAMINA:[M.getStaminaLoss()]
"}
else
- atomsnowflake += "[D]"
+ atomsnowflake += "[D]"
if(A.dir)
- atomsnowflake += "
<< [dir2text(A.dir)] >>"
+ atomsnowflake += "
<< [dir2text(A.dir)] >>"
else
atomsnowflake += "[D]"
@@ -124,12 +124,12 @@
if (islist)
dropdownoptions = list(
"---",
- "Add Item" = "?_src_=vars;listadd=[refid]",
- "Remove Nulls" = "?_src_=vars;listnulls=[refid]",
- "Remove Dupes" = "?_src_=vars;listdupes=[refid]",
- "Set len" = "?_src_=vars;listlen=[refid]",
- "Shuffle" = "?_src_=vars;listshuffle=[refid]",
- "Show VV To Player" = "?_src_=vars;expose=[refid]"
+ "Add Item" = "?_src_=vars;[HrefToken()];listadd=[refid]",
+ "Remove Nulls" = "?_src_=vars;[HrefToken()];listnulls=[refid]",
+ "Remove Dupes" = "?_src_=vars;[HrefToken()];listdupes=[refid]",
+ "Set len" = "?_src_=vars;[HrefToken()];listlen=[refid]",
+ "Shuffle" = "?_src_=vars;[HrefToken()];listshuffle=[refid]",
+ "Show VV To Player" = "?_src_=vars;[HrefToken()];expose=[refid]"
)
else
dropdownoptions = D.vv_get_dropdown()
@@ -335,7 +335,7 @@