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/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/components.dm b/code/__DEFINES/components.dm index 0225dad669..1943ba15cd 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -5,7 +5,7 @@ // How multiple components of the exact same type are handled in the same datum #define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default) -#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed +#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed #define COMPONENT_DUPE_UNIQUE 2 //new component is deleted // All signals. Format: @@ -16,3 +16,9 @@ #define COMSIG_PARENT_QDELETED "parent_qdeleted" //before a datum's Destroy() is called: () #define COMSIG_ATOM_ENTERED "atom_entered" //from base of atom/Entered(): (atom/movable, atom) #define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (atom/movable) +#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from the base of atom/attackby: (obj/item, mob/living, params) +#define COMSIG_PARENT_EXAMINE "atom_examine" //from the base of atom/examine: (mob) +#define COMSIG_ATOM_ENTERED "atom_entered" //from base of atom/Entered(): (atom/movable, atom) +#define COMSIG_ATOM_EX_ACT "atom_ex_act" //from base of atom/ex_act(): (severity, target) +#define COMSIG_ATOM_SING_PULL "atom_sing_pull" //from base of atom/singularity_pull(): (S, current_size) +#define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (atom/movable) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 16b2a8f4fb..2c18e51f01 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -25,6 +25,8 @@ #define islava(A) (istype(A, /turf/open/lava)) +#define isplatingturf(A) (istype(A, /turf/open/floor/plating)) + //Mobs #define isliving(A) (istype(A, /mob/living)) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 209a33e0c5..1c79dde65f 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -458,3 +458,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/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/server_tools.dm b/code/__DEFINES/server_tools.dm index a4afa58a87..d2be3e578e 100644 --- a/code/__DEFINES/server_tools.dm +++ b/code/__DEFINES/server_tools.dm @@ -9,7 +9,9 @@ //keep these in sync with TGS3 #define SERVICE_WORLD_PARAM "server_service" -#define SERVICE_PR_TEST_JSON "..\\..\\prtestjob.json" +#define SERVICE_VERSION_PARAM "server_service_version" +#define SERVICE_PR_TEST_JSON "prtestjob.json" +#define SERVICE_PR_TEST_JSON_OLD "..\\..\\[SERVICE_PR_TEST_JSON]" #define SERVICE_CMD_HARD_REBOOT "hard_reboot" #define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown" 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/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/datums/components/archaeology.dm b/code/datums/components/archaeology.dm new file mode 100644 index 0000000000..22a6b24bc4 --- /dev/null +++ b/code/datums/components/archaeology.dm @@ -0,0 +1,92 @@ +/datum/component/archaeology + dupe_type = COMPONENT_DUPE_UNIQUE + var/list/archdrops + var/prob2drop + var/dug + +/datum/component/archaeology/Initialize(_prob2drop, list/_archdrops = list()) + prob2drop = Clamp(_prob2drop, 0, 100) + archdrops = _archdrops + RegisterSignal(COMSIG_PARENT_ATTACKBY,.proc/Dig) + RegisterSignal(COMSIG_ATOM_EX_ACT, .proc/BombDig) + RegisterSignal(COMSIG_ATOM_SING_PULL, .proc/SingDig) + +/datum/component/archaeology/InheritComponent(datum/component/archaeology/A, i_am_original) + var/list/other_archdrops = A.archdrops + var/list/_archdrops = archdrops + for(var/I in other_archdrops) + _archdrops[I] += other_archdrops[I] + +/datum/component/archaeology/proc/Dig(mob/user, obj/item/W) + if(dug) + to_chat(user, "Looks like someone has dug here already.") + return FALSE + else + var/digging_speed + if (istype(W, /obj/item/shovel)) + var/obj/item/shovel/S = W + digging_speed = S.digspeed + else if (istype(W, /obj/item/pickaxe)) + var/obj/item/pickaxe/P = W + digging_speed = P.digspeed + + if (digging_speed && isturf(user.loc)) + to_chat(user, "You start digging...") + playsound(parent, 'sound/effects/shovel_dig.ogg', 50, 1) + + if(do_after(user, digging_speed, target = parent)) + to_chat(user, "You dig a hole.") + gets_dug() + dug = TRUE + SSblackbox.add_details("pick_used_mining",W.type) + return TRUE + return FALSE + +/datum/component/archaeology/proc/gets_dug() + if(dug) + return + else + var/turf/open/OT = get_turf(parent) + for(var/thing in archdrops) + var/maxtodrop = archdrops[thing] + for(var/i in 1 to maxtodrop) + if(prob(prob2drop)) // can't win them all! + new thing(OT) + + if(isopenturf(OT)) + if(OT.postdig_icon_change) + if(istype(OT, /turf/open/floor/plating/asteroid/) && !OT.postdig_icon) + var/turf/open/floor/plating/asteroid/AOT = parent + AOT.icon_plating = "[AOT.environment_type]_dug" + AOT.icon_state = "[AOT.environment_type]_dug" + else + if(isplatingturf(OT)) + var/turf/open/floor/plating/POT = parent + POT.icon_plating = "[POT.postdig_icon]" + OT.icon_state = "[OT.postdig_icon]" + + if(OT.slowdown) //Things like snow slow you down until you dig them. + OT.slowdown = 0 + dug = TRUE + +/datum/component/archaeology/proc/SingDig(S, current_size) + switch(current_size) + if(STAGE_THREE) + if(prob(30)) + gets_dug() + if(STAGE_FOUR) + if(prob(50)) + gets_dug() + else + if(current_size >= STAGE_FIVE && prob(70)) + gets_dug() + +/datum/component/archaeology/proc/BombDig(severity, target) + switch(severity) + if(3) + return + if(2) + if(prob(20)) + gets_dug() + if(1) + gets_dug() diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 87c6ad7334..d55a32609a 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -1,934 +1,955 @@ -/datum - var/var_edited = FALSE //Warrenty void if seal is broken - var/fingerprintslast = null - -/datum/proc/can_vv_get(var_name) - return TRUE - -/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited - switch(var_name) - if ("vars") - return FALSE - if ("var_edited") - return FALSE - var_edited = TRUE - vars[var_name] = var_value - -/datum/proc/vv_get_var(var_name) - switch(var_name) - if ("vars") - return debug_variable(var_name, list(), 0, src) - return debug_variable(var_name, vars[var_name], 0, src) - -//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down +/datum + var/var_edited = FALSE //Warrenty void if seal is broken + var/fingerprintslast = null + +/datum/proc/can_vv_get(var_name) + return TRUE + +/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited + switch(var_name) + if ("vars") + return FALSE + if ("var_edited") + return FALSE + var_edited = TRUE + vars[var_name] = var_value + +/datum/proc/vv_get_var(var_name) + switch(var_name) + if ("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + +//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down //add separaters by doing . += "---" -/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]" - - -/datum/proc/on_reagent_change() - return - - -/client/proc/debug_variables(datum/D in world) - set category = "Debug" - set name = "View Variables" - //set src in world - var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. - - if(!usr.client || !usr.client.holder) - to_chat(usr, "You need to be an administrator to access this.") - return - - if(!D) - return - - var/islist = islist(D) - if (!islist && !istype(D)) - return - - var/title = "" - var/refid = "\ref[D]" - var/icon/sprite - var/hash - - var/type = /list - if (!islist) - type = D.type - - - +/datum/proc/vv_get_dropdown() + . = list() + . += "---" + .["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() + return + + +/client/proc/debug_variables(datum/D in world) + set category = "Debug" + set name = "View Variables" + //set src in world + var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. + + if(!usr.client || !usr.client.holder) //The usr vs src abuse in this proc is intentional and must not be changed + to_chat(usr, "You need to be an administrator to access this.") + return + + if(!D) + return + + var/islist = islist(D) + if (!islist && !istype(D)) + return + + var/title = "" + var/refid = "\ref[D]" + var/icon/sprite + var/hash + + var/type = /list + if (!islist) + type = D.type + + + if(istype(D, /atom)) - var/atom/AT = D - if(AT.icon && AT.icon_state) - sprite = new /icon(AT.icon, AT.icon_state) - hash = md5(AT.icon) - hash = md5(hash + AT.icon_state) - usr << browse_rsc(sprite, "vv[hash].png") - - title = "[D] (\ref[D]) = [type]" - - var/sprite_text - if(sprite) - sprite_text = "" - var/list/atomsnowflake = list() - + var/atom/AT = D + if(AT.icon && AT.icon_state) + sprite = new /icon(AT.icon, AT.icon_state) + hash = md5(AT.icon) + hash = md5(hash + AT.icon_state) + src << browse_rsc(sprite, "vv[hash].png") + + title = "[D] (\ref[D]) = [type]" + + var/sprite_text + if(sprite) + sprite_text = "" + var/list/atomsnowflake = list() + if(istype(D, /atom)) - var/atom/A = D - if(isliving(A)) - atomsnowflake += "[D]" - if(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"] -
- BRUTE:[M.getBruteLoss()] - FIRE:[M.getFireLoss()] - TOXIN:[M.getToxLoss()] - OXY:[M.getOxyLoss()] - CLONE:[M.getCloneLoss()] - BRAIN:[M.getBrainLoss()] - STAMINA:[M.getStaminaLoss()] - - "} - else - atomsnowflake += "[D]" - if(A.dir) - atomsnowflake += "
<< [dir2text(A.dir)] >>" - else - atomsnowflake += "[D]" - - var/formatted_type = "[type]" - if(length(formatted_type) > 25) - var/middle_point = length(formatted_type) / 2 - var/splitpoint = findtext(formatted_type,"/",middle_point) - if(splitpoint) - formatted_type = "[copytext(formatted_type,1,splitpoint)]
[copytext(formatted_type,splitpoint)]" - else - formatted_type = "Type too long" //No suitable splitpoint (/) found. - - var/marked - if(holder.marked_datum && holder.marked_datum == D) - marked = "
Marked Object" - var/varedited_line = "" - if(!islist && D.var_edited) - varedited_line = "
Var Edited" - - var/list/dropdownoptions = list() - 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]" - ) - else - dropdownoptions = D.vv_get_dropdown() - var/list/dropdownoptions_html = list() - - for (var/name in dropdownoptions) - var/link = dropdownoptions[name] - if (link) - dropdownoptions_html += "" - else - dropdownoptions_html += "" - - var/list/names = list() - if (!islist) - for (var/V in D.vars) - names += V - sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. - - var/list/variable_html = list() - if (islist) - var/list/L = D - for (var/i in 1 to L.len) - var/key = L[i] - var/value - if (IS_NORMAL_LIST(L) && !isnum(key)) - value = L[key] - variable_html += debug_variable(i, value, 0, D) - else - - names = sortList(names) - for (var/V in names) - if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) - - var/html = {" - - - [title] - - - - -
- - - - - -
- - - - -
- [sprite_text] -
- [atomsnowflake.Join()] -
-
-
- [formatted_type] - [marked] - [varedited_line] -
-
-
- Refresh -
- -
-
-
-
-
- - E - Edit, tries to determine the variable type by itself.
- C - Change, asks you for the var type first.
- M - Mass modify: changes this variable for all objects of this type.
-
-
- - - - - -
-
- Search: -
-
- -
-
-
    - [variable_html.Join()] -
- - - -"} - - usr << browse(html, "window=variables[refid];size=475x650") - - -#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) -/proc/debug_variable(name, value, level, datum/DA = null, sanitize = TRUE) - var/header - if(DA) - if (islist(DA)) - var/index = name - if (value) - name = DA[name] //name is really the index until this line - else - value = DA[name] - header = "
  • (E) (C) (-) " - else - header = "
  • (E) (C) (M) " - else - header = "
  • " - - var/item - if (isnull(value)) - item = "[VV_HTML_ENCODE(name)] = null" - - else if (istext(value)) - item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" - - else if (isicon(value)) - #ifdef VARSICON - var/icon/I = new/icon(value) - var/rnd = rand(1,10000) - var/rname = "tmp\ref[I][rnd].png" - usr << browse_rsc(I, rname) - item = "[VV_HTML_ENCODE(name)] = ([value]) " - #else - item = "[VV_HTML_ENCODE(name)] = /icon ([value])" - #endif - -/* else if (istype(value, /image)) - #ifdef VARSICON - var/rnd = rand(1, 10000) - var/image/I = value - - src << browse_rsc(I.icon, "tmp\ref[value][rnd].png") - html += "[name] = " - #else - html += "[name] = /image ([value])" - #endif -*/ - else if (isfile(value)) - item = "[VV_HTML_ENCODE(name)] = '[value]'" - - //else if (istype(value, /client)) - // var/client/C = value - // item = "[VV_HTML_ENCODE(name)] \ref[value] = [C] [C.type]" - - else if (istype(value, /datum)) - var/datum/D = value - if ("[D]" != "[D.type]") //if the thing as a name var, lets use it. - item = "[VV_HTML_ENCODE(name)] \ref[value] = [D] [D.type]" - else - item = "[VV_HTML_ENCODE(name)] \ref[value] = [D.type]" - - else if (islist(value)) - var/list/L = value - var/list/items = list() - - if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150))) - for (var/i in 1 to L.len) - var/key = L[i] - var/val - if (IS_NORMAL_LIST(L) && !isnum(key)) - val = L[key] - if (!val) - val = key - key = i - - items += debug_variable(key, val, level + 1, sanitize = sanitize) - - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " - else - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" - - else - item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" - - return "[header][item]
  • " - -#undef VV_HTML_ENCODE - -/client/proc/view_var_Topic(href, href_list, hsrc) - if( (usr.client != src) || !src.holder ) - return - if(href_list["Vars"]) - debug_variables(locate(href_list["Vars"])) - - else if(href_list["datumrefresh"]) - var/datum/DAT = locate(href_list["datumrefresh"]) - if(!DAT) //can't be an istype() because /client etc aren't datums - return - src.debug_variables(DAT) - - else if(href_list["mob_player_panel"]) - if(!check_rights(0)) - return - + var/atom/A = D + if(isliving(A)) + atomsnowflake += "[D]" + if(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"] +
    + BRUTE:[M.getBruteLoss()] + FIRE:[M.getFireLoss()] + TOXIN:[M.getToxLoss()] + OXY:[M.getOxyLoss()] + CLONE:[M.getCloneLoss()] + BRAIN:[M.getBrainLoss()] + STAMINA:[M.getStaminaLoss()] + + "} + else + atomsnowflake += "[D]" + if(A.dir) + atomsnowflake += "
    << [dir2text(A.dir)] >>" + else + atomsnowflake += "[D]" + + var/formatted_type = "[type]" + if(length(formatted_type) > 25) + var/middle_point = length(formatted_type) / 2 + var/splitpoint = findtext(formatted_type,"/",middle_point) + if(splitpoint) + formatted_type = "[copytext(formatted_type,1,splitpoint)]
    [copytext(formatted_type,splitpoint)]" + else + formatted_type = "Type too long" //No suitable splitpoint (/) found. + + var/marked + if(holder && holder.marked_datum && holder.marked_datum == D) + marked = "
    Marked Object" + var/varedited_line = "" + if(!islist && D.var_edited) + varedited_line = "
    Var Edited" + + var/list/dropdownoptions = list() + if (islist) + dropdownoptions = list( + "---", + "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() + var/list/dropdownoptions_html = list() + + for (var/name in dropdownoptions) + var/link = dropdownoptions[name] + if (link) + dropdownoptions_html += "" + else + dropdownoptions_html += "" + + var/list/names = list() + if (!islist) + for (var/V in D.vars) + names += V + sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. + + var/list/variable_html = list() + if (islist) + var/list/L = D + for (var/i in 1 to L.len) + var/key = L[i] + var/value + if (IS_NORMAL_LIST(L) && !isnum(key)) + value = L[key] + variable_html += debug_variable(i, value, 0, D) + else + + names = sortList(names) + for (var/V in names) + if(D.can_vv_get(V)) + variable_html += D.vv_get_var(V) + + var/html = {" + + + [title] + + + + +
    + + + + + +
    + + + + +
    + [sprite_text] +
    + [atomsnowflake.Join()] +
    +
    +
    + [formatted_type] + [marked] + [varedited_line] +
    +
    +
    + Refresh +
    + +
    +
    +
    +
    +
    + + E - Edit, tries to determine the variable type by itself.
    + C - Change, asks you for the var type first.
    + M - Mass modify: changes this variable for all objects of this type.
    +
    +
    + + + + + +
    +
    + Search: +
    +
    + +
    +
    +
      + [variable_html.Join()] +
    + + + +"} + src << browse(html, "window=variables[refid];size=475x650") + + +#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) +/proc/debug_variable(name, value, level, datum/DA = null, sanitize = TRUE) + var/header + if(DA) + if (islist(DA)) + var/index = name + if (value) + name = DA[name] //name is really the index until this line + else + value = DA[name] + header = "
  • (E) (C) (-) " + else + header = "
  • (E) (C) (M) " + else + header = "
  • " + + var/item + if (isnull(value)) + item = "[VV_HTML_ENCODE(name)] = null" + + else if (istext(value)) + item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" + + else if (isicon(value)) + #ifdef VARSICON + var/icon/I = new/icon(value) + var/rnd = rand(1,10000) + var/rname = "tmp\ref[I][rnd].png" + usr << browse_rsc(I, rname) + item = "[VV_HTML_ENCODE(name)] = ([value]) " + #else + item = "[VV_HTML_ENCODE(name)] = /icon ([value])" + #endif + +/* else if (istype(value, /image)) + #ifdef VARSICON + var/rnd = rand(1, 10000) + var/image/I = value + + src << browse_rsc(I.icon, "tmp\ref[value][rnd].png") + html += "[name] = " + #else + html += "[name] = /image ([value])" + #endif +*/ + else if (isfile(value)) + item = "[VV_HTML_ENCODE(name)] = '[value]'" + + //else if (istype(value, /client)) + // var/client/C = value + // item = "[VV_HTML_ENCODE(name)] \ref[value] = [C] [C.type]" + + else if (istype(value, /datum)) + var/datum/D = value + if ("[D]" != "[D.type]") //if the thing as a name var, lets use it. + item = "[VV_HTML_ENCODE(name)] \ref[value] = [D] [D.type]" + else + item = "[VV_HTML_ENCODE(name)] \ref[value] = [D.type]" + + else if (islist(value)) + var/list/L = value + var/list/items = list() + + if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150))) + for (var/i in 1 to L.len) + var/key = L[i] + var/val + if (IS_NORMAL_LIST(L) && !isnum(key)) + val = L[key] + if (!val) + val = key + key = i + + items += debug_variable(key, val, level + 1, sanitize = sanitize) + + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " + else + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + + else + item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" + + return "[header][item]
  • " + +#undef VV_HTML_ENCODE + +/client/proc/view_var_Topic(href, href_list, hsrc) + if( (usr.client != src) || !src.holder || !holder.CheckAdminHref(href, href_list)) + return + if(href_list["Vars"]) + debug_variables(locate(href_list["Vars"])) + + else if(href_list["datumrefresh"]) + var/datum/DAT = locate(href_list["datumrefresh"]) + if(!DAT) //can't be an istype() because /client etc aren't datums + return + src.debug_variables(DAT) + + else if(href_list["mob_player_panel"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["mob_player_panel"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.holder.show_player_panel(M) - href_list["datumrefresh"] = href_list["mob_player_panel"] - - else if(href_list["godmode"]) - if(!check_rights(R_ADMIN)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.holder.show_player_panel(M) + href_list["datumrefresh"] = href_list["mob_player_panel"] + + else if(href_list["godmode"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locate(href_list["godmode"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_godmode(M) - href_list["datumrefresh"] = href_list["godmode"] - - else if(href_list["mark_object"]) - if(!check_rights(0)) - return - - var/datum/D = locate(href_list["mark_object"]) - if(!istype(D)) - to_chat(usr, "This can only be done to instances of type /datum") - return - - src.holder.marked_datum = D - href_list["datumrefresh"] = href_list["mark_object"] - - else if(href_list["proc_call"]) - if(!check_rights(0)) - return - - var/T = locate(href_list["proc_call"]) - - if(T) - callproc_datum(T) - - else if(href_list["delete"]) - if(!check_rights(R_DEBUG, 0)) - return - - var/datum/D = locate(href_list["delete"]) - if(!D) - to_chat(usr, "Unable to locate item!") - admin_delete(D) - href_list["datumrefresh"] = href_list["delete"] - - else if(href_list["regenerateicons"]) - if(!check_rights(0)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.cmd_admin_godmode(M) + href_list["datumrefresh"] = href_list["godmode"] + + else if(href_list["mark_object"]) + if(!check_rights(0)) + return + + var/datum/D = locate(href_list["mark_object"]) + if(!istype(D)) + to_chat(usr, "This can only be done to instances of type /datum") + return + + src.holder.marked_datum = D + href_list["datumrefresh"] = href_list["mark_object"] + + else if(href_list["proc_call"]) + if(!check_rights(0)) + return + + var/T = locate(href_list["proc_call"]) + + if(T) + callproc_datum(T) + + else if(href_list["delete"]) + if(!check_rights(R_DEBUG, 0)) + return + + var/datum/D = locate(href_list["delete"]) + if(!D) + to_chat(usr, "Unable to locate item!") + admin_delete(D) + href_list["datumrefresh"] = href_list["delete"] + + else if(href_list["regenerateicons"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["regenerateicons"]) in GLOB.mob_list - if(!ismob(M)) - to_chat(usr, "This can only be done to instances of type /mob") - return - M.regenerate_icons() - -//Needs +VAREDIT past this point - - else if(check_rights(R_VAREDIT)) - - - //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). - - if(href_list["rename"]) - if(!check_rights(0)) - return - + if(!ismob(M)) + to_chat(usr, "This can only be done to instances of type /mob") + return + M.regenerate_icons() + else if(href_list["expose"]) + if(!check_rights(R_ADMIN, FALSE)) + return + var/thing = locate(href_list["expose"]) + if (!thing) + return + var/value = vv_get_value(VV_CLIENT) + if (value["class"] != VV_CLIENT) + return + var/client/C = value["value"] + if (!C) + return + var/prompt = alert("Do you want to grant [C] access to view this VV window? (they will not be able to edit or change anything nor open nested vv windows unless they themselves are an admin)", "Confirm", "Yes", "No") + if (prompt != "Yes" || !usr.client) + return + message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a VV window") + log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [thing]") + to_chat(C, "[usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window") + C.debug_variables(thing) + + +//Needs +VAREDIT past this point + + else if(check_rights(R_VAREDIT)) + + + //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). + + if(href_list["rename"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["rename"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN) - if( !new_name || !M ) - return - - message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") - M.fully_replace_character_name(M.real_name,new_name) - href_list["datumrefresh"] = href_list["rename"] - - else if(href_list["varnameedit"] && href_list["datumedit"]) - if(!check_rights(0)) - return - - var/D = locate(href_list["datumedit"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN) + if( !new_name || !M ) + return + + message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") + M.fully_replace_character_name(M.real_name,new_name) + href_list["datumrefresh"] = href_list["rename"] + + else if(href_list["varnameedit"] && href_list["datumedit"]) + if(!check_rights(0)) + return + + var/D = locate(href_list["datumedit"]) if(!istype(D, /datum)) - to_chat(usr, "This can only be used on datums") - return - - modify_variables(D, href_list["varnameedit"], 1) - - else if(href_list["varnamechange"] && href_list["datumchange"]) - if(!check_rights(0)) - return - - var/D = locate(href_list["datumchange"]) + to_chat(usr, "This can only be used on datums") + return + + modify_variables(D, href_list["varnameedit"], 1) + + else if(href_list["varnamechange"] && href_list["datumchange"]) + if(!check_rights(0)) + return + + var/D = locate(href_list["datumchange"]) if(!istype(D, /datum)) - to_chat(usr, "This can only be used on datums") - return - - modify_variables(D, href_list["varnamechange"], 0) - - else if(href_list["varnamemass"] && href_list["datummass"]) - if(!check_rights(0)) - return - - var/datum/D = locate(href_list["datummass"]) - if(!istype(D)) - to_chat(usr, "This can only be used on instances of type /datum") - return - - cmd_mass_modify_object_variables(D, href_list["varnamemass"]) - - else if(href_list["listedit"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if (!index) - return - - var/list/L = locate(href_list["listedit"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) - - else if(href_list["listchange"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if (!index) - return - - var/list/L = locate(href_list["listchange"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) - - else if(href_list["listremove"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if (!index) - return - - var/list/L = locate(href_list["listremove"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - var/variable = L[index] - var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") - if (prompt != "Yes") - return - L.Cut(index, index+1) - log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") - log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") - message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") - - else if(href_list["listadd"]) - var/list/L = locate(href_list["listadd"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list_add(L, null, "list", "contents") - - else if(href_list["listdupes"]) - var/list/L = locate(href_list["listdupes"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - uniqueList_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") - log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") - - else if(href_list["listnulls"]) - var/list/L = locate(href_list["listnulls"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - listclearnulls(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") - log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") - - else if(href_list["listlen"]) - var/list/L = locate(href_list["listlen"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - var/value = vv_get_value(VV_NUM) - if (value["class"] != VV_NUM) - return - - L.len = value["value"] - log_world("### ListVarEdit by [src]: /list len: [L.len]") - log_admin("[key_name(src)] modified list's len: [L.len]") - message_admins("[key_name_admin(src)] modified list's len: [L.len]") - - else if(href_list["listshuffle"]) - var/list/L = locate(href_list["listshuffle"]) - if (!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - shuffle_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") - log_admin("[key_name(src)] modified list's contents: SHUFFLE") - message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") - - else if(href_list["give_spell"]) - if(!check_rights(0)) - return - + to_chat(usr, "This can only be used on datums") + return + + modify_variables(D, href_list["varnamechange"], 0) + + else if(href_list["varnamemass"] && href_list["datummass"]) + if(!check_rights(0)) + return + + var/datum/D = locate(href_list["datummass"]) + if(!istype(D)) + to_chat(usr, "This can only be used on instances of type /datum") + return + + cmd_mass_modify_object_variables(D, href_list["varnamemass"]) + + else if(href_list["listedit"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if (!index) + return + + var/list/L = locate(href_list["listedit"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) + + else if(href_list["listchange"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if (!index) + return + + var/list/L = locate(href_list["listchange"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) + + else if(href_list["listremove"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if (!index) + return + + var/list/L = locate(href_list["listremove"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + var/variable = L[index] + var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") + if (prompt != "Yes") + return + L.Cut(index, index+1) + log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") + log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") + message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") + + else if(href_list["listadd"]) + var/list/L = locate(href_list["listadd"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list_add(L, null, "list", "contents") + + else if(href_list["listdupes"]) + var/list/L = locate(href_list["listdupes"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + uniqueList_inplace(L) + log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") + log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") + + else if(href_list["listnulls"]) + var/list/L = locate(href_list["listnulls"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + listclearnulls(L) + log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") + log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") + + else if(href_list["listlen"]) + var/list/L = locate(href_list["listlen"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + var/value = vv_get_value(VV_NUM) + if (value["class"] != VV_NUM) + return + + L.len = value["value"] + log_world("### ListVarEdit by [src]: /list len: [L.len]") + log_admin("[key_name(src)] modified list's len: [L.len]") + message_admins("[key_name_admin(src)] modified list's len: [L.len]") + + else if(href_list["listshuffle"]) + var/list/L = locate(href_list["listshuffle"]) + if (!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + shuffle_inplace(L) + log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") + log_admin("[key_name(src)] modified list's contents: SHUFFLE") + message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") + + else if(href_list["give_spell"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["give_spell"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_spell(M) - href_list["datumrefresh"] = href_list["give_spell"] - - else if(href_list["remove_spell"]) - if(!check_rights(0)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.give_spell(M) + href_list["datumrefresh"] = href_list["give_spell"] + + else if(href_list["remove_spell"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["remove_spell"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - remove_spell(M) - href_list["datumrefresh"] = href_list["remove_spell"] - - else if(href_list["give_disease"]) - if(!check_rights(0)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + remove_spell(M) + href_list["datumrefresh"] = href_list["remove_spell"] + + else if(href_list["give_disease"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["give_disease"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_disease(M) - href_list["datumrefresh"] = href_list["give_spell"] - - else if(href_list["gib"]) - if(!check_rights(R_FUN)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.give_disease(M) + href_list["datumrefresh"] = href_list["give_spell"] + + else if(href_list["gib"]) + if(!check_rights(R_FUN)) + return + var/mob/M = locate(href_list["gib"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_gib(M) - - else if(href_list["build_mode"]) - if(!check_rights(R_BUILDMODE)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.cmd_admin_gib(M) + + else if(href_list["build_mode"]) + if(!check_rights(R_BUILDMODE)) + return + var/mob/M = locate(href_list["build_mode"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - togglebuildmode(M) - href_list["datumrefresh"] = href_list["build_mode"] - - else if(href_list["drop_everything"]) - if(!check_rights(0)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + togglebuildmode(M) + href_list["datumrefresh"] = href_list["build_mode"] + + else if(href_list["drop_everything"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["drop_everything"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_admin_drop_everything(M) - - else if(href_list["direct_control"]) - if(!check_rights(0)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(usr.client) + usr.client.cmd_admin_drop_everything(M) + + else if(href_list["direct_control"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["direct_control"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_assume_direct_control(M) - - else if(href_list["offer_control"]) - if(!check_rights(0)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(usr.client) + usr.client.cmd_assume_direct_control(M) + + else if(href_list["offer_control"]) + if(!check_rights(0)) + return + var/mob/M = locate(href_list["offer_control"]) in GLOB.mob_list - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - offer_control(M) - - else if(href_list["delall"]) - if(!check_rights(R_DEBUG|R_SERVER)) - return - - var/obj/O = locate(href_list["delall"]) - if(!isobj(O)) - to_chat(usr, "This can only be used on instances of type /obj") - return - - var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") - if(action_type == "Cancel" || !action_type) - return - - if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") - return - - if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") - return - - var/O_type = O.type - switch(action_type) - if("Strict type") - var/i = 0 - for(var/obj/Obj in world) - if(Obj.type == O_type) - i++ - qdel(Obj) - CHECK_TICK - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") - message_admins("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") - if("Type and subtypes") - var/i = 0 - for(var/obj/Obj in world) - if(istype(Obj,O_type)) - i++ - qdel(Obj) - CHECK_TICK - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") - message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") - - else if(href_list["addreagent"]) - if(!check_rights(0)) - return - - var/atom/A = locate(href_list["addreagent"]) - - if(!A.reagents) - var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num - if(amount) - A.create_reagents(amount) - - if(A.reagents) - var/chosen_id - var/list/reagent_options = sortList(GLOB.chemical_reagents_list) - switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID")) - if("Enter ID") - var/valid_id - while(!valid_id) - chosen_id = stripped_input(usr, "Enter the ID of the reagent you want to add.") - if(!chosen_id) //Get me out of here! - break - for(var/ID in reagent_options) - if(ID == chosen_id) - valid_id = 1 - if(!valid_id) - to_chat(usr, "A reagent with that ID doesn't exist!") - if("Choose ID") - chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in reagent_options - if(chosen_id) - var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num - if(amount) - A.reagents.add_reagent(chosen_id, amount) - log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - - href_list["datumrefresh"] = href_list["addreagent"] - - else if(href_list["explode"]) - if(!check_rights(R_FUN)) - return - - var/atom/A = locate(href_list["explode"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_explosion(A) - href_list["datumrefresh"] = href_list["explode"] - - else if(href_list["emp"]) - if(!check_rights(R_FUN)) - return - - var/atom/A = locate(href_list["emp"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_emp(A) - href_list["datumrefresh"] = href_list["emp"] - - else if(href_list["rotatedatum"]) - if(!check_rights(0)) - return - - var/atom/A = locate(href_list["rotatedatum"]) - if(!istype(A)) - to_chat(usr, "This can only be done to instances of type /atom") - return - - switch(href_list["rotatedir"]) - if("right") - A.setDir(turn(A.dir, -45)) - if("left") - A.setDir(turn(A.dir, 45)) - href_list["datumrefresh"] = href_list["rotatedatum"] - - else if(href_list["editorgans"]) - if(!check_rights(0)) - return - + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + offer_control(M) + + else if(href_list["delall"]) + if(!check_rights(R_DEBUG|R_SERVER)) + return + + var/obj/O = locate(href_list["delall"]) + if(!isobj(O)) + to_chat(usr, "This can only be used on instances of type /obj") + return + + var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") + if(action_type == "Cancel" || !action_type) + return + + if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") + return + + if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") + return + + var/O_type = O.type + switch(action_type) + if("Strict type") + var/i = 0 + for(var/obj/Obj in world) + if(Obj.type == O_type) + i++ + qdel(Obj) + CHECK_TICK + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") + message_admins("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ") + if("Type and subtypes") + var/i = 0 + for(var/obj/Obj in world) + if(istype(Obj,O_type)) + i++ + qdel(Obj) + CHECK_TICK + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") + message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") + + else if(href_list["addreagent"]) + if(!check_rights(0)) + return + + var/atom/A = locate(href_list["addreagent"]) + + if(!A.reagents) + var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num + if(amount) + A.create_reagents(amount) + + if(A.reagents) + var/chosen_id + var/list/reagent_options = sortList(GLOB.chemical_reagents_list) + switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID")) + if("Enter ID") + var/valid_id + while(!valid_id) + chosen_id = stripped_input(usr, "Enter the ID of the reagent you want to add.") + if(!chosen_id) //Get me out of here! + break + for(var/ID in reagent_options) + if(ID == chosen_id) + valid_id = 1 + if(!valid_id) + to_chat(usr, "A reagent with that ID doesn't exist!") + if("Choose ID") + chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in reagent_options + if(chosen_id) + var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num + if(amount) + A.reagents.add_reagent(chosen_id, amount) + log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") + message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") + + href_list["datumrefresh"] = href_list["addreagent"] + + else if(href_list["explode"]) + if(!check_rights(R_FUN)) + return + + var/atom/A = locate(href_list["explode"]) + if(!isobj(A) && !ismob(A) && !isturf(A)) + to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") + return + + src.cmd_admin_explosion(A) + href_list["datumrefresh"] = href_list["explode"] + + else if(href_list["emp"]) + if(!check_rights(R_FUN)) + return + + var/atom/A = locate(href_list["emp"]) + if(!isobj(A) && !ismob(A) && !isturf(A)) + to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") + return + + src.cmd_admin_emp(A) + href_list["datumrefresh"] = href_list["emp"] + + else if(href_list["rotatedatum"]) + if(!check_rights(0)) + return + + var/atom/A = locate(href_list["rotatedatum"]) + if(!istype(A)) + to_chat(usr, "This can only be done to instances of type /atom") + return + + switch(href_list["rotatedir"]) + if("right") + A.setDir(turn(A.dir, -45)) + if("left") + A.setDir(turn(A.dir, 45)) + href_list["datumrefresh"] = href_list["rotatedatum"] + + else if(href_list["editorgans"]) + if(!check_rights(0)) + return + var/mob/living/carbon/C = locate(href_list["editorgans"]) in GLOB.mob_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - manipulate_organs(C) - href_list["datumrefresh"] = href_list["editorgans"] - + if(!istype(C)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + manipulate_organs(C) + href_list["datumrefresh"] = href_list["editorgans"] + else if(href_list["hallucinate"]) if(!check_rights(0)) return @@ -949,238 +970,238 @@ if(result) new result(C, TRUE) - else if(href_list["makehuman"]) - if(!check_rights(R_SPAWN)) - return - + else if(href_list["makehuman"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list - if(!istype(Mo)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!Mo) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("humanone"=href_list["makehuman"])) - - else if(href_list["makemonkey"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(Mo)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + if(!Mo) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("humanone"=href_list["makehuman"])) + + else if(href_list["makemonkey"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/human/H = locate(href_list["makemonkey"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) + + else if(href_list["makerobot"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/human/H = locate(href_list["makerobot"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makerobot"=href_list["makerobot"])) - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makerobot"=href_list["makerobot"])) + + else if(href_list["makealien"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/human/H = locate(href_list["makealien"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makealien"=href_list["makealien"])) - - else if(href_list["makeslime"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makealien"=href_list["makealien"])) + + else if(href_list["makeslime"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/human/H = locate(href_list["makeslime"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeslime"=href_list["makeslime"])) - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makeslime"=href_list["makeslime"])) + + else if(href_list["makeai"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/H = locate(href_list["makeai"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") - return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeai"=href_list["makeai"])) - - else if(href_list["setspecies"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") + return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makeai"=href_list["makeai"])) + + else if(href_list["setspecies"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/human/H = locate(href_list["setspecies"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - var/newtype = GLOB.species_list[result] - admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [H] to [result]") - H.set_species(newtype) - - else if(href_list["editbodypart"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(result) + var/newtype = GLOB.species_list[result] + admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [H] to [result]") + H.set_species(newtype) + + else if(href_list["editbodypart"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/C = locate(href_list["editbodypart"]) in GLOB.mob_list - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/edit_action = input(usr, "What would you like to do?","Modify Body Part") as null|anything in list("add","remove", "augment") - if(!edit_action) - return - var/list/limb_list = list("head", "l_arm", "r_arm", "l_leg", "r_leg") - if(edit_action == "augment") - limb_list += "chest" - var/result = input(usr, "Please choose which body part to [edit_action]","[capitalize(edit_action)] Body Part") as null|anything in limb_list - - if(!C) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - var/obj/item/bodypart/BP = C.get_bodypart(result) - switch(edit_action) - if("remove") - if(BP) - BP.drop_limb() - else - to_chat(usr, "[C] doesn't have such bodypart.") - if("add") - if(BP) - to_chat(usr, "[C] already has such bodypart.") - else - if(!C.regenerate_limb(result)) - to_chat(usr, "[C] cannot have such bodypart.") - if("augment") - if(ishuman(C)) - if(BP) - BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE) - else - to_chat(usr, "[C] doesn't have such bodypart.") - else - to_chat(usr, "Only humans can be augmented.") - admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [C]") - - - else if(href_list["purrbation"]) - if(!check_rights(R_SPAWN)) - return - + if(!istype(C)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + var/edit_action = input(usr, "What would you like to do?","Modify Body Part") as null|anything in list("add","remove", "augment") + if(!edit_action) + return + var/list/limb_list = list("head", "l_arm", "r_arm", "l_leg", "r_leg") + if(edit_action == "augment") + limb_list += "chest" + var/result = input(usr, "Please choose which body part to [edit_action]","[capitalize(edit_action)] Body Part") as null|anything in limb_list + + if(!C) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(result) + var/obj/item/bodypart/BP = C.get_bodypart(result) + switch(edit_action) + if("remove") + if(BP) + BP.drop_limb() + else + to_chat(usr, "[C] doesn't have such bodypart.") + if("add") + if(BP) + to_chat(usr, "[C] already has such bodypart.") + else + if(!C.regenerate_limb(result)) + to_chat(usr, "[C] cannot have such bodypart.") + if("augment") + if(ishuman(C)) + if(BP) + BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE) + else + to_chat(usr, "[C] doesn't have such bodypart.") + else + to_chat(usr, "Only humans can be augmented.") + admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [C]") + + + else if(href_list["purrbation"]) + if(!check_rights(R_SPAWN)) + return + var/mob/living/carbon/human/H = locate(href_list["purrbation"]) in GLOB.mob_list - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - if(!ishumanbasic(H)) - to_chat(usr, "This can only be done to the basic human species at the moment.") - return - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - var/success = purrbation_toggle(H) - if(success) - to_chat(usr, "Put [H] on purrbation.") - log_admin("[key_name(usr)] has put [key_name(H)] on purrbation.") - var/msg = "[key_name_admin(usr)] has put [key_name(H)] on purrbation." - message_admins(msg) - admin_ticket_log(H, msg) - - else - to_chat(usr, "Removed [H] from purrbation.") - log_admin("[key_name(usr)] has removed [key_name(H)] from purrbation.") - var/msg = "[key_name_admin(usr)] has removed [key_name(H)] from purrbation." - message_admins(msg) - admin_ticket_log(H, msg) - - else if(href_list["adjustDamage"] && href_list["mobToDamage"]) - if(!check_rights(0)) - return - + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + if(!ishumanbasic(H)) + to_chat(usr, "This can only be done to the basic human species at the moment.") + return + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + var/success = purrbation_toggle(H) + if(success) + to_chat(usr, "Put [H] on purrbation.") + log_admin("[key_name(usr)] has put [key_name(H)] on purrbation.") + var/msg = "[key_name_admin(usr)] has put [key_name(H)] on purrbation." + message_admins(msg) + admin_ticket_log(H, msg) + + else + to_chat(usr, "Removed [H] from purrbation.") + log_admin("[key_name(usr)] has removed [key_name(H)] from purrbation.") + var/msg = "[key_name_admin(usr)] has removed [key_name(H)] from purrbation." + message_admins(msg) + admin_ticket_log(H, msg) + + else if(href_list["adjustDamage"] && href_list["mobToDamage"]) + if(!check_rights(0)) + return + var/mob/living/L = locate(href_list["mobToDamage"]) in GLOB.mob_list - if(!istype(L)) - return - - var/Text = href_list["adjustDamage"] - - var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num - - if(!L) - to_chat(usr, "Mob doesn't exist anymore") - return - - switch(Text) - if("brute") - L.adjustBruteLoss(amount) - if("fire") - L.adjustFireLoss(amount) - if("toxin") - L.adjustToxLoss(amount) - if("oxygen") - L.adjustOxyLoss(amount) - if("brain") - L.adjustBrainLoss(amount) - if("clone") - L.adjustCloneLoss(amount) - if("stamina") - L.adjustStaminaLoss(amount) - else - to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") - return - - if(amount != 0) - log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L] ") - var/msg = "[key_name(usr)] dealt [amount] amount of [Text] damage to [L] " - message_admins(msg) - admin_ticket_log(L, msg) - href_list["datumrefresh"] = href_list["mobToDamage"] - + if(!istype(L)) + return + + var/Text = href_list["adjustDamage"] + + var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num + + if(!L) + to_chat(usr, "Mob doesn't exist anymore") + return + + switch(Text) + if("brute") + L.adjustBruteLoss(amount) + if("fire") + L.adjustFireLoss(amount) + if("toxin") + L.adjustToxLoss(amount) + if("oxygen") + L.adjustOxyLoss(amount) + if("brain") + L.adjustBrainLoss(amount) + if("clone") + L.adjustCloneLoss(amount) + if("stamina") + L.adjustStaminaLoss(amount) + else + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") + return + + if(amount != 0) + log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L] ") + var/msg = "[key_name(usr)] dealt [amount] amount of [Text] damage to [L] " + message_admins(msg) + admin_ticket_log(L, msg) + href_list["datumrefresh"] = href_list["mobToDamage"] + diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index 806f48a24f..c6958fa997 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -6,8 +6,14 @@ var/date /datum/getrev/New() - if(world.RunningService() && fexists(SERVICE_PR_TEST_JSON)) - testmerge = json_decode(file2text(SERVICE_PR_TEST_JSON)) + if(world.RunningService()) + var/file_name + if(ServiceVersion()) //will return null for versions < 3.0.91.0 + file_name = SERVICE_PR_TEST_JSON_OLD + else + file_name = SERVICE_PR_TEST_JSON + if(fexists(file_name)) + testmerge = json_decode(file2text(file_name)) #ifdef SERVERTOOLS else if(!world.RunningService() && fexists("../prtestjob.lk")) //tgs2 support var/list/tmp = world.file2list("..\\prtestjob.lk") diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 78a402a2dc..7df65438f2 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -311,7 +311,7 @@ traitor_mob.mind.store_memory("Radio Frequency: [format_frequency(R.traitor_frequency)] ([R.name]).") else if(uplink_loc == PDA) - PDA.lock_code = "[rand(100,999)] [pick("Alpha","Bravo","Charlie","Delta","Echo","Foxtrot","Golf","Hotel","India","Juliet","Kilo","Lima","Mike","November","Oscar","Papa","Quebec","Romeo","Sierra","Tango","Uniform","Victor","Whiskey","X-ray","Yankee","Zulu")]" + PDA.lock_code = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]" if(!silent) to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[PDA.lock_code]\" into the ringtone select to unlock its hidden features.") traitor_mob.mind.store_memory("Uplink Passcode: [PDA.lock_code] ([PDA.name]).") diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 77bcefeddf..c399f7c00d 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -304,6 +304,7 @@ /atom/proc/ex_act(severity, target) set waitfor = FALSE contents_explosion(severity, target) + SendSignal(COMSIG_ATOM_EX_ACT, severity, target) /atom/proc/blob_act(obj/structure/blob/B) return @@ -468,8 +469,8 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) /atom/proc/singularity_act() return -/atom/proc/singularity_pull() - return +/atom/proc/singularity_pull(obj/singularity/S, current_size) + SendSignal(COMSIG_ATOM_SING_PULL, S, current_size) /atom/proc/acid_act(acidpwr, acid_volume) return @@ -613,10 +614,10 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) . += "---" var/turf/curturf = get_turf(src) if (curturf) - .["Jump to"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" - .["Add reagent"] = "?_src_=vars;addreagent=\ref[src]" - .["Trigger EM pulse"] = "?_src_=vars;emp=\ref[src]" - .["Trigger explosion"] = "?_src_=vars;explode=\ref[src]" + .["Jump to"] = "?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" + .["Add reagent"] = "?_src_=vars;[HrefToken()];addreagent=\ref[src]" + .["Trigger EM pulse"] = "?_src_=vars;[HrefToken()];emp=\ref[src]" + .["Trigger explosion"] = "?_src_=vars;[HrefToken()];explode=\ref[src]" /atom/proc/drop_location() var/atom/L = loc diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index f4cbcda098..cee9fc6262 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -215,7 +215,7 @@ //to differentiate it, naturally everyone forgot about this immediately and so some things //would bump twice, so now it's called Collide /atom/movable/proc/Collide(atom/A) - if((A)) + if(A) if(throwing) throwing.hit_atom(A) . = 1 @@ -497,7 +497,7 @@ /atom/movable/vv_get_dropdown() . = ..() . -= "Jump to" - .["Follow"] = "?_src_=holder;adminplayerobservefollow=\ref[src]" + .["Follow"] = "?_src_=holder;[HrefToken()];adminplayerobservefollow=\ref[src]" /atom/movable/proc/ex_check(ex_id) if(!ex_id) diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm index 389888ac28..6baf8b4dbe 100644 --- a/code/game/gamemodes/blob/powers.dm +++ b/code/game/gamemodes/blob/powers.dm @@ -363,7 +363,7 @@ to_chat(src, "Node Blobs are blobs which grow, like the core. Like the core it can activate resource and factory blobs.") to_chat(src, "In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.") to_chat(src, "Shortcuts: Click = Expand Blob | Middle Mouse Click = Rally Spores | Ctrl Click = Create Shield Blob | Alt Click = Remove Blob") - to_chat(src, "Attempting to talk will send a message to all other GLOB.overminds, allowing you to coordinate with them.") + to_chat(src, "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them.") if(!placed && autoplace_max_time <= world.time) to_chat(src, "You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.") to_chat(src, "You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen.") diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm index c2511f2302..8443f3d6f1 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -90,8 +90,10 @@ /obj/structure/blob/proc/Life() return -/obj/structure/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2) - src.Be_Pulsed() +/obj/structure/blob/proc/Pulse_Area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2) + if(QDELETED(pulsing_overmind)) + pulsing_overmind = overmind + Be_Pulsed() var/expanded = FALSE if(prob(70) && expand()) expanded = TRUE diff --git a/code/game/gamemodes/clock_cult/clock_structures/prolonging_prism.dm b/code/game/gamemodes/clock_cult/clock_structures/prolonging_prism.dm index cf730189e3..9ef7fa4ab7 100644 --- a/code/game/gamemodes/clock_cult/clock_structures/prolonging_prism.dm +++ b/code/game/gamemodes/clock_cult/clock_structures/prolonging_prism.dm @@ -26,8 +26,8 @@ to_chat(user, "An emergency shuttle has arrived and this prism is no longer useful; attempt to activate it to gain a partial refund of components used.") else var/efficiency = get_efficiency_mod(TRUE) - to_chat(user, "It requires at least [get_delay_cost()]W of power to attempt to delay the arrival of an emergency shuttle by [2 * efficiency] minutes.") - to_chat(user, "This cost increases by [delay_cost_increase]W for every previous activation.") + to_chat(user, "It requires at least [DisplayPower(get_delay_cost())] of power to attempt to delay the arrival of an emergency shuttle by [2 * efficiency] minutes.") + to_chat(user, "This cost increases by [DisplayPower(delay_cost_increase)] for every previous activation.") /obj/structure/destructible/clockwork/powered/prolonging_prism/forced_disable(bad_effects) if(active) @@ -126,7 +126,7 @@ if(!hex_combo) hex_combo = mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER) else - hex_combo.overlays += mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER) + hex_combo.add_overlay(mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER)) if(hex_combo) //YOU BUILT A HEXAGON hex_combo.pixel_x = -16 hex_combo.pixel_y = -16 diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 24d5311af3..8b16b2b0d5 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -309,8 +309,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( owner_AI.nuking = TRUE owner_AI.doomsday_device = DOOM owner_AI.doomsday_device.start() - for(var/pinpointer in GLOB.pinpointer_list) - var/obj/item/pinpointer/P = pinpointer + for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list) P.switch_mode_to(TRACK_MALF_AI) //Pinpointers start tracking the AI wherever it goes qdel(src) diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm.rej b/code/game/gamemodes/malfunction/Malf_Modules.dm.rej new file mode 100644 index 0000000000..97dbe2bbdb --- /dev/null +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm.rej @@ -0,0 +1,11 @@ +diff a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm (rejected hunks) +@@ -309,8 +309,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( + owner_AI.nuking = TRUE + owner_AI.doomsday_device = DOOM + owner_AI.doomsday_device.start() +- for(var/pinpointer in GLOB.pinpointer_list) +- var/obj/item/weapon/pinpointer/P = pinpointer ++ for(var/obj/item/weapon/pinpointer/nuke/P in GLOB.pinpointer_list) + P.switch_mode_to(TRACK_MALF_AI) //Pinpointers start tracking the AI wherever it goes + qdel(src) + diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm index 58ca8bbea9..182b7e2756 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm @@ -12,6 +12,8 @@ var/list/abductee_minds var/flash = " - || - " var/obj/machinery/abductor/console/console + var/message_cooldown = 0 + var/breakout_time = 0.75 /obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user) if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) @@ -40,25 +42,23 @@ /obj/machinery/abductor/experiment/relaymove(mob/user) if(user.stat != CONSCIOUS) return - container_resist(user) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") /obj/machinery/abductor/experiment/container_resist(mob/living/user) - var/breakout_time = 600 user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT - to_chat(user, "You lean on the back of [src] and start pushing the door open... (this will take about a minute.)") - user.visible_message("You hear a metallic creaking from [src]!") - - if(do_after(user,(breakout_time), target = src)) + user.visible_message("You see [user] kicking against the door of [src]!", \ + "You lean on the back of [src] and start pushing the door open... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear a metallic creaking from [src].") + if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) return - - visible_message("[user] successfully broke out of [src]!") - to_chat(user, "You successfully break out of [src]!") - + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") open_machine() - /obj/machinery/abductor/experiment/proc/dissection_icon(mob/living/carbon/human/H) var/icon/photo = null var/g = (H.gender == FEMALE) ? "f" : "m" diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 59650561a6..1b1bec68c1 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -325,7 +325,7 @@ gloves = /obj/item/clothing/gloves/combat back = /obj/item/storage/backpack ears = /obj/item/device/radio/headset/syndicate/alt - l_pocket = /obj/item/pinpointer/syndicate + l_pocket = /obj/item/pinpointer/nuke/syndicate id = /obj/item/card/id/syndicate belt = /obj/item/gun/ballistic/automatic/pistol backpack_contents = list(/obj/item/storage/box/syndie=1) diff --git a/code/game/gamemodes/nuclear/nuclear.dm.rej b/code/game/gamemodes/nuclear/nuclear.dm.rej new file mode 100644 index 0000000000..19648547c7 --- /dev/null +++ b/code/game/gamemodes/nuclear/nuclear.dm.rej @@ -0,0 +1,10 @@ +diff a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm (rejected hunks) +@@ -325,7 +325,7 @@ + gloves = /obj/item/clothing/gloves/combat + back = /obj/item/weapon/storage/backpack + ears = /obj/item/device/radio/headset/syndicate/alt +- l_pocket = /obj/item/weapon/pinpointer/syndicate ++ l_pocket = /obj/item/weapon/pinpointer/nuke/syndicate + id = /obj/item/weapon/card/id/syndicate + belt = /obj/item/weapon/gun/ballistic/automatic/pistol + backpack_contents = list(/obj/item/weapon/storage/box/syndie=1) diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index ee611349f8..95fc735ec6 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -362,9 +362,9 @@ if(safety) if(timing) set_security_level(previous_level) - for(var/obj/item/pinpointer/syndicate/S in GLOB.pinpointer_list) + for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) S.switch_mode_to(initial(S.mode)) - S.nuke_warning = FALSE + S.alert = FALSE timing = FALSE bomb_set = TRUE detonation_timer = null @@ -381,16 +381,16 @@ bomb_set = TRUE set_security_level("delta") detonation_timer = world.time + (timer_set * 10) - for(var/obj/item/pinpointer/syndicate/S in GLOB.pinpointer_list) + for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) S.switch_mode_to(TRACK_INFILTRATOR) countdown.start() else bomb_set = FALSE detonation_timer = null set_security_level(previous_level) - for(var/obj/item/pinpointer/syndicate/S in GLOB.pinpointer_list) + for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) S.switch_mode_to(initial(S.mode)) - S.nuke_warning = FALSE + S.alert = FALSE countdown.stop() update_icon() diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm.rej b/code/game/gamemodes/nuclear/nuclearbomb.dm.rej new file mode 100644 index 0000000000..6a35e91cde --- /dev/null +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm.rej @@ -0,0 +1,33 @@ +diff a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm (rejected hunks) +@@ -362,9 +362,9 @@ + if(safety) + if(timing) + set_security_level(previous_level) +- for(var/obj/item/weapon/pinpointer/syndicate/S in GLOB.pinpointer_list) ++ for(var/obj/item/weapon/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) + S.switch_mode_to(initial(S.mode)) +- S.nuke_warning = FALSE ++ S.alert = FALSE + timing = FALSE + bomb_set = TRUE + detonation_timer = null +@@ -381,16 +381,16 @@ + bomb_set = TRUE + set_security_level("delta") + detonation_timer = world.time + (timer_set * 10) +- for(var/obj/item/weapon/pinpointer/syndicate/S in GLOB.pinpointer_list) ++ for(var/obj/item/weapon/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) + S.switch_mode_to(TRACK_INFILTRATOR) + countdown.start() + else + bomb_set = FALSE + detonation_timer = null + set_security_level(previous_level) +- for(var/obj/item/weapon/pinpointer/syndicate/S in GLOB.pinpointer_list) ++ for(var/obj/item/weapon/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) + S.switch_mode_to(initial(S.mode)) +- S.nuke_warning = FALSE ++ S.alert = FALSE + countdown.stop() + update_icon() + diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 9f63309643..461ef78ef3 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,56 +1,7 @@ -//Pinpointers are used to track atoms from a distance as long as they're on the same z-level. The captain and nuke ops have ones that track the nuclear authentication disk. -/obj/item/pinpointer - name = "pinpointer" - desc = "A handheld tracking device that locks onto certain signals." - icon = 'icons/obj/device.dmi' - icon_state = "pinoff" - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL = 500, MAT_GLASS = 250) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/active = FALSE - var/atom/movable/target = null //The thing we're searching for - var/atom/movable/constant_target = null //The thing we're always focused on, if we're in the right mode - var/target_x = 0 //The target coordinates if we're tracking those - var/target_y = 0 - var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination - var/nuke_warning = FALSE // If we've set off a miniature alarm about an armed nuke - var/mode = TRACK_NUKE_DISK //What are we looking for? +/obj/item/pinpointer/nuke + var/mode = TRACK_NUKE_DISK -/obj/item/pinpointer/New() - ..() - GLOB.pinpointer_list += src - -/obj/item/pinpointer/Destroy() - STOP_PROCESSING(SSfastprocess, src) - GLOB.pinpointer_list -= src - return ..() - -/obj/item/pinpointer/attack_self(mob/living/user) - active = !active - user.visible_message("[user] [active ? "" : "de"]activates their pinpointer.", "You [active ? "" : "de"]activate your pinpointer.") - playsound(user, 'sound/items/screwdriver2.ogg', 50, 1) - icon_state = "pin[active ? "onnull" : "off"]" - if(active) - START_PROCESSING(SSfastprocess, src) - else - target = null //Restarting the pinpointer forces a target reset - STOP_PROCESSING(SSfastprocess, src) - -/obj/item/pinpointer/attackby(obj/item/I, mob/living/user, params) - if(mode != TRACK_ATOM) - return ..() - user.visible_message("[user] tunes [src] to [I].", "You fine-tune [src]'s tracking to track [I].") - playsound(src, 'sound/machines/click.ogg', 50, 1) - constant_target = I - -/obj/item/pinpointer/examine(mob/user) +/obj/item/pinpointer/nuke/examine(mob/user) ..() var/msg = "Its tracking indicator reads " switch(mode) @@ -60,12 +11,6 @@ msg += "\"01000001 01001001\"." if(TRACK_INFILTRATOR) msg += "\"vasvygengbefuvc\"." - if(TRACK_OPERATIVES) - msg += "\"[target ? "Operative [target]" : "friends"]\"." - if(TRACK_ATOM) - msg += "\"[initial(constant_target.name)]\"." - if(TRACK_COORDINATES) - msg += "\"([target_x], [target_y])\"." else msg = "Its tracking indicator is blank." to_chat(user, msg) @@ -73,22 +18,20 @@ if(bomb.timing) to_chat(user, "Extreme danger. Arming signal detected. Time remaining: [bomb.get_time_left()]") -/obj/item/pinpointer/process() - if(!active) - STOP_PROCESSING(SSfastprocess, src) - return - scan_for_target() - point_to_target() - my_god_jc_a_bomb() - addtimer(CALLBACK(src, .proc/refresh_target), 50, TIMER_UNIQUE) +/obj/item/pinpointer/nuke/process() + ..() + if(active) // If shit's going down + for(var/obj/machinery/nuclearbomb/bomb in GLOB.nuke_list) + if(bomb.timing) + if(!alert) + alert = TRUE + playsound(src, 'sound/items/nuke_toy_lowpower.ogg', 50, 0) + if(isliving(loc)) + var/mob/living/L = loc + to_chat(L, "Your [name] vibrates and lets out a tinny alarm. Uh oh.") -/obj/item/pinpointer/proc/scan_for_target() //Looks for whatever it's tracking - if(target) - if(isliving(target)) - var/mob/living/L = target - if(L.stat == DEAD) - target = null - return +/obj/item/pinpointer/nuke/scan_for_target() + target = null switch(mode) if(TRACK_NUKE_DISK) var/obj/item/disk/nuclear/N = locate() in GLOB.poi_list @@ -104,76 +47,36 @@ target = A if(TRACK_INFILTRATOR) target = SSshuttle.getShuttle("syndicate") - if(TRACK_OPERATIVES) - var/list/possible_targets = list() - var/turf/here = get_turf(src) - for(var/V in SSticker.mode.syndicates) - var/datum/mind/M = V - if(M.current && M.current.stat != DEAD) - possible_targets |= M.current - var/mob/living/closest_operative = get_closest_atom(/mob/living/carbon/human, possible_targets, here) - if(closest_operative) - target = closest_operative - if(TRACK_ATOM) - if(constant_target) - target = constant_target - if(TRACK_COORDINATES) - var/turf/T = get_turf(src) - target = locate(target_x, target_y, T.z) + ..() -/obj/item/pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction - if(!active) - return - if(!target || (mode == TRACK_ATOM && !constant_target)) - icon_state = "pinon[nuke_warning ? "alert" : ""]null" - return - var/turf/here = get_turf(src) - var/turf/there = get_turf(target) - if(here.z != there.z) - icon_state = "pinon[nuke_warning ? "alert" : ""]null" - return - if(get_dist_euclidian(here,there)<=minimum_range) - icon_state = "pinon[nuke_warning ? "alert" : ""]direct" - else - setDir(get_dir(here, there)) - switch(get_dist(here, there)) - if(1 to 8) - icon_state = "pinon[nuke_warning ? "alert" : "close"]" - if(9 to 16) - icon_state = "pinon[nuke_warning ? "alert" : "medium"]" - if(16 to INFINITY) - icon_state = "pinon[nuke_warning ? "alert" : "far"]" - -/obj/item/pinpointer/proc/my_god_jc_a_bomb() //If we should get the hell back to the ship - for(var/obj/machinery/nuclearbomb/bomb in GLOB.nuke_list) - if(bomb.timing) - if(!nuke_warning) - nuke_warning = TRUE - playsound(src, 'sound/items/nuke_toy_lowpower.ogg', 50, 0) - if(isliving(loc)) - var/mob/living/L = loc - to_chat(L, "Your [name] vibrates and lets out a tinny alarm. Uh oh.") - -/obj/item/pinpointer/proc/switch_mode_to(new_mode) //If we shouldn't be tracking what we are +/obj/item/pinpointer/nuke/proc/switch_mode_to(new_mode) if(isliving(loc)) var/mob/living/L = loc to_chat(L, "Your [name] beeps as it reconfigures its tracking algorithms.") playsound(L, 'sound/machines/triple_beep.ogg', 50, 1) mode = new_mode - target = null //Switch modes so we can find the new target + scan_for_target() -/obj/item/pinpointer/proc/refresh_target() //Periodically removes the target to allow the pinpointer to update (i.e. malf AI shunts, an operative dies) - target = null - -/obj/item/pinpointer/syndicate //Syndicate pinpointers automatically point towards the infiltrator once the nuke is active. +/obj/item/pinpointer/nuke/syndicate // Syndicate pinpointers automatically point towards the infiltrator once the nuke is active. name = "syndicate pinpointer" desc = "A handheld tracking device that locks onto certain signals. It's configured to switch tracking modes once it detects the activation signal of a nuclear device." + icon_state = "pinpointer_syndicate" -/obj/item/pinpointer/syndicate/cyborg //Cyborg pinpointers just look for a random operative. +/obj/item/pinpointer/syndicate_cyborg // Cyborg pinpointers just look for a random operative. name = "cyborg syndicate pinpointer" desc = "An integrated tracking device, jury-rigged to search for living Syndicate operatives." - mode = TRACK_OPERATIVES flags_1 = NODROP_1 - +/obj/item/pinpointer/syndicate_cyborg/scan_for_target() + target = null + var/list/possible_targets = list() + var/turf/here = get_turf(src) + for(var/V in SSticker.mode.syndicates) + var/datum/mind/M = V + if(M.current && M.current.stat != DEAD) + possible_targets |= M.current + var/mob/living/closest_operative = get_closest_atom(/mob/living/carbon/human, possible_targets, here) + if(closest_operative) + target = closest_operative + ..() diff --git a/code/game/gamemodes/nuclear/pinpointer.dm.rej b/code/game/gamemodes/nuclear/pinpointer.dm.rej new file mode 100644 index 0000000000..79d652af9b --- /dev/null +++ b/code/game/gamemodes/nuclear/pinpointer.dm.rej @@ -0,0 +1,21 @@ +diff a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm (rejected hunks) +@@ -30,7 +30,6 @@ + var/mob/living/L = loc + to_chat(L, "Your [name] vibrates and lets out a tinny alarm. Uh oh.") + +- + /obj/item/pinpointer/nuke/scan_for_target() + target = null + switch(mode) +@@ -58,10 +57,10 @@ + mode = new_mode + scan_for_target() + +- + /obj/item/pinpointer/nuke/syndicate // Syndicate pinpointers automatically point towards the infiltrator once the nuke is active. + name = "syndicate pinpointer" + desc = "A handheld tracking device that locks onto certain signals. It's configured to switch tracking modes once it detects the activation signal of a nuclear device." ++ icon_state = "pinpointer_syndicate" + + /obj/item/pinpointer/syndicate_cyborg // Cyborg pinpointers just look for a random operative. + name = "cyborg syndicate pinpointer" diff --git a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm index ba0f727203..c0aee8bb76 100644 --- a/code/game/gamemodes/objective_items.dm +++ b/code/game/gamemodes/objective_items.dm @@ -186,7 +186,7 @@ return ..() //Old ninja objectives. -/datum/objective_item/special/pinpointer +/datum/objective_item/special/pinpointer/nuke name = "the captain's pinpointer." targetitem = /obj/item/pinpointer difficulty = 10 diff --git a/code/game/gamemodes/objective_items.dm.rej b/code/game/gamemodes/objective_items.dm.rej new file mode 100644 index 0000000000..3770d7eea9 --- /dev/null +++ b/code/game/gamemodes/objective_items.dm.rej @@ -0,0 +1,10 @@ +diff a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm (rejected hunks) +@@ -158,7 +158,7 @@ + difficulty = 10 + + //Old ninja objectives. +-/datum/objective_item/special/pinpointer ++/datum/objective_item/special/pinpointer/nuke + name = "the captain's pinpointer." + targetitem = /obj/item/pinpointer + difficulty = 10 diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 7ea954a671..2276b5154b 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -392,6 +392,9 @@ QDEL_IN(mob_occupant, 40) /obj/machinery/clonepod/relaymove(mob/user) + container_resist() + +/obj/machinery/clonepod/container_resist(mob/living/user) if(user.stat == CONSCIOUS) go_out() diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm index 65a6088721..4085b4edb4 100644 --- a/code/game/machinery/dna_scanner.dm +++ b/code/game/machinery/dna_scanner.dm @@ -14,6 +14,8 @@ var/damage_coeff var/scan_level var/precision_coeff + var/message_cooldown + var/breakout_time = 2 /obj/machinery/dna_scannernew/RefreshParts() scan_level = 0 @@ -65,23 +67,20 @@ open_machine() /obj/machinery/dna_scannernew/container_resist(mob/living/user) - var/breakout_time = 2 - if(state_open || !locked) //Open and unlocked, no need to escape - state_open = TRUE + if(!locked) + open_machine() return user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT - to_chat(user, "You lean on the back of [src] and start pushing the door open... (this will take about [breakout_time] minutes.)") - user.visible_message("You hear a metallic creaking from [src]!") - + user.visible_message("You see [user] kicking against the door of [src]!", \ + "You lean on the back of [src] and start pushing the door open... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear a metallic creaking from [src].") if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked) return - locked = FALSE - visible_message("[user] successfully broke out of [src]!") - to_chat(user, "You successfully break out of [src]!") - + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") open_machine() /obj/machinery/dna_scannernew/proc/locate_computer(type_) @@ -122,10 +121,11 @@ /obj/machinery/dna_scannernew/relaymove(mob/user as mob) if(user.stat || locked) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") return - open_machine() - return /obj/machinery/dna_scannernew/attackby(obj/item/I, mob/user, params) diff --git a/code/game/machinery/gulag_teleporter.dm b/code/game/machinery/gulag_teleporter.dm index b37c5a10a7..d77e91af90 100644 --- a/code/game/machinery/gulag_teleporter.dm +++ b/code/game/machinery/gulag_teleporter.dm @@ -19,6 +19,8 @@ The console is located at computer/gulag_teleporter.dm active_power_usage = 5000 circuit = /obj/item/circuitboard/machine/gulag_teleporter var/locked = FALSE + var/message_cooldown + var/breakout_time = 1 var/jumpsuit_type = /obj/item/clothing/under/rank/prisoner var/shoes_type = /obj/item/clothing/shoes/sneakers/orange var/obj/machinery/gulag_item_reclaimer/linked_reclaimer @@ -46,7 +48,7 @@ The console is located at computer/gulag_teleporter.dm /obj/machinery/gulag_teleporter/interact(mob/user) if(locked) - to_chat(user, "[src] is locked.") + to_chat(user, "[src] is locked!") return toggle_open() @@ -89,28 +91,27 @@ The console is located at computer/gulag_teleporter.dm if(user.stat != CONSCIOUS) return if(locked) - to_chat(user, "[src] is locked!") + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") return open_machine() /obj/machinery/gulag_teleporter/container_resist(mob/living/user) - var/breakout_time = 600 if(!locked) open_machine() return user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT - to_chat(user, "You lean on the back of [src] and start pushing the door open... (this will take about a minute.)") - user.visible_message("You hear a metallic creaking from [src]!") - - if(do_after(user,(breakout_time), target = src)) + user.visible_message("You see [user] kicking against the door of [src]!", \ + "You lean on the back of [src] and start pushing the door open... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear a metallic creaking from [src].") + if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked) return - locked = FALSE - visible_message("[user] successfully broke out of [src]!") - to_chat(user, "You successfully break out of [src]!") - + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") open_machine() /obj/machinery/gulag_teleporter/proc/locate_reclaimer() diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index cd2a5bd3b5..a78fa50a6a 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -1,379 +1,406 @@ -// SUIT STORAGE UNIT ///////////////// -/obj/machinery/suit_storage_unit - name = "suit storage unit" - desc = "An industrial unit made to hold space suits. It comes with a built-in UV cauterization mechanism. A small warning label advises that organic matter should not be placed into the unit." - icon = 'icons/obj/suitstorage.dmi' - icon_state = "close" +// SUIT STORAGE UNIT ///////////////// +/obj/machinery/suit_storage_unit + name = "suit storage unit" + desc = "An industrial unit made to hold space suits. It comes with a built-in UV cauterization mechanism. A small warning label advises that organic matter should not be placed into the unit." + icon = 'icons/obj/suitstorage.dmi' + icon_state = "close" anchored = TRUE density = TRUE - max_integrity = 250 - - var/obj/item/clothing/suit/space/suit = null - var/obj/item/clothing/head/helmet/space/helmet = null - var/obj/item/clothing/mask/mask = null - var/obj/item/storage = null - - var/suit_type = null - var/helmet_type = null - var/mask_type = null - var/storage_type = null - - state_open = FALSE - var/locked = FALSE - panel_open = FALSE - var/safeties = TRUE - - var/uv = FALSE - var/uv_super = FALSE - var/uv_cycles = 6 - -/obj/machinery/suit_storage_unit/standard_unit - suit_type = /obj/item/clothing/suit/space/eva - helmet_type = /obj/item/clothing/head/helmet/space/eva - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/captain - suit_type = /obj/item/clothing/suit/space/hardsuit/captain - mask_type = /obj/item/clothing/mask/gas/sechailer - storage_type = /obj/item/tank/jetpack/oxygen/captain - -/obj/machinery/suit_storage_unit/engine - suit_type = /obj/item/clothing/suit/space/hardsuit/engine - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/ce - suit_type = /obj/item/clothing/suit/space/hardsuit/engine/elite - mask_type = /obj/item/clothing/mask/breath - storage_type= /obj/item/clothing/shoes/magboots/advance - -/obj/machinery/suit_storage_unit/security - suit_type = /obj/item/clothing/suit/space/hardsuit/security - mask_type = /obj/item/clothing/mask/gas/sechailer - -/obj/machinery/suit_storage_unit/hos - suit_type = /obj/item/clothing/suit/space/hardsuit/security/hos - mask_type = /obj/item/clothing/mask/gas/sechailer - storage_type = /obj/item/tank/internals/oxygen - -/obj/machinery/suit_storage_unit/atmos - suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos - mask_type = /obj/item/clothing/mask/gas - storage_type = /obj/item/watertank/atmos - -/obj/machinery/suit_storage_unit/mining - suit_type = /obj/item/clothing/suit/hooded/explorer - mask_type = /obj/item/clothing/mask/gas/explorer - -/obj/machinery/suit_storage_unit/mining/eva - suit_type = /obj/item/clothing/suit/space/hardsuit/mining - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/cmo - suit_type = /obj/item/clothing/suit/space/hardsuit/medical - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/rd - suit_type = /obj/item/clothing/suit/space/hardsuit/rd - mask_type = /obj/item/clothing/mask/breath - -/obj/machinery/suit_storage_unit/syndicate - suit_type = /obj/item/clothing/suit/space/hardsuit/syndi - mask_type = /obj/item/clothing/mask/gas/syndicate - storage_type = /obj/item/tank/jetpack/oxygen/harness - -/obj/machinery/suit_storage_unit/ert/command - suit_type = /obj/item/clothing/suit/space/hardsuit/ert - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - -/obj/machinery/suit_storage_unit/ert/security - suit_type = /obj/item/clothing/suit/space/hardsuit/ert/sec - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - -/obj/machinery/suit_storage_unit/ert/engineer - suit_type = /obj/item/clothing/suit/space/hardsuit/ert/engi - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - -/obj/machinery/suit_storage_unit/ert/medical - suit_type = /obj/item/clothing/suit/space/hardsuit/ert/med - mask_type = /obj/item/clothing/mask/breath - storage_type = /obj/item/tank/internals/emergency_oxygen/double - + max_integrity = 250 + + var/obj/item/clothing/suit/space/suit = null + var/obj/item/clothing/head/helmet/space/helmet = null + var/obj/item/clothing/mask/mask = null + var/obj/item/storage = null + + var/suit_type = null + var/helmet_type = null + var/mask_type = null + var/storage_type = null + + state_open = FALSE + var/locked = FALSE + panel_open = FALSE + var/safeties = TRUE + + var/uv = FALSE + var/uv_super = FALSE + var/uv_cycles = 6 + var/message_cooldown + var/breakout_time = 0.5 + +/obj/machinery/suit_storage_unit/standard_unit + suit_type = /obj/item/clothing/suit/space/eva + helmet_type = /obj/item/clothing/head/helmet/space/eva + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/captain + suit_type = /obj/item/clothing/suit/space/hardsuit/captain + mask_type = /obj/item/clothing/mask/gas/sechailer + storage_type = /obj/item/tank/jetpack/oxygen/captain + +/obj/machinery/suit_storage_unit/engine + suit_type = /obj/item/clothing/suit/space/hardsuit/engine + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/ce + suit_type = /obj/item/clothing/suit/space/hardsuit/engine/elite + mask_type = /obj/item/clothing/mask/breath + storage_type= /obj/item/clothing/shoes/magboots/advance + +/obj/machinery/suit_storage_unit/security + suit_type = /obj/item/clothing/suit/space/hardsuit/security + mask_type = /obj/item/clothing/mask/gas/sechailer + +/obj/machinery/suit_storage_unit/hos + suit_type = /obj/item/clothing/suit/space/hardsuit/security/hos + mask_type = /obj/item/clothing/mask/gas/sechailer + storage_type = /obj/item/tank/internals/oxygen + +/obj/machinery/suit_storage_unit/atmos + suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos + mask_type = /obj/item/clothing/mask/gas + storage_type = /obj/item/watertank/atmos + +/obj/machinery/suit_storage_unit/mining + suit_type = /obj/item/clothing/suit/hooded/explorer + mask_type = /obj/item/clothing/mask/gas/explorer + +/obj/machinery/suit_storage_unit/mining/eva + suit_type = /obj/item/clothing/suit/space/hardsuit/mining + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/cmo + suit_type = /obj/item/clothing/suit/space/hardsuit/medical + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/rd + suit_type = /obj/item/clothing/suit/space/hardsuit/rd + mask_type = /obj/item/clothing/mask/breath + +/obj/machinery/suit_storage_unit/syndicate + suit_type = /obj/item/clothing/suit/space/hardsuit/syndi + mask_type = /obj/item/clothing/mask/gas/syndicate + storage_type = /obj/item/tank/jetpack/oxygen/harness + +/obj/machinery/suit_storage_unit/ert/command + suit_type = /obj/item/clothing/suit/space/hardsuit/ert + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + +/obj/machinery/suit_storage_unit/ert/security + suit_type = /obj/item/clothing/suit/space/hardsuit/ert/sec + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + +/obj/machinery/suit_storage_unit/ert/engineer + suit_type = /obj/item/clothing/suit/space/hardsuit/ert/engi + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + +/obj/machinery/suit_storage_unit/ert/medical + suit_type = /obj/item/clothing/suit/space/hardsuit/ert/med + mask_type = /obj/item/clothing/mask/breath + storage_type = /obj/item/tank/internals/emergency_oxygen/double + /obj/machinery/suit_storage_unit/Initialize() . = ..() - wires = new /datum/wires/suit_storage_unit(src) - if(suit_type) - suit = new suit_type(src) - if(helmet_type) - helmet = new helmet_type(src) - if(mask_type) - mask = new mask_type(src) - if(storage_type) - storage = new storage_type(src) - update_icon() - -/obj/machinery/suit_storage_unit/Destroy() + wires = new /datum/wires/suit_storage_unit(src) + if(suit_type) + suit = new suit_type(src) + if(helmet_type) + helmet = new helmet_type(src) + if(mask_type) + mask = new mask_type(src) + if(storage_type) + storage = new storage_type(src) + update_icon() + +/obj/machinery/suit_storage_unit/Destroy() QDEL_NULL(suit) QDEL_NULL(helmet) QDEL_NULL(mask) QDEL_NULL(storage) - return ..() - -/obj/machinery/suit_storage_unit/update_icon() - cut_overlays() - - if(uv) - if(uv_super) - add_overlay("super") - else if(occupant) - add_overlay("uvhuman") - else - add_overlay("uv") - else if(state_open) - if(stat & BROKEN) - add_overlay("broken") - else - add_overlay("open") - if(suit) - add_overlay("suit") - if(helmet) - add_overlay("helm") - if(storage) - add_overlay("storage") - else if(occupant) - add_overlay("human") - -/obj/machinery/suit_storage_unit/power_change() - ..() - if(!is_operational() && state_open) - open_machine() - dump_contents() - update_icon() - -/obj/machinery/suit_storage_unit/proc/dump_contents() - dropContents() - helmet = null - suit = null - mask = null - storage = null - occupant = null - -/obj/machinery/suit_storage_unit/deconstruct(disassembled = TRUE) + return ..() + +/obj/machinery/suit_storage_unit/update_icon() + cut_overlays() + + if(uv) + if(uv_super) + add_overlay("super") + else if(occupant) + add_overlay("uvhuman") + else + add_overlay("uv") + else if(state_open) + if(stat & BROKEN) + add_overlay("broken") + else + add_overlay("open") + if(suit) + add_overlay("suit") + if(helmet) + add_overlay("helm") + if(storage) + add_overlay("storage") + else if(occupant) + add_overlay("human") + +/obj/machinery/suit_storage_unit/power_change() + ..() + if(!is_operational() && state_open) + open_machine() + dump_contents() + update_icon() + +/obj/machinery/suit_storage_unit/proc/dump_contents() + dropContents() + helmet = null + suit = null + mask = null + storage = null + occupant = null + +/obj/machinery/suit_storage_unit/deconstruct(disassembled = TRUE) if(!(flags_1 & NODECONSTRUCT_1)) - open_machine() - dump_contents() - new /obj/item/stack/sheet/metal (loc, 2) - qdel(src) - -/obj/machinery/suit_storage_unit/MouseDrop_T(atom/A, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !Adjacent(A) || !isliving(A)) - return - var/mob/living/target = A - if(!state_open) - to_chat(user, "The unit's doors are shut!") - return - if(!is_operational()) - to_chat(user, "The unit is not operational!") - return - if(occupant || helmet || suit || storage) - to_chat(user, "It's too cluttered inside to fit in!") - return - - if(target == user) - user.visible_message("[user] starts squeezing into [src]!", "You start working your way into [src]...") - else - target.visible_message("[user] starts shoving [target] into [src]!", "[user] starts shoving you into [src]!") - - if(do_mob(user, target, 30)) - if(occupant || helmet || suit || storage) - return - if(target == user) - user.visible_message("[user] slips into [src] and closes the door behind them!", "You slip into [src]'s cramped space and shut its door.") - else - target.visible_message("[user] pushes [target] into [src] and shuts its door!", "[user] shoves you into [src] and shuts the door!") - close_machine(target) - add_fingerprint(user) - -/obj/machinery/suit_storage_unit/proc/cook() - if(uv_cycles) - uv_cycles-- - uv = TRUE - locked = TRUE - update_icon() - if(occupant) - var/mob/living/mob_occupant = occupant - if(uv_super) - mob_occupant.adjustFireLoss(rand(20, 36)) - else - mob_occupant.adjustFireLoss(rand(10, 16)) - mob_occupant.emote("scream") - addtimer(CALLBACK(src, .proc/cook), 50) - else - uv_cycles = initial(uv_cycles) - uv = FALSE - locked = FALSE - if(uv_super) - visible_message("[src]'s door creaks open with a loud whining noise. A cloud of foul black smoke escapes from its chamber.") - playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50, 1) - helmet = null - qdel(helmet) - suit = null - qdel(suit) // Delete everything but the occupant. - mask = null - qdel(mask) - storage = null - qdel(storage) - // The wires get damaged too. - wires.cut_all() - else - if(!occupant) - visible_message("[src]'s door slides open. The glowing yellow lights dim to a gentle green.") - else - visible_message("[src]'s door slides open, barraging you with the nauseating smell of charred flesh.") - playsound(src, 'sound/machines/airlockclose.ogg', 25, 1) - for(var/obj/item/I in src) //Scorches away blood and forensic evidence, although the SSU itself is unaffected - I.clean_blood() - I.fingerprints = list() - open_machine(FALSE) - if(occupant) - dump_contents() - -/obj/machinery/suit_storage_unit/proc/shock(mob/user, prb) - if(!prob(prb)) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() - if(electrocute_mob(user, src, src, 1, TRUE)) - return 1 - -/obj/machinery/suit_storage_unit/relaymove(mob/user) - container_resist(user) - -/obj/machinery/suit_storage_unit/container_resist(mob/living/user) - add_fingerprint(user) - if(locked) - visible_message("You see [user] kicking against the doors of [src]!", "You start kicking against the doors...") - addtimer(CALLBACK(src, .proc/resist_open, user), 300) - else - open_machine() - dump_contents() - -/obj/machinery/suit_storage_unit/proc/resist_open(mob/user) - if(!state_open && occupant && (user in src) && user.stat == 0) // Check they're still here. - visible_message("You see [user] bursts out of [src]!", "You escape the cramped confines of [src]!") - open_machine() - -/obj/machinery/suit_storage_unit/attackby(obj/item/I, mob/user, params) - if(state_open && is_operational()) - if(istype(I, /obj/item/clothing/suit/space)) - if(suit) - to_chat(user, "The unit already contains a suit!.") - return - if(!user.drop_item()) - return - suit = I - else if(istype(I, /obj/item/clothing/head/helmet)) - if(helmet) - to_chat(user, "The unit already contains a helmet!") - return - if(!user.drop_item()) - return - helmet = I - else if(istype(I, /obj/item/clothing/mask)) - if(mask) - to_chat(user, "The unit already contains a mask!") - return - if(!user.drop_item()) - return - mask = I - else - if(storage) - to_chat(user, "The auxiliary storage compartment is full!") - return - if(!user.drop_item()) - return - storage = I - - I.loc = src - visible_message("[user] inserts [I] into [src]", "You load [I] into [src].") - update_icon() - return - - if(panel_open && is_wire_tool(I)) - wires.interact(user) - if(!state_open) - if(default_deconstruction_screwdriver(user, "panel", "close", I)) - return - if(default_pry_open(I)) - dump_contents() - return - - return ..() - + open_machine() + dump_contents() + new /obj/item/stack/sheet/metal (loc, 2) + qdel(src) + +/obj/machinery/suit_storage_unit/MouseDrop_T(atom/A, mob/user) + if(user.stat || user.lying || !Adjacent(user) || !Adjacent(A) || !isliving(A)) + return + var/mob/living/target = A + if(!state_open) + to_chat(user, "The unit's doors are shut!") + return + if(!is_operational()) + to_chat(user, "The unit is not operational!") + return + if(occupant || helmet || suit || storage) + to_chat(user, "It's too cluttered inside to fit in!") + return + + if(target == user) + user.visible_message("[user] starts squeezing into [src]!", "You start working your way into [src]...") + else + target.visible_message("[user] starts shoving [target] into [src]!", "[user] starts shoving you into [src]!") + + if(do_mob(user, target, 30)) + if(occupant || helmet || suit || storage) + return + if(target == user) + user.visible_message("[user] slips into [src] and closes the door behind them!", "You slip into [src]'s cramped space and shut its door.") + else + target.visible_message("[user] pushes [target] into [src] and shuts its door!", "[user] shoves you into [src] and shuts the door!") + close_machine(target) + add_fingerprint(user) + +/obj/machinery/suit_storage_unit/proc/cook() + if(uv_cycles) + uv_cycles-- + uv = TRUE + locked = TRUE + update_icon() + if(occupant) + var/mob/living/mob_occupant = occupant + if(uv_super) + mob_occupant.adjustFireLoss(rand(20, 36)) + else + mob_occupant.adjustFireLoss(rand(10, 16)) + mob_occupant.emote("scream") + addtimer(CALLBACK(src, .proc/cook), 50) + else + uv_cycles = initial(uv_cycles) + uv = FALSE + locked = FALSE + if(uv_super) + visible_message("[src]'s door creaks open with a loud whining noise. A cloud of foul black smoke escapes from its chamber.") + playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50, 1) + helmet = null + qdel(helmet) + suit = null + qdel(suit) // Delete everything but the occupant. + mask = null + qdel(mask) + storage = null + qdel(storage) + // The wires get damaged too. + wires.cut_all() + else + if(!occupant) + visible_message("[src]'s door slides open. The glowing yellow lights dim to a gentle green.") + else + visible_message("[src]'s door slides open, barraging you with the nauseating smell of charred flesh.") + playsound(src, 'sound/machines/airlockclose.ogg', 25, 1) + for(var/obj/item/I in src) //Scorches away blood and forensic evidence, although the SSU itself is unaffected + I.clean_blood() + I.fingerprints = list() + open_machine(FALSE) + if(occupant) + dump_contents() + +/obj/machinery/suit_storage_unit/proc/shock(mob/user, prb) + if(!prob(prb)) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, src) + s.start() + if(electrocute_mob(user, src, src, 1, TRUE)) + return 1 + +/obj/machinery/suit_storage_unit/relaymove(mob/user) + if(locked) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") + return + open_machine() + dump_contents() + +/obj/machinery/suit_storage_unit/container_resist(mob/living/user) + if(!locked) + open_machine() + dump_contents() + return + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + user.visible_message("You see [user] kicking against the doors of [src]!", \ + "You start kicking against the doors... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear a thump from [src].") + if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds + if(!user || user.stat != CONSCIOUS || user.loc != src ) + return + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") + open_machine() + dump_contents() + + add_fingerprint(user) + if(locked) + visible_message("You see [user] kicking against the doors of [src]!", \ + "You start kicking against the doors...") + addtimer(CALLBACK(src, .proc/resist_open, user), 300) + else + open_machine() + dump_contents() + +/obj/machinery/suit_storage_unit/proc/resist_open(mob/user) + if(!state_open && occupant && (user in src) && user.stat == 0) // Check they're still here. + visible_message("You see [user] bursts out of [src]!", \ + "You escape the cramped confines of [src]!") + open_machine() + +/obj/machinery/suit_storage_unit/attackby(obj/item/I, mob/user, params) + if(state_open && is_operational()) + if(istype(I, /obj/item/clothing/suit/space)) + if(suit) + to_chat(user, "The unit already contains a suit!.") + return + if(!user.drop_item()) + return + suit = I + else if(istype(I, /obj/item/clothing/head/helmet)) + if(helmet) + to_chat(user, "The unit already contains a helmet!") + return + if(!user.drop_item()) + return + helmet = I + else if(istype(I, /obj/item/clothing/mask)) + if(mask) + to_chat(user, "The unit already contains a mask!") + return + if(!user.drop_item()) + return + mask = I + else + if(storage) + to_chat(user, "The auxiliary storage compartment is full!") + return + if(!user.drop_item()) + return + storage = I + + I.loc = src + visible_message("[user] inserts [I] into [src]", "You load [I] into [src].") + update_icon() + return + + if(panel_open && is_wire_tool(I)) + wires.interact(user) + if(!state_open) + if(default_deconstruction_screwdriver(user, "panel", "close", I)) + return + if(default_pry_open(I)) + dump_contents() + return + + return ..() + /obj/machinery/suit_storage_unit/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "suit_storage_unit", name, 400, 305, master_ui, state) - ui.open() - -/obj/machinery/suit_storage_unit/ui_data() - var/list/data = list() - data["locked"] = locked - data["open"] = state_open - data["safeties"] = safeties - data["uv_active"] = uv - data["uv_super"] = uv_super - if(helmet) - data["helmet"] = helmet.name - if(suit) - data["suit"] = suit.name - if(mask) - data["mask"] = mask.name - if(storage) - data["storage"] = storage.name - if(occupant) - data["occupied"] = 1 - return data - -/obj/machinery/suit_storage_unit/ui_act(action, params) - if(..() || uv) - return - switch(action) - if("door") - if(state_open) - close_machine() - else - open_machine(0) - if(occupant) - dump_contents() // Dump out contents if someone is in there. - . = TRUE - if("lock") - locked = !locked - . = TRUE - if("uv") - if(occupant && safeties) - return - else if(!helmet && !mask && !suit && !storage && !occupant) - return - else - if(occupant) - var/mob/living/mob_occupant = occupant - to_chat(mob_occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!") - cook() - . = TRUE - if("dispense") - if(!state_open) - return - - var/static/list/valid_items = list("helmet", "suit", "mask", "storage") - var/item_name = params["item"] - if(item_name in valid_items) - var/obj/item/I = vars[item_name] - vars[item_name] = null - if(I) - I.forceMove(loc) - . = TRUE - update_icon() + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "suit_storage_unit", name, 400, 305, master_ui, state) + ui.open() + +/obj/machinery/suit_storage_unit/ui_data() + var/list/data = list() + data["locked"] = locked + data["open"] = state_open + data["safeties"] = safeties + data["uv_active"] = uv + data["uv_super"] = uv_super + if(helmet) + data["helmet"] = helmet.name + if(suit) + data["suit"] = suit.name + if(mask) + data["mask"] = mask.name + if(storage) + data["storage"] = storage.name + if(occupant) + data["occupied"] = 1 + return data + +/obj/machinery/suit_storage_unit/ui_act(action, params) + if(..() || uv) + return + switch(action) + if("door") + if(state_open) + close_machine() + else + open_machine(0) + if(occupant) + dump_contents() // Dump out contents if someone is in there. + . = TRUE + if("lock") + locked = !locked + . = TRUE + if("uv") + if(occupant && safeties) + return + else if(!helmet && !mask && !suit && !storage && !occupant) + return + else + if(occupant) + var/mob/living/mob_occupant = occupant + to_chat(mob_occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!") + cook() + . = TRUE + if("dispense") + if(!state_open) + return + + var/static/list/valid_items = list("helmet", "suit", "mask", "storage") + var/item_name = params["item"] + if(item_name in valid_items) + var/obj/item/I = vars[item_name] + vars[item_name] = null + if(I) + I.forceMove(loc) + . = TRUE + update_icon() diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 427bc95253..bf879f84da 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -869,7 +869,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C products = list(/obj/item/reagent_containers/syringe = 12, /obj/item/reagent_containers/dropper = 3, /obj/item/stack/medical/gauze = 8, /obj/item/reagent_containers/pill/patch/styptic = 5, /obj/item/reagent_containers/pill/insulin = 10, /obj/item/reagent_containers/pill/patch/silver_sulf = 5, /obj/item/reagent_containers/glass/bottle/charcoal = 4, /obj/item/reagent_containers/spray/medical/sterilizer = 1, /obj/item/reagent_containers/glass/bottle/epinephrine = 4, /obj/item/reagent_containers/glass/bottle/morphine = 4, /obj/item/reagent_containers/glass/bottle/salglu_solution = 3, - /obj/item/reagent_containers/glass/bottle/toxin = 3, /obj/item/reagent_containers/syringe/antiviral = 6, /obj/item/reagent_containers/pill/salbutamol = 2, /obj/item/device/healthanalyzer = 4, /obj/item/device/sensor_device = 2) + /obj/item/reagent_containers/glass/bottle/toxin = 3, /obj/item/reagent_containers/syringe/antiviral = 6, /obj/item/reagent_containers/pill/salbutamol = 2, /obj/item/device/healthanalyzer = 4, /obj/item/device/sensor_device = 2, /obj/item/pinpointer/crew = 2) contraband = list(/obj/item/reagent_containers/pill/tox = 3, /obj/item/reagent_containers/pill/morphine = 4, /obj/item/reagent_containers/pill/charcoal = 6) premium = list(/obj/item/storage/box/hug/medical = 1, /obj/item/reagent_containers/hypospray/medipen = 3, /obj/item/storage/belt/medical = 3, /obj/item/wrench/medical = 1) armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm index 6d15716941..d0a1310e8e 100644 --- a/code/game/mecha/equipment/tools/mining_tools.dm +++ b/code/game/mecha/equipment/tools/mining_tools.dm @@ -58,7 +58,9 @@ /turf/open/floor/plating/asteroid/drill_act(obj/item/mecha_parts/mecha_equipment/drill/drill) for(var/turf/open/floor/plating/asteroid/M in range(1, drill.chassis)) if(get_dir(drill.chassis,M)&drill.chassis.dir) - M.gets_dug() + for(var/I in GetComponents(/datum/component/archaeology)) + var/datum/component/archaeology/archy = I + archy.gets_dug() drill.log_message("Drilled through [src]") drill.move_ores() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 96dcba36cf..f640e0f90b 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -549,9 +549,10 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) transfer_blood = 0 /obj/item/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FOUR) throw_at(S,14,3, spin=0) - else ..() + else return /obj/item/throw_impact(atom/A) if(A && !QDELETED(A)) diff --git a/code/game/objects/items/devices/PDA/virus_cart.dm b/code/game/objects/items/devices/PDA/virus_cart.dm index 2b0a9d3b3c..8c4a42a5aa 100644 --- a/code/game/objects/items/devices/PDA/virus_cart.dm +++ b/code/game/objects/items/devices/PDA/virus_cart.dm @@ -90,7 +90,7 @@ return if(!isnull(target) && !target.toff) charges-- - var/lock_code = "[rand(100,999)] [pick("Alpha","Bravo","Charlie","Delta","Echo","Foxtrot","Golf","Hotel","India","Juliet","Kilo","Lima","Mike","November","Oscar","Papa","Quebec","Romeo","Sierra","Tango","Uniform","Victor","Whiskey","X-ray","Yankee","Zulu")]" + var/lock_code = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]" to_chat(U, "Virus Sent! The unlock code to the target is: [lock_code]") if(!target.hidden_uplink) var/obj/item/device/uplink/uplink = new(target) diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index b41438451c..9c19f37f3f 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -428,7 +428,7 @@ /obj/item/device/flashlight/glowstick/update_icon() item_state = "glowstick" - overlays.Cut() + cut_overlays() if(!fuel) icon_state = "glowstick-empty" cut_overlays() diff --git a/code/game/objects/items/devices/pinpointer.dm b/code/game/objects/items/devices/pinpointer.dm new file mode 100644 index 0000000000..1d11f312a4 --- /dev/null +++ b/code/game/objects/items/devices/pinpointer.dm @@ -0,0 +1,152 @@ +//Pinpointers are used to track atoms from a distance as long as they're on the same z-level. The captain and nuke ops have ones that track the nuclear authentication disk. +/obj/item/weapon/pinpointer + name = "pinpointer" + desc = "A handheld tracking device that locks onto certain signals." + icon = 'icons/obj/device.dmi' + icon_state = "pinpointer" + flags = CONDUCT + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL = 500, MAT_GLASS = 250) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/active = FALSE + var/atom/movable/target = null //The thing we're searching for + var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination + var/alert = FALSE // TRUE to display things more seriously + +/obj/item/weapon/pinpointer/New() + ..() + GLOB.pinpointer_list += src + +/obj/item/weapon/pinpointer/Destroy() + STOP_PROCESSING(SSfastprocess, src) + GLOB.pinpointer_list -= src + return ..() + +/obj/item/weapon/pinpointer/attack_self(mob/living/user) + active = !active + user.visible_message("[user] [active ? "" : "de"]activates their pinpointer.", "You [active ? "" : "de"]activate your pinpointer.") + playsound(user, 'sound/items/screwdriver2.ogg', 50, 1) + if(active) + START_PROCESSING(SSfastprocess, src) + else + target = null + STOP_PROCESSING(SSfastprocess, src) + update_pointer_overlay() + +/obj/item/weapon/pinpointer/process() + if(!active) + STOP_PROCESSING(SSfastprocess, src) + return + scan_for_target() + update_pointer_overlay() + +/obj/item/weapon/pinpointer/proc/scan_for_target() + return + +/obj/item/weapon/pinpointer/proc/update_pointer_overlay() + cut_overlays() + if(!active) + return + if(!target) + add_overlay("pinon[alert ? "alert" : ""]null") + var/turf/here = get_turf(src) + var/turf/there = get_turf(target) + if(here.z != there.z) + add_overlay("pinon[alert ? "alert" : ""]null") + return + if(get_dist_euclidian(here,there) <= minimum_range) + add_overlay("pinon[alert ? "alert" : ""]direct") + else + setDir(get_dir(here, there)) + switch(get_dist(here, there)) + if(1 to 8) + add_overlay("pinon[alert ? "alert" : "close"]") + if(9 to 16) + add_overlay("pinon[alert ? "alert" : "medium"]") + if(16 to INFINITY) + add_overlay("pinon[alert ? "alert" : "far"]") + +/obj/item/weapon/pinpointer/crew // A replacement for the old crew monitoring consoles + name = "crew pinpointer" + desc = "A handheld tracking device that points to crew suit sensors." + icon_state = "pinpointer_crew" + +/obj/item/weapon/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + if((H.z == 0 || H.z == here.z) && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + + // Suit sensors must be on maximum. + if(!U.has_sensor || U.sensor_mode < SENSOR_COORDS) + return FALSE + + var/turf/there = get_turf(H) + return (H.z != 0 || (there && there.z == H.z)) + + return FALSE + +/obj/item/weapon/pinpointer/crew/attack_self(mob/living/user) + if(active) + active = FALSE + user.visible_message("[user] deactivates their pinpointer.", "You deactivate your pinpointer.") + playsound(user, 'sound/items/screwdriver2.ogg', 50, 1) + target = null //Restarting the pinpointer forces a target reset + STOP_PROCESSING(SSfastprocess, src) + update_pointer_overlay() + return + + var/list/name_counts = list() + var/list/names = list() + + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(!trackable(H)) + continue + + var/name = "Unknown" + if(H.wear_id) + var/obj/item/weapon/card/id/I = H.wear_id.GetID() + name = I.registered_name + + while(name in name_counts) + name_counts[name]++ + name = text("[] ([])", name, name_counts[name]) + names[name] = H + name_counts[name] = 1 + + if(!names.len) + user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") + return + + var/A = input(user, "Person to track", "Pinpoint") in names + if(!src || QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated() || !A) + return + + target = names[A] + active = TRUE + user.visible_message("[user] activates their pinpointer.", "You activate your pinpointer.") + playsound(user, 'sound/items/screwdriver2.ogg', 50, 1) + START_PROCESSING(SSfastprocess, src) + update_pointer_overlay() + +/obj/item/weapon/pinpointer/crew/scan_for_target() + if(target) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(!trackable(H)) + target = null + if(!target) + active = FALSE + +/obj/item/weapon/pinpointer/process() + if(!active) + STOP_PROCESSING(SSfastprocess, src) + return + scan_for_target() + update_pointer_overlay() + diff --git a/code/game/objects/items/devices/pressureplates.dm b/code/game/objects/items/devices/pressureplates.dm index bab40edb29..b8b4884db7 100644 --- a/code/game/objects/items/devices/pressureplates.dm +++ b/code/game/objects/items/devices/pressureplates.dm @@ -86,7 +86,7 @@ icon_state = null active = TRUE if(tile_overlay) - loc.overlays += tile_overlay + loc.add_overlay(tile_overlay) else if(crossed) trigger() //no cheesing. diff --git a/code/game/objects/items/implants/implantchair.dm b/code/game/objects/items/implants/implantchair.dm index 55a35960cf..2f59020cbe 100644 --- a/code/game/objects/items/implants/implantchair.dm +++ b/code/game/objects/items/implants/implantchair.dm @@ -1,187 +1,188 @@ -/obj/machinery/implantchair - name = "mindshield implanter" - desc = "Used to implant occupants with mindshield implants." - icon = 'icons/obj/machines/implantchair.dmi' - icon_state = "implantchair" - density = TRUE - opacity = 0 - anchored = TRUE - - var/ready = TRUE - var/replenishing = FALSE - - var/ready_implants = 5 - var/max_implants = 5 - var/injection_cooldown = 600 - var/replenish_cooldown = 6000 - var/implant_type = /obj/item/implant/mindshield - var/auto_inject = FALSE - var/auto_replenish = TRUE - var/special = FALSE - var/special_name = "special function" - -/obj/machinery/implantchair/Initialize() - . = ..() - open_machine() - update_icon() - - -/obj/machinery/implantchair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "implantchair", name, 375, 280, master_ui, state) - ui.open() - - -/obj/machinery/implantchair/ui_data() - var/list/data = list() - data["occupied"] = occupant ? 1 : 0 - data["open"] = state_open - - data["occupant"] = list() - if(occupant) - var/mob/living/mob_occupant = occupant - data["occupant"]["name"] = mob_occupant.name - data["occupant"]["stat"] = mob_occupant.stat - - data["special_name"] = special ? special_name : null - data["ready_implants"] = ready_implants - data["ready"] = ready - data["replenishing"] = replenishing - - return data - -/obj/machinery/implantchair/ui_act(action, params) - if(..()) - return - switch(action) - if("door") - if(state_open) - close_machine() - else - open_machine() - . = TRUE - if("implant") - implant(occupant,usr) - . = TRUE - -/obj/machinery/implantchair/proc/implant(mob/living/M,mob/user) - if (!istype(M)) - return - if(!ready_implants || !ready) - return - if(implant_action(M,user)) - ready_implants-- - if(!replenishing && auto_replenish) - replenishing = TRUE - addtimer(CALLBACK(src,"replenish"),replenish_cooldown) - if(injection_cooldown > 0) - ready = FALSE - addtimer(CALLBACK(src,"set_ready"),injection_cooldown) - else - playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 25, 1) - update_icon() - -/obj/machinery/implantchair/proc/implant_action(mob/living/M) - var/obj/item/implant/I = new implant_type - if(I.implant(M)) - visible_message("[M] has been implanted by the [name].") - return 1 - -/obj/machinery/implantchair/update_icon() - icon_state = initial(icon_state) - if(state_open) - icon_state += "_open" - if(occupant) - icon_state += "_occupied" - if(ready) - add_overlay("ready") - else - cut_overlays() - -/obj/machinery/implantchair/proc/replenish() - if(ready_implants < max_implants) - ready_implants++ - if(ready_implants < max_implants) - addtimer(CALLBACK(src,"replenish"),replenish_cooldown) - else - replenishing = FALSE - -/obj/machinery/implantchair/proc/set_ready() - ready = TRUE - update_icon() - -/obj/machinery/implantchair/container_resist(mob/living/user) - if(state_open) - return - user.changeNext_move(CLICK_CD_BREAKOUT) - user.last_special = world.time + CLICK_CD_BREAKOUT - to_chat(user, "You lean on the back of [src] and start pushing the door open... (this will take about about a minute.)") - audible_message("You hear a metallic creaking from [src]!",hearing_distance = 2) - - if(do_after(user, 600, target = src)) - if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) - return - visible_message("[user] successfully broke out of [src]!") - to_chat(user, "You successfully break out of [src]!") - open_machine() - -/obj/machinery/implantchair/relaymove(mob/user) - container_resist(user) - -/obj/machinery/implantchair/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !isliving(target) || !user.IsAdvancedToolUser()) - return - close_machine(target) - -/obj/machinery/implantchair/close_machine(mob/living/user) - if((isnull(user) || istype(user)) && state_open) - ..(user) - if(auto_inject && ready && ready_implants > 0) - implant(user,null) - -/obj/machinery/implantchair/genepurge - name = "Genetic purifier" - desc = "Used to purge human genome of foreign influences" - special = TRUE - special_name = "Purge genome" - injection_cooldown = 0 - replenish_cooldown = 300 - -/obj/machinery/implantchair/genepurge/implant_action(mob/living/carbon/human/H,mob/user) - if(!istype(H)) - return 0 - H.set_species(/datum/species/human, 1)//lizards go home - purrbation_remove(H)//remove cats - H.dna.remove_all_mutations()//hulks out - return 1 - - -/obj/machinery/implantchair/brainwash - name = "Neural Imprinter" - desc = "Used to indoctrinate rehabilitate hardened recidivists." - special_name = "Imprint" - injection_cooldown = 3000 - auto_inject = FALSE - auto_replenish = FALSE - special = TRUE - var/objective = "Obey the law. Praise Nanotrasen." - var/custom = FALSE - -/obj/machinery/implantchair/brainwash/implant_action(mob/living/C,mob/user) - if(!istype(C) || !C.mind) // I don't know how this makes any sense for silicons but laws trump objectives anyway. - return 0 - if(custom) - if(!user || !user.Adjacent(src)) - return 0 - objective = stripped_input(usr,"What order do you want to imprint on [C]?","Enter the order","",120) - message_admins("[key_name_admin(user)] set brainwash machine objective to '[objective]'.") - log_game("[key_name_admin(user)] set brainwash machine objective to '[objective]'.") - var/datum/objective/custom_objective = new/datum/objective(objective) - custom_objective.owner = C.mind - C.mind.objectives += custom_objective - C.mind.announce_objectives() - message_admins("[key_name_admin(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.") - log_game("[key_name_admin(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.") - return 1 - +/obj/machinery/implantchair + name = "mindshield implanter" + desc = "Used to implant occupants with mindshield implants." + icon = 'icons/obj/machines/implantchair.dmi' + icon_state = "implantchair" + density = TRUE + opacity = 0 + anchored = TRUE + + var/ready = TRUE + var/replenishing = FALSE + + var/ready_implants = 5 + var/max_implants = 5 + var/injection_cooldown = 600 + var/replenish_cooldown = 6000 + var/implant_type = /obj/item/implant/mindshield + var/auto_inject = FALSE + var/auto_replenish = TRUE + var/special = FALSE + var/special_name = "special function" + var/message_cooldown + var/breakout_time = 1 + +/obj/machinery/implantchair/Initialize() + . = ..() + open_machine() + update_icon() + + +/obj/machinery/implantchair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "implantchair", name, 375, 280, master_ui, state) + ui.open() + + +/obj/machinery/implantchair/ui_data() + var/list/data = list() + data["occupied"] = occupant ? 1 : 0 + data["open"] = state_open + + data["occupant"] = list() + if(occupant) + var/mob/living/mob_occupant = occupant + data["occupant"]["name"] = mob_occupant.name + data["occupant"]["stat"] = mob_occupant.stat + + data["special_name"] = special ? special_name : null + data["ready_implants"] = ready_implants + data["ready"] = ready + data["replenishing"] = replenishing + + return data + +/obj/machinery/implantchair/ui_act(action, params) + if(..()) + return + switch(action) + if("door") + if(state_open) + close_machine() + else + open_machine() + . = TRUE + if("implant") + implant(occupant,usr) + . = TRUE + +/obj/machinery/implantchair/proc/implant(mob/living/M,mob/user) + if (!istype(M)) + return + if(!ready_implants || !ready) + return + if(implant_action(M,user)) + ready_implants-- + if(!replenishing && auto_replenish) + replenishing = TRUE + addtimer(CALLBACK(src,"replenish"),replenish_cooldown) + if(injection_cooldown > 0) + ready = FALSE + addtimer(CALLBACK(src,"set_ready"),injection_cooldown) + else + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 25, 1) + update_icon() + +/obj/machinery/implantchair/proc/implant_action(mob/living/M) + var/obj/item/implant/I = new implant_type + if(I.implant(M)) + visible_message("[M] has been implanted by the [name].") + return 1 + +/obj/machinery/implantchair/update_icon() + icon_state = initial(icon_state) + if(state_open) + icon_state += "_open" + if(occupant) + icon_state += "_occupied" + if(ready) + add_overlay("ready") + else + cut_overlays() + +/obj/machinery/implantchair/proc/replenish() + if(ready_implants < max_implants) + ready_implants++ + if(ready_implants < max_implants) + addtimer(CALLBACK(src,"replenish"),replenish_cooldown) + else + replenishing = FALSE + +/obj/machinery/implantchair/proc/set_ready() + ready = TRUE + update_icon() + +/obj/machinery/implantchair/container_resist(mob/living/user) + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + user.visible_message("You see [user] kicking against the door of [src]!", \ + "You lean on the back of [src] and start pushing the door open... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear a metallic creaking from [src].") + if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds + if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) + return + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") + open_machine() + +/obj/machinery/implantchair/relaymove(mob/user) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") + +/obj/machinery/implantchair/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !isliving(target) || !user.IsAdvancedToolUser()) + return + close_machine(target) + +/obj/machinery/implantchair/close_machine(mob/living/user) + if((isnull(user) || istype(user)) && state_open) + ..(user) + if(auto_inject && ready && ready_implants > 0) + implant(user,null) + +/obj/machinery/implantchair/genepurge + name = "Genetic purifier" + desc = "Used to purge human genome of foreign influences" + special = TRUE + special_name = "Purge genome" + injection_cooldown = 0 + replenish_cooldown = 300 + +/obj/machinery/implantchair/genepurge/implant_action(mob/living/carbon/human/H,mob/user) + if(!istype(H)) + return 0 + H.set_species(/datum/species/human, 1)//lizards go home + purrbation_remove(H)//remove cats + H.dna.remove_all_mutations()//hulks out + return 1 + + +/obj/machinery/implantchair/brainwash + name = "Neural Imprinter" + desc = "Used to indoctrinate rehabilitate hardened recidivists." + special_name = "Imprint" + injection_cooldown = 3000 + auto_inject = FALSE + auto_replenish = FALSE + special = TRUE + var/objective = "Obey the law. Praise Nanotrasen." + var/custom = FALSE + +/obj/machinery/implantchair/brainwash/implant_action(mob/living/C,mob/user) + if(!istype(C) || !C.mind) // I don't know how this makes any sense for silicons but laws trump objectives anyway. + return 0 + if(custom) + if(!user || !user.Adjacent(src)) + return 0 + objective = stripped_input(usr,"What order do you want to imprint on [C]?","Enter the order","",120) + message_admins("[key_name_admin(user)] set brainwash machine objective to '[objective]'.") + log_game("[key_name_admin(user)] set brainwash machine objective to '[objective]'.") + var/datum/objective/custom_objective = new/datum/objective(objective) + custom_objective.owner = C.mind + C.mind.objectives += custom_objective + C.mind.announce_objectives() + message_admins("[key_name_admin(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.") + log_game("[key_name_admin(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.") + return 1 diff --git a/code/game/objects/items/pinpointer.dm b/code/game/objects/items/pinpointer.dm new file mode 100644 index 0000000000..28488a231f --- /dev/null +++ b/code/game/objects/items/pinpointer.dm @@ -0,0 +1,150 @@ +//Pinpointers are used to track atoms from a distance as long as they're on the same z-level. The captain and nuke ops have ones that track the nuclear authentication disk. +/obj/item/pinpointer + name = "pinpointer" + desc = "A handheld tracking device that locks onto certain signals." + icon = 'icons/obj/device.dmi' + icon_state = "pinpointer" + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL = 500, MAT_GLASS = 250) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/active = FALSE + var/atom/movable/target //The thing we're searching for + var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination + var/alert = FALSE // TRUE to display things more seriously + +/obj/item/pinpointer/Initialize() + . = ..() + GLOB.pinpointer_list += src + +/obj/item/pinpointer/Destroy() + STOP_PROCESSING(SSfastprocess, src) + GLOB.pinpointer_list -= src + return ..() + +/obj/item/pinpointer/attack_self(mob/living/user) + active = !active + user.visible_message("[user] [active ? "" : "de"]activates their pinpointer.", "You [active ? "" : "de"]activate your pinpointer.") + playsound(src, 'sound/items/screwdriver2.ogg', 50, 1) + if(active) + START_PROCESSING(SSfastprocess, src) + else + target = null + STOP_PROCESSING(SSfastprocess, src) + update_icon() + +/obj/item/pinpointer/process() + if(!active) + return PROCESS_KILL + scan_for_target() + update_icon() + +/obj/item/pinpointer/proc/scan_for_target() + return + +/obj/item/pinpointer/update_icon() + cut_overlays() + if(!active) + return + if(!target) + add_overlay("pinon[alert ? "alert" : ""]null") + var/turf/here = get_turf(src) + var/turf/there = get_turf(target) + if(here.z != there.z) + add_overlay("pinon[alert ? "alert" : ""]null") + return + if(get_dist_euclidian(here,there) <= minimum_range) + add_overlay("pinon[alert ? "alert" : ""]direct") + else + setDir(get_dir(here, there)) + switch(get_dist(here, there)) + if(1 to 8) + add_overlay("pinon[alert ? "alert" : "close"]") + if(9 to 16) + add_overlay("pinon[alert ? "alert" : "medium"]") + if(16 to INFINITY) + add_overlay("pinon[alert ? "alert" : "far"]") + +/obj/item/pinpointer/crew // A replacement for the old crew monitoring consoles + name = "crew pinpointer" + desc = "A handheld tracking device that points to crew suit sensors." + icon_state = "pinpointer_crew" + +/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + if((H.z == 0 || H.z == here.z) && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + + // Suit sensors must be on maximum. + if(!U.has_sensor || U.sensor_mode < SENSOR_COORDS) + return FALSE + + var/turf/there = get_turf(H) + return (H.z != 0 || (there && there.z == H.z)) + + return FALSE + +/obj/item/pinpointer/crew/attack_self(mob/living/user) + if(active) + active = FALSE + user.visible_message("[user] deactivates their pinpointer.", "You deactivate your pinpointer.") + playsound(src, 'sound/items/screwdriver2.ogg', 50, 1) + target = null //Restarting the pinpointer forces a target reset + STOP_PROCESSING(SSfastprocess, src) + update_icon() + return + + var/list/name_counts = list() + var/list/names = list() + + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(!trackable(H)) + continue + + var/crewmember_name = "Unknown" + if(H.wear_id) + var/obj/item/card/id/I = H.wear_id.GetID() + crewmember_name = I.registered_name + + while(crewmember_name in name_counts) + name_counts[crewmember_name]++ + crewmember_name = text("[] ([])", crewmember_name, name_counts[crewmember_name]) + names[crewmember_name] = H + name_counts[crewmember_name] = 1 + + if(!names.len) + user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") + return + + var/A = input(user, "Person to track", "Pinpoint") in names + if(!A || QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated()) + return + + target = names[A] + active = TRUE + user.visible_message("[user] activates their pinpointer.", "You activate your pinpointer.") + playsound(src, 'sound/items/screwdriver2.ogg', 50, 1) + START_PROCESSING(SSfastprocess, src) + update_icon() + +/obj/item/pinpointer/crew/scan_for_target() + if(target) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(!trackable(H)) + target = null + if(!target) //target can be set to null from above code, or elsewhere + active = FALSE + +/obj/item/pinpointer/process() + if(!active) + return PROCESS_KILL + scan_for_target() + update_icon() + diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm index 1edbba1f56..007c590cac 100644 --- a/code/game/objects/items/religion.dm +++ b/code/game/objects/items/religion.dm @@ -1,27 +1,201 @@ /obj/item/banner name = "banner" + desc = "A banner with Nanotrasen's logo on it." icon = 'icons/obj/items_and_weapons.dmi' icon_state = "banner" item_state = "banner" + force = 8 + attack_verb = list("forcefully inspired", "violently encouraged", "relentlessly galvanized") lefthand_file = 'icons/mob/inhands/equipment/banners_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/banners_righthand.dmi' - desc = "A banner with Nanotrasen's logo on it." - var/moralecooldown = 0 - var/moralewait = 600 + var/inspiration_available = TRUE //If this banner can be used to inspire crew + var/morale_time = 0 + var/morale_cooldown = 600 //How many deciseconds between uses + var/list/job_loyalties //Mobs with any of these assigned roles will be inspired + var/list/role_loyalties //Mobs with any of these special roles will be inspired + var/warcry + +/obj/item/banner/examine(mob/user) + ..() + if(inspiration_available) + to_chat(user, "Activate it in your hand to inspire nearby allies of this banner's allegiance!") /obj/item/banner/attack_self(mob/living/carbon/human/user) - if(moralecooldown + moralewait > world.time) + if(!inspiration_available) return - to_chat(user, "You increase the morale of your fellows!") - moralecooldown = world.time + if(morale_time > world.time) + to_chat(user, "You aren't feeling inspired enough to flourish [src] again yet.") + return + user.visible_message("[user] flourishes [src]!", \ + "You raise [src] skywards, inspiring your allies!") + playsound(src, "rustle", 100, FALSE) + if(warcry) + user.say("[warcry]") + var/old_transform = user.transform + user.transform *= 1.2 + animate(user, transform = old_transform, time = 10) + morale_time = world.time + morale_cooldown - for(var/mob/living/carbon/human/H in range(4,get_turf(src))) - to_chat(H, "Your morale is increased by [user]'s banner!") - H.adjustBruteLoss(-15) - H.adjustFireLoss(-15) - H.AdjustStun(-40) - H.AdjustKnockdown(-40) - H.AdjustUnconscious(-40) + var/list/inspired = list() + var/has_job_loyalties = LAZYLEN(job_loyalties) + var/has_role_loyalties = LAZYLEN(role_loyalties) + inspired += user //The user is always inspired, regardless of loyalties + for(var/mob/living/carbon/human/H in range(4, get_turf(src))) + if(H.stat == DEAD || H == user) + continue + if(H.mind && (has_job_loyalties || has_role_loyalties)) + if(has_job_loyalties && H.mind.assigned_role in job_loyalties) + inspired += H + else if(has_role_loyalties && H.mind.special_role in role_loyalties) + inspired += H + else if(check_inspiration(H)) + inspired += H + + for(var/V in inspired) + var/mob/living/carbon/human/H = V + if(H != user) + to_chat(H, "Your confidence surges as [user] flourishes [user.p_their()] [name]!") + inspiration(H) + special_inspiration(H) + +/obj/item/banner/proc/check_inspiration(mob/living/carbon/human/H) //Banner-specific conditions for being eligible + return + +/obj/item/banner/proc/inspiration(mob/living/carbon/human/H) + H.adjustBruteLoss(-15) + H.adjustFireLoss(-15) + H.AdjustStun(-40) + H.AdjustKnockdown(-40) + H.AdjustUnconscious(-40) + playsound(H, 'sound/magic/staff_healing.ogg', 25, FALSE) + +/obj/item/banner/proc/special_inspiration(mob/living/carbon/human/H) //Any banner-specific inspiration effects go here + return + +/obj/item/banner/security + name = "securistan banner" + desc = "The banner of Securistan, ruling the station with an iron fist." + icon_state = "banner_security" + job_loyalties = list("Security Officer", "Warden", "Detective", "Head of Security") + warcry = "EVERYONE DOWN ON THE GROUND!!" + +/obj/item/banner/security/mundane + inspiration_available = FALSE + +/datum/crafting_recipe/security_banner + name = "Securistan Banner" + result = /obj/item/banner/security/mundane + time = 40 + reqs = list(/obj/item/stack/rods = 2, + /obj/item/clothing/under/rank/security = 1) + category = CAT_MISC + +/obj/item/banner/medical + name = "meditopia banner" + desc = "The banner of Meditopia, generous benefactors that cure wounds and shelter the weak." + icon_state = "banner_medical" + job_loyalties = list("Medical Doctor", "Chemist", "Geneticist", "Virologist", "Chief Medical Officer") + warcry = "No wounds cannot be healed!" + +/obj/item/banner/medical/mundane + inspiration_available = FALSE + +/obj/item/banner/medical/check_inspiration(mob/living/carbon/human/H) + return H.stat //Meditopia is moved to help those in need + +/datum/crafting_recipe/medical_banner + name = "Meditopia Banner" + result = /obj/item/banner/medical/mundane + time = 40 + reqs = list(/obj/item/stack/rods = 2, + /obj/item/clothing/under/rank/medical = 1) + category = CAT_MISC + +/obj/item/banner/medical/special_inspiration(mob/living/carbon/human/H) + H.adjustToxLoss(-15) + H.setOxyLoss(0) + H.reagents.add_reagent("inaprovaline", 5) + +/obj/item/banner/science + name = "sciencia banner" + desc = "The banner of Sciencia, bold and daring thaumaturges and researchers that take the path less traveled." + icon_state = "banner_science" + job_loyalties = list("Scientist", "Roboticist", "Research Director") + warcry = "For Cuban Pete!" + +/obj/item/banner/science/mundane + inspiration_available = FALSE + +/obj/item/banner/science/check_inspiration(mob/living/carbon/human/H) + return H.on_fire //Sciencia is pleased by dedication to the art of Toxins + +/datum/crafting_recipe/science_banner + name = "Sciencia Banner" + result = /obj/item/banner/science/mundane + time = 40 + reqs = list(/obj/item/stack/rods = 2, + /obj/item/clothing/under/rank/scientist = 1) + category = CAT_MISC + +/obj/item/banner/cargo + name = "cargonia banner" + desc = "The banner of the eternal Cargonia, with the mystical power of conjuring any object into existence." + icon_state = "banner_cargo" + job_loyalties = list("Cargo Technician", "Shaft Miner", "Quartermaster") + warcry = "Hail Cargonia!" + +/obj/item/banner/cargo/mundane + inspiration_available = FALSE + +/datum/crafting_recipe/cargo_banner + name = "Cargonia Banner" + result = /obj/item/banner/cargo/mundane + time = 40 + reqs = list(/obj/item/stack/rods = 2, + /obj/item/clothing/under/rank/cargotech = 1) + category = CAT_MISC + +/obj/item/banner/engineering + name = "engitopia banner" + desc = "The banner of Engitopia, wielders of limitless power." + icon_state = "banner_engineering" + job_loyalties = list("Station Engineer", "Atmospheric Technician", "Chief Engineer") + warcry = "All hail lord Singuloth!!" + +/obj/item/banner/engineering/mundane + inspiration_available = FALSE + +/obj/item/banner/engineering/special_inspiration(mob/living/carbon/human/H) + H.radiation = 0 + +/datum/crafting_recipe/engineering_banner + name = "Engitopia Banner" + result = /obj/item/banner/engineering/mundane + time = 40 + reqs = list(/obj/item/stack/rods = 2, + /obj/item/clothing/under/rank/engineer = 1) + category = CAT_MISC + +/obj/item/banner/command + name = "command banner" + desc = "The banner of Command, a staunch and ancient line of bueraucratic kings and queens." + //No icon state here since the default one is the NT banner + job_loyalties = list("Captain", "Head of Personnel", "Chief Engineer", "Head of Security", "Research Director", "Chief Medical Officer") + warcry = "Hail Nanotrasen!" + +/obj/item/banner/command/mundane + inspiration_available = FALSE + +/obj/item/banner/command/check_inspiration(mob/living/carbon/human/H) + return H.isloyal() //Command is stalwart but rewards their allies. + +/datum/crafting_recipe/command_banner + name = "Command Banner" + result = /obj/item/banner/command/mundane + time = 40 + reqs = list(/obj/item/stack/rods = 2, + /obj/item/clothing/under/captainparade = 1) + category = CAT_MISC /obj/item/banner/red name = "red banner" diff --git a/code/game/objects/items/tools.dm b/code/game/objects/items/tools.dm index 23f68ca93c..8bfc90decb 100644 --- a/code/game/objects/items/tools.dm +++ b/code/game/objects/items/tools.dm @@ -1,790 +1,790 @@ -#define WELDER_FUEL_BURN_INTERVAL 13 - -/* Tools! - * Note: Multitools are /obj/item/device - * - * Contains: - * Wrench - * Screwdriver - * Wirecutters - * Welding Tool - * Crowbar - */ - -/* - * Wrench - */ -/obj/item/wrench - name = "wrench" - desc = "A wrench with common uses. Can be found in your hand." - icon = 'icons/obj/tools.dmi' - icon_state = "wrench" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT - force = 5 - throwforce = 7 - w_class = WEIGHT_CLASS_SMALL - usesound = 'sound/items/ratchet.ogg' - materials = list(MAT_METAL=150) - origin_tech = "materials=1;engineering=1" - attack_verb = list("bashed", "battered", "bludgeoned", "whacked") - toolspeed = 1 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) - -/obj/item/wrench/suicide_act(mob/user) - user.visible_message("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) - return (BRUTELOSS) - -/obj/item/wrench/cyborg - name = "automatic wrench" - desc = "An advanced robotic wrench. Can be found in construction cyborgs." - toolspeed = 0.5 - -/obj/item/wrench/brass - name = "brass wrench" - desc = "A brass wrench. It's faintly warm to the touch." - resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "wrench_brass" - toolspeed = 0.5 - -/obj/item/wrench/abductor - name = "alien wrench" - desc = "A polarized wrench. It causes anything placed between the jaws to turn." - icon = 'icons/obj/abductor.dmi' - icon_state = "wrench" - usesound = 'sound/effects/empulse.ogg' - toolspeed = 0.1 - origin_tech = "materials=5;engineering=5;abductor=3" - -/obj/item/wrench/power - name = "hand drill" - desc = "A simple powered hand drill. It's fitted with a bolt bit." - icon_state = "drill_bolt" - item_state = "drill" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - usesound = 'sound/items/drill_use.ogg' - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get - force = 8 //might or might not be too high, subject to change - w_class = WEIGHT_CLASS_SMALL - throwforce = 8 - attack_verb = list("drilled", "screwed", "jabbed") - toolspeed = 0.25 - -/obj/item/wrench/power/attack_self(mob/user) - playsound(get_turf(user),'sound/items/change_drill.ogg',50,1) - var/obj/item/wirecutters/power/s_drill = new /obj/item/screwdriver/power - to_chat(user, "You attach the screw driver bit to [src].") - qdel(src) - user.put_in_active_hand(s_drill) - -/obj/item/wrench/power/suicide_act(mob/user) - user.visible_message("[user] is pressing [src] against [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!") - return (BRUTELOSS) - -/obj/item/wrench/medical - name = "medical wrench" - desc = "A medical wrench with common(medical?) uses. Can be found in your hand." - icon_state = "wrench_medical" - force = 2 //MEDICAL - throwforce = 4 - origin_tech = "materials=1;engineering=1;biotech=3" - attack_verb = list("wrenched", "medicaled", "tapped", "jabbed", "whacked") - -/obj/item/wrench/medical/suicide_act(mob/living/user) - user.visible_message("[user] is praying to the medical wrench to take [user.p_their()] soul. It looks like [user.p_theyre()] trying to commit suicide!") - // TODO Make them glow with the power of the M E D I C A L W R E N C H - // during their ascension - - // Stun stops them from wandering off - user.Stun(100, ignore_canstun = TRUE) - playsound(loc, 'sound/effects/pray.ogg', 50, 1, -1) - - // Let the sound effect finish playing - sleep(20) - - if(!user) - return - - for(var/obj/item/W in user) - user.dropItemToGround(W) - - var/obj/item/wrench/medical/W = new /obj/item/wrench/medical(loc) - W.add_fingerprint(user) - W.desc += " For some reason, it reminds you of [user.name]." - - if(!user) - return - - user.dust() - - return OXYLOSS - -/* - * Screwdriver - */ -/obj/item/screwdriver - name = "screwdriver" - desc = "You can be totally screwy with this." - icon = 'icons/obj/tools.dmi' - icon_state = "screwdriver" - item_state = "screwdriver" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT - force = 5 - w_class = WEIGHT_CLASS_TINY - throwforce = 5 - throw_speed = 3 - throw_range = 5 - materials = list(MAT_METAL=75) - attack_verb = list("stabbed") - hitsound = 'sound/weapons/bladeslice.ogg' - usesound = 'sound/items/screwdriver.ogg' - toolspeed = 1 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) - var/random_color = TRUE //if the screwdriver uses random coloring - var/static/list/screwdriver_colors = list(\ - "blue" = rgb(24, 97, 213), \ - "red" = rgb(149, 23, 16), \ - "pink" = rgb(213, 24, 141), \ - "brown" = rgb(160, 82, 18), \ - "green" = rgb(14, 127, 27), \ - "cyan" = rgb(24, 162, 213), \ - "yellow" = rgb(213, 140, 24), \ - ) - -/obj/item/screwdriver/suicide_act(mob/user) - user.visible_message("[user] is stabbing [src] into [user.p_their()] [pick("temple", "heart")]! It looks like [user.p_theyre()] trying to commit suicide!") - return(BRUTELOSS) - -/obj/item/screwdriver/Initialize() - . = ..() - if(random_color) //random colors! - var/our_color = pick(screwdriver_colors) - add_atom_colour(screwdriver_colors[our_color], FIXED_COLOUR_PRIORITY) - update_icon() - if(prob(75)) - pixel_y = rand(0, 16) - -/obj/item/screwdriver/update_icon() - if(!random_color) //icon override - return - cut_overlays() - var/mutable_appearance/base_overlay = mutable_appearance(icon, "screwdriver_screwybits") - base_overlay.appearance_flags = RESET_COLOR - add_overlay(base_overlay) - -/obj/item/screwdriver/worn_overlays(isinhands = FALSE, icon_file) - . = list() - if(isinhands && random_color) - var/mutable_appearance/M = mutable_appearance(icon_file, "screwdriver_head") - M.appearance_flags = RESET_COLOR - . += M - -/obj/item/screwdriver/get_belt_overlay() - if(random_color) - var/mutable_appearance/body = mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "screwdriver") - var/mutable_appearance/head = mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "screwdriver_head") - body.color = color - head.overlays += body - return head - else - return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', icon_state) - -/obj/item/screwdriver/attack(mob/living/carbon/M, mob/living/carbon/user) - if(!istype(M)) - return ..() - if(user.zone_selected != "eyes" && user.zone_selected != "head") - return ..() - if(user.disabilities & CLUMSY && prob(50)) - M = user - return eyestab(M,user) - -/obj/item/screwdriver/brass - name = "brass screwdriver" - desc = "A screwdriver made of brass. The handle feels freezing cold." - resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "screwdriver_brass" - item_state = "screwdriver_brass" - toolspeed = 0.5 - random_color = FALSE - -/obj/item/screwdriver/abductor - name = "alien screwdriver" - desc = "An ultrasonic screwdriver." - icon = 'icons/obj/abductor.dmi' - icon_state = "screwdriver_a" - item_state = "screwdriver_nuke" - usesound = 'sound/items/pshoom.ogg' - toolspeed = 0.1 - random_color = FALSE - -/obj/item/screwdriver/power - name = "hand drill" - desc = "A simple powered hand drill. It's fitted with a screw bit." - icon_state = "drill_screw" - item_state = "drill" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get - force = 8 //might or might not be too high, subject to change - w_class = WEIGHT_CLASS_SMALL - throwforce = 8 - throw_speed = 2 - throw_range = 3//it's heavier than a screw driver/wrench, so it does more damage, but can't be thrown as far - attack_verb = list("drilled", "screwed", "jabbed","whacked") - hitsound = 'sound/items/drill_hit.ogg' - usesound = 'sound/items/drill_use.ogg' - toolspeed = 0.25 - random_color = FALSE - -/obj/item/screwdriver/power/suicide_act(mob/user) - user.visible_message("[user] is putting [src] to [user.p_their()] temple. It looks like [user.p_theyre()] trying to commit suicide!") - return(BRUTELOSS) - -/obj/item/screwdriver/power/attack_self(mob/user) - playsound(get_turf(user),'sound/items/change_drill.ogg',50,1) - var/obj/item/wrench/power/b_drill = new /obj/item/wrench/power - to_chat(user, "You attach the bolt driver bit to [src].") - qdel(src) - user.put_in_active_hand(b_drill) - -/obj/item/screwdriver/cyborg - name = "powered screwdriver" - desc = "An electrical screwdriver, designed to be both precise and quick." - usesound = 'sound/items/drill_use.ogg' - toolspeed = 0.5 - -/* - * Wirecutters - */ -/obj/item/wirecutters - name = "wirecutters" - desc = "This cuts wires." - icon = 'icons/obj/tools.dmi' - icon_state = null - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT - force = 6 - throw_speed = 3 - throw_range = 7 - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=80) - attack_verb = list("pinched", "nipped") - hitsound = 'sound/items/wirecutter.ogg' - usesound = 'sound/items/wirecutter.ogg' - origin_tech = "materials=1;engineering=1" - toolspeed = 1 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) - - -/obj/item/wirecutters/New(loc, var/param_color = null) - ..() - if(!icon_state) - if(!param_color) - param_color = pick("yellow","red") - icon_state = "cutters_[param_color]" - -/obj/item/wirecutters/attack(mob/living/carbon/C, mob/user) - if(istype(C) && C.handcuffed && istype(C.handcuffed, /obj/item/restraints/handcuffs/cable)) - user.visible_message("[user] cuts [C]'s restraints with [src]!") - qdel(C.handcuffed) - C.handcuffed = null - if(C.buckled && C.buckled.buckle_requires_restraints) - C.buckled.unbuckle_mob(C) - C.update_handcuffed() - return - else - ..() - -/obj/item/wirecutters/suicide_act(mob/user) - user.visible_message("[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, usesound, 50, 1, -1) - return (BRUTELOSS) - -/obj/item/wirecutters/brass - name = "brass wirecutters" - desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch." - resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "cutters_brass" - toolspeed = 0.5 - -/obj/item/wirecutters/abductor - name = "alien wirecutters" - desc = "Extremely sharp wirecutters, made out of a silvery-green metal." - icon = 'icons/obj/abductor.dmi' - icon_state = "cutters" - toolspeed = 0.1 - origin_tech = "materials=5;engineering=4;abductor=3" - -/obj/item/wirecutters/cyborg - name = "wirecutters" - desc = "This cuts wires." - toolspeed = 0.5 - -/obj/item/wirecutters/power - name = "jaws of life" - desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a cutting head." - icon_state = "jaws_cutter" - item_state = "jawsoflife" - origin_tech = "materials=2;engineering=2" - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - usesound = 'sound/items/jaws_cut.ogg' - toolspeed = 0.25 - -/obj/item/wirecutters/power/suicide_act(mob/user) - user.visible_message("[user] is wrapping \the [src] around [user.p_their()] neck. It looks like [user.p_theyre()] trying to rip [user.p_their()] head off!") - playsound(loc, 'sound/items/jaws_cut.ogg', 50, 1, -1) - if(iscarbon(user)) - var/mob/living/carbon/C = user - var/obj/item/bodypart/BP = C.get_bodypart("head") - if(BP) - BP.drop_limb() - playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1) - return (BRUTELOSS) - -/obj/item/wirecutters/power/attack_self(mob/user) - playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) - var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power - to_chat(user, "You attach the pry jaws to [src].") - qdel(src) - user.put_in_active_hand(pryjaws) -/* - * Welding Tool - */ -/obj/item/weldingtool - name = "welding tool" - desc = "A standard edition welder provided by NanoTrasen." - icon = 'icons/obj/tools.dmi' - icon_state = "welder" - item_state = "welder" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT - force = 3 - throwforce = 5 - hitsound = "swing_hit" - usesound = 'sound/items/welder.ogg' - var/acti_sound = 'sound/items/welderactivate.ogg' - var/deac_sound = 'sound/items/welderdeactivate.ogg' - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 30) - resistance_flags = FIRE_PROOF - - materials = list(MAT_METAL=70, MAT_GLASS=30) - origin_tech = "engineering=1;plasmatech=1" - var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2) - var/status = TRUE //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower) - var/max_fuel = 20 //The max amount of fuel the welder can hold - var/change_icons = 1 - var/can_off_process = 0 - var/light_intensity = 2 //how powerful the emitted light is when used. - var/burned_fuel_for = 0 //when fuel was last removed - heat = 3800 - toolspeed = 1 - -/obj/item/weldingtool/Initialize() - . = ..() - create_reagents(max_fuel) - reagents.add_reagent("welding_fuel", max_fuel) - update_icon() - - -/obj/item/weldingtool/proc/update_torch() - if(welding) - add_overlay("[initial(icon_state)]-on") - item_state = "[initial(item_state)]1" - else - item_state = "[initial(item_state)]" - - -/obj/item/weldingtool/update_icon() - cut_overlays() - if(change_icons) - var/ratio = get_fuel() / max_fuel - ratio = Ceiling(ratio*4) * 25 - add_overlay("[initial(icon_state)][ratio]") - update_torch() - return - - -/obj/item/weldingtool/process() - switch(welding) - if(0) - force = 3 - damtype = "brute" - update_icon() - if(!can_off_process) - STOP_PROCESSING(SSobj, src) - return - //Welders left on now use up fuel, but lets not have them run out quite that fast - if(1) - force = 15 - damtype = "fire" - ++burned_fuel_for - if(burned_fuel_for >= WELDER_FUEL_BURN_INTERVAL) - remove_fuel(1) - update_icon() - - //This is to start fires. process() is only called if the welder is on. - open_flame() - - -/obj/item/weldingtool/suicide_act(mob/user) - user.visible_message("[user] welds [user.p_their()] every orifice closed! It looks like [user.p_theyre()] trying to commit suicide!") - return (FIRELOSS) - - -/obj/item/weldingtool/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - flamethrower_screwdriver(I, user) - else if(istype(I, /obj/item/stack/rods)) - flamethrower_rods(I, user) - else - return ..() - - -/obj/item/weldingtool/attack(mob/living/carbon/human/H, mob/user) - if(!istype(H)) - return ..() - - var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected)) - - if(affecting && affecting.status == BODYPART_ROBOTIC && user.a_intent != INTENT_HARM) - if(src.remove_fuel(1)) - playsound(loc, usesound, 50, 1) - if(user == H) - user.visible_message("[user] starts to fix some of the dents on [H]'s [affecting.name].", "You start fixing some of the dents on [H]'s [affecting.name].") - if(!do_mob(user, H, 50)) - return - item_heal_robotic(H, user, 15, 0) - else - return ..() - - -/obj/item/weldingtool/afterattack(atom/O, mob/user, proximity) - if(!proximity) return - - if(welding) - remove_fuel(1) - var/turf/location = get_turf(user) - location.hotspot_expose(700, 50, 1) - if(get_fuel() <= 0) - set_light(0) - - if(isliving(O)) - var/mob/living/L = O - if(L.IgniteMob()) - message_admins("[key_name_admin(user)] set [key_name_admin(L)] on fire") - log_game("[key_name(user)] set [key_name(L)] on fire") - - -/obj/item/weldingtool/attack_self(mob/user) - switched_on(user) - if(welding) - set_light(light_intensity) - - update_icon() - - -//Returns the amount of fuel in the welder -/obj/item/weldingtool/proc/get_fuel() - return reagents.get_reagent_amount("welding_fuel") - - -//Removes fuel from the welding tool. If a mob is passed, it will try to flash the mob's eyes. This should probably be renamed to use() -/obj/item/weldingtool/proc/remove_fuel(amount = 1, mob/living/M = null) - if(!welding || !check_fuel()) - return 0 - if(amount) - burned_fuel_for = 0 - if(get_fuel() >= amount) - reagents.remove_reagent("welding_fuel", amount) - check_fuel() - if(M) - M.flash_act(light_intensity) - return TRUE - else - if(M) - to_chat(M, "You need more welding fuel to complete this task!") - return FALSE - - -//Turns off the welder if there is no more fuel (does this really need to be its own proc?) -/obj/item/weldingtool/proc/check_fuel(mob/user) - if(get_fuel() <= 0 && welding) - switched_on(user) - update_icon() - //mob icon update - if(ismob(loc)) - var/mob/M = loc - M.update_inv_hands(0) - - return 0 - return 1 - -//Switches the welder on -/obj/item/weldingtool/proc/switched_on(mob/user) - if(!status) - to_chat(user, "[src] can't be turned on while unsecured!") - return - welding = !welding - if(welding) - if(get_fuel() >= 1) - to_chat(user, "You switch [src] on.") - playsound(loc, acti_sound, 50, 1) - force = 15 - damtype = "fire" - hitsound = 'sound/items/welder.ogg' - update_icon() - START_PROCESSING(SSobj, src) - else - to_chat(user, "You need more fuel!") - switched_off(user) - else - to_chat(user, "You switch [src] off.") - playsound(loc, deac_sound, 50, 1) - switched_off(user) - -//Switches the welder off -/obj/item/weldingtool/proc/switched_off(mob/user) - welding = 0 - set_light(0) - - force = 3 - damtype = "brute" - hitsound = "swing_hit" - update_icon() - - -/obj/item/weldingtool/examine(mob/user) - ..() - to_chat(user, "It contains [get_fuel()] unit\s of fuel out of [max_fuel].") - -/obj/item/weldingtool/is_hot() - return welding * heat - -//Returns whether or not the welding tool is currently on. -/obj/item/weldingtool/proc/isOn() - return welding - - -/obj/item/weldingtool/proc/flamethrower_screwdriver(obj/item/I, mob/user) - if(welding) - to_chat(user, "Turn it off first!") - return - status = !status - if(status) - to_chat(user, "You resecure [src].") - else - to_chat(user, "[src] can now be attached and modified.") - add_fingerprint(user) - -/obj/item/weldingtool/proc/flamethrower_rods(obj/item/I, mob/user) - if(!status) - var/obj/item/stack/rods/R = I - if (R.use(1)) - var/obj/item/flamethrower/F = new /obj/item/flamethrower(user.loc) - if(!remove_item_from_storage(F)) - user.transferItemToLoc(src, F, TRUE) - F.weldtool = src - add_fingerprint(user) - to_chat(user, "You add a rod to a welder, starting to build a flamethrower.") - user.put_in_hands(F) - else - to_chat(user, "You need one rod to start building a flamethrower!") - -/obj/item/weldingtool/ignition_effect(atom/A, mob/user) - if(welding && remove_fuel(1, user)) - . = "[user] casually lights [A] with [src], what a badass." - else - . = "" - -/obj/item/weldingtool/largetank - name = "industrial welding tool" - desc = "A slightly larger welder with a larger tank." - icon_state = "indwelder" - max_fuel = 40 - materials = list(MAT_GLASS=60) - origin_tech = "engineering=2;plasmatech=2" - -/obj/item/weldingtool/largetank/cyborg - name = "integrated welding tool" - desc = "An advanced welder designed to be used in robotic systems." - toolspeed = 0.5 - -/obj/item/weldingtool/largetank/flamethrower_screwdriver() - return - - -/obj/item/weldingtool/mini - name = "emergency welding tool" - desc = "A miniature welder used during emergencies." - icon_state = "miniwelder" - max_fuel = 10 - w_class = WEIGHT_CLASS_TINY - materials = list(MAT_METAL=30, MAT_GLASS=10) - change_icons = 0 - -/obj/item/weldingtool/mini/flamethrower_screwdriver() - return - -/obj/item/weldingtool/abductor - name = "alien welding tool" - desc = "An alien welding tool. Whatever fuel it uses, it never runs out." - icon = 'icons/obj/abductor.dmi' - icon_state = "welder" - toolspeed = 0.1 - light_intensity = 0 - change_icons = 0 - origin_tech = "plasmatech=5;engineering=5;abductor=3" - -/obj/item/weldingtool/abductor/process() - if(get_fuel() <= max_fuel) - reagents.add_reagent("welding_fuel", 1) - ..() - -/obj/item/weldingtool/hugetank - name = "upgraded industrial welding tool" - desc = "An upgraded welder based of the industrial welder." - icon_state = "upindwelder" - item_state = "upindwelder" - max_fuel = 80 - materials = list(MAT_METAL=70, MAT_GLASS=120) - origin_tech = "engineering=3;plasmatech=2" - -/obj/item/weldingtool/experimental - name = "experimental welding tool" - desc = "An experimental welder capable of self-fuel generation and less harmful to the eyes." - icon_state = "exwelder" - item_state = "exwelder" - max_fuel = 40 - materials = list(MAT_METAL=70, MAT_GLASS=120) - origin_tech = "materials=4;engineering=4;bluespace=3;plasmatech=4" - var/last_gen = 0 - change_icons = 0 - can_off_process = 1 - light_intensity = 1 - toolspeed = 0.5 - var/nextrefueltick = 0 - -/obj/item/weldingtool/experimental/brass - name = "brass welding tool" - desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch." - resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "brasswelder" - item_state = "brasswelder" - - -/obj/item/weldingtool/experimental/process() - ..() - if(get_fuel() < max_fuel && nextrefueltick < world.time) - nextrefueltick = world.time + 10 - reagents.add_reagent("welding_fuel", 1) - - -/* - * Crowbar - */ - -/obj/item/crowbar - name = "pocket crowbar" - desc = "A small crowbar. This handy tool is useful for lots of things, such as prying floor tiles or opening unpowered doors." - icon = 'icons/obj/tools.dmi' - icon_state = "crowbar" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - usesound = 'sound/items/crowbar.ogg' - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT - force = 5 - throwforce = 7 - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50) - origin_tech = "engineering=1;combat=1" - attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked") - toolspeed = 1 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) - -/obj/item/crowbar/suicide_act(mob/user) - user.visible_message("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) - return (BRUTELOSS) - -/obj/item/crowbar/red - icon_state = "crowbar_red" - force = 8 - -/obj/item/crowbar/brass - name = "brass crowbar" - desc = "A brass crowbar. It feels faintly warm to the touch." - resistance_flags = FIRE_PROOF | ACID_PROOF - icon_state = "crowbar_brass" - toolspeed = 0.5 - -/obj/item/crowbar/abductor - name = "alien crowbar" - desc = "A hard-light crowbar. It appears to pry by itself, without any effort required." - icon = 'icons/obj/abductor.dmi' - usesound = 'sound/weapons/sonic_jackhammer.ogg' - icon_state = "crowbar" - toolspeed = 0.1 - origin_tech = "combat=4;engineering=4;abductor=3" - -/obj/item/crowbar/large - name = "crowbar" - desc = "It's a big crowbar. It doesn't fit in your pockets, because it's big." - force = 12 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 3 - throw_range = 3 - materials = list(MAT_METAL=70) - icon_state = "crowbar_large" - item_state = "crowbar" - toolspeed = 0.5 - -/obj/item/crowbar/cyborg - name = "hydraulic crowbar" - desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbar in construction cyborgs." - usesound = 'sound/items/jaws_pry.ogg' - force = 10 - toolspeed = 0.5 - -/obj/item/crowbar/power - name = "jaws of life" - desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a prying head." - icon_state = "jaws_pry" - item_state = "jawsoflife" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - origin_tech = "materials=2;engineering=2" - usesound = 'sound/items/jaws_pry.ogg' - force = 15 - toolspeed = 0.25 - -/obj/item/crowbar/power/suicide_act(mob/user) - user.visible_message("[user] is putting [user.p_their()] head in [src], it looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/items/jaws_pry.ogg', 50, 1, -1) - return (BRUTELOSS) - -/obj/item/crowbar/power/attack_self(mob/user) - playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) - var/obj/item/wirecutters/power/cutjaws = new /obj/item/wirecutters/power - to_chat(user, "You attach the cutting jaws to [src].") - qdel(src) - user.put_in_active_hand(cutjaws) - -#undef WELDER_FUEL_BURN_INTERVAL +#define WELDER_FUEL_BURN_INTERVAL 13 + +/* Tools! + * Note: Multitools are /obj/item/device + * + * Contains: + * Wrench + * Screwdriver + * Wirecutters + * Welding Tool + * Crowbar + */ + +/* + * Wrench + */ +/obj/item/wrench + name = "wrench" + desc = "A wrench with common uses. Can be found in your hand." + icon = 'icons/obj/tools.dmi' + icon_state = "wrench" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT + force = 5 + throwforce = 7 + w_class = WEIGHT_CLASS_SMALL + usesound = 'sound/items/ratchet.ogg' + materials = list(MAT_METAL=150) + origin_tech = "materials=1;engineering=1" + attack_verb = list("bashed", "battered", "bludgeoned", "whacked") + toolspeed = 1 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) + +/obj/item/wrench/suicide_act(mob/user) + user.visible_message("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) + return (BRUTELOSS) + +/obj/item/wrench/cyborg + name = "automatic wrench" + desc = "An advanced robotic wrench. Can be found in construction cyborgs." + toolspeed = 0.5 + +/obj/item/wrench/brass + name = "brass wrench" + desc = "A brass wrench. It's faintly warm to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon_state = "wrench_brass" + toolspeed = 0.5 + +/obj/item/wrench/abductor + name = "alien wrench" + desc = "A polarized wrench. It causes anything placed between the jaws to turn." + icon = 'icons/obj/abductor.dmi' + icon_state = "wrench" + usesound = 'sound/effects/empulse.ogg' + toolspeed = 0.1 + origin_tech = "materials=5;engineering=5;abductor=3" + +/obj/item/wrench/power + name = "hand drill" + desc = "A simple powered hand drill. It's fitted with a bolt bit." + icon_state = "drill_bolt" + item_state = "drill" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + usesound = 'sound/items/drill_use.ogg' + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get + force = 8 //might or might not be too high, subject to change + w_class = WEIGHT_CLASS_SMALL + throwforce = 8 + attack_verb = list("drilled", "screwed", "jabbed") + toolspeed = 0.25 + +/obj/item/wrench/power/attack_self(mob/user) + playsound(get_turf(user),'sound/items/change_drill.ogg',50,1) + var/obj/item/wirecutters/power/s_drill = new /obj/item/screwdriver/power + to_chat(user, "You attach the screw driver bit to [src].") + qdel(src) + user.put_in_active_hand(s_drill) + +/obj/item/wrench/power/suicide_act(mob/user) + user.visible_message("[user] is pressing [src] against [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!") + return (BRUTELOSS) + +/obj/item/wrench/medical + name = "medical wrench" + desc = "A medical wrench with common(medical?) uses. Can be found in your hand." + icon_state = "wrench_medical" + force = 2 //MEDICAL + throwforce = 4 + origin_tech = "materials=1;engineering=1;biotech=3" + attack_verb = list("wrenched", "medicaled", "tapped", "jabbed", "whacked") + +/obj/item/wrench/medical/suicide_act(mob/living/user) + user.visible_message("[user] is praying to the medical wrench to take [user.p_their()] soul. It looks like [user.p_theyre()] trying to commit suicide!") + // TODO Make them glow with the power of the M E D I C A L W R E N C H + // during their ascension + + // Stun stops them from wandering off + user.Stun(100, ignore_canstun = TRUE) + playsound(loc, 'sound/effects/pray.ogg', 50, 1, -1) + + // Let the sound effect finish playing + sleep(20) + + if(!user) + return + + for(var/obj/item/W in user) + user.dropItemToGround(W) + + var/obj/item/wrench/medical/W = new /obj/item/wrench/medical(loc) + W.add_fingerprint(user) + W.desc += " For some reason, it reminds you of [user.name]." + + if(!user) + return + + user.dust() + + return OXYLOSS + +/* + * Screwdriver + */ +/obj/item/screwdriver + name = "screwdriver" + desc = "You can be totally screwy with this." + icon = 'icons/obj/tools.dmi' + icon_state = "screwdriver" + item_state = "screwdriver" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT + force = 5 + w_class = WEIGHT_CLASS_TINY + throwforce = 5 + throw_speed = 3 + throw_range = 5 + materials = list(MAT_METAL=75) + attack_verb = list("stabbed") + hitsound = 'sound/weapons/bladeslice.ogg' + usesound = 'sound/items/screwdriver.ogg' + toolspeed = 1 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) + var/random_color = TRUE //if the screwdriver uses random coloring + var/static/list/screwdriver_colors = list(\ + "blue" = rgb(24, 97, 213), \ + "red" = rgb(149, 23, 16), \ + "pink" = rgb(213, 24, 141), \ + "brown" = rgb(160, 82, 18), \ + "green" = rgb(14, 127, 27), \ + "cyan" = rgb(24, 162, 213), \ + "yellow" = rgb(213, 140, 24), \ + ) + +/obj/item/screwdriver/suicide_act(mob/user) + user.visible_message("[user] is stabbing [src] into [user.p_their()] [pick("temple", "heart")]! It looks like [user.p_theyre()] trying to commit suicide!") + return(BRUTELOSS) + +/obj/item/screwdriver/Initialize() + . = ..() + if(random_color) //random colors! + var/our_color = pick(screwdriver_colors) + add_atom_colour(screwdriver_colors[our_color], FIXED_COLOUR_PRIORITY) + update_icon() + if(prob(75)) + pixel_y = rand(0, 16) + +/obj/item/screwdriver/update_icon() + if(!random_color) //icon override + return + cut_overlays() + var/mutable_appearance/base_overlay = mutable_appearance(icon, "screwdriver_screwybits") + base_overlay.appearance_flags = RESET_COLOR + add_overlay(base_overlay) + +/obj/item/screwdriver/worn_overlays(isinhands = FALSE, icon_file) + . = list() + if(isinhands && random_color) + var/mutable_appearance/M = mutable_appearance(icon_file, "screwdriver_head") + M.appearance_flags = RESET_COLOR + . += M + +/obj/item/screwdriver/get_belt_overlay() + if(random_color) + var/mutable_appearance/body = mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "screwdriver") + var/mutable_appearance/head = mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "screwdriver_head") + body.color = color + head.add_overlay(body) + return head + else + return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', icon_state) + +/obj/item/screwdriver/attack(mob/living/carbon/M, mob/living/carbon/user) + if(!istype(M)) + return ..() + if(user.zone_selected != "eyes" && user.zone_selected != "head") + return ..() + if(user.disabilities & CLUMSY && prob(50)) + M = user + return eyestab(M,user) + +/obj/item/screwdriver/brass + name = "brass screwdriver" + desc = "A screwdriver made of brass. The handle feels freezing cold." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon_state = "screwdriver_brass" + item_state = "screwdriver_brass" + toolspeed = 0.5 + random_color = FALSE + +/obj/item/screwdriver/abductor + name = "alien screwdriver" + desc = "An ultrasonic screwdriver." + icon = 'icons/obj/abductor.dmi' + icon_state = "screwdriver_a" + item_state = "screwdriver_nuke" + usesound = 'sound/items/pshoom.ogg' + toolspeed = 0.1 + random_color = FALSE + +/obj/item/screwdriver/power + name = "hand drill" + desc = "A simple powered hand drill. It's fitted with a screw bit." + icon_state = "drill_screw" + item_state = "drill" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get + force = 8 //might or might not be too high, subject to change + w_class = WEIGHT_CLASS_SMALL + throwforce = 8 + throw_speed = 2 + throw_range = 3//it's heavier than a screw driver/wrench, so it does more damage, but can't be thrown as far + attack_verb = list("drilled", "screwed", "jabbed","whacked") + hitsound = 'sound/items/drill_hit.ogg' + usesound = 'sound/items/drill_use.ogg' + toolspeed = 0.25 + random_color = FALSE + +/obj/item/screwdriver/power/suicide_act(mob/user) + user.visible_message("[user] is putting [src] to [user.p_their()] temple. It looks like [user.p_theyre()] trying to commit suicide!") + return(BRUTELOSS) + +/obj/item/screwdriver/power/attack_self(mob/user) + playsound(get_turf(user),'sound/items/change_drill.ogg',50,1) + var/obj/item/wrench/power/b_drill = new /obj/item/wrench/power + to_chat(user, "You attach the bolt driver bit to [src].") + qdel(src) + user.put_in_active_hand(b_drill) + +/obj/item/screwdriver/cyborg + name = "powered screwdriver" + desc = "An electrical screwdriver, designed to be both precise and quick." + usesound = 'sound/items/drill_use.ogg' + toolspeed = 0.5 + +/* + * Wirecutters + */ +/obj/item/wirecutters + name = "wirecutters" + desc = "This cuts wires." + icon = 'icons/obj/tools.dmi' + icon_state = null + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT + force = 6 + throw_speed = 3 + throw_range = 7 + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=80) + attack_verb = list("pinched", "nipped") + hitsound = 'sound/items/wirecutter.ogg' + usesound = 'sound/items/wirecutter.ogg' + origin_tech = "materials=1;engineering=1" + toolspeed = 1 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) + + +/obj/item/wirecutters/New(loc, var/param_color = null) + ..() + if(!icon_state) + if(!param_color) + param_color = pick("yellow","red") + icon_state = "cutters_[param_color]" + +/obj/item/wirecutters/attack(mob/living/carbon/C, mob/user) + if(istype(C) && C.handcuffed && istype(C.handcuffed, /obj/item/restraints/handcuffs/cable)) + user.visible_message("[user] cuts [C]'s restraints with [src]!") + qdel(C.handcuffed) + C.handcuffed = null + if(C.buckled && C.buckled.buckle_requires_restraints) + C.buckled.unbuckle_mob(C) + C.update_handcuffed() + return + else + ..() + +/obj/item/wirecutters/suicide_act(mob/user) + user.visible_message("[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, usesound, 50, 1, -1) + return (BRUTELOSS) + +/obj/item/wirecutters/brass + name = "brass wirecutters" + desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon_state = "cutters_brass" + toolspeed = 0.5 + +/obj/item/wirecutters/abductor + name = "alien wirecutters" + desc = "Extremely sharp wirecutters, made out of a silvery-green metal." + icon = 'icons/obj/abductor.dmi' + icon_state = "cutters" + toolspeed = 0.1 + origin_tech = "materials=5;engineering=4;abductor=3" + +/obj/item/wirecutters/cyborg + name = "wirecutters" + desc = "This cuts wires." + toolspeed = 0.5 + +/obj/item/wirecutters/power + name = "jaws of life" + desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a cutting head." + icon_state = "jaws_cutter" + item_state = "jawsoflife" + origin_tech = "materials=2;engineering=2" + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + usesound = 'sound/items/jaws_cut.ogg' + toolspeed = 0.25 + +/obj/item/wirecutters/power/suicide_act(mob/user) + user.visible_message("[user] is wrapping \the [src] around [user.p_their()] neck. It looks like [user.p_theyre()] trying to rip [user.p_their()] head off!") + playsound(loc, 'sound/items/jaws_cut.ogg', 50, 1, -1) + if(iscarbon(user)) + var/mob/living/carbon/C = user + var/obj/item/bodypart/BP = C.get_bodypart("head") + if(BP) + BP.drop_limb() + playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1) + return (BRUTELOSS) + +/obj/item/wirecutters/power/attack_self(mob/user) + playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) + var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power + to_chat(user, "You attach the pry jaws to [src].") + qdel(src) + user.put_in_active_hand(pryjaws) +/* + * Welding Tool + */ +/obj/item/weldingtool + name = "welding tool" + desc = "A standard edition welder provided by Nanotrasen." + icon = 'icons/obj/tools.dmi' + icon_state = "welder" + item_state = "welder" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT + force = 3 + throwforce = 5 + hitsound = "swing_hit" + usesound = 'sound/items/welder.ogg' + var/acti_sound = 'sound/items/welderactivate.ogg' + var/deac_sound = 'sound/items/welderdeactivate.ogg' + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 30) + resistance_flags = FIRE_PROOF + + materials = list(MAT_METAL=70, MAT_GLASS=30) + origin_tech = "engineering=1;plasmatech=1" + var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2) + var/status = TRUE //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower) + var/max_fuel = 20 //The max amount of fuel the welder can hold + var/change_icons = 1 + var/can_off_process = 0 + var/light_intensity = 2 //how powerful the emitted light is when used. + var/burned_fuel_for = 0 //when fuel was last removed + heat = 3800 + toolspeed = 1 + +/obj/item/weldingtool/Initialize() + . = ..() + create_reagents(max_fuel) + reagents.add_reagent("welding_fuel", max_fuel) + update_icon() + + +/obj/item/weldingtool/proc/update_torch() + if(welding) + add_overlay("[initial(icon_state)]-on") + item_state = "[initial(item_state)]1" + else + item_state = "[initial(item_state)]" + + +/obj/item/weldingtool/update_icon() + cut_overlays() + if(change_icons) + var/ratio = get_fuel() / max_fuel + ratio = Ceiling(ratio*4) * 25 + add_overlay("[initial(icon_state)][ratio]") + update_torch() + return + + +/obj/item/weldingtool/process() + switch(welding) + if(0) + force = 3 + damtype = "brute" + update_icon() + if(!can_off_process) + STOP_PROCESSING(SSobj, src) + return + //Welders left on now use up fuel, but lets not have them run out quite that fast + if(1) + force = 15 + damtype = "fire" + ++burned_fuel_for + if(burned_fuel_for >= WELDER_FUEL_BURN_INTERVAL) + remove_fuel(1) + update_icon() + + //This is to start fires. process() is only called if the welder is on. + open_flame() + + +/obj/item/weldingtool/suicide_act(mob/user) + user.visible_message("[user] welds [user.p_their()] every orifice closed! It looks like [user.p_theyre()] trying to commit suicide!") + return (FIRELOSS) + + +/obj/item/weldingtool/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + flamethrower_screwdriver(I, user) + else if(istype(I, /obj/item/stack/rods)) + flamethrower_rods(I, user) + else + return ..() + + +/obj/item/weldingtool/attack(mob/living/carbon/human/H, mob/user) + if(!istype(H)) + return ..() + + var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected)) + + if(affecting && affecting.status == BODYPART_ROBOTIC && user.a_intent != INTENT_HARM) + if(src.remove_fuel(1)) + playsound(loc, usesound, 50, 1) + if(user == H) + user.visible_message("[user] starts to fix some of the dents on [H]'s [affecting.name].", "You start fixing some of the dents on [H]'s [affecting.name].") + if(!do_mob(user, H, 50)) + return + item_heal_robotic(H, user, 15, 0) + else + return ..() + + +/obj/item/weldingtool/afterattack(atom/O, mob/user, proximity) + if(!proximity) return + + if(welding) + remove_fuel(1) + var/turf/location = get_turf(user) + location.hotspot_expose(700, 50, 1) + if(get_fuel() <= 0) + set_light(0) + + if(isliving(O)) + var/mob/living/L = O + if(L.IgniteMob()) + message_admins("[key_name_admin(user)] set [key_name_admin(L)] on fire") + log_game("[key_name(user)] set [key_name(L)] on fire") + + +/obj/item/weldingtool/attack_self(mob/user) + switched_on(user) + if(welding) + set_light(light_intensity) + + update_icon() + + +//Returns the amount of fuel in the welder +/obj/item/weldingtool/proc/get_fuel() + return reagents.get_reagent_amount("welding_fuel") + + +//Removes fuel from the welding tool. If a mob is passed, it will try to flash the mob's eyes. This should probably be renamed to use() +/obj/item/weldingtool/proc/remove_fuel(amount = 1, mob/living/M = null) + if(!welding || !check_fuel()) + return 0 + if(amount) + burned_fuel_for = 0 + if(get_fuel() >= amount) + reagents.remove_reagent("welding_fuel", amount) + check_fuel() + if(M) + M.flash_act(light_intensity) + return TRUE + else + if(M) + to_chat(M, "You need more welding fuel to complete this task!") + return FALSE + + +//Turns off the welder if there is no more fuel (does this really need to be its own proc?) +/obj/item/weldingtool/proc/check_fuel(mob/user) + if(get_fuel() <= 0 && welding) + switched_on(user) + update_icon() + //mob icon update + if(ismob(loc)) + var/mob/M = loc + M.update_inv_hands(0) + + return 0 + return 1 + +//Switches the welder on +/obj/item/weldingtool/proc/switched_on(mob/user) + if(!status) + to_chat(user, "[src] can't be turned on while unsecured!") + return + welding = !welding + if(welding) + if(get_fuel() >= 1) + to_chat(user, "You switch [src] on.") + playsound(loc, acti_sound, 50, 1) + force = 15 + damtype = "fire" + hitsound = 'sound/items/welder.ogg' + update_icon() + START_PROCESSING(SSobj, src) + else + to_chat(user, "You need more fuel!") + switched_off(user) + else + to_chat(user, "You switch [src] off.") + playsound(loc, deac_sound, 50, 1) + switched_off(user) + +//Switches the welder off +/obj/item/weldingtool/proc/switched_off(mob/user) + welding = 0 + set_light(0) + + force = 3 + damtype = "brute" + hitsound = "swing_hit" + update_icon() + + +/obj/item/weldingtool/examine(mob/user) + ..() + to_chat(user, "It contains [get_fuel()] unit\s of fuel out of [max_fuel].") + +/obj/item/weldingtool/is_hot() + return welding * heat + +//Returns whether or not the welding tool is currently on. +/obj/item/weldingtool/proc/isOn() + return welding + + +/obj/item/weldingtool/proc/flamethrower_screwdriver(obj/item/I, mob/user) + if(welding) + to_chat(user, "Turn it off first!") + return + status = !status + if(status) + to_chat(user, "You resecure [src].") + else + to_chat(user, "[src] can now be attached and modified.") + add_fingerprint(user) + +/obj/item/weldingtool/proc/flamethrower_rods(obj/item/I, mob/user) + if(!status) + var/obj/item/stack/rods/R = I + if (R.use(1)) + var/obj/item/flamethrower/F = new /obj/item/flamethrower(user.loc) + if(!remove_item_from_storage(F)) + user.transferItemToLoc(src, F, TRUE) + F.weldtool = src + add_fingerprint(user) + to_chat(user, "You add a rod to a welder, starting to build a flamethrower.") + user.put_in_hands(F) + else + to_chat(user, "You need one rod to start building a flamethrower!") + +/obj/item/weldingtool/ignition_effect(atom/A, mob/user) + if(welding && remove_fuel(1, user)) + . = "[user] casually lights [A] with [src], what a badass." + else + . = "" + +/obj/item/weldingtool/largetank + name = "industrial welding tool" + desc = "A slightly larger welder with a larger tank." + icon_state = "indwelder" + max_fuel = 40 + materials = list(MAT_GLASS=60) + origin_tech = "engineering=2;plasmatech=2" + +/obj/item/weldingtool/largetank/cyborg + name = "integrated welding tool" + desc = "An advanced welder designed to be used in robotic systems." + toolspeed = 0.5 + +/obj/item/weldingtool/largetank/flamethrower_screwdriver() + return + + +/obj/item/weldingtool/mini + name = "emergency welding tool" + desc = "A miniature welder used during emergencies." + icon_state = "miniwelder" + max_fuel = 10 + w_class = WEIGHT_CLASS_TINY + materials = list(MAT_METAL=30, MAT_GLASS=10) + change_icons = 0 + +/obj/item/weldingtool/mini/flamethrower_screwdriver() + return + +/obj/item/weldingtool/abductor + name = "alien welding tool" + desc = "An alien welding tool. Whatever fuel it uses, it never runs out." + icon = 'icons/obj/abductor.dmi' + icon_state = "welder" + toolspeed = 0.1 + light_intensity = 0 + change_icons = 0 + origin_tech = "plasmatech=5;engineering=5;abductor=3" + +/obj/item/weldingtool/abductor/process() + if(get_fuel() <= max_fuel) + reagents.add_reagent("welding_fuel", 1) + ..() + +/obj/item/weldingtool/hugetank + name = "upgraded industrial welding tool" + desc = "An upgraded welder based of the industrial welder." + icon_state = "upindwelder" + item_state = "upindwelder" + max_fuel = 80 + materials = list(MAT_METAL=70, MAT_GLASS=120) + origin_tech = "engineering=3;plasmatech=2" + +/obj/item/weldingtool/experimental + name = "experimental welding tool" + desc = "An experimental welder capable of self-fuel generation and less harmful to the eyes." + icon_state = "exwelder" + item_state = "exwelder" + max_fuel = 40 + materials = list(MAT_METAL=70, MAT_GLASS=120) + origin_tech = "materials=4;engineering=4;bluespace=3;plasmatech=4" + var/last_gen = 0 + change_icons = 0 + can_off_process = 1 + light_intensity = 1 + toolspeed = 0.5 + var/nextrefueltick = 0 + +/obj/item/weldingtool/experimental/brass + name = "brass welding tool" + desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon_state = "brasswelder" + item_state = "brasswelder" + + +/obj/item/weldingtool/experimental/process() + ..() + if(get_fuel() < max_fuel && nextrefueltick < world.time) + nextrefueltick = world.time + 10 + reagents.add_reagent("welding_fuel", 1) + + +/* + * Crowbar + */ + +/obj/item/crowbar + name = "pocket crowbar" + desc = "A small crowbar. This handy tool is useful for lots of things, such as prying floor tiles or opening unpowered doors." + icon = 'icons/obj/tools.dmi' + icon_state = "crowbar" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + usesound = 'sound/items/crowbar.ogg' + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT + force = 5 + throwforce = 7 + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50) + origin_tech = "engineering=1;combat=1" + attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked") + toolspeed = 1 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30) + +/obj/item/crowbar/suicide_act(mob/user) + user.visible_message("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) + return (BRUTELOSS) + +/obj/item/crowbar/red + icon_state = "crowbar_red" + force = 8 + +/obj/item/crowbar/brass + name = "brass crowbar" + desc = "A brass crowbar. It feels faintly warm to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon_state = "crowbar_brass" + toolspeed = 0.5 + +/obj/item/crowbar/abductor + name = "alien crowbar" + desc = "A hard-light crowbar. It appears to pry by itself, without any effort required." + icon = 'icons/obj/abductor.dmi' + usesound = 'sound/weapons/sonic_jackhammer.ogg' + icon_state = "crowbar" + toolspeed = 0.1 + origin_tech = "combat=4;engineering=4;abductor=3" + +/obj/item/crowbar/large + name = "crowbar" + desc = "It's a big crowbar. It doesn't fit in your pockets, because it's big." + force = 12 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 3 + throw_range = 3 + materials = list(MAT_METAL=70) + icon_state = "crowbar_large" + item_state = "crowbar" + toolspeed = 0.5 + +/obj/item/crowbar/cyborg + name = "hydraulic crowbar" + desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbar in construction cyborgs." + usesound = 'sound/items/jaws_pry.ogg' + force = 10 + toolspeed = 0.5 + +/obj/item/crowbar/power + name = "jaws of life" + desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a prying head." + icon_state = "jaws_pry" + item_state = "jawsoflife" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + origin_tech = "materials=2;engineering=2" + usesound = 'sound/items/jaws_pry.ogg' + force = 15 + toolspeed = 0.25 + +/obj/item/crowbar/power/suicide_act(mob/user) + user.visible_message("[user] is putting [user.p_their()] head in [src], it looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/items/jaws_pry.ogg', 50, 1, -1) + return (BRUTELOSS) + +/obj/item/crowbar/power/attack_self(mob/user) + playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) + var/obj/item/wirecutters/power/cutjaws = new /obj/item/wirecutters/power + to_chat(user, "You attach the cutting jaws to [src].") + qdel(src) + user.put_in_active_hand(cutjaws) + +#undef WELDER_FUEL_BURN_INTERVAL diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index 323527fc88..6c19b49073 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -487,7 +487,7 @@ righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' flags_1 = CONDUCT_1 force = 13 - var/force_on = 21 + var/force_on = 24 w_class = WEIGHT_CLASS_HUGE throwforce = 13 throw_speed = 2 diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 13acf7e990..17bbb0a12f 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -1,592 +1,592 @@ -/obj/item/banhammer - desc = "A banhammer" - name = "banhammer" - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "toyhammer" - slot_flags = SLOT_BELT - throwforce = 0 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - attack_verb = list("banned") - max_integrity = 200 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 70) - resistance_flags = FIRE_PROOF - -/obj/item/banhammer/suicide_act(mob/user) - user.visible_message("[user] is hitting [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.") - return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS) - -/obj/item/banhammer/attack(mob/M, mob/user) +/obj/item/banhammer + desc = "A banhammer" + name = "banhammer" + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "toyhammer" + slot_flags = SLOT_BELT + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + attack_verb = list("banned") + max_integrity = 200 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 70) + resistance_flags = FIRE_PROOF + +/obj/item/banhammer/suicide_act(mob/user) + user.visible_message("[user] is hitting [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.") + return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS) + +/obj/item/banhammer/attack(mob/M, mob/user) if(user.zone_selected == "head") M.visible_message("[user] are stroking the head of [M] with a bangammer", "[user] are stroking the head with a bangammer", "you hear a bangammer stroking a head"); else M.visible_message("[M] has been banned FOR NO REISIN by [user]", "You have been banned FOR NO REISIN by [user]", "you hear a banhammer banning someone") - playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much - -/obj/item/sord - name = "\improper SORD" - desc = "This thing is so unspeakably shitty you are having a hard time even holding it." - icon_state = "sord" - item_state = "sord" - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - slot_flags = SLOT_BELT - force = 2 - throwforce = 1 - w_class = WEIGHT_CLASS_NORMAL - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - -/obj/item/sord/suicide_act(mob/user) - user.visible_message("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty.", \ - "You try to impale yourself with [src], but it's USELESS...") - return SHAME - -/obj/item/claymore - name = "claymore" - desc = "What are you standing around staring at this for? Get to killing!" - icon_state = "claymore" - item_state = "claymore" - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - hitsound = 'sound/weapons/bladeslice.ogg' - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT | SLOT_BACK - force = 40 - throwforce = 10 - w_class = WEIGHT_CLASS_NORMAL - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 50 - sharpness = IS_SHARP - max_integrity = 200 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/item/claymore/suicide_act(mob/user) - user.visible_message("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!") - return(BRUTELOSS) - -/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS - desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim." - flags_1 = CONDUCT_1 | NODROP_1 | DROPDEL_1 - slot_flags = null - block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY - luminosity = 3 - attack_verb = list("brutalized", "eviscerated", "disemboweled", "hacked", "carved", "cleaved") //ONLY THE MOST VISCERAL ATTACK VERBS - var/notches = 0 //HOW MANY PEOPLE HAVE BEEN SLAIN WITH THIS BLADE - var/obj/item/disk/nuclear/nuke_disk //OUR STORED NUKE DISK - -/obj/item/claymore/highlander/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/item/claymore/highlander/Destroy() - if(nuke_disk) - nuke_disk.forceMove(get_turf(src)) - nuke_disk.visible_message("The nuke disk is vulnerable!") - nuke_disk = null - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/claymore/highlander/process() - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - loc.layer = LARGE_MOB_LAYER //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS) - H.bleedsuppress = TRUE //AND WE WON'T BLEED OUT LIKE COWARDS - else - if(!admin_spawned) - qdel(src) - - -/obj/item/claymore/highlander/pickup(mob/living/user) - to_chat(user, "The power of Scotland protects you! You are shielded from all stuns and knockdowns.") - user.add_stun_absorption("highlander", INFINITY, 1, " is protected by the power of Scotland!", "The power of Scotland absorbs the stun!", " is protected by the power of Scotland!") - user.status_flags += IGNORESLOWDOWN - -/obj/item/claymore/highlander/dropped(mob/living/user) - user.status_flags -= IGNORESLOWDOWN - qdel(src) //If this ever happens, it's because you lost an arm - -/obj/item/claymore/highlander/examine(mob/user) - ..() - to_chat(user, "It has [!notches ? "nothing" : "[notches] notches"] scratched into the blade.") - if(nuke_disk) - to_chat(user, "It's holding the nuke disk!") - -/obj/item/claymore/highlander/attack(mob/living/target, mob/living/user) - . = ..() - if(target && target.stat == DEAD && target.mind && target.mind.special_role == "highlander") - user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES - add_notch(user) - target.visible_message("[target] crumbles to dust beneath [user]'s blows!", "As you fall, your body crumbles to dust!") - target.dust() - -/obj/item/claymore/highlander/attack_self(mob/living/user) - var/closest_victim - var/closest_distance = 255 - for(var/mob/living/carbon/human/H in GLOB.player_list - user) - if(H.client && H.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) - closest_victim = H - if(!closest_victim) - to_chat(user, "[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.") - return - to_chat(user, "[src] thrums and points to the [dir2text(get_dir(user, closest_victim))].") - -/obj/item/claymore/highlander/IsReflect() - return 1 //YOU THINK YOUR PUNY LASERS CAN STOP ME? - -/obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE - notches++ - force++ - var/new_name = name - switch(notches) - if(1) - to_chat(user, "Your first kill - hopefully one of many. You scratch a notch into [src]'s blade.") - to_chat(user, "You feel your fallen foe's soul entering your blade, restoring your wounds!") - new_name = "notched claymore" - if(2) - to_chat(user, "Another falls before you. Another soul fuses with your own. Another notch in the blade.") - new_name = "double-notched claymore" - add_atom_colour(rgb(255, 235, 235), ADMIN_COLOUR_PRIORITY) - if(3) - to_chat(user, "You're beginning to relish the thrill of battle.") - new_name = "triple-notched claymore" - add_atom_colour(rgb(255, 215, 215), ADMIN_COLOUR_PRIORITY) - if(4) - to_chat(user, "You've lost count of how many you've killed.") - new_name = "many-notched claymore" - add_atom_colour(rgb(255, 195, 195), ADMIN_COLOUR_PRIORITY) - if(5) - to_chat(user, "Five voices now echo in your mind, cheering the slaughter.") - new_name = "battle-tested claymore" - add_atom_colour(rgb(255, 175, 175), ADMIN_COLOUR_PRIORITY) - if(6) - to_chat(user, "Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe.") - new_name = "battle-scarred claymore" - add_atom_colour(rgb(255, 155, 155), ADMIN_COLOUR_PRIORITY) - if(7) - to_chat(user, "Kill. Butcher. Conquer.") - new_name = "vicious claymore" - add_atom_colour(rgb(255, 135, 135), ADMIN_COLOUR_PRIORITY) - if(8) - to_chat(user, "IT NEVER GETS OLD. THE SCREAMING. THE BLOOD AS IT SPRAYS ACROSS YOUR FACE.") - new_name = "bloodthirsty claymore" - add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY) - if(9) - to_chat(user, "ANOTHER ONE FALLS TO YOUR BLOWS. ANOTHER WEAKLING UNFIT TO LIVE.") - new_name = "gore-stained claymore" - add_atom_colour(rgb(255, 95, 95), ADMIN_COLOUR_PRIORITY) - if(10) - user.visible_message("[user]'s eyes light up with a vengeful fire!", \ - "YOU FEEL THE POWER OF VALHALLA FLOWING THROUGH YOU! THERE CAN BE ONLY ONE!!!") - user.update_icons() - new_name = "GORE-DRENCHED CLAYMORE OF [pick("THE WHIMSICAL SLAUGHTER", "A THOUSAND SLAUGHTERED CATTLE", "GLORY AND VALHALLA", "ANNIHILATION", "OBLITERATION")]" - icon_state = "claymore_valhalla" - item_state = "cultblade" - remove_atom_colour(ADMIN_COLOUR_PRIORITY) - - name = new_name - playsound(user, 'sound/items/screwdriver2.ogg', 50, 1) - -/obj/item/katana - name = "katana" - desc = "Woefully underpowered in D20" - icon_state = "katana" - item_state = "katana" - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - flags_1 = CONDUCT_1 - slot_flags = SLOT_BELT | SLOT_BACK - force = 40 - throwforce = 10 - w_class = WEIGHT_CLASS_NORMAL - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 50 - sharpness = IS_SHARP - max_integrity = 200 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/item/katana/cursed - slot_flags = null - -/obj/item/katana/suicide_act(mob/user) - user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!") - return(BRUTELOSS) - -/obj/item/wirerod - name = "wired rod" - desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit." - icon_state = "wiredrod" - item_state = "rods" - flags_1 = CONDUCT_1 - force = 9 - throwforce = 10 - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=1150, MAT_GLASS=75) - attack_verb = list("hit", "bludgeoned", "whacked", "bonked") - -/obj/item/wirerod/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/shard)) - var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear - - remove_item_from_storage(user) - qdel(I) - qdel(src) - - user.put_in_hands(S) - to_chat(user, "You fasten the glass shard to the top of the rod with the cable.") - - else if(istype(I, /obj/item/device/assembly/igniter) && !(I.flags_1 & NODROP_1)) - var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod - - remove_item_from_storage(user) - - to_chat(user, "You fasten [I] to the top of the rod with the cable.") - - qdel(I) - qdel(src) - - user.put_in_hands(P) - else - return ..() - - -/obj/item/throwing_star - name = "throwing star" - desc = "An ancient weapon still used to this day due to it's ease of lodging itself into victim's body parts" - icon_state = "throwingstar" - item_state = "eshield0" - lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' - force = 2 - throwforce = 20 //This is never used on mobs since this has a 100% embed chance. - throw_speed = 4 - embedded_pain_multiplier = 4 - w_class = WEIGHT_CLASS_SMALL - embed_chance = 100 - embedded_fall_chance = 0 //Hahaha! - sharpness = IS_SHARP - materials = list(MAT_METAL=500, MAT_GLASS=500) - resistance_flags = FIRE_PROOF - - -/obj/item/switchblade - name = "switchblade" - icon_state = "switchblade" - lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' - desc = "A sharp, concealable, spring-loaded knife." - flags_1 = CONDUCT_1 - force = 3 - w_class = WEIGHT_CLASS_SMALL - throwforce = 5 - throw_speed = 3 - throw_range = 6 - materials = list(MAT_METAL=12000) - origin_tech = "engineering=3;combat=2" - hitsound = 'sound/weapons/genhit.ogg' - attack_verb = list("stubbed", "poked") - resistance_flags = FIRE_PROOF - var/extended = 0 - -/obj/item/switchblade/attack_self(mob/user) - extended = !extended - playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) - if(extended) - force = 20 - w_class = WEIGHT_CLASS_NORMAL - throwforce = 23 - icon_state = "switchblade_ext" - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP - else - force = 3 - w_class = WEIGHT_CLASS_SMALL - throwforce = 5 - icon_state = "switchblade" - attack_verb = list("stubbed", "poked") - hitsound = 'sound/weapons/genhit.ogg' - sharpness = IS_BLUNT - -/obj/item/switchblade/suicide_act(mob/user) - user.visible_message("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!") - return (BRUTELOSS) - -/obj/item/phone - name = "red phone" - desc = "Should anything ever go wrong..." - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "red_phone" - force = 3 - throwforce = 2 - throw_speed = 3 - throw_range = 4 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("called", "rang") - hitsound = 'sound/weapons/ring.ogg' - -/obj/item/phone/suicide_act(mob/user) - if(locate(/obj/structure/chair/stool) in user.loc) - user.visible_message("[user] begins to tie a noose with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!") - else - user.visible_message("[user] is strangling [user.p_them()]self with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!") - return(OXYLOSS) - -/obj/item/cane - name = "cane" - desc = "A cane used by a true gentleman. Or a clown." - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "cane" - item_state = "stick" - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 5 - throwforce = 5 - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50) - attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") - -/obj/item/staff - name = "wizard staff" - desc = "Apparently a staff used by the wizard." - icon = 'icons/obj/wizard.dmi' - icon_state = "staff" - lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' - force = 3 - throwforce = 5 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - armour_penetration = 100 - attack_verb = list("bludgeoned", "whacked", "disciplined") - resistance_flags = FLAMMABLE - -/obj/item/staff/broom - name = "broom" - desc = "Used for sweeping, and flying into the night while cackling. Black cat not included." - icon = 'icons/obj/wizard.dmi' - icon_state = "broom" - resistance_flags = FLAMMABLE - -/obj/item/staff/stick - name = "stick" - desc = "A great tool to drag someone else's drinks across the bar." - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "stick" - item_state = "stick" - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 3 - throwforce = 5 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - -/obj/item/ectoplasm - name = "ectoplasm" - desc = "spooky" - gender = PLURAL - icon = 'icons/obj/wizard.dmi' - icon_state = "ectoplasm" - -/obj/item/ectoplasm/suicide_act(mob/user) - user.visible_message("[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane.") - return (OXYLOSS) - -/obj/item/mounted_chainsaw - name = "mounted chainsaw" - desc = "A chainsaw that has replaced your arm." - icon_state = "chainsaw_on" - item_state = "mounted_chainsaw" - lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' - flags_1 = NODROP_1 | ABSTRACT_1 | DROPDEL_1 - w_class = WEIGHT_CLASS_HUGE - force = 21 - throwforce = 0 - throw_range = 0 - throw_speed = 0 - sharpness = IS_SHARP - attack_verb = list("sawed", "torn", "cut", "chopped", "diced") - hitsound = 'sound/weapons/chainsawhit.ogg' - -/obj/item/mounted_chainsaw/Destroy() - var/obj/item/bodypart/part - new /obj/item/twohanded/required/chainsaw(get_turf(src)) - if(iscarbon(loc)) - var/mob/living/carbon/holder = loc - var/index = holder.get_held_index_of_item(src) - if(index) - part = holder.hand_bodyparts[index] - . = ..() - if(part) - part.drop_limb() - -/obj/item/statuebust - name = "bust" - desc = "A priceless ancient marble bust, the kind that belongs in a museum." //or you can hit people with it - icon = 'icons/obj/statue.dmi' - icon_state = "bust" - force = 15 - throwforce = 10 - throw_speed = 5 - throw_range = 2 - attack_verb = list("busted") - -/obj/item/tailclub - name = "tail club" - desc = "For the beating to death of lizards with their own tails." - icon_state = "tailclub" - force = 14 - throwforce = 1 // why are you throwing a club do you even weapon - throw_speed = 1 - throw_range = 1 - attack_verb = list("clubbed", "bludgeoned") - -/obj/item/melee/chainofcommand/tailwhip - name = "liz o' nine tails" - desc = "A whip fashioned from the severed tails of lizards." - icon_state = "tailwhip" - origin_tech = "engineering=3;combat=3;biotech=3" - needs_permit = 0 - -/obj/item/melee/chainofcommand/tailwhip/kitty - name = "cat o' nine tails" - desc = "A whip fashioned from the severed tails of cats." - icon_state = "catwhip" - -/obj/item/melee/skateboard - name = "skateboard" - desc = "A skateboard. It can be placed on its wheels and ridden, or used as a strong weapon." - icon_state = "skateboard" - item_state = "skateboard" - force = 12 - throwforce = 4 - w_class = WEIGHT_CLASS_HUGE - attack_verb = list("smacked", "whacked", "slammed", "smashed") - -/obj/item/melee/skateboard/attack_self(mob/user) - new /obj/vehicle/scooter/skateboard(get_turf(user)) - qdel(src) - -/obj/item/melee/baseball_bat - name = "baseball bat" - desc = "There ain't a skull in the league that can withstand a swatter." - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "baseball_bat" - item_state = "baseball_bat" - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 10 - throwforce = 12 - attack_verb = list("beat", "smacked") - w_class = WEIGHT_CLASS_HUGE - var/homerun_ready = 0 - var/homerun_able = 0 - -/obj/item/melee/baseball_bat/homerun - name = "home run bat" - desc = "This thing looks dangerous... Dangerously good at baseball, that is." - homerun_able = 1 - -/obj/item/melee/baseball_bat/attack_self(mob/user) - if(!homerun_able) - ..() - return - if(homerun_ready) - to_chat(user, "You're already ready to do a home run!") - ..() - return - to_chat(user, "You begin gathering strength...") - playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1) - if(do_after(user, 90, target = src)) - to_chat(user, "You gather power! Time for a home run!") - homerun_ready = 1 - ..() - -/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user) - . = ..() - var/atom/throw_target = get_edge_target_turf(target, user.dir) - if(homerun_ready) - user.visible_message("It's a home run!") - target.throw_at(throw_target, rand(8,10), 14, user) - target.ex_act(EXPLODE_HEAVY) - playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1) - homerun_ready = 0 - return - else if(!target.anchored) - target.throw_at(throw_target, rand(1,2), 7, user) - -/obj/item/melee/baseball_bat/ablative - name = "metal baseball bat" - desc = "This bat is made of highly reflective, highly armored material." - icon_state = "baseball_bat_metal" - item_state = "baseball_bat_metal" - force = 12 - throwforce = 15 - -/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers - var/picksound = rand(1,2) - var/turf = get_turf(src) - if(picksound == 1) - playsound(turf, 'sound/weapons/effects/batreflect1.ogg', 50, 1) - if(picksound == 2) - playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1) - return 1 - -/obj/item/melee/flyswatter - name = "flyswatter" - desc = "Useful for killing insects of all sizes." - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "flyswatter" - item_state = "flyswatter" - lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' - righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' - force = 1 - throwforce = 1 - attack_verb = list("swatted", "smacked") - hitsound = 'sound/effects/snap.ogg' - w_class = WEIGHT_CLASS_SMALL - //Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc. - var/list/strong_against - -/obj/item/melee/flyswatter/Initialize() - . = ..() - strong_against = typecacheof(list( - /mob/living/simple_animal/hostile/poison/bees/, - /mob/living/simple_animal/butterfly, - /mob/living/simple_animal/cockroach, - /obj/item/queen_bee - )) - - -/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag) - if(proximity_flag) - if(is_type_in_typecache(target, strong_against)) - new /obj/effect/decal/cleanable/deadcockroach(get_turf(target)) - to_chat(user, "You easily splat the [target].") - if(istype(target, /mob/living/)) - var/mob/living/bug = target - bug.death(1) - else - qdel(target) - -/obj/item/circlegame - name = "circled hand" - desc = "If somebody looks at this while it's below your waist, you get to bop them." - icon_state = "madeyoulook" - force = 0 - throwforce = 0 - flags_1 = DROPDEL_1 | ABSTRACT_1 - attack_verb = list("bopped") - -/obj/item/proc/can_trigger_gun(mob/living/user) - if(!user.can_use_guns(src)) - return FALSE + playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much + +/obj/item/sord + name = "\improper SORD" + desc = "This thing is so unspeakably shitty you are having a hard time even holding it." + icon_state = "sord" + item_state = "sord" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + slot_flags = SLOT_BELT + force = 2 + throwforce = 1 + w_class = WEIGHT_CLASS_NORMAL + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + +/obj/item/sord/suicide_act(mob/user) + user.visible_message("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty.", \ + "You try to impale yourself with [src], but it's USELESS...") + return SHAME + +/obj/item/claymore + name = "claymore" + desc = "What are you standing around staring at this for? Get to killing!" + icon_state = "claymore" + item_state = "claymore" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + hitsound = 'sound/weapons/bladeslice.ogg' + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT | SLOT_BACK + force = 40 + throwforce = 10 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + block_chance = 50 + sharpness = IS_SHARP + max_integrity = 200 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/item/claymore/suicide_act(mob/user) + user.visible_message("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!") + return(BRUTELOSS) + +/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS + desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim." + flags_1 = CONDUCT_1 | NODROP_1 | DROPDEL_1 + slot_flags = null + block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY + luminosity = 3 + attack_verb = list("brutalized", "eviscerated", "disemboweled", "hacked", "carved", "cleaved") //ONLY THE MOST VISCERAL ATTACK VERBS + var/notches = 0 //HOW MANY PEOPLE HAVE BEEN SLAIN WITH THIS BLADE + var/obj/item/disk/nuclear/nuke_disk //OUR STORED NUKE DISK + +/obj/item/claymore/highlander/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/item/claymore/highlander/Destroy() + if(nuke_disk) + nuke_disk.forceMove(get_turf(src)) + nuke_disk.visible_message("The nuke disk is vulnerable!") + nuke_disk = null + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/claymore/highlander/process() + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + loc.layer = LARGE_MOB_LAYER //NO HIDING BEHIND PLANTS FOR YOU, DICKWEED (HA GET IT, BECAUSE WEEDS ARE PLANTS) + H.bleedsuppress = TRUE //AND WE WON'T BLEED OUT LIKE COWARDS + else + if(!admin_spawned) + qdel(src) + + +/obj/item/claymore/highlander/pickup(mob/living/user) + to_chat(user, "The power of Scotland protects you! You are shielded from all stuns and knockdowns.") + user.add_stun_absorption("highlander", INFINITY, 1, " is protected by the power of Scotland!", "The power of Scotland absorbs the stun!", " is protected by the power of Scotland!") + user.status_flags += IGNORESLOWDOWN + +/obj/item/claymore/highlander/dropped(mob/living/user) + user.status_flags -= IGNORESLOWDOWN + qdel(src) //If this ever happens, it's because you lost an arm + +/obj/item/claymore/highlander/examine(mob/user) + ..() + to_chat(user, "It has [!notches ? "nothing" : "[notches] notches"] scratched into the blade.") + if(nuke_disk) + to_chat(user, "It's holding the nuke disk!") + +/obj/item/claymore/highlander/attack(mob/living/target, mob/living/user) + . = ..() + if(!QDELETED(target) && iscarbon(target) && target.stat == DEAD && target.mind && target.mind.special_role == "highlander") + user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES + add_notch(user) + target.visible_message("[target] crumbles to dust beneath [user]'s blows!", "As you fall, your body crumbles to dust!") + target.dust() + +/obj/item/claymore/highlander/attack_self(mob/living/user) + var/closest_victim + var/closest_distance = 255 + for(var/mob/living/carbon/human/H in GLOB.player_list - user) + if(H.client && H.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) + closest_victim = H + if(!closest_victim) + to_chat(user, "[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.") + return + to_chat(user, "[src] thrums and points to the [dir2text(get_dir(user, closest_victim))].") + +/obj/item/claymore/highlander/IsReflect() + return 1 //YOU THINK YOUR PUNY LASERS CAN STOP ME? + +/obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE + notches++ + force++ + var/new_name = name + switch(notches) + if(1) + to_chat(user, "Your first kill - hopefully one of many. You scratch a notch into [src]'s blade.") + to_chat(user, "You feel your fallen foe's soul entering your blade, restoring your wounds!") + new_name = "notched claymore" + if(2) + to_chat(user, "Another falls before you. Another soul fuses with your own. Another notch in the blade.") + new_name = "double-notched claymore" + add_atom_colour(rgb(255, 235, 235), ADMIN_COLOUR_PRIORITY) + if(3) + to_chat(user, "You're beginning to relish the thrill of battle.") + new_name = "triple-notched claymore" + add_atom_colour(rgb(255, 215, 215), ADMIN_COLOUR_PRIORITY) + if(4) + to_chat(user, "You've lost count of how many you've killed.") + new_name = "many-notched claymore" + add_atom_colour(rgb(255, 195, 195), ADMIN_COLOUR_PRIORITY) + if(5) + to_chat(user, "Five voices now echo in your mind, cheering the slaughter.") + new_name = "battle-tested claymore" + add_atom_colour(rgb(255, 175, 175), ADMIN_COLOUR_PRIORITY) + if(6) + to_chat(user, "Is this what the vikings felt like? Visions of glory fill your head as you slay your sixth foe.") + new_name = "battle-scarred claymore" + add_atom_colour(rgb(255, 155, 155), ADMIN_COLOUR_PRIORITY) + if(7) + to_chat(user, "Kill. Butcher. Conquer.") + new_name = "vicious claymore" + add_atom_colour(rgb(255, 135, 135), ADMIN_COLOUR_PRIORITY) + if(8) + to_chat(user, "IT NEVER GETS OLD. THE SCREAMING. THE BLOOD AS IT SPRAYS ACROSS YOUR FACE.") + new_name = "bloodthirsty claymore" + add_atom_colour(rgb(255, 115, 115), ADMIN_COLOUR_PRIORITY) + if(9) + to_chat(user, "ANOTHER ONE FALLS TO YOUR BLOWS. ANOTHER WEAKLING UNFIT TO LIVE.") + new_name = "gore-stained claymore" + add_atom_colour(rgb(255, 95, 95), ADMIN_COLOUR_PRIORITY) + if(10) + user.visible_message("[user]'s eyes light up with a vengeful fire!", \ + "YOU FEEL THE POWER OF VALHALLA FLOWING THROUGH YOU! THERE CAN BE ONLY ONE!!!") + user.update_icons() + new_name = "GORE-DRENCHED CLAYMORE OF [pick("THE WHIMSICAL SLAUGHTER", "A THOUSAND SLAUGHTERED CATTLE", "GLORY AND VALHALLA", "ANNIHILATION", "OBLITERATION")]" + icon_state = "claymore_valhalla" + item_state = "cultblade" + remove_atom_colour(ADMIN_COLOUR_PRIORITY) + + name = new_name + playsound(user, 'sound/items/screwdriver2.ogg', 50, 1) + +/obj/item/katana + name = "katana" + desc = "Woefully underpowered in D20" + icon_state = "katana" + item_state = "katana" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT | SLOT_BACK + force = 40 + throwforce = 10 + w_class = WEIGHT_CLASS_NORMAL + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + block_chance = 50 + sharpness = IS_SHARP + max_integrity = 200 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/item/katana/cursed + slot_flags = null + +/obj/item/katana/suicide_act(mob/user) + user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!") + return(BRUTELOSS) + +/obj/item/wirerod + name = "wired rod" + desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit." + icon_state = "wiredrod" + item_state = "rods" + flags_1 = CONDUCT_1 + force = 9 + throwforce = 10 + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=1150, MAT_GLASS=75) + attack_verb = list("hit", "bludgeoned", "whacked", "bonked") + +/obj/item/wirerod/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/shard)) + var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear + + remove_item_from_storage(user) + qdel(I) + qdel(src) + + user.put_in_hands(S) + to_chat(user, "You fasten the glass shard to the top of the rod with the cable.") + + else if(istype(I, /obj/item/device/assembly/igniter) && !(I.flags_1 & NODROP_1)) + var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod + + remove_item_from_storage(user) + + to_chat(user, "You fasten [I] to the top of the rod with the cable.") + + qdel(I) + qdel(src) + + user.put_in_hands(P) + else + return ..() + + +/obj/item/throwing_star + name = "throwing star" + desc = "An ancient weapon still used to this day due to it's ease of lodging itself into victim's body parts" + icon_state = "throwingstar" + item_state = "eshield0" + lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' + force = 2 + throwforce = 20 //This is never used on mobs since this has a 100% embed chance. + throw_speed = 4 + embedded_pain_multiplier = 4 + w_class = WEIGHT_CLASS_SMALL + embed_chance = 100 + embedded_fall_chance = 0 //Hahaha! + sharpness = IS_SHARP + materials = list(MAT_METAL=500, MAT_GLASS=500) + resistance_flags = FIRE_PROOF + + +/obj/item/switchblade + name = "switchblade" + icon_state = "switchblade" + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + desc = "A sharp, concealable, spring-loaded knife." + flags_1 = CONDUCT_1 + force = 3 + w_class = WEIGHT_CLASS_SMALL + throwforce = 5 + throw_speed = 3 + throw_range = 6 + materials = list(MAT_METAL=12000) + origin_tech = "engineering=3;combat=2" + hitsound = 'sound/weapons/genhit.ogg' + attack_verb = list("stubbed", "poked") + resistance_flags = FIRE_PROOF + var/extended = 0 + +/obj/item/switchblade/attack_self(mob/user) + extended = !extended + playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) + if(extended) + force = 20 + w_class = WEIGHT_CLASS_NORMAL + throwforce = 23 + icon_state = "switchblade_ext" + attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + sharpness = IS_SHARP + else + force = 3 + w_class = WEIGHT_CLASS_SMALL + throwforce = 5 + icon_state = "switchblade" + attack_verb = list("stubbed", "poked") + hitsound = 'sound/weapons/genhit.ogg' + sharpness = IS_BLUNT + +/obj/item/switchblade/suicide_act(mob/user) + user.visible_message("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + return (BRUTELOSS) + +/obj/item/phone + name = "red phone" + desc = "Should anything ever go wrong..." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "red_phone" + force = 3 + throwforce = 2 + throw_speed = 3 + throw_range = 4 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("called", "rang") + hitsound = 'sound/weapons/ring.ogg' + +/obj/item/phone/suicide_act(mob/user) + if(locate(/obj/structure/chair/stool) in user.loc) + user.visible_message("[user] begins to tie a noose with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!") + else + user.visible_message("[user] is strangling [user.p_them()]self with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!") + return(OXYLOSS) + +/obj/item/cane + name = "cane" + desc = "A cane used by a true gentleman. Or a clown." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "cane" + item_state = "stick" + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 5 + throwforce = 5 + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50) + attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") + +/obj/item/staff + name = "wizard staff" + desc = "Apparently a staff used by the wizard." + icon = 'icons/obj/wizard.dmi' + icon_state = "staff" + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' + force = 3 + throwforce = 5 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + armour_penetration = 100 + attack_verb = list("bludgeoned", "whacked", "disciplined") + resistance_flags = FLAMMABLE + +/obj/item/staff/broom + name = "broom" + desc = "Used for sweeping, and flying into the night while cackling. Black cat not included." + icon = 'icons/obj/wizard.dmi' + icon_state = "broom" + resistance_flags = FLAMMABLE + +/obj/item/staff/stick + name = "stick" + desc = "A great tool to drag someone else's drinks across the bar." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "stick" + item_state = "stick" + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 3 + throwforce = 5 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + +/obj/item/ectoplasm + name = "ectoplasm" + desc = "spooky" + gender = PLURAL + icon = 'icons/obj/wizard.dmi' + icon_state = "ectoplasm" + +/obj/item/ectoplasm/suicide_act(mob/user) + user.visible_message("[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane.") + return (OXYLOSS) + +/obj/item/mounted_chainsaw + name = "mounted chainsaw" + desc = "A chainsaw that has replaced your arm." + icon_state = "chainsaw_on" + item_state = "mounted_chainsaw" + lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' + flags_1 = NODROP_1 | ABSTRACT_1 | DROPDEL_1 + w_class = WEIGHT_CLASS_HUGE + force = 21 + throwforce = 0 + throw_range = 0 + throw_speed = 0 + sharpness = IS_SHARP + attack_verb = list("sawed", "torn", "cut", "chopped", "diced") + hitsound = 'sound/weapons/chainsawhit.ogg' + +/obj/item/mounted_chainsaw/Destroy() + var/obj/item/bodypart/part + new /obj/item/twohanded/required/chainsaw(get_turf(src)) + if(iscarbon(loc)) + var/mob/living/carbon/holder = loc + var/index = holder.get_held_index_of_item(src) + if(index) + part = holder.hand_bodyparts[index] + . = ..() + if(part) + part.drop_limb() + +/obj/item/statuebust + name = "bust" + desc = "A priceless ancient marble bust, the kind that belongs in a museum." //or you can hit people with it + icon = 'icons/obj/statue.dmi' + icon_state = "bust" + force = 15 + throwforce = 10 + throw_speed = 5 + throw_range = 2 + attack_verb = list("busted") + +/obj/item/tailclub + name = "tail club" + desc = "For the beating to death of lizards with their own tails." + icon_state = "tailclub" + force = 14 + throwforce = 1 // why are you throwing a club do you even weapon + throw_speed = 1 + throw_range = 1 + attack_verb = list("clubbed", "bludgeoned") + +/obj/item/melee/chainofcommand/tailwhip + name = "liz o' nine tails" + desc = "A whip fashioned from the severed tails of lizards." + icon_state = "tailwhip" + origin_tech = "engineering=3;combat=3;biotech=3" + needs_permit = 0 + +/obj/item/melee/chainofcommand/tailwhip/kitty + name = "cat o' nine tails" + desc = "A whip fashioned from the severed tails of cats." + icon_state = "catwhip" + +/obj/item/melee/skateboard + name = "skateboard" + desc = "A skateboard. It can be placed on its wheels and ridden, or used as a strong weapon." + icon_state = "skateboard" + item_state = "skateboard" + force = 12 + throwforce = 4 + w_class = WEIGHT_CLASS_HUGE + attack_verb = list("smacked", "whacked", "slammed", "smashed") + +/obj/item/melee/skateboard/attack_self(mob/user) + new /obj/vehicle/scooter/skateboard(get_turf(user)) + qdel(src) + +/obj/item/melee/baseball_bat + name = "baseball bat" + desc = "There ain't a skull in the league that can withstand a swatter." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "baseball_bat" + item_state = "baseball_bat" + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 10 + throwforce = 12 + attack_verb = list("beat", "smacked") + w_class = WEIGHT_CLASS_HUGE + var/homerun_ready = 0 + var/homerun_able = 0 + +/obj/item/melee/baseball_bat/homerun + name = "home run bat" + desc = "This thing looks dangerous... Dangerously good at baseball, that is." + homerun_able = 1 + +/obj/item/melee/baseball_bat/attack_self(mob/user) + if(!homerun_able) + ..() + return + if(homerun_ready) + to_chat(user, "You're already ready to do a home run!") + ..() + return + to_chat(user, "You begin gathering strength...") + playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1) + if(do_after(user, 90, target = src)) + to_chat(user, "You gather power! Time for a home run!") + homerun_ready = 1 + ..() + +/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user) + . = ..() + var/atom/throw_target = get_edge_target_turf(target, user.dir) + if(homerun_ready) + user.visible_message("It's a home run!") + target.throw_at(throw_target, rand(8,10), 14, user) + target.ex_act(EXPLODE_HEAVY) + playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1) + homerun_ready = 0 + return + else if(!target.anchored) + target.throw_at(throw_target, rand(1,2), 7, user) + +/obj/item/melee/baseball_bat/ablative + name = "metal baseball bat" + desc = "This bat is made of highly reflective, highly armored material." + icon_state = "baseball_bat_metal" + item_state = "baseball_bat_metal" + force = 12 + throwforce = 15 + +/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers + var/picksound = rand(1,2) + var/turf = get_turf(src) + if(picksound == 1) + playsound(turf, 'sound/weapons/effects/batreflect1.ogg', 50, 1) + if(picksound == 2) + playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1) + return 1 + +/obj/item/melee/flyswatter + name = "flyswatter" + desc = "Useful for killing insects of all sizes." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "flyswatter" + item_state = "flyswatter" + lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' + force = 1 + throwforce = 1 + attack_verb = list("swatted", "smacked") + hitsound = 'sound/effects/snap.ogg' + w_class = WEIGHT_CLASS_SMALL + //Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc. + var/list/strong_against + +/obj/item/melee/flyswatter/Initialize() + . = ..() + strong_against = typecacheof(list( + /mob/living/simple_animal/hostile/poison/bees/, + /mob/living/simple_animal/butterfly, + /mob/living/simple_animal/cockroach, + /obj/item/queen_bee + )) + + +/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag) + if(proximity_flag) + if(is_type_in_typecache(target, strong_against)) + new /obj/effect/decal/cleanable/deadcockroach(get_turf(target)) + to_chat(user, "You easily splat the [target].") + if(istype(target, /mob/living/)) + var/mob/living/bug = target + bug.death(1) + else + qdel(target) + +/obj/item/circlegame + name = "circled hand" + desc = "If somebody looks at this while it's below your waist, you get to bop them." + icon_state = "madeyoulook" + force = 0 + throwforce = 0 + flags_1 = DROPDEL_1 | ABSTRACT_1 + attack_verb = list("bopped") + +/obj/item/proc/can_trigger_gun(mob/living/user) + if(!user.can_use_guns(src)) + return FALSE return TRUE diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 1f3668f283..68311d6d1d 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -169,6 +169,7 @@ return /obj/singularity_pull(S, current_size) + ..() if(!anchored || current_size >= STAGE_FIVE) step_towards(src,S) @@ -198,7 +199,7 @@ /obj/vv_get_dropdown() . = ..() - .["Delete all of type"] = "?_src_=vars;delall=\ref[src]" + .["Delete all of type"] = "?_src_=vars;[HrefToken()];delall=\ref[src]" /obj/examine(mob/user) ..() diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 78e672e49d..4f1348f3da 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -16,7 +16,7 @@ integrity_failure = 50 armor = list(melee = 20, bullet = 10, laser = 10, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 70, acid = 60) var/breakout_time = 2 - var/lastbang + var/message_cooldown var/can_weld_shut = TRUE var/horizontal = FALSE var/allow_objects = FALSE @@ -300,14 +300,12 @@ /obj/structure/closet/relaymove(mob/user) if(user.stat || !isturf(loc) || !isliving(user)) return - var/mob/living/L = user - if(!open()) - if(L.last_special <= world.time) - container_resist(L) - if(world.time > lastbang+5) - lastbang = world.time - for(var/mob/M in get_hearers_in_view(src, null)) - M.show_message("BANG, bang!", 2) + if(locked) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") + return + container_resist() /obj/structure/closet/attack_hand(mob/user) ..() @@ -367,9 +365,10 @@ //okay, so the closet is either welded or locked... resist!!! user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT - to_chat(user, "You lean on the back of [src] and start pushing the door open.") - visible_message("[src] begins to shake violently!") - if(do_after(user,(breakout_time * 60 * 10), target = src)) //minutes * 60seconds * 10deciseconds + user.visible_message("[src] begins to shake violently!", \ + "You lean on the back of [src] and start pushing the door open... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear banging from [src].") + if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds if(!user || user.stat != CONSCIOUS || user.loc != src || opened || (!locked && !welded) ) return //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting @@ -388,7 +387,7 @@ /obj/structure/closet/AltClick(mob/user) ..() - if(!user.canUseTopic(src, be_close=TRUE)) + if(!user.canUseTopic(src, be_close=TRUE) || !isturf(loc)) to_chat(user, "You can't do that right now!") return if(opened || !secure) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index cefd972aaf..aeacf17598 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -83,7 +83,7 @@ new /obj/item/storage/belt/security/full(src) new /obj/item/gun/energy/e_gun/hos(src) new /obj/item/device/flashlight/seclite(src) - new /obj/item/pinpointer(src) + new /obj/item/pinpointer/nuke(src) /obj/structure/closet/secure_closet/warden name = "\proper warden's locker" @@ -108,6 +108,7 @@ new /obj/item/clothing/gloves/krav_maga/sec(src) new /obj/item/door_remote/head_of_security(src) new /obj/item/gun/ballistic/shotgun/automatic/combat/compact(src) + new /obj/item/pinpointer/crew(src) /obj/structure/closet/secure_closet/security name = "security officer's locker" @@ -183,6 +184,7 @@ new /obj/item/reagent_containers/spray/pepper(src) new /obj/item/clothing/suit/armor/vest/det_suit(src) new /obj/item/storage/belt/holster/full(src) + new /obj/item/pinpointer/crew(src) new /obj/item/device/mass_spectrometer(src) /obj/structure/closet/secure_closet/injection diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index c1a45ad20e..d0ad32113d 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -118,5 +118,13 @@ else return QDEL_HINT_LETMELIVE +/obj/structure/ladder/unbreakable/singularity_pull() + return + /obj/structure/ladder/auto_connect //They will connect to ladders with the same X and Y without needing to share an ID - auto_connect = TRUE \ No newline at end of file + auto_connect = TRUE + + +/obj/structure/ladder/singularity_pull() + visible_message("[src] is torn to pieces by the gravitational pull!") + qdel(src) diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index 8a24b0271f..081cf186a3 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -45,6 +45,7 @@ qdel(src) /obj/structure/lattice/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FOUR) deconstruct() diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 600e15201d..f4a98811c0 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -23,6 +23,8 @@ var/obj/structure/tray/connected = null var/locked = FALSE var/opendir = SOUTH + var/message_cooldown + var/breakout_time = 1 /obj/structure/bodycontainer/Destroy() open() @@ -41,6 +43,11 @@ /obj/structure/bodycontainer/relaymove(mob/user) if(user.stat || !isturf(loc)) return + if(locked) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") + return open() /obj/structure/bodycontainer/attack_paw(mob/user) @@ -84,11 +91,20 @@ qdel(src) /obj/structure/bodycontainer/container_resist(mob/living/user) - open() - -/obj/structure/bodycontainer/relay_container_resist(mob/living/user, obj/O) - to_chat(user, "You slam yourself into the side of [O].") - container_resist(user) + if(!locked) + open() + return + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + user.visible_message(null, \ + "You lean on the back of [src] and start pushing the tray open... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear a metallic creaking from [src].") + if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds + if(!user || user.stat != CONSCIOUS || user.loc != src ) + return + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") + open() /obj/structure/bodycontainer/proc/open() playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) diff --git a/code/game/objects/structures/transit_tubes/transit_tube.dm b/code/game/objects/structures/transit_tubes/transit_tube.dm index 7b86cdffcc..10d883d392 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube.dm @@ -30,6 +30,7 @@ return ..() /obj/structure/transit_tube/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) deconstruct(FALSE) diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm index 89f02b13af..43394f01c9 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm @@ -62,10 +62,14 @@ AM.ex_act(severity, target) /obj/structure/transit_tube_pod/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) deconstruct(FALSE) /obj/structure/transit_tube_pod/container_resist(mob/living/user) + if(!user.incapacitated()) + empty_pod() + return if(!moving) user.changeNext_move(CLICK_CD_BREAKOUT) user.last_special = world.time + CLICK_CD_BREAKOUT diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index e70d5d3ddb..170a651315 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -105,6 +105,7 @@ qdel(src) /obj/structure/window/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) deconstruct(FALSE) diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index 2622a4e74c..f97f79068d 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -4,6 +4,9 @@ var/wet = 0 var/wet_time = 0 // Time in seconds that this floor will be wet for. var/mutable_appearance/wet_overlay + var/postdig_icon_change = FALSE + var/postdig_icon + var/list/archdrops /turf/open/indestructible name = "floor" diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index cfc834037d..df3d2aba86 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -170,6 +170,7 @@ return make_plating() /turf/open/floor/singularity_pull(S, current_size) + ..() if(current_size == STAGE_THREE) if(prob(30)) if(floor_tile) diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index f8d6c1da71..f2475cf997 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -269,6 +269,7 @@ narsie_act(force, ignore_mobs, probability) /turf/open/floor/vines/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) if(prob(50)) ChangeTurf(src.baseturf) diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm index b24b758d73..c6dd8cb2eb 100644 --- a/code/game/turfs/simulated/floor/plating/asteroid.dm +++ b/code/game/turfs/simulated/floor/plating/asteroid.dm @@ -9,11 +9,11 @@ icon = 'icons/turf/floors.dmi' icon_state = "asteroid" icon_plating = "asteroid" + postdig_icon_change = TRUE var/environment_type = "asteroid" var/turf_type = /turf/open/floor/plating/asteroid //Because caves do whacky shit to revert to normal - var/dug = 0 //0 = has not yet been dug, 1 = has already been dug - var/sand_type = /obj/item/ore/glass var/floor_variance = 20 //probability floor has a different icon state + archdrops = list(/obj/item/ore/glass = 5) /turf/open/floor/plating/asteroid/Initialize() var/proper_name = name @@ -22,6 +22,9 @@ if(prob(floor_variance)) icon_state = "[environment_type][rand(0,12)]" + if(LAZYLEN(archdrops)) + AddComponent(/datum/component/archaeology, 100, archdrops) + /turf/open/floor/plating/asteroid/burn_tile() return @@ -31,46 +34,7 @@ /turf/open/floor/plating/asteroid/MakeDry(wet_setting = TURF_WET_WATER) return -/turf/open/floor/plating/asteroid/ex_act(severity, target) - contents_explosion(severity, target) - switch(severity) - if(3) - return - if(2) - if(prob(20)) - src.gets_dug() - if(1) - src.gets_dug() - /turf/open/floor/plating/asteroid/attackby(obj/item/W, mob/user, params) - //note that this proc does not call ..() - if(!W || !user) - return 0 - var/digging_speed = 0 - if (istype(W, /obj/item/shovel)) - var/obj/item/shovel/S = W - digging_speed = S.digspeed - else if (istype(W, /obj/item/pickaxe)) - var/obj/item/pickaxe/P = W - digging_speed = P.digspeed - if (digging_speed) - var/turf/T = user.loc - if(!isturf(T)) - return - - if (dug) - to_chat(user, "This area has already been dug!") - return - - to_chat(user, "You start digging...") - playsound(src, 'sound/effects/shovel_dig.ogg', 50, 1) - - if(do_after(user, digging_speed, target = src)) - if(istype(src, /turf/open/floor/plating/asteroid)) - to_chat(user, "You dig a hole.") - gets_dug() - SSblackbox.add_details("pick_used_mining","[W.type]") - if(istype(W, /obj/item/storage/bag/ore)) var/obj/item/storage/bag/ore/S = W if(S.collection_mode == 1) @@ -88,37 +52,16 @@ var/turf/open/floor/light/F = T F.state = L.state playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - -/turf/open/floor/plating/asteroid/proc/gets_dug() - if(dug) return - for(var/i in 1 to 5) - new sand_type(src) - dug = 1 - icon_plating = "[environment_type]_dug" - icon_state = "[environment_type]_dug" - slowdown = 0 - return + + return ..() + /turf/open/floor/plating/asteroid/singularity_act() if(turf_z_is_planet(src)) return ..() ChangeTurf(/turf/open/space) -/turf/open/floor/plating/asteroid/singularity_pull(S, current_size) - if(dug) - return - switch(current_size) - if(STAGE_THREE) - if(!prob(30)) - gets_dug() - if(STAGE_FOUR) - if(prob(50)) - gets_dug() - else - if(current_size >= STAGE_FIVE && prob(70)) - gets_dug() - /turf/open/floor/plating/asteroid/basalt name = "volcanic floor" @@ -127,7 +70,7 @@ icon_state = "basalt" icon_plating = "basalt" environment_type = "basalt" - sand_type = /obj/item/ore/glass/basalt + archdrops = list(/obj/item/ore/glass/basalt = 5) floor_variance = 15 /turf/open/floor/plating/asteroid/basalt/lava //lava underneath @@ -147,10 +90,10 @@ if("basalt5", "basalt9") B.set_light(1.4, 0.6, LIGHT_COLOR_LAVA) //barely anything! -/turf/open/floor/plating/asteroid/basalt/gets_dug() - if(!dug) - set_light(0) +/turf/open/floor/plating/asteroid/basalt/ComponentActivated(datum/component/C) ..() + if(istype(C, /datum/component/archaeology)) + set_light(0) ///////Surface. The surface is warm, but survivable without a suit. Internals are required. The floors break to chasms, which drop you into the underground. @@ -340,8 +283,8 @@ initial_gas_mix = "TEMP=180" slowdown = 2 environment_type = "snow" - sand_type = /obj/item/stack/sheet/mineral/snow flags_1 = NONE + archdrops = list(/obj/item/stack/sheet/mineral/snow = 5) /turf/open/floor/plating/asteroid/snow/airless initial_gas_mix = "TEMP=2.7" diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm index df353653d7..eaf0e512f7 100644 --- a/code/game/turfs/simulated/floor/reinf_floor.dm +++ b/code/game/turfs/simulated/floor/reinf_floor.dm @@ -58,6 +58,7 @@ make_plating(1) /turf/open/floor/engine/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) if(floor_tile) if(prob(30)) diff --git a/code/game/turfs/simulated/wall/reinf_walls.dm b/code/game/turfs/simulated/wall/reinf_walls.dm index 261e856629..5ebe1d632a 100644 --- a/code/game/turfs/simulated/wall/reinf_walls.dm +++ b/code/game/turfs/simulated/wall/reinf_walls.dm @@ -247,6 +247,7 @@ icon_state = "r_wall" /turf/closed/wall/r_wall/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) if(prob(30)) dismantle_wall() diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 57774a5754..9171c7f54c 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -245,6 +245,7 @@ QDEL_IN(O, 50) /turf/closed/wall/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) if(prob(50)) dismantle_wall() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index e6314861b6..c8be2aa28a 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -524,12 +524,12 @@ /turf/proc/photograph(limit=20) var/image/I = new() - I.overlays += src + I.add_overlay(src) for(var/V in contents) var/atom/A = V if(A.invisibility) continue - I.overlays += A + I.add_overlay(A) if(limit) limit-- else diff --git a/code/game/world.dm b/code/game/world.dm index a1fcc364cd..991e6230e6 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -1,3 +1,6 @@ +GLOBAL_VAR(security_mode) +GLOBAL_PROTECT(security_mode) + /world/New() log_world("World loaded at [time_stamp()]") @@ -5,6 +8,8 @@ GLOB.config_error_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = file("data/logs/config_error.log") //temporary file used to record errors with loading config, moved to log directory once logging is set bl + CheckSecurityMode() + make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once) config = new @@ -94,6 +99,20 @@ if(GLOB.round_id) log_game("Round ID: [GLOB.round_id]") +/world/proc/CheckSecurityMode() + //try to write to data + if(!text2file("The world is running at least safe mode", "data/server_security_check.lock")) + GLOB.security_mode = SECURITY_ULTRASAFE + warning("/tg/station 13 is not supported in ultrasafe security mode. Everything will break!") + return + + //try to shell + if(shell("echo \"The world is running in trusted mode\"") != null) + GLOB.security_mode = SECURITY_TRUSTED + else + GLOB.security_mode = SECURITY_SAFE + warning("/tg/station 13 uses many file operations, a few shell()s, and some external call()s. Trusted mode is recommended. You can download our source code for your own browsing and compilation at https://github.com/tgstation/tgstation") + /world/Topic(T, addr, master, key) var/list/input = params2list(T) diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm index a83ced850e..f447b2f3e9 100644 --- a/code/modules/VR/vr_sleeper.dm +++ b/code/modules/VR/vr_sleeper.dm @@ -49,6 +49,10 @@ open_machine() +/obj/machinery/vr_sleeper/container_resist(mob/living/user) + open_machine() + + /obj/machinery/vr_sleeper/Destroy() open_machine() cleanup_vr_human() diff --git a/code/modules/admin/DB_ban/functions.dm b/code/modules/admin/DB_ban/functions.dm index e580b306b3..c6f44fd1da 100644 --- a/code/modules/admin/DB_ban/functions.dm +++ b/code/modules/admin/DB_ban/functions.dm @@ -414,7 +414,7 @@ if(bancount > bansperpage) output += "
    Page: " while(bancount > 0) - output+= "|[pagecount == page ? "\[[pagecount]\]" : "\[[pagecount]\]"]" + output+= "|[pagecount == page ? "\[[pagecount]\]" : "\[[pagecount]\]"]" bancount -= bansperpage pagecount++ output += "|" @@ -462,25 +462,25 @@ if("PERMABAN") typedesc = "PERMABAN" if("TEMPBAN") - typedesc = "TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " + typedesc = "TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " if("JOB_PERMABAN") typedesc = "JOBBAN
    ([job])" if("JOB_TEMPBAN") - typedesc = "TEMP JOBBAN
    ([job])
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]" + typedesc = "TEMP JOBBAN
    ([job])
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]" if("ADMIN_PERMABAN") typedesc = "ADMIN PERMABAN" if("ADMIN_TEMPBAN") - typedesc = "ADMIN TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " + typedesc = "ADMIN TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " output += "" output += "[typedesc]" output += "[ckey]" output += "[bantime]" output += "[ackey]" - output += "[(unbanned) ? "" : "Unban"]" + output += "[(unbanned) ? "" : "Unban"]" output += "" output += "" - output += "Reason: [(unbanned) ? "" : "(Edit)"] \"[reason]\"" + output += "Reason: [(unbanned) ? "" : "(Edit)"] \"[reason]\"" output += "" if(edits) output += "" diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index b88451b724..392d361773 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -30,68 +30,68 @@ body += "Options panel for [M]" if(M.client) body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" if(config.use_exp_tracking) - body += "\[" + M.client.get_exp_living() + "\]" + body += "\[" + M.client.get_exp_living() + "\]" if(isnewplayer(M)) body += " Hasn't Entered Game " else - body += " \[Heal\] " + body += " \[Heal\] " if(M.client) body += "
    \[First Seen: [M.client.player_join_date]\]\[Byond account registered on: [M.client.account_join_date]\]" body += "

    Show related accounts by: " - body += "\[ CID | " - body += "IP \]" + body += "\[ CID | " + body += "IP \]" body += "

    \[ " - body += "VV - " - body += "TP - " + body += "VV - " + body += "TP - " body += "PM - " - body += "SM - " - body += "FLW - " - body += "LOGS\]
    " + body += "SM - " + body += "FLW - " + body += "LOGS\]
    " body += "Mob type = [M.type]

    " - body += "Kick | " - body += "Ban | " - body += "Jobban | " - body += "Identity Ban | " + body += "Kick | " + body += "Ban | " + body += "Jobban | " + body += "Identity Ban | " if(jobban_isbanned(M, "OOC")) - body+= "OOCBan | " + body+= "OOCBan | " else - body+= "OOCBan | " + body+= "OOCBan | " if(jobban_isbanned(M, "emote")) - body+= "EmoteBan | " + body+= "EmoteBan | " else - body+= "Emoteban | " + body+= "Emoteban | " - body += "Notes | Messages | Watchlist | " + body += "Notes | Messages | Watchlist | " if(M.client) - body += "| Prison | " - body += "\ Send back to Lobby | " + body += "| Prison | " + body += "\ Send back to Lobby | " var/muted = M.client.prefs.muted body += "
    Mute: " - body += "\[IC | " - body += "OOC | " - body += "PRAY | " - body += "ADMINHELP | " - body += "DEADCHAT\]" - body += "(toggle all)" + body += "\[IC | " + body += "OOC | " + body += "PRAY | " + body += "ADMINHELP | " + body += "DEADCHAT\]" + body += "(toggle all)" body += "

    " - body += "Jump to | " - body += "Get | " - body += "Send To" + body += "Jump to | " + body += "Get | " + body += "Send To" body += "

    " - body += "Traitor panel | " - body += "Narrate to | " - body += "Subtle message | " - body += "Language Menu" + body += "Traitor panel | " + body += "Narrate to | " + body += "Subtle message | " + body += "Language Menu" if (M.client) if(!isnewplayer(M)) @@ -103,73 +103,71 @@ if(ishuman(M)) body += "Human | " else - body += "Humanize | " + body += "Humanize | " //Monkey if(ismonkey(M)) body += "Monkeyized | " else - body += "Monkeyize | " + body += "Monkeyize | " //Corgi if(iscorgi(M)) body += "Corgized | " else - body += "Corgize | " + body += "Corgize | " //AI / Cyborg if(isAI(M)) body += "Is an AI " else if(ishuman(M)) - body += "Make AI | " - body += "Make Robot | " - body += "Make Alien | " - body += "Make Slime | " - body += "Make Blob | " + body += "Make AI | " + body += "Make Robot | " + body += "Make Alien | " + body += "Make Slime | " + body += "Make Blob | " //Simple Animals if(isanimal(M)) - body += "Re-Animalize | " + body += "Re-Animalize | " else - body += "Animalize | " + body += "Animalize | " body += "

    " body += "Rudimentary transformation:
    These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.

    " - body += "Observer | " - body += "\[ Alien: Drone, " - body += "Hunter, " - body += "Sentinel, " - body += "Praetorian, " - body += "Queen, " - body += "Larva \] " - body += "Human " - body += "\[ slime: Baby, " - body += "Adult \] " - body += "Monkey | " - body += "Cyborg | " - body += "Cat | " - body += "Runtime | " - body += "Corgi | " - body += "Ian | " - body += "Crab | " - body += "Coffee | " - //body += "Parrot | " - //body += "Poly | " - body += "\[ Construct: Juggernaut , " - body += "Artificer , " - body += "Wraith \] " - body += "Shade" + body += "Observer | " + body += "\[ Alien: Drone, " + body += "Hunter, " + body += "Sentinel, " + body += "Praetorian, " + body += "Queen, " + body += "Larva \] " + body += "Human " + body += "\[ slime: Baby, " + body += "Adult \] " + body += "Monkey | " + body += "Cyborg | " + body += "Cat | " + body += "Runtime | " + body += "Corgi | " + body += "Ian | " + body += "Crab | " + body += "Coffee | " + body += "\[ Construct: Juggernaut , " + body += "Artificer , " + body += "Wraith \] " + body += "Shade" body += "
    " if (M.client) body += "

    " body += "Other actions:" body += "
    " - body += "Forcesay | " - body += "Thunderdome 1 | " - body += "Thunderdome 2 | " - body += "Thunderdome Admin | " - body += "Thunderdome Observer | " + body += "Forcesay | " + body += "Thunderdome 1 | " + body += "Thunderdome 2 | " + body += "Thunderdome Admin | " + body += "Thunderdome Observer | " body += "
    " body += "" @@ -197,19 +195,19 @@ dat += "
    Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units." dat += "
    Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!
    " if(GLOB.news_network.wanted_issue.active) - dat+= "
    Read Wanted Issue" - dat+= "

    Create Feed Channel" - dat+= "
    View Feed Channels" - dat+= "
    Submit new Feed story" - dat+= "

    Exit" + dat+= "
    Read Wanted Issue" + dat+= "

    Create Feed Channel" + dat+= "
    View Feed Channels" + dat+= "
    Submit new Feed story" + dat+= "

    Exit" var/wanted_already = 0 if(GLOB.news_network.wanted_issue.active) wanted_already = 1 dat+="
    Feed Security functions:
    " - dat+="
    [(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue" - dat+="
    Censor Feed Stories" - dat+="
    Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel)." - dat+="

    The newscaster recognises you as:
    [src.admin_signature]
    " + dat+="
    [(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue" + dat+="
    Censor Feed Stories" + dat+="
    Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel)." + dat+="

    The newscaster recognises you as:
    [src.admin_signature]
    " if(1) dat+= "Station Feed Channels
    " if( isemptylist(GLOB.news_network.network_channels) ) @@ -219,34 +217,34 @@ if(CHANNEL.is_admin_channel) dat+="[CHANNEL.channel_name]
    " else - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
    " - dat+="

    Refresh" - dat+="
    Back" + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
    " + dat+="

    Refresh" + dat+="
    Back" if(2) dat+="Creating new Feed Channel..." - dat+="
    Channel Name: [src.admincaster_feed_channel.channel_name]
    " - dat+="Channel Author: [src.admin_signature]
    " - dat+="Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

    " - dat+="
    Submit

    Cancel
    " + dat+="
    Channel Name: [src.admincaster_feed_channel.channel_name]
    " + dat+="Channel Author: [src.admin_signature]
    " + dat+="Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

    " + dat+="
    Submit

    Cancel
    " if(3) dat+="Creating new Feed Message..." - dat+="
    Receiving Channel: [src.admincaster_feed_channel.channel_name]
    " //MARK + dat+="
    Receiving Channel: [src.admincaster_feed_channel.channel_name]
    " //MARK dat+="Message Author: [src.admin_signature]
    " - dat+="Message Body: [src.admincaster_feed_message.returnBody(-1)]
    " - dat+="
    Submit

    Cancel
    " + dat+="Message Body: [src.admincaster_feed_message.returnBody(-1)]
    " + dat+="
    Submit

    Cancel
    " if(4) dat+="Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

    " - dat+="
    Return
    " + dat+="
    Return
    " if(5) dat+="Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

    " - dat+="
    Return
    " + dat+="
    Return
    " if(6) dat+="ERROR: Could not submit Feed story to Network.

    " if(src.admincaster_feed_channel.channel_name=="") dat+="•Invalid receiving channel name.
    " if(src.admincaster_feed_message.returnBody(-1) == "" || src.admincaster_feed_message.returnBody(-1) == "\[REDACTED\]") dat+="•Invalid message body.
    " - dat+="
    Return
    " + dat+="
    Return
    " if(7) dat+="ERROR: Could not submit Feed Channel to Network.

    " if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") @@ -258,7 +256,7 @@ break if(check) dat+="•Channel name already in use.
    " - dat+="
    Return
    " + dat+="
    Return
    " if(9) dat+="[admincaster_feed_channel.channel_name]: \[created by: [admincaster_feed_channel.returnAuthor(-1)]\]
    " if(src.admincaster_feed_channel.censored) @@ -280,8 +278,8 @@ for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments) dat+="[comment.body]
    [comment.author] [comment.time_stamp]
    " dat+="
    " - dat+="

    Refresh" - dat+="
    Back" + dat+="

    Refresh" + dat+="
    Back" if(10) dat+="Nanotrasen Feed Censorship Tool
    " dat+="NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
    " @@ -291,8 +289,8 @@ dat+="No feed channels found active...
    " else for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
    " - dat+="
    Cancel" + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
    " + dat+="
    Cancel" if(11) dat+="Nanotrasen D-Notice Handler
    " dat+="A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's" @@ -302,26 +300,26 @@ dat+="No feed channels found active...
    " else for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
    " + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
    " - dat+="
    Back" + dat+="
    Back" if(12) dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \]
    " - dat+="[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]
    " + dat+="[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]
    " if( isemptylist(src.admincaster_feed_channel.messages) ) dat+="No feed messages found in channel...
    " else for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) dat+="-[MESSAGE.returnBody(-1)]
    \[Story by [MESSAGE.returnAuthor(-1)]\]
    " - dat+="[(MESSAGE.bodyCensor) ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.authorCensor) ? ("Undo Author Censorship") : ("Censor message Author")]
    " - dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: [MESSAGE.locked ? "Unlock" : "Lock"]
    " + dat+="[(MESSAGE.bodyCensor) ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.authorCensor) ? ("Undo Author Censorship") : ("Censor message Author")]
    " + dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: [MESSAGE.locked ? "Unlock" : "Lock"]
    " for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments) - dat+="[comment.body] X
    [comment.author] [comment.time_stamp]
    " - dat+="
    Back" + dat+="[comment.body] X
    [comment.author] [comment.time_stamp]
    " + dat+="
    Back" if(13) dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \]
    " - dat+="Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
    " + dat+="Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
    " if(src.admincaster_feed_channel.censored) dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
    " dat+="No further feed story additions are allowed while the D-Notice is in effect.


    " @@ -331,7 +329,7 @@ else for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) dat+="-[MESSAGE.returnBody(-1)]
    \[Story by [MESSAGE.returnAuthor(-1)]\]
    " - dat+="
    Back" + dat+="
    Back" if(14) dat+="Wanted Issue Handler:" var/wanted_already = 0 @@ -342,29 +340,29 @@ if(wanted_already) dat+="
    A wanted issue is already in Feed Circulation. You can edit or cancel it below.
    " dat+="
    " - dat+="Criminal Name: [src.admincaster_wanted_message.criminal]
    " - dat+="Description: [src.admincaster_wanted_message.body]
    " + dat+="Criminal Name: [src.admincaster_wanted_message.criminal]
    " + dat+="Description: [src.admincaster_wanted_message.body]
    " if(wanted_already) dat+="Wanted Issue created by:[GLOB.news_network.wanted_issue.scannedUser]
    " else dat+="Wanted Issue will be created under prosecutor:[src.admin_signature]
    " - dat+="
    [(wanted_already) ? ("Edit Issue") : ("Submit")]" + dat+="
    [(wanted_already) ? ("Edit Issue") : ("Submit")]" if(wanted_already) - dat+="
    Take down Issue" - dat+="
    Cancel" + dat+="
    Take down Issue" + dat+="
    Cancel" if(15) dat+="Wanted issue for [src.admincaster_wanted_message.criminal] is now in Network Circulation.

    " - dat+="
    Return
    " + dat+="
    Return
    " if(16) dat+="ERROR: Wanted Issue rejected by Network.

    " if(src.admincaster_wanted_message.criminal =="" || src.admincaster_wanted_message.criminal == "\[REDACTED\]") dat+="•Invalid name for person wanted.
    " if(src.admincaster_wanted_message.body == "" || src.admincaster_wanted_message.body == "\[REDACTED\]") dat+="•Invalid description.
    " - dat+="
    Return
    " + dat+="
    Return
    " if(17) dat+="Wanted Issue successfully deleted from Circulation
    " - dat+="
    Return
    " + dat+="
    Return
    " if(18) dat+="-- STATIONWIDE WANTED ISSUE --
    \[Submitted by: [GLOB.news_network.wanted_issue.scannedUser]\]
    " dat+="Criminal: [GLOB.news_network.wanted_issue.criminal]
    " @@ -375,10 +373,10 @@ dat+="
    " else dat+="None" - dat+="
    Back
    " + dat+="
    Back
    " if(19) dat+="Wanted issue for [src.admincaster_wanted_message.criminal] successfully edited.

    " - dat+="
    Return
    " + dat+="
    Return
    " else dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" @@ -394,21 +392,21 @@ var/dat = {"
    Game Panel

    \n - Change Game Mode
    + Change Game Mode
    "} if(GLOB.master_mode == "secret") - dat += "(Force Secret Mode)
    " + dat += "(Force Secret Mode)
    " dat += {"
    - Create Object
    - Quick Create Object
    - Create Turf
    - Create Mob
    + Create Object
    + Quick Create Object
    + Create Turf
    + Create Mob
    "} if(marked_datum && istype(marked_datum, /atom)) - dat += "Duplicate Marked Datum
    " + dat += "Duplicate Marked Datum
    " usr << browse(dat, "window=admin2;size=210x200") return @@ -755,14 +753,14 @@ dat += " (Cannot Late Join)
    " continue if(job.total_positions >= 0) - dat += " Add | " + dat += " Add | " if(job.total_positions > job.current_positions) - dat += "Remove | " + dat += "Remove | " else dat += "Remove | " - dat += "Unlimit" + dat += "Unlimit" else - dat += " Limit" + dat += " Limit" dat += "
    " dat += "" diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 0128b719d7..a260746e66 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -22,6 +22,8 @@ return 0 /proc/jobban_buildcache(client/C) + if(!SSdbcore.Connect()) + return if(C && istype(C)) C.jobbancache = list() var/datum/DBQuery/query_jobban_build_cache = SSdbcore.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index cf0d4be37f..e76385db59 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -1,26 +1,25 @@ - -/datum/admins/proc/create_mob(mob/user) - var/static/create_mob_html - if (!create_mob_html) - var/mobjs = null - mobjs = jointext(typesof(/mob), ";") - create_mob_html = file2text('html/create_object.html') - create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"") - - user << browse(replacetext(create_mob_html, "/* ref src */", "\ref[src]"), "window=create_mob;size=425x475") - -/proc/randomize_human(mob/living/carbon/human/H) - H.gender = pick(MALE, FEMALE) - H.real_name = random_unique_name(H.gender) - H.name = H.real_name - H.underwear = random_underwear(H.gender) - H.skin_tone = random_skin_tone() - H.hair_style = random_hair_style(H.gender) - H.facial_hair_style = random_facial_hair_style(H.gender) - H.hair_color = random_short_color() - H.facial_hair_color = H.hair_color - H.eye_color = random_eye_color() - H.dna.blood_type = random_blood_type() - H.update_body() - H.update_hair() - H.update_body_parts() \ No newline at end of file +/datum/admins/proc/create_mob(mob/user) + var/static/create_mob_html + if (!create_mob_html) + var/mobjs = null + mobjs = jointext(typesof(/mob), ";") + create_mob_html = file2text('html/create_object.html') + create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"") + + user << browse(replacetext(create_mob_html, "/* ref src */", "\ref[src];[HrefToken()]"), "window=create_mob;size=425x475") + +/proc/randomize_human(mob/living/carbon/human/H) + H.gender = pick(MALE, FEMALE) + H.real_name = random_unique_name(H.gender) + H.name = H.real_name + H.underwear = random_underwear(H.gender) + H.skin_tone = random_skin_tone() + H.hair_style = random_hair_style(H.gender) + H.facial_hair_style = random_facial_hair_style(H.gender) + H.hair_color = random_short_color() + H.facial_hair_color = H.hair_color + H.eye_color = random_eye_color() + H.dna.blood_type = random_blood_type() + H.update_body() + H.update_hair() + H.update_body_parts() diff --git a/code/modules/admin/create_object.dm b/code/modules/admin/create_object.dm index 32801149f9..cb947355b1 100644 --- a/code/modules/admin/create_object.dm +++ b/code/modules/admin/create_object.dm @@ -1,26 +1,26 @@ -/datum/admins/proc/create_object(mob/user) - var/static/create_object_html = null - if (!create_object_html) - var/objectjs = null - objectjs = jointext(typesof(/obj), ";") - create_object_html = file2text('html/create_object.html') - create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"") - - user << browse(replacetext(create_object_html, "/* ref src */", "\ref[src]"), "window=create_object;size=425x475") - -/datum/admins/proc/quick_create_object(mob/user) - var/static/list/create_object_forms = list( - /obj, /obj/structure, /obj/machinery, /obj/effect, - /obj/item, /obj/item/clothing, /obj/item/stack, /obj/item/device, - /obj/item/weapon, /obj/item/reagent_containers, /obj/item/gun) - - var/path = input("Select the path of the object you wish to create.", "Path", /obj) in create_object_forms - var/html_form = create_object_forms[path] - - if (!html_form) - var/objectjs = jointext(typesof(path), ";") - html_form = file2text('html/create_object.html') - html_form = replacetext(html_form, "null /* object types */", "\"[objectjs]\"") - create_object_forms[path] = html_form - - user << browse(replacetext(html_form, "/* ref src */", "\ref[src]"), "window=qco[path];size=425x475") \ No newline at end of file +/datum/admins/proc/create_object(mob/user) + var/static/create_object_html = null + if (!create_object_html) + var/objectjs = null + objectjs = jointext(typesof(/obj), ";") + create_object_html = file2text('html/create_object.html') + create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"") + + user << browse(replacetext(create_object_html, "/* ref src */", "\ref[src];[HrefToken()]"), "window=create_object;size=425x475") + +/datum/admins/proc/quick_create_object(mob/user) + var/static/list/create_object_forms = list( + /obj, /obj/structure, /obj/machinery, /obj/effect, + /obj/item, /obj/item/clothing, /obj/item/stack, /obj/item/device, + /obj/item/reagent_containers, /obj/item/gun) + + var/path = input("Select the path of the object you wish to create.", "Path", /obj) in create_object_forms + var/html_form = create_object_forms[path] + + if (!html_form) + var/objectjs = jointext(typesof(path), ";") + html_form = file2text('html/create_object.html') + html_form = replacetext(html_form, "null /* object types */", "\"[objectjs]\"") + create_object_forms[path] = html_form + + user << browse(replacetext(html_form, "/* ref src */", "\ref[src];[HrefToken()]"), "window=qco[path];size=425x475") diff --git a/code/modules/admin/create_turf.dm b/code/modules/admin/create_turf.dm index 63e3b8cf69..f38b86a7b9 100644 --- a/code/modules/admin/create_turf.dm +++ b/code/modules/admin/create_turf.dm @@ -1,9 +1,9 @@ -/datum/admins/proc/create_turf(mob/user) - var/static/create_turf_html - if (!create_turf_html) - var/turfjs = null - turfjs = jointext(typesof(/turf), ";") - create_turf_html = file2text('html/create_object.html') - create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"") - - user << browse(replacetext(create_turf_html, "/* ref src */", "\ref[src]"), "window=create_turf;size=425x475") +/datum/admins/proc/create_turf(mob/user) + var/static/create_turf_html + if (!create_turf_html) + var/turfjs = null + turfjs = jointext(typesof(/turf), ";") + create_turf_html = file2text('html/create_object.html') + create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"") + + user << browse(replacetext(create_turf_html, "/* ref src */", "\ref[src];[HrefToken()]"), "window=create_turf;size=425x475") diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index bdd799badf..153fb2d8c2 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -1,105 +1,126 @@ -GLOBAL_LIST_EMPTY(admin_datums) -GLOBAL_PROTECT(admin_datums) - -/datum/admins - var/datum/admin_rank/rank - - var/client/owner = null - var/fakekey = null - - var/datum/marked_datum - - var/spamcooldown = 0 - - var/admincaster_screen = 0 //TODO: remove all these 5 variables, they are completly unacceptable - var/datum/newscaster/feed_message/admincaster_feed_message = new /datum/newscaster/feed_message - var/datum/newscaster/wanted_message/admincaster_wanted_message = new /datum/newscaster/wanted_message - var/datum/newscaster/feed_channel/admincaster_feed_channel = new /datum/newscaster/feed_channel - var/admin_signature - -/datum/admins/New(datum/admin_rank/R, ckey) - if(!ckey) - QDEL_IN(src, 0) - throw EXCEPTION("Admin datum created without a ckey") - return - if(!istype(R)) - QDEL_IN(src, 0) - throw EXCEPTION("Admin datum created without a rank") - return - rank = R - admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" - GLOB.admin_datums[ckey] = src - -/datum/admins/proc/associate(client/C) - if(IsAdminAdvancedProcCall()) - var/msg = " has tried to elevate permissions!" - message_admins("[key_name_admin(usr)][msg]") - log_admin_private("[key_name(usr)][msg]") - return - if(istype(C)) - owner = C - owner.holder = src - owner.add_admin_verbs() //TODO - owner.verbs -= /client/proc/readmin - GLOB.admins |= C - -/datum/admins/proc/disassociate() - if(owner) - GLOB.admins -= owner - owner.remove_admin_verbs() - owner.holder = null - owner = null - -/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other) - if(!other) - return 1 //they have no rights - if(rank.rights == 65535) - return 1 //we have all the rights - if(src == other) - return 1 //you always have more rights than yourself - if(rank.rights != other.rank.rights) - if( (rank.rights & other.rank.rights) == other.rank.rights ) - return 1 //we have all the rights they have and more - return 0 - -/datum/admins/vv_edit_var(var_name, var_value) - return FALSE //nice try trialmin - -/* -checks if usr is an admin with at least ONE of the flags_1 in rights_required. (Note, they don't need all the flags_1) -if rights_required == 0, then it simply checks if they are an admin. -if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed -generally it would be used like so: - -/proc/admin_proc() - if(!check_rights(R_ADMIN)) return - to_chat(world, "you have enough rights!") - -NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call -you will have to do something like if(client.rights & R_ADMIN) yourself. -*/ -/proc/check_rights(rights_required, show_msg=1) - if(usr && usr.client) - if (check_rights_for(usr.client, rights_required)) - return 1 - else - if(show_msg) - to_chat(usr, "Error: You do not have sufficient rights to do that. You require one of the following flags_1:[rights2text(rights_required," ")].") - return 0 - -//probably a bit iffy - will hopefully figure out a better solution -/proc/check_if_greater_rights_than(client/other) - if(usr && usr.client) - if(usr.client.holder) - if(!other || !other.holder) - return 1 - return usr.client.holder.check_if_greater_rights_than_holder(other.holder) - return 0 - -//This proc checks whether subject has at least ONE of the rights specified in rights_required. -/proc/check_rights_for(client/subject, rights_required) - if(subject && subject.holder && subject.holder.rank) - if(rights_required && !(rights_required & subject.holder.rank.rights)) - return 0 - return 1 - return 0 \ No newline at end of file +GLOBAL_LIST_EMPTY(admin_datums) +GLOBAL_PROTECT(admin_datums) + +GLOBAL_VAR_INIT(href_token, GenerateToken()) +GLOBAL_PROTECT(href_token) + +/datum/admins + var/datum/admin_rank/rank + + var/client/owner = null + var/fakekey = null + + var/datum/marked_datum + + var/spamcooldown = 0 + + var/admincaster_screen = 0 //TODO: remove all these 5 variables, they are completly unacceptable + var/datum/newscaster/feed_message/admincaster_feed_message = new /datum/newscaster/feed_message + var/datum/newscaster/wanted_message/admincaster_wanted_message = new /datum/newscaster/wanted_message + var/datum/newscaster/feed_channel/admincaster_feed_channel = new /datum/newscaster/feed_channel + var/admin_signature + var/href_token + +/datum/admins/New(datum/admin_rank/R, ckey) + if(!ckey) + QDEL_IN(src, 0) + throw EXCEPTION("Admin datum created without a ckey") + return + if(!istype(R)) + QDEL_IN(src, 0) + throw EXCEPTION("Admin datum created without a rank") + return + rank = R + admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" + href_token = GenerateToken() + GLOB.admin_datums[ckey] = src + +/proc/GenerateToken() + . = "" + for(var/I in 1 to 32) + . += "[rand(10)]" + +/proc/HrefToken(forceGlobal = FALSE) + var/tok = GLOB.href_token + if(!forceGlobal && usr) + var/client/C = usr.client + if(!C) + CRASH("No client for HrefToken()!") + var/datum/admins/holder = C.holder + if(holder) + tok = holder.href_token + return "admin_token=[tok]" + +/datum/admins/proc/associate(client/C) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin_private("[key_name(usr)][msg]") + return + if(istype(C)) + owner = C + owner.holder = src + owner.add_admin_verbs() //TODO + owner.verbs -= /client/proc/readmin + GLOB.admins |= C + +/datum/admins/proc/disassociate() + if(owner) + GLOB.admins -= owner + owner.remove_admin_verbs() + owner.holder = null + owner = null + +/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other) + if(!other) + return 1 //they have no rights + if(rank.rights == 65535) + return 1 //we have all the rights + if(src == other) + return 1 //you always have more rights than yourself + if(rank.rights != other.rank.rights) + if( (rank.rights & other.rank.rights) == other.rank.rights ) + return 1 //we have all the rights they have and more + return 0 + +/datum/admins/vv_edit_var(var_name, var_value) + return FALSE //nice try trialmin + +/* +checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) +if rights_required == 0, then it simply checks if they are an admin. +if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed +generally it would be used like so: + +/proc/admin_proc() + if(!check_rights(R_ADMIN)) return + to_chat(world, "you have enough rights!") + +NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call +you will have to do something like if(client.rights & R_ADMIN) yourself. +*/ +/proc/check_rights(rights_required, show_msg=1) + if(usr && usr.client) + if (check_rights_for(usr.client, rights_required)) + return 1 + else + if(show_msg) + to_chat(usr, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") + return 0 + +//probably a bit iffy - will hopefully figure out a better solution +/proc/check_if_greater_rights_than(client/other) + if(usr && usr.client) + if(usr.client.holder) + if(!other || !other.holder) + return 1 + return usr.client.holder.check_if_greater_rights_than_holder(other.holder) + return 0 + +//This proc checks whether subject has at least ONE of the rights specified in rights_required. +/proc/check_rights_for(client/subject, rights_required) + if(subject && subject.holder && subject.holder.rank) + if(rights_required && !(rights_required & subject.holder.rank.rights)) + return 0 + return 1 + return 0 diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm index aafe3b393e..552757b750 100644 --- a/code/modules/admin/permissionverbs/permissionedit.dm +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -20,7 +20,7 @@
    - + @@ -36,10 +36,10 @@ if(!rights) rights = "*none*" output += "" - output += "" - output += "" - output += "" - output += "" + output += "" + output += "" + output += "" + output += "" output += "" output += {" diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 9237d1f1e2..0a3520285c 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -75,16 +75,16 @@ body += "
    CKEY \[+\]CKEY \[+\] RANK PERMISSIONS VERB-OVERRIDES
    [adm_ckey] \[-\][D.rank.name][rights][rights2text(0," ",D.rank.adds,D.rank.subs)][adm_ckey] \[-\][D.rank.name][rights][rights2text(0," ",D.rank.adds,D.rank.subs)]
    "; - body += "PP - " - body += "N - " + body += "PP - " + body += "N - " body += "VV - " - body += "TP - " + body += "TP - " body += "PM - " - body += "SM - " - body += "FLW - " - body += "LOGS
    " + body += "SM - " + body += "FLW - " + body += "LOGS
    " if(antagonist > 0) - body += "Antagonist"; + body += "Antagonist"; body += "
    "; @@ -195,7 +195,7 @@ Player panel
    - Hover over a line to see more information - Check antagonists - Kick everyone/AFKers in lobby + Hover over a line to see more information - Check antagonists - Kick everyone/AFKers in lobby

    @@ -320,26 +320,26 @@ dat += "Round Duration: [round(world.time / 36000)]:[add_zero("[world.time / 600 % 60]", 2)]:[world.time / 100 % 6][world.time / 100 % 10]
    " dat += "Emergency shuttle
    " if(EMERGENCY_IDLE_OR_RECALLED) - dat += "Call Shuttle
    " + dat += "Call Shuttle
    " else var/timeleft = SSshuttle.emergency.timeLeft() if(SSshuttle.emergency.mode == SHUTTLE_CALL) - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " - dat += "Send Back
    " + dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " + dat += "Send Back
    " else - dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " + dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
    " dat += "Continuous Round Status
    " - dat += "[config.continuous[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]" + dat += "[config.continuous[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]" if(config.continuous[SSticker.mode.config_tag]) - dat += ", [config.midround_antag[SSticker.mode.config_tag] ? "creating replacement antagonists" : "not creating new antagonists"]
    " + dat += ", [config.midround_antag[SSticker.mode.config_tag] ? "creating replacement antagonists" : "not creating new antagonists"]
    " else dat += "
    " if(config.midround_antag[SSticker.mode.config_tag]) - dat += "Time limit: [config.midround_antag_time_check] minutes into round
    " - dat += "Living crew limit: [config.midround_antag_life_check * 100]% of crew alive
    " - dat += "If limits past: [SSticker.mode.round_ends_with_antag_death ? "End The Round" : "Continue As Extended"]
    " - dat += "End Round Now
    " - dat += "[SSticker.delay_end ? "End Round Normally" : "Delay Round End"]" + dat += "Time limit: [config.midround_antag_time_check] minutes into round
    " + dat += "Living crew limit: [config.midround_antag_life_check * 100]% of crew alive
    " + dat += "If limits past: [SSticker.mode.round_ends_with_antag_death ? "End The Round" : "Continue As Extended"]
    " + dat += "End Round Now
    " + dat += "[SSticker.delay_end ? "End Round Normally" : "Delay Round End"]" var/connected_players = GLOB.clients.len var/lobby_players = 0 var/observers = 0 @@ -389,11 +389,11 @@ for(var/datum/mind/N in SSticker.mode.syndicates) var/mob/M = N.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" + dat += "FLW" else - dat += "[N.name]([N.key]) Nuclear Operative Body destroyed!" + dat += "[N.name]([N.key]) Nuclear Operative Body destroyed!" dat += "PM" dat += "
    " for(var/obj/item/disk/nuclear/N in GLOB.poi_list) @@ -402,7 +402,7 @@ while(!isturf(disk_loc)) if(ismob(disk_loc)) var/mob/M = disk_loc - dat += "carried by [M.real_name] " + dat += "carried by [M.real_name] " if(isobj(disk_loc)) var/obj/O = disk_loc dat += "in \a [O.name] " @@ -415,29 +415,29 @@ for(var/datum/mind/N in SSticker.mode.head_revolutionaries) var/mob/M = N.current if(!M) - dat += "" + dat += "" dat += "" else - dat += "" + dat += "" dat += "" - dat += "" + dat += "" for(var/datum/mind/N in SSticker.mode.revolutionaries) var/mob/M = N.current if(M) - dat += "" + dat += "" dat += "" - dat += "" + dat += "" dat += "
    Nuclear Disk(s)
    [N.name]([N.key])Head Revolutionary body destroyed!
    [N.name]([N.key])Head Revolutionary body destroyed!PM
    [M.real_name] (Leader)[M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]
    [M.real_name] (Leader)[M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]PMFLW
    FLW
    [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]
    [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]PMFLW
    FLW
    " for(var/datum/mind/N in SSticker.mode.get_living_heads()) var/mob/M = N.current if(M) - dat += "" + dat += "" dat += "" - dat += "" + dat += "" var/turf/mob_loc = get_turf(M) dat += "" else - dat += "" + dat += "" dat += "" dat += "
    Target(s)Location
    [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]
    [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]PMFLWFLW[mob_loc.loc]
    [N.name]([N.key])Head body destroyed!
    [N.name]([N.key])Head body destroyed!PM
    " @@ -464,12 +464,12 @@ for(var/datum/mind/changeling in SSticker.mode.changelings) var/mob/M = changeling.current if(M) - dat += "[M.mind.changeling.changelingID] as [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.mind.changeling.changelingID] as [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" - dat += "Show Objective" + dat += "FLW" + dat += "Show Objective" else - dat += "[changeling.name]([changeling.key])Changeling body destroyed!" + dat += "[changeling.name]([changeling.key])Changeling body destroyed!" dat += "PM" dat += "" @@ -478,12 +478,12 @@ for(var/datum/mind/wizard in SSticker.mode.wizards) var/mob/M = wizard.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" - dat += "Show Objective" + dat += "FLW" + dat += "Show Objective" else - dat += "[wizard.name]([wizard.key])Wizard body destroyed!" + dat += "[wizard.name]([wizard.key])Wizard body destroyed!" dat += "PM" dat += "" @@ -492,12 +492,12 @@ for(var/datum/mind/apprentice in SSticker.mode.apprentices) var/mob/M = apprentice.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" - dat += "Show Objective" + dat += "FLW" + dat += "Show Objective" else - dat += "[apprentice.name]([apprentice.key])Apprentice body destroyed!!" + dat += "[apprentice.name]([apprentice.key])Apprentice body destroyed!!" dat += "PM" dat += "" @@ -506,9 +506,9 @@ for(var/datum/mind/N in SSticker.mode.cult) var/mob/M = N.current if(M) - dat += "[M.real_name][N.has_antag_datum(ANTAG_DATUM_CULT_MASTER) ? " \[Master\]" : ""][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][N.has_antag_datum(ANTAG_DATUM_CULT_MASTER) ? " \[Master\]" : ""][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" + dat += "FLW" dat += "" if(SSticker.mode.servants_of_ratvar.len) @@ -516,9 +516,9 @@ for(var/datum/mind/N in SSticker.mode.servants_of_ratvar) var/mob/M = N.current if(M) - dat += "[M.real_name][M.client ? "" : " (ghost)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (ghost)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" + dat += "FLW" dat += "" if(SSticker.mode.traitors.len > 0) @@ -526,12 +526,12 @@ for(var/datum/mind/traitor in SSticker.mode.traitors) var/mob/M = traitor.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" - dat += "Show Objective" + dat += "FLW" + dat += "Show Objective" else - dat += "[traitor.name]([traitor.key])Traitor body destroyed!" + dat += "[traitor.name]([traitor.key])Traitor body destroyed!" dat += "PM" dat += "" @@ -540,12 +540,12 @@ for(var/datum/mind/abductor in SSticker.mode.abductors) var/mob/M = abductor.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" - dat += "Show Objective" + dat += "FLW" + dat += "Show Objective" else - dat += "[abductor.name]([abductor.key])Abductor body destroyed!" + dat += "[abductor.name]([abductor.key])Abductor body destroyed!" dat += "PM" dat += "" dat += "
    " @@ -553,12 +553,12 @@ for(var/datum/mind/abductee in E.abductee_minds) var/mob/M = abductee.current if(M) - dat += "" + dat += "" dat += "" - dat += "" - dat += "" + dat += "" + dat += "" else - dat += "" + dat += "" dat += "" dat += "
    Abductees
    [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]
    [M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]PMFLWShow Objective
    FLWShow Objective
    [abductee.name]([abductee.key])Abductee body destroyed!
    [abductee.name]([abductee.key])Abductee body destroyed!PM
    " @@ -569,12 +569,12 @@ var/mob/M = devil.current var/datum/antagonist/devil/devilinfo = devil.has_antag_datum(ANTAG_DATUM_DEVIL) if(M) - dat += "[M.real_name] : [devilinfo.truename][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name] : [devilinfo.truename][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "Show Objective" - dat += "Show all devil info" + dat += "Show Objective" + dat += "Show all devil info" else - dat += "[devil.name] : [devilinfo.truename] ([devil.key])devil body destroyed!" + dat += "[devil.name] : [devilinfo.truename] ([devil.key])devil body destroyed!" dat += "PM" dat += "" @@ -584,11 +584,11 @@ var/datum/mind/sintouched = X var/mob/M = sintouched.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "Show Objective" + dat += "Show Objective" else - dat += "[sintouched.name]([sintouched.key])sintouched body destroyed!" + dat += "[sintouched.name]([sintouched.key])sintouched body destroyed!" dat += "PM" dat += "" @@ -606,11 +606,11 @@ for(var/datum/mind/blob in blob_minds) var/mob/M = blob.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" + dat += "FLW" else - dat += "[blob.name]([blob.key])Blob not found!" + dat += "[blob.name]([blob.key])Blob not found!" dat += "PM" dat += "" @@ -622,11 +622,11 @@ for(var/datum/mind/eek in mode.ape_infectees) var/mob/M = eek.current if(M) - dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" + dat += "[M.real_name][M.client ? "" : " (No Client)"][M.stat == DEAD ? " (DEAD)" : ""]" dat += "PM" - dat += "FLW" + dat += "FLW" else - dat += "[eek.name]([eek.key])Monkey not found!" + dat += "[eek.name]([eek.key])Monkey not found!" dat += "PM" dat += "" diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 70c7346369..c277dd59e0 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -7,9 +7,9 @@ dat +={" General Secrets

    - Show Job Debug
    - Admin Log
    - Show Admin List
    + Show Job Debug
    + Admin Log
    + Show Admin List

    "} @@ -17,27 +17,27 @@ dat += {" Admin Secrets

    - Cure all diseases currently in existence
    - Bombing List
    - Show current traitors and objectives
    - Show last [length(GLOB.lastsignalers)] signalers
    - Show last [length(GLOB.lawchanges)] law changes
    - Show AI Laws
    - Show Game Mode
    - Show Crew Manifest
    - List DNA (Blood)
    - List Fingerprints
    - Enable/Disable CTF

    - Reset Thunderdome to default state
    - Rename Station Name
    - Reset Station Name
    + Cure all diseases currently in existence
    + Bombing List
    + Show current traitors and objectives
    + Show last [length(GLOB.lastsignalers)] signalers
    + Show last [length(GLOB.lawchanges)] law changes
    + Show AI Laws
    + Show Game Mode
    + Show Crew Manifest
    + List DNA (Blood)
    + List Fingerprints
    + Enable/Disable CTF

    + Reset Thunderdome to default state
    + Rename Station Name
    + Reset Station Name

    Shuttles

    - Move Ferry
    - Toggle Arrivals Ferry
    - Move Mining Shuttle
    - Move Labor Shuttle
    + Move Ferry
    + Toggle Arrivals Ferry
    + Move Mining Shuttle
    + Move Labor Shuttle

    "} @@ -45,31 +45,33 @@ dat += {" Fun Secrets

    +<<<<<<< HEAD - Trigger a Virus Outbreak
    - Turn all humans into monkeys
    - Chinese Cartoons
    - Change the species of all humans
    - Make all areas powered
    - Make all areas unpowered
    - Power all SMES
    - Triple AI mode (needs to be used in the lobby)
    - Everyone is the traitor
    - Summon Guns
    - Summon Magic
    - Summon Events (Toggle)
    - There can only be one!
    - There can only be one! (40-second delay)
    - There can only be me!
    - Make all players retarded
    - Egalitarian Station Mode
    - Break all lights
    - Fix all lights
    - The floor is lava! (DANGEROUS: extremely lame)
    +======= +>>>>>>> 6e5ebf9c41fc97d5ee0daf4fd22536844438ace0 + Trigger a Virus Outbreak
    + Turn all humans into monkeys
    + Chinese Cartoons
    + Change the species of all humans
    + Make all areas powered
    + Make all areas unpowered
    + Power all SMES
    + Triple AI mode (needs to be used in the lobby)
    + Everyone is the traitor
    + Summon Guns
    + Summon Magic
    + Summon Events (Toggle)
    + There can only be one!
    + There can only be one! (40-second delay)
    + Make all players retarded
    + Egalitarian Station Mode
    + Break all lights
    + Fix all lights
    + The floor is lava! (DANGEROUS: extremely lame)

    - Change bomb cap
    - Mass Purrbation
    - Mass Remove Purrbation
    + Change bomb cap
    + Mass Purrbation
    + Mass Remove Purrbation
    "} dat += "
    " @@ -78,9 +80,9 @@ dat += {" Security Level Elevated

    - Change all maintenance doors to engie/brig access only
    - Change all maintenance doors to brig access only
    - Remove cap on security officers
    + Change all maintenance doors to engie/brig access only
    + Change all maintenance doors to brig access only
    + Remove cap on security officers

    "} @@ -526,12 +528,6 @@ usr.client.only_one_delayed() sound_to_playing_players('sound/misc/highlander_delayed.ogg') - if("onlyme") - if(!check_rights(R_FUN)) - return - SSblackbox.add_details("admin_secrets_fun_used","There Can Be Only Me") - only_me() - if("maint_access_brig") if(!check_rights(R_DEBUG)) return diff --git a/code/modules/admin/sql_message_system.dm b/code/modules/admin/sql_message_system.dm index b42fe93eef..2d35dcad80 100644 --- a/code/modules/admin/sql_message_system.dm +++ b/code/modules/admin/sql_message_system.dm @@ -56,7 +56,7 @@ if(browse) browse_messages("[type]") else - browse_messages(target_ckey = target_ckey) + browse_messages(target_ckey = target_ckey, agegate = TRUE) /proc/delete_message(message_id, logged = 1, browse) if(!SSdbcore.Connect()) @@ -68,14 +68,14 @@ var/type var/target_ckey var/text - var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, targetckey, adminckey, text FROM [format_table_name("messages")] WHERE id = [message_id]") + var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, targetckey, adminckey, text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") if(!query_find_del_message.warn_execute()) return if(query_find_del_message.NextRow()) type = query_find_del_message.item[1] target_ckey = query_find_del_message.item[2] text = query_find_del_message.item[4] - var/datum/DBQuery/query_del_message = SSdbcore.NewQuery("DELETE FROM [format_table_name("messages")] WHERE id = [message_id]") + var/datum/DBQuery/query_del_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET deleted = 1 WHERE id = [message_id]") if(!query_del_message.warn_execute()) return if(logged) @@ -84,7 +84,7 @@ if(browse) browse_messages("[type]") else - browse_messages(target_ckey = target_ckey) + browse_messages(target_ckey = target_ckey, agegate = TRUE) /proc/edit_message(message_id, browse) if(!SSdbcore.Connect()) @@ -93,7 +93,7 @@ message_id = text2num(message_id) if(!message_id) return - var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, targetckey, adminckey, text FROM [format_table_name("messages")] WHERE id = [message_id]") + var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, targetckey, adminckey, text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") if(!query_find_edit_message.warn_execute()) return if(query_find_edit_message.NextRow()) @@ -107,7 +107,7 @@ return new_text = sanitizeSQL(new_text) var/edit_text = sanitizeSQL("Edited by [editor_ckey] on [SQLtime()] from
    [old_text]
    to
    [new_text]


    ") - var/datum/DBQuery/query_edit_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET text = '[new_text]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id]") + var/datum/DBQuery/query_edit_message = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET text = '[new_text]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0") if(!query_edit_message.warn_execute()) return log_admin_private("[key_name(usr)] has edited a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_ckey]" : ""] made by [admin_ckey] from [old_text] to [new_text]") @@ -115,7 +115,7 @@ if(browse) browse_messages("[type]") else - browse_messages(target_ckey = target_ckey) + browse_messages(target_ckey = target_ckey, agegate = TRUE) /proc/toggle_message_secrecy(message_id) if(!SSdbcore.Connect()) @@ -124,7 +124,7 @@ message_id = text2num(message_id) if(!message_id) return - var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, targetckey, adminckey, secret FROM [format_table_name("messages")] WHERE id = [message_id]") + var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, targetckey, adminckey, secret FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") if(!query_find_message_secret.warn_execute()) return if(query_find_message_secret.NextRow()) @@ -139,18 +139,18 @@ return log_admin_private("[key_name(usr)] has toggled [target_ckey]'s [type] made by [admin_ckey] to [secret ? "not secret" : "secret"]") message_admins("[key_name_admin(usr)] has toggled [target_ckey]'s [type] made by [admin_ckey] to [secret ? "not secret" : "secret"]") - browse_messages(target_ckey = target_ckey) + browse_messages(target_ckey = target_ckey, agegate = TRUE) -/proc/browse_messages(type, target_ckey, index, linkless = 0, filter) +/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE) if(!SSdbcore.Connect()) to_chat(usr, "Failed to establish database connection.") return var/output var/ruler = "
    " - var/navbar = "\[All\]|\[#\]" + var/navbar = "\[All\]|\[#\]" for(var/letter in GLOB.alphabet) - navbar += "|\[[letter]\]" - navbar += "|\[Memos\]|\[Watchlist\]" + navbar += "|\[[letter]\]" + navbar += "|\[Memos\]|\[Watchlist\]" navbar += "
    \ \ \ @@ -160,16 +160,16 @@ if(type == "memo" || type == "watchlist entry") if(type == "memo") output += "

    Admin memos

    " - output += "\[Add memo\]" + output += "\[Add memo\]" else if(type == "watchlist entry") output += "

    Watchlist entries

    " - output += "\[Add watchlist entry\]" + output += "\[Add watchlist entry\]" if(filter) - output += "|\[Unfilter clients\]" + output += "|\[Unfilter clients\]" else - output += "|\[Filter offline clients\]" + output += "|\[Filter offline clients\]" output += ruler - var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery("SELECT id, targetckey, adminckey, text, timestamp, server, lasteditor FROM [format_table_name("messages")] WHERE type = '[type]'") + var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery("SELECT id, targetckey, adminckey, text, timestamp, server, lasteditor FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0") if(!query_get_type_messages.warn_execute()) return while(query_get_type_messages.NextRow()) @@ -186,19 +186,20 @@ if(type == "watchlist entry") output += "[t_ckey] | " output += "[timestamp] | [server] | [admin_ckey]" - output += " \[Delete\]" - output += " \[Edit\]" + output += " \[Delete\]" + output += " \[Edit\]" if(editor_ckey) - output += " Last edit by [editor_ckey] (Click here to see edit log)" + output += " Last edit by [editor_ckey] (Click here to see edit log)" output += "
    [text]
    " if(target_ckey) target_ckey = sanitizeSQL(target_ckey) - var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery("SELECT type, secret, id, adminckey, text, timestamp, server, lasteditor FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey = '[target_ckey]' ORDER BY timestamp DESC") + var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery("SELECT type, secret, id, adminckey, text, timestamp, server, lasteditor, DATEDIFF(NOW(), timestamp) AS `age` FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey = '[target_ckey]' AND deleted = 0 ORDER BY timestamp DESC") if(!query_get_messages.warn_execute()) return var/messagedata var/watchdata var/notedata + var/skipped = 0 while(query_get_messages.NextRow()) type = query_get_messages.item[1] if(type == "memo") @@ -212,21 +213,34 @@ var/timestamp = query_get_messages.item[6] var/server = query_get_messages.item[7] var/editor_ckey = query_get_messages.item[8] + var/age = text2num(query_get_messages.item[9]) + var/alphatext = "" + if (agegate && type == "note" && isnum(config.note_stale_days) && isnum(config.note_fresh_days) && config.note_stale_days > config.note_fresh_days) + var/alpha = Clamp(100 - (age - config.note_fresh_days) * (85 / (config.note_stale_days - config.note_fresh_days)), 15, 100) + if (alpha < 100) + if (alpha <= 15) + if (skipped) + skipped++ + continue + alpha = 10 + skipped = TRUE + alphatext = "filter: alpha(opacity=[alpha]); opacity: [alpha/100];" + var/data - data += "[timestamp] | [server] | [admin_ckey]" + data += "

    [timestamp] | [server] | [admin_ckey]" if(!linkless) - data += " \[Delete\]" + data += " \[Delete\]" if(type == "note") - data += " [secret ? "\[Secret\]" : "\[Not secret\]"]" + data += " [secret ? "\[Secret\]" : "\[Not secret\]"]" if(type == "message sent") data += " Message has been sent" if(editor_ckey) data += "|" else - data += " \[Edit\]" + data += " \[Edit\]" if(editor_ckey) - data += " Last edit by [editor_ckey] (Click here to see edit log)" - data += "
    [text]


    " + data += " Last edit by [editor_ckey] (Click here to see edit log)" + data += "
    [text]


    " switch(type) if("message") messagedata += data @@ -238,12 +252,12 @@ notedata += data output += "

    [target_ckey]

    " if(!linkless) - output += "\[Add note\]" - output += " \[Add message\]" - output += " \[Add to watchlist\]" - output += " \[Refresh page\]
    " + output += "\[Add note\]" + output += " \[Add message\]" + output += " \[Add to watchlist\]" + output += " \[Refresh page\]" else - output += " \[Refresh page\]" + output += " \[Refresh page\]" output += ruler if(messagedata) output += "

    Messages

    " @@ -254,10 +268,19 @@ if(notedata) output += "

    Notes

    " output += notedata + if(!linkless) + if (agegate) + if (skipped) //the first skipped message is still shown so that we can put this link over it. + output += "
    \[Show [skipped] hidden messages\]
    " + else + output += "
    \[Show All\]
    " + + else + output += "
    \[Hide Old\]
    " if(index) var/index_ckey var/search - output += "
    \[Add message\]\[Add watchlist entry\]\[Add note\]
    " + output += "
    \[Add message\]\[Add watchlist entry\]\[Add note\]
    " output += ruler if(!isnum(index)) index = sanitizeSQL(index) @@ -268,16 +291,16 @@ search = "^\[^\[:alpha:\]\]" else search = "^[index]" - var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery("SELECT DISTINCT targetckey FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey REGEXP '[search]' ORDER BY targetckey") + var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery("SELECT DISTINCT targetckey FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey REGEXP '[search]' AND deleted = 0 ORDER BY targetckey") if(!query_list_messages.warn_execute()) return while(query_list_messages.NextRow()) index_ckey = query_list_messages.item[1] - output += "[index_ckey]
    " + output += "[index_ckey]
    " else if(!type && !target_ckey && !index) - output += "
    \[Add message\]\[Add watchlist entry\]\[Add note\]
    " + output += "
    \[Add message\]\[Add watchlist entry\]\[Add note\]
    " output += ruler - usr << browse(output, "window=browse_messages;size=900x500") + usr << browse({"[output]"}, "window=browse_messages;size=900x500") proc/get_message_output(type, target_ckey) if(!SSdbcore.Connect()) @@ -288,7 +311,7 @@ proc/get_message_output(type, target_ckey) var/output if(target_ckey) target_ckey = sanitizeSQL(target_ckey) - var/query = "SELECT id, adminckey, text, timestamp, lasteditor FROM [format_table_name("messages")] WHERE type = '[type]'" + var/query = "SELECT id, adminckey, text, timestamp, lasteditor FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0" if(type == "message" || type == "watchlist entry") query += " AND targetckey = '[target_ckey]'" var/datum/DBQuery/query_get_message_output = SSdbcore.NewQuery(query) @@ -313,7 +336,7 @@ proc/get_message_output(type, target_ckey) if("memo") output += "Memo by [admin_ckey] on [timestamp]" if(editor_ckey) - output += "
    Last edit by [editor_ckey] (Click here to see edit log)" + output += "
    Last edit by [editor_ckey] (Click here to see edit log)" output += "
    [text]

    " return output diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm index 9ac85e49fa..f5d6c72ffb 100644 --- a/code/modules/admin/stickyban.dm +++ b/code/modules/admin/stickyban.dm @@ -152,11 +152,11 @@ /datum/admins/proc/stickyban_gethtml(ckey, ban) . = {" - \[-\] - \[revert\] + \[-\] + \[revert\] [ckey]
    " - [ban["message"]] \[Edit\]
    + [ban["message"]] \[Edit\]
    "} if (ban["admin"]) . += "[ban["admin"]]
    " @@ -166,7 +166,7 @@ for (var/key in ban["keys"]) if (ckey(key) == ckey) continue - . += "
  • \[-\][key]
  • " + . += "
  • \[-\][key]
  • " . += "\n" /datum/admins/proc/stickyban_show() @@ -185,7 +185,7 @@ Sticky Bans -

    All Sticky Bans:

    \[+\]
    +

    All Sticky Bans:

    \[+\]
    [banhtml] "} diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 88e3fb8e16..8487e58810 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -9,8 +9,8 @@ message_admins("Debug mode enabled, call not blocked. Please ask your coders to review this round's logs.") log_world("UAH: [href]") return TRUE - log_admin_private("[key_name(usr)] clicked an href with [msg] authorization key! [href]") - +log_admin_private("[key_name(usr)] clicked an href with [msg] authorization key! [href]") + /datum/admins/Topic(href, href_list) ..() @@ -18,6 +18,10 @@ message_admins("[usr.key] has attempted to override the admin panel!") log_admin("[key_name(usr)] tried to use the admin panel without authorization.") return + + if(!CheckAdminHref(href, href_list)) + return + if(href_list["ahelp"]) if(!check_rights(R_ADMIN, TRUE)) return @@ -1120,7 +1124,10 @@ else if(href_list["showmessageckey"]) var/target = href_list["showmessageckey"] - browse_messages(target_ckey = target) + var/agegate = TRUE + if (href_list["showall"]) + agegate = FALSE + browse_messages(target_ckey = target, agegate = agegate) else if(href_list["showmessageckeylinkless"]) var/target = href_list["showmessageckeylinkless"] diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index 5d4e0dc29a..eaa98934d5 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -122,7 +122,7 @@ /proc/SDQL_gen_vv_href(t) var/text = "" - text += "\ref[t]" + text += "\ref[t]" if(istype(t, /atom)) var/atom/a = t var/turf/T = a.loc diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index d593973188..c1c1894184 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -80,10 +80,10 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) if(!l2b) return var/list/dat = list("[title]") - dat += "Refresh

    " + dat += "Refresh

    " for(var/I in l2b) var/datum/admin_help/AH = I - dat += "Ticket #[AH.id]: [AH.initiator_key_name]: [AH.name]
    " + dat += "Ticket #[AH.id]: [AH.initiator_key_name]: [AH.name]
    " usr << browse(dat.Join(), "window=ahelp_list[state];size=600x480") @@ -228,22 +228,22 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) /datum/admin_help/proc/ClosureLinks(ref_src) if(!ref_src) ref_src = "\ref[src]" - . = " (REJT)" - . += " (IC)" - . += " (CLOSE)" - . += " (RSLVE)" + . = " (REJT)" + . += " (IC)" + . += " (CLOSE)" + . += " (RSLVE)" //private /datum/admin_help/proc/LinkedReplyName(ref_src) if(!ref_src) ref_src = "\ref[src]" - return "[initiator_key_name]" + return "[initiator_key_name]" //private /datum/admin_help/proc/TicketHref(msg, ref_src, action = "ticket") if(!ref_src) ref_src = "\ref[src]" - return "[msg]" + return "[msg]" //message from the initiator without a target, all admins will see this //won't bug irc @@ -675,7 +675,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) if(found.mind && found.mind.special_role) is_antag = 1 founds += "Name: [found.name]([found.real_name]) Ckey: [found.ckey] [is_antag ? "(Antag)" : null] " - msg += "[original_word](?|F) " + msg += "[original_word](?|F) " continue msg += "[original_word] " if(irc) diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 707a76854f..8e543c8a73 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -12,7 +12,7 @@ log_talk(mob,"[key_name(src)] : [msg]",LOGASAY) msg = keywords_lookup(msg) if(check_rights(R_ADMIN,0)) - msg = "ADMIN: [key_name(usr, 1)] (FLW): [msg]" + msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" to_chat(GLOB.admins, msg) else msg = "ADMIN: [key_name(usr, 1)]: [msg]" diff --git a/code/modules/admin/verbs/individual_logging.dm b/code/modules/admin/verbs/individual_logging.dm index ca84d5d759..cd3feed5d0 100644 --- a/code/modules/admin/verbs/individual_logging.dm +++ b/code/modules/admin/verbs/individual_logging.dm @@ -1,12 +1,12 @@ /proc/show_individual_logging_panel(mob/M, type = INDIVIDUAL_ATTACK_LOG) if(!M || !ismob(M)) return - var/dat = "
    Attack log | " - dat += "Say log | " - dat += "Emote log | " - dat += "OOC log | " - dat += "Show all | " - dat += "Refresh
    " + var/dat = "
    Attack log | " + dat += "Say log | " + dat += "Emote log | " + dat += "OOC log | " + dat += "Show all | " + dat += "Refresh
    " dat += "
    " diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index c8df7f56f1..de0cb36448 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -11,18 +11,17 @@ /datum/admins/proc/one_click_antag() var/dat = {" - Make Traitors
    - Make Changelings
    - Make Revs
    - Make Cult
    - Make Clockwork Cult
    - Make Blob
    - Make Gangsters
    - Make Wizard (Requires Ghosts)
    - Make Nuke Team (Requires Ghosts)
    - Make CentCom Response Team (Requires Ghosts)
    - Make Abductor Team (Requires Ghosts)
    - Make Revenant (Requires Ghost)
    + Make Traitors
    + Make Changelings
    + Make Revs
    + Make Cult
    + Make Clockwork Cult
    + Make Blob
    + Make Wizard (Requires Ghosts)
    + Make Nuke Team (Requires Ghosts)
    + Make CentCom Response Team (Requires Ghosts)
    + Make Abductor Team (Requires Ghosts)
    + Make Revenant (Requires Ghost)
    "} var/datum/browser/popup = new(usr, "oneclickantag", "Quick-Create Antagonist", 400, 400) @@ -358,7 +357,6 @@ return - /datum/admins/proc/makeGangsters() var/datum/game_mode/gang/temp = new @@ -395,6 +393,7 @@ return 0 + /datum/admins/proc/makeOfficial() var/mission = input("Assign a task for the official", "Assign Task", "Conduct a routine preformance review of [station_name()] and its Captain.") var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered to be a CentCom Official?", "deathsquad") diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm index 7301724eb6..abb87a4235 100644 --- a/code/modules/admin/verbs/onlyone.dm +++ b/code/modules/admin/verbs/onlyone.dm @@ -50,8 +50,8 @@ GLOBAL_VAR_INIT(highlander, FALSE) equip_to_slot_or_del(new /obj/item/device/radio/headset/heads/captain(src), slot_ears) equip_to_slot_or_del(new /obj/item/clothing/head/beret/highlander(src), slot_head) equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_l_store) - for(var/obj/item/pinpointer/P in src) + equip_to_slot_or_del(new /obj/item/pinpointer/nuke(src), slot_l_store) + for(var/obj/item/pinpointer/nuke/P in src) P.attack_self(src) var/obj/item/card/id/W = new(src) W.icon_state = "centcom" diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 20c1d91fd2..bfc4701012 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -135,4 +135,7 @@ for(var/mob/M in GLOB.player_list) if(M.client) SEND_SOUND(M, sound(null)) + var/client/C = M.client + if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) + C.chatOutput.sendMusic(" ") SSblackbox.add_details("admin_verb","Stop All Playing Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 62f3cab1d1..cb27d019ce 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1147,8 +1147,8 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits /datum/admins/proc/modify_goals() var/dat = "" for(var/datum/station_goal/S in SSticker.mode.station_goals) - dat += "[S.name] - Announce | Remove
    " - dat += "
    Add New Goal" + dat += "[S.name] - Announce | Remove
    " + dat += "
    Add New Goal" usr << browse(dat, "window=goals;size=400x400") @@ -1219,7 +1219,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits var/list/msg = list() msg += "Playtime ReportPlaytime:
    " src << browse(msg.Join(), "window=Player_playtime_check") @@ -1233,7 +1233,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits var/list/body = list() body += "Playtime for [C.key]
    Playtime:" body += C.get_exp_report() - body += "Toggle Exempt status" + body += "Toggle Exempt status" body += "" usr << browse(body.Join(), "window=playerplaytime[C.ckey];size=550x615") diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index e22da2f1b1..977d48fbd7 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -222,6 +222,7 @@ Pipelines + Other Objects -> Pipe network build_network() /obj/machinery/atmospherics/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) deconstruct(FALSE) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index f0f41e375a..89e6538b07 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -32,6 +32,8 @@ var/running_bob_anim = FALSE var/escape_in_progress = FALSE + var/message_cooldown + var/breakout_time = 0.5 /obj/machinery/atmospherics/components/unary/cryo_cell/Initialize() . = ..() @@ -219,7 +221,9 @@ update_icon() /obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user) - container_resist(user) + if(message_cooldown <= world.time) + message_cooldown = world.time + 50 + to_chat(user, "[src]'s door won't budge!") /obj/machinery/atmospherics/components/unary/cryo_cell/open_machine(drop = 0) if(!state_open && !panel_open) @@ -239,16 +243,17 @@ return occupant /obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user) - if(escape_in_progress) - to_chat(user, "You are already trying to exit (This will take around 30 seconds)") - return - escape_in_progress = TRUE - to_chat(user, "You struggle inside the cryotube, kicking the release with your foot... (This will take around 30 seconds.)") - audible_message("You hear a thump from [src].") - if(do_after(user, 300)) - if(occupant == user) // Check they're still here. - open_machine() - escape_in_progress = FALSE + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + user.visible_message("You see [user] kicking against the glass of [src]!", \ + "You struggle inside [src], kicking the release with your foot... (this will take about [(breakout_time<1) ? "[breakout_time*60] seconds" : "[breakout_time] minute\s"].)", \ + "You hear a thump from [src].") + if(do_after(user,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds + if(!user || user.stat != CONSCIOUS || user.loc != src ) + return + user.visible_message("[user] successfully broke out of [src]!", \ + "You successfully break out of [src]!") + open_machine() /obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user) ..() diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index 89f8b8d2cb..b73fd986f8 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -120,6 +120,7 @@ return 1 /obj/machinery/meter/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) new /obj/item/pipe_meter(loc) qdel(src) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 6135e6ac9d..6fbc7d552f 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -577,13 +577,13 @@ GLOBAL_LIST(external_rsc_urls) var/const/adminckey = "CID-Error" var/sql_ckey = sanitizeSQL(ckey) //check to see if we noted them in the last day. - var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW()") + var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0") if(!query_get_notes.Execute()) return if(query_get_notes.NextRow()) return //regardless of above, make sure their last note is not from us, as no point in repeating the same note over and over. - query_get_notes = SSdbcore.NewQuery("SELECT adminckey FROM [format_table_name("messages")] WHERE targetckey = '[sql_ckey]' ORDER BY timestamp DESC LIMIT 1") + query_get_notes = SSdbcore.NewQuery("SELECT adminckey FROM [format_table_name("messages")] WHERE targetckey = '[sql_ckey]' AND deleted = 0 ORDER BY timestamp DESC LIMIT 1") if(!query_get_notes.Execute()) return if(query_get_notes.NextRow()) diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 8e8690bde3..adf775f0e8 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -233,6 +233,9 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleprayersounds)() set category = "Preferences" set desc = "Stop Current Sounds" SEND_SOUND(usr, sound(null)) + var/client/C = usr.client + if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded) + C.chatOutput.sendMusic(" ") SSblackbox.add_details("preferences_verb","Stop Self Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index cd0f893fa6..a86b30e605 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -234,7 +234,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, OOC_COLOR) to_chat(usr, "Sorry, that function is not enabled on this server.") return - browse_messages(null, usr.ckey, null, 1) + browse_messages(null, usr.ckey, null, TRUE) /client/proc/ignore_key(client) var/client/C = client diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 1f12e671ec..d3b53d3a41 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -308,8 +308,8 @@ name = "Chainsaw" result = /obj/item/twohanded/required/chainsaw reqs = list(/obj/item/circular_saw = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/plasteel = 1) + /obj/item/stack/cable_coil = 3, + /obj/item/stack/sheet/plasteel = 5) tools = list(/obj/item/weldingtool) time = 50 category = CAT_WEAPONRY diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm index eee95fe0af..dddff75bb2 100644 --- a/code/modules/error_handler/error_viewer.dm +++ b/code/modules/error_handler/error_viewer.dm @@ -71,7 +71,7 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache) if (linear) back_to_param += ";viewruntime_linear=1" - return "[linktext]" + return "[linktext]" /datum/error_viewer/error_cache var/list/errors = list() @@ -181,12 +181,12 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache) var/html = build_header(back_to, linear) html += "[name]
    [desc]
    " if (usr_ref) - html += "
    usr: VV" - html += " PP" - html += " Follow" + html += "
    usr: VV" + html += " PP" + html += " Follow" if (istype(usr_loc)) - html += "
    usr.loc: VV" - html += " JMP" + html += "
    usr.loc: VV" + html += " JMP" browse_to(user, html) diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 1feb7aa116..5ea8c20e32 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -390,10 +390,10 @@ /datum/spacevine_controller/vv_get_dropdown() . = ..() . += "---" - .["Delete Vines"] = "?_src_=\ref[src];purge_vines=1" + .["Delete Vines"] = "?_src_=\ref[src];[HrefToken()];purge_vines=1" /datum/spacevine_controller/Topic(href, href_list) - if(..() || !check_rights(R_ADMIN, FALSE)) + if(..() || !check_rights(R_ADMIN, FALSE) || !usr.client.holder.CheckAdminHref(href, href_list)) return if(href_list["purge_vines"]) diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm index d9f70160a4..978e8af62d 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm @@ -51,6 +51,9 @@ /obj/machinery/gibber/container_resist(mob/living/user) go_out() +/obj/machinery/gibber/relaymove(mob/living/user) + go_out() + /obj/machinery/gibber/attack_hand(mob/user) if(stat & (NOPOWER|BROKEN)) return @@ -225,4 +228,4 @@ if(M.loc == input_plate) M.forceMove(src) - M.gib() \ No newline at end of file + M.gib() diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index e996429939..806fbc1f90 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -142,69 +142,60 @@ user.set_machine(src) interact(user) -/******************* -* SmartFridge Menu -********************/ -/obj/machinery/smartfridge/interact(mob/user) - if(stat) - return FALSE - var/dat = "Select an item:
    " +/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "smartvend", name, 440, 550, master_ui, state) + ui.set_autoupdate(FALSE) + ui.open() - if (contents.len == 0) - dat += "No product loaded!" - else - var/listofitems = list() - for (var/atom/movable/O in contents) +/obj/machinery/smartfridge/ui_data(mob/user) + . = list() + + var/listofitems = list() + for (var/I in src) + var/atom/movable/O = I + if (!QDELETED(O)) if (listofitems[O.name]) - listofitems[O.name]++ + listofitems[O.name]["amount"]++ else - listofitems[O.name] = 1 - sortList(listofitems) + listofitems[O.name] = list("name" = O.name, "type" = O.type, "amount" = 1) + sortList(listofitems) - for (var/O in listofitems) - if(listofitems[O] <= 0) - continue - var/N = listofitems[O] - var/itemName = url_encode(O) - dat += "[capitalize(O)]:" - dat += " [N] " - dat += "Vend " - if(N > 5) - dat += "(x5)" - if(N > 10) - dat += "(x10)" - if(N > 25) - dat += "(x25)" - if(N > 1) - dat += "(All)" + .["contents"] = listofitems + .["name"] = name + .["isdryer"] = FALSE - dat += "
    " - dat += "
    " - user << browse("[src] supplies[dat]", "window=smartfridge") - onclose(user, "smartfridge") - return dat +/obj/machinery/smartfridge/handle_atom_del(atom/A) // Update the UIs in case something inside gets deleted + SStgui.update_uis(src) -/obj/machinery/smartfridge/Topic(var/href, var/list/href_list) - if(..()) +/obj/machinery/smartfridge/ui_act(action, params) + . = ..() + if(.) return - usr.set_machine(src) + switch(action) + if("Release") + var/desired = 0 - var/N = href_list["vend"] - var/amount = text2num(href_list["amount"]) + if (params["amount"]) + desired = text2num(params["amount"]) + else + desired = input("How many items?", "How many items would you like to take out?", 1) as null|num - var/i = amount - for(var/obj/O in contents) - if(i <= 0) - break - if(O.name == N) - O.loc = src.loc - i-- + if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src)) // Sanity checkin' in case stupid stuff happens while we wait for input() + return FALSE - - updateUsrDialog() + for(var/obj/item/O in src) + if(desired <= 0) + break + if(O.name == params["name"]) + O.forceMove(drop_location()) + desired-- + return TRUE + return FALSE // ---------------------------- @@ -240,20 +231,23 @@ /obj/machinery/smartfridge/drying_rack/default_deconstruction_crowbar(obj/item/crowbar/C, ignore_panel = 1) ..() -/obj/machinery/smartfridge/drying_rack/interact(mob/user) - var/dat = ..() - if(dat) - dat += "
    " - dat += "Toggle Drying " - user << browse("[src] supplies[dat]", "window=smartfridge") - onclose(user, "smartfridge") +/obj/machinery/smartfridge/drying_rack/ui_data(mob/user) + . = ..() + .["isdryer"] = TRUE + .["verb"] = "Take" + .["drying"] = drying -/obj/machinery/smartfridge/drying_rack/Topic(href, list/href_list) - ..() - if(href_list["dry"]) - toggle_drying(FALSE) - updateUsrDialog() - update_icon() + +/obj/machinery/smartfridge/drying_rack/ui_act(action, params) + . = ..() + if(.) + update_icon() // This is to handle a case where the last item is taken out manually instead of through drying pop-out + return + switch(action) + if("Dry") + toggle_drying(FALSE) + return TRUE + return FALSE /obj/machinery/smartfridge/drying_rack/power_change() if(powered() && anchored) @@ -279,6 +273,7 @@ ..() if(drying) if(rack_dry())//no need to update unless something got dried + SStgui.update_uis(src) update_icon() /obj/machinery/smartfridge/drying_rack/accept_check(obj/item/O) diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm index 8dfbfa7c63..86481b4c14 100644 --- a/code/modules/holodeck/items.dm +++ b/code/modules/holodeck/items.dm @@ -120,17 +120,15 @@ else ..() -/obj/structure/holohoop/CanPass(atom/movable/mover, turf/target) - if (isitem(mover) && mover.throwing) - var/obj/item/I = mover - if(istype(I, /obj/item/projectile)) - return +/obj/structure/holohoop/hitby(atom/movable/AM) + if (isitem(AM) && !istype(AM,/obj/item/projectile)) if(prob(50)) - I.forceMove(get_turf(src)) - visible_message("Swish! [I] lands in [src].") + AM.forceMove(get_turf(src)) + visible_message("Swish! [AM] lands in [src].") + return else - visible_message("[I] bounces off of [src]'s rim!") - return 0 + visible_message("[AM] bounces off of [src]'s rim!") + return ..() else return ..() diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 66a15ac705..52f5e4e138 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -248,7 +248,7 @@ if(istype(src, /obj/machinery/hydroponics/soil)) add_atom_colour(rgb(255, 175, 0), FIXED_COLOUR_PRIORITY) else - overlays += mutable_appearance('icons/obj/hydroponics/equipment.dmi', "gaia_blessing") + add_overlay(mutable_appearance('icons/obj/hydroponics/equipment.dmi', "gaia_blessing")) set_light(3) update_icon_hoses() diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm index 94d2753fc5..9a33e1e759 100644 --- a/code/modules/jobs/job_types/medical.dm +++ b/code/modules/jobs/job_types/medical.dm @@ -33,6 +33,7 @@ Chief Medical Officer id = /obj/item/card/id/silver belt = /obj/item/device/pda/heads/cmo + l_pocket = /obj/item/pinpointer/crew ears = /obj/item/device/radio/headset/heads/cmo uniform = /obj/item/clothing/under/rank/chief_medical_officer shoes = /obj/item/clothing/shoes/sneakers/brown diff --git a/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm b/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm index 648e6170fa..228c9fe6e7 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories_Citadel.dm @@ -1,8 +1,8 @@ /datum/sprite_accessory - var/extra = 0 + var/extra = FALSE var/extra_icon = 'icons/mob/mam_bodyparts.dmi' var/extra_color_src = MUTCOLORS2 //The color source for the extra overlay. - var/extra2 = 0 + var/extra2 = FALSE var/extra2_icon = 'icons/mob/mam_bodyparts.dmi' var/extra2_color_src = MUTCOLORS3 // var/list/ckeys_allowed = null @@ -13,6 +13,10 @@ icon = 'icons/mob/mam_bodyparts.dmi' */ +/***************** Alphabetical Order please *************** +************* Keep it to Ears, Tails, Tails Animated *********/ + + /datum/sprite_accessory/tails/lizard/none name = "None" icon_state = "None" @@ -31,12 +35,17 @@ color_src = 0 icon = 'icons/mob/mam_bodyparts.dmi' +/datum/sprite_accessory/ears/human/bear + name = "Bear" + icon_state = "bear" + icon = 'icons/mob/mam_bodyparts.dmi' + /datum/sprite_accessory/tails/human/bear name = "Bear" icon_state = "bear" icon = 'icons/mob/mam_bodyparts.dmi' -/datum/sprite_accessory/ears/human/bear +/datum/sprite_accessory/tails_animated/human/bear name = "Bear" icon_state = "bear" icon = 'icons/mob/mam_bodyparts.dmi' @@ -51,10 +60,27 @@ icon_state = "catbig" icon = 'icons/mob/mam_bodyparts.dmi' +/datum/sprite_accessory/ears/human/cow + name = "Cow" + icon_state = "cow" + icon = 'icons/mob/mam_bodyparts.dmi' + gender_specific = 1 + +/datum/sprite_accessory/tails/human/cow + name = "Cow" + icon_state = "cow" + icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/tails_animated/human/cow + name = "Cow" + icon_state = "cow" + icon = 'icons/mob/mam_bodyparts.dmi' + /datum/sprite_accessory/ears/fennec name = "Fennec" icon_state = "fennec" icon = 'icons/mob/mam_bodyparts.dmi' + hasinner = 1 /datum/sprite_accessory/tails/human/fennec name = "Fennec" @@ -76,37 +102,49 @@ name = "Fox" icon_state = "fox" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/tails_animated/human/fox name = "Fox" icon_state = "fox" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE + +/datum/sprite_accessory/tails/human/horse + name = "Horse" + icon_state = "horse" + icon = 'icons/mob/mam_bodyparts.dmi' + color_src = HAIR + +/datum/sprite_accessory/tails_animated/human/horse + name = "Horse" + icon_state = "horse" + icon = 'icons/mob/mam_bodyparts.dmi' + color_src = HAIR /datum/sprite_accessory/tails/human/husky name = "Husky" icon_state = "husky" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/tails_animated/human/husky name = "Husky" icon_state = "husky" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/tails/human/kitsune name = "Kitsune" icon_state = "kitsune" - extra = 1 + extra = TRUE extra_color_src = MUTCOLORS2 icon = 'icons/mob/mam_bodyparts.dmi' /datum/sprite_accessory/tails_animated/human/kitsune name = "Kitsune" icon_state = "kitsune" - extra = 1 + extra = TRUE extra_color_src = MUTCOLORS2 icon = 'icons/mob/mam_bodyparts.dmi' @@ -146,6 +184,24 @@ name = "Otusian" icon_state = "otie" +/datum/sprite_accessory/ears/human/rabbit + name = "Rabbit" + icon_state = "rabbit" + hasinner= 1 + icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/tails/human/rabbit + name = "Rabbit" + icon_state = "rabbit" + color_src = 0 + icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/tails_animated/human/rabbit + name = "Rabbit" + icon_state = "rabbit" + color_src = 0 + icon = 'icons/mob/mam_bodyparts.dmi' + /datum/sprite_accessory/ears/human/skunk name = "skunk" icon_state = "skunk" @@ -191,7 +247,7 @@ /datum/sprite_accessory/ears/wolf name = "Wolf" icon_state = "wolf" - extra = 1 + hasinner = 1 /datum/sprite_accessory/tails/human/wolf name = "Wolf" @@ -203,18 +259,6 @@ icon_state = "wolf" icon = 'icons/mob/mam_bodyparts.dmi' -/datum/sprite_accessory/tails/human/rabbit - name = "Rabbit" - icon_state = "rabbit" - color_src = 0 - icon = 'icons/mob/mam_bodyparts.dmi' - -/datum/sprite_accessory/ears/human/rabbit - name = "Rabbit" - icon_state = "rabbit" - hasinner= 1 - icon = 'icons/mob/mam_bodyparts.dmi' - /****************************************** *************** Body Parts **************** *******************************************/ @@ -248,36 +292,37 @@ name = "Beak" icon_state = "bird" icon = 'icons/mob/mam_bodyparts.dmi' + color_src = MUTCOLORS3 /datum/sprite_accessory/snouts/lcanid name = "Fox, Long" icon_state = "lcanid" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/snouts/scanid name = "Fox, Short" icon_state = "scanid" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/snouts/wolf name = "Wolf" icon_state = "wolf" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/snouts/husky name = "Husky" icon_state = "husky" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/snouts/otie name = "Otie" icon_state = "otie" icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /****************************************** ************ Actual Species *************** @@ -286,31 +331,30 @@ /datum/sprite_accessory/mam_tails/ailurus name = "Ailurus" icon_state = "ailurus" - extra = 1 - extra_color_src = MUTCOLORS2 + extra = TRUE /datum/sprite_accessory/mam_tails_animated/ailurus name = "Ailurus" icon_state = "ailurus" - extra = 1 - extra_color_src = MUTCOLORS2 - -/datum/sprite_accessory/mam_tails/bear - name = "Bear" - icon_state = "bear" - icon = 'icons/mob/mam_bodyparts.dmi' + extra = TRUE /datum/sprite_accessory/mam_ears/bear name = "Bear" icon_state = "bear" - icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/mam_tails/bear + name = "Bear" + icon_state = "bear" + +/datum/sprite_accessory/mam_tails_animated/bear + name = "Bear" + icon_state = "bear" /datum/sprite_accessory/mam_ears/catbig name = "Cat, Big" icon_state = "cat" hasinner = 1 - icon = 'icons/mob/mutant_bodyparts.dmi' - + /datum/sprite_accessory/mam_tails/catbig name = "Cat, Big" icon_state = "catbig" @@ -323,6 +367,14 @@ name = "Cow" icon_state = "cow" gender_specific = 1 + +/datum/sprite_accessory/mam_tail/cow + name = "Cow" + icon_state = "cow" + +/datum/sprite_accessory/mam_tails_animated/cow + name = "Cow" + icon_state = "cow" /datum/sprite_accessory/mam_ears/deer name = "Deer" @@ -331,8 +383,7 @@ /datum/sprite_accessory/mam_tails/eevee name = "Eevee" icon_state = "eevee" - extra = 1 - extra_color_src = MUTCOLORS2 + extra = TRUE /datum/sprite_accessory/mam_ears/eevee name = "Eevee" @@ -341,8 +392,7 @@ /datum/sprite_accessory/mam_tails_animated/eevee name = "Eevee" icon_state = "eevee" - extra = 1 - extra_color_src = MUTCOLORS2 + extra = TRUE /datum/sprite_accessory/mam_ears/fennec name = "Fennec" @@ -360,25 +410,17 @@ /datum/sprite_accessory/mam_ears/fox name = "Fox" icon_state = "fox" - hasinner = 0 + hasinner = 1 /datum/sprite_accessory/mam_tails/fox name = "Fox" icon_state = "fox" - extra = 1 - extra_color_src = MUTCOLORS2 + extra = TRUE /datum/sprite_accessory/mam_tails_animated/fox name = "Fox" icon_state = "fox" - extra = 1 - extra_color_src = MUTCOLORS2 - -/datum/sprite_accessory/mam_ears/husky - name = "Husky" - icon_state = "wolf" - icon = 'icons/mob/mam_bodyparts.dmi' - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_tails/hawk name = "Hawk" @@ -388,20 +430,36 @@ name = "Hawk" icon_state = "hawk" +/datum/sprite_accessory/mam_tails/horse + name = "Horse" + icon_state = "horse" + color_src = HAIR + +/datum/sprite_accessory/mam_tails_animated/horse + name = "Horse" + icon_state = "Horse" + color_src = HAIR + +/datum/sprite_accessory/mam_ears/husky + name = "Husky" + icon_state = "wolf" + icon = 'icons/mob/mam_bodyparts.dmi' + extra = TRUE + /datum/sprite_accessory/mam_tails/husky name = "Husky" icon_state = "husky" - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_tails_animated/husky name = "Husky" icon_state = "husky" - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_ears/kangaroo name = "kangaroo" icon_state = "kangaroo" - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_tails/kangaroo name = "kangaroo" @@ -414,14 +472,12 @@ /datum/sprite_accessory/mam_tails/kitsune name = "Kitsune" icon_state = "kitsune" - extra = 1 - extra_color_src = MUTCOLORS2 + extra = TRUE /datum/sprite_accessory/mam_tails_animated/kitsune name = "Kitsune" icon_state = "kitsune" - extra = 1 - extra_color_src = MUTCOLORS2 + extra = TRUE /datum/sprite_accessory/mam_ears/lab name = "Dog, Long" @@ -462,6 +518,32 @@ name = "Otusian" icon_state = "otie" +/datum/sprite_accessory/mam_ears/rabbit + name = "Rabbit" + icon_state = "rabbit" + hasinner= 1 + +/datum/sprite_accessory/mam_tails/rabbit + name = "Rabbit" + icon_state = "rabbit" + +/datum/sprite_accessory/mam_tails_animated/rabbit + name = "Rabbit" + icon_state = "rabbit" + +/datum/sprite_accessory/mam_ears/sergal + name = "Sergal" + icon_state = "sergal" + hasinner= 1 + +/datum/sprite_accessory/mam_tails/sergal + name = "Sergal" + icon_state = "sergal" + +/datum/sprite_accessory/mam_tails_animated/sergal + name = "Sergal" + icon_state = "sergal" + /datum/sprite_accessory/mam_ears/skunk name = "skunk" icon_state = "skunk" @@ -470,13 +552,13 @@ name = "skunk" icon_state = "skunk" color_src = 0 - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_tails_animated/skunk name = "skunk" icon_state = "skunk" color_src = 0 - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_tails/shark name = "Shark" @@ -486,19 +568,19 @@ /datum/sprite_accessory/mam_tails_animated/shark name = "Shark" icon_state = "shark" - color_src = 0 + color_src = MUTCOLORS /datum/sprite_accessory/mam_tails/shepherd name = "Shepherd" icon_state = "shepherd" - extra = 1 - extra2 = 1 + extra = TRUE + extra2 = TRUE /datum/sprite_accessory/mam_tails_animated/shepherd name = "Shepherd" icon_state = "shepherd" - extra = 1 - extra2 = 1 + extra = TRUE + extra2 = TRUE /datum/sprite_accessory/mam_ears/squirrel name = "Squirrel" @@ -516,7 +598,7 @@ /datum/sprite_accessory/mam_ears/wolf name = "Wolf" icon_state = "wolf" - extra = 1 + hasinner = 1 /datum/sprite_accessory/mam_tails/wolf name = "Wolf" @@ -526,26 +608,13 @@ name = "Wolf" icon_state = "wolf" -/datum/sprite_accessory/mam_tails/rabbit - name = "Rabbit" - icon_state = "rabbit" - -/datum/sprite_accessory/mam_tails_animated/rabbit - name = "Rabbit" - icon_state = "rabbit" - -/datum/sprite_accessory/mam_ears/rabbit - name = "Rabbit" - icon_state = "rabbit" - hasinner= 1 - /****************************************** ************ Body Markings **************** *******************************************/ /datum/sprite_accessory/mam_body_markings - extra = 1 - extra2 = 1 + extra = TRUE + extra2 = TRUE icon = 'icons/mob/mam_body_markings.dmi' /datum/sprite_accessory/mam_body_markings/none @@ -555,8 +624,6 @@ /datum/sprite_accessory/mam_body_markings/ailurus name = "Red Panda" icon_state = "ailurus" - extra_color_src = MUTCOLORS2 - extra2_color_src = MUTCOLORS3 gender_specific = 1 /datum/sprite_accessory/mam_body_markings/belly @@ -598,13 +665,11 @@ /datum/sprite_accessory/mam_body_markings/fennec name = "Fennec" icon_state = "Fennec" - extra_color_src = MUTCOLORS3 gender_specific = 1 /datum/sprite_accessory/mam_body_markings/fox name = "Fox" icon_state = "fox" - extra_color_src = MUTCOLORS3 gender_specific = 1 /datum/sprite_accessory/mam_body_markings/hawk @@ -668,9 +733,9 @@ /datum/sprite_accessory/taur icon = 'icons/mob/mam_taur.dmi' extra_icon = 'icons/mob/mam_taur.dmi' - extra = 1 + extra = TRUE extra2_icon = 'icons/mob/mam_taur.dmi' - extra2 = 1 + extra2 = TRUE center = TRUE dimension_x = 64 @@ -805,19 +870,17 @@ /datum/sprite_accessory/mam_body_markings/guilmon name = "Guilmon" icon_state = "guilmon" - extra_color_src = MUTCOLORS2 - extra2_color_src = MUTCOLORS3 gender_specific = 1 /datum/sprite_accessory/mam_tails/guilmon name = "Guilmon" icon_state = "guilmon" - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_tails_animated/guilmon name = "Guilmon" icon_state = "guilmon" - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_ears/guilmon name = "Guilmon" @@ -834,33 +897,33 @@ name = "DataShark" icon_state = "datashark" color_src = 0 - + /* //Till I get my snowflake only ckey lock, these are locked-locked :D /datum/sprite_accessory/mam_ears/sabresune name = "sabresune" icon_state = "sabresune" - extra = 1 + extra = TRUE extra_color_src = MUTCOLORS3 locked = TRUE /datum/sprite_accessory/mam_tails/sabresune name = "sabresune" icon_state = "sabresune" - extra = 1 + extra = TRUE locked = TRUE /datum/sprite_accessory/mam_tails_animated/sabresune name = "sabresune" icon_state = "sabresune" - extra = 1 + extra = TRUE /datum/sprite_accessory/mam_body_markings/sabresune name = "Sabresune" icon_state = "sabresune" color_src = MUTCOLORS2 - extra = 0 - extra2 = 0 + extra = FALSE + extra2 = FALSE locked = TRUE */ diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 84ff0c2ef2..717c5478a5 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -860,7 +860,7 @@ /mob/living/carbon/vv_get_dropdown() . = ..() . += "---" - .["Make AI"] = "?_src_=vars;makeai=\ref[src]" - .["Modify bodypart"] = "?_src_=vars;editbodypart=\ref[src]" - .["Modify organs"] = "?_src_=vars;editorgans=\ref[src]" - .["Hallucinate"] = "?_src_=vars;hallucinate=\ref[src]" + .["Make AI"] = "?_src_=vars;[HrefToken()];makeai=\ref[src]" + .["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=\ref[src]" + .["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=\ref[src]" + .["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=\ref[src]" diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 304a745220..dc7542fbab 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -643,6 +643,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) update_hair() /mob/living/carbon/human/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_THREE) for(var/obj/item/hand in held_items) if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && dropItemToGround(hand)) @@ -651,7 +652,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) rad_act(current_size * 3) if(mob_negates_gravity()) return - ..() /mob/living/carbon/human/proc/do_cpr(mob/living/carbon/C) CHECK_DNA_AND_SPECIES(C) @@ -906,12 +906,12 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) /mob/living/carbon/human/vv_get_dropdown() . = ..() . += "---" - .["Make monkey"] = "?_src_=vars;makemonkey=\ref[src]" - .["Set Species"] = "?_src_=vars;setspecies=\ref[src]" - .["Make cyborg"] = "?_src_=vars;makerobot=\ref[src]" - .["Make alien"] = "?_src_=vars;makealien=\ref[src]" - .["Make slime"] = "?_src_=vars;makeslime=\ref[src]" - .["Toggle Purrbation"] = "?_src_=vars;purrbation=\ref[src]" + .["Make monkey"] = "?_src_=vars;[HrefToken()];makemonkey=\ref[src]" + .["Set Species"] = "?_src_=vars;[HrefToken()];setspecies=\ref[src]" + .["Make cyborg"] = "?_src_=vars;[HrefToken()];makerobot=\ref[src]" + .["Make alien"] = "?_src_=vars;[HrefToken()];makealien=\ref[src]" + .["Make slime"] = "?_src_=vars;[HrefToken()];makeslime=\ref[src]" + .["Toggle Purrbation"] = "?_src_=vars;[HrefToken()];purrbation=\ref[src]" /mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user) if((target != pulling) || (grab_state < GRAB_AGGRESSIVE) || (user != target) || !isliving(user) || stat || user.stat)//Get consent first :^) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 52650ec273..06f3007e6b 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,67 +1,67 @@ -/mob/living/carbon/human/movement_delay() - . = 0 - . += ..() - . += config.human_delay - . += dna.species.movement_delay(src) - -/mob/living/carbon/human/slip(knockdown_amount, obj/O, lube) +/mob/living/carbon/human/movement_delay() + . = 0 + . += ..() + . += config.human_delay + . += dna.species.movement_delay(src) + +/mob/living/carbon/human/slip(knockdown_amount, obj/O, lube) if(isobj(shoes) && (shoes.flags_1&NOSLIP_1) && !(lube&GALOSHES_DONT_HELP)) - return 0 - return ..() - -/mob/living/carbon/human/experience_pressure_difference() - playsound(src, 'sound/effects/space_wind.ogg', 50, 1) + return 0 + return ..() + +/mob/living/carbon/human/experience_pressure_difference() + playsound(src, 'sound/effects/space_wind.ogg', 50, 1) if(shoes && shoes.flags_1&NOSLIP_1) - return 0 - return ..() - -/mob/living/carbon/human/mob_has_gravity() - . = ..() - if(!.) - if(mob_negates_gravity()) - . = 1 - -/mob/living/carbon/human/mob_negates_gravity() - return ((shoes && shoes.negates_gravity()) || dna.species.negates_gravity(src)) - -/mob/living/carbon/human/Move(NewLoc, direct) - . = ..() - for(var/datum/mutation/human/HM in dna.mutations) - HM.on_move(src, NewLoc) - if(shoes) - if(!lying && !buckled) - if(loc == NewLoc) - if(!has_gravity(loc)) - return - var/obj/item/clothing/shoes/S = shoes - - //Bloody footprints - var/turf/T = get_turf(src) - if(S.bloody_shoes && S.bloody_shoes[S.blood_state]) - var/obj/effect/decal/cleanable/blood/footprints/oldFP = locate(/obj/effect/decal/cleanable/blood/footprints) in T - if(oldFP && oldFP.blood_state == S.blood_state) - return - else - //No oldFP or it's a different kind of blood - S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state]-BLOOD_LOSS_PER_STEP) - var/obj/effect/decal/cleanable/blood/footprints/FP = new /obj/effect/decal/cleanable/blood/footprints(T) - FP.blood_state = S.blood_state - FP.entered_dirs |= dir - FP.bloodiness = S.bloody_shoes[S.blood_state] - if(S.blood_DNA && S.blood_DNA.len) - FP.transfer_blood_dna(S.blood_DNA) - FP.update_icon() - update_inv_shoes() - //End bloody footprints - - S.step_action() - -/mob/living/carbon/human/Moved() - . = ..() - if(buckled_mobs && buckled_mobs.len && riding_datum) - riding_datum.on_vehicle_move() - -/mob/living/carbon/human/Process_Spacemove(movement_dir = 0) //Temporary laziness thing. Will change to handles by species reee. - if(..()) - return 1 - return dna.species.space_move(src) + return 0 + return ..() + +/mob/living/carbon/human/mob_has_gravity() + . = ..() + if(!.) + if(mob_negates_gravity()) + . = 1 + +/mob/living/carbon/human/mob_negates_gravity() + return ((shoes && shoes.negates_gravity()) || dna.species.negates_gravity(src)) + +/mob/living/carbon/human/Move(NewLoc, direct) + . = ..() + for(var/datum/mutation/human/HM in dna.mutations) + HM.on_move(src, NewLoc) + + if(shoes) + if(!lying && !buckled) + if(loc == NewLoc) + if(!has_gravity(loc)) + return + var/obj/item/clothing/shoes/S = shoes + + //Bloody footprints + var/turf/T = get_turf(src) + if(S.bloody_shoes && S.bloody_shoes[S.blood_state]) + var/obj/effect/decal/cleanable/blood/footprints/oldFP = locate(/obj/effect/decal/cleanable/blood/footprints) in T + if(oldFP && oldFP.blood_state == S.blood_state) + return + else + //No oldFP or it's a different kind of blood + S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state]-BLOOD_LOSS_PER_STEP) + var/obj/effect/decal/cleanable/blood/footprints/FP = new /obj/effect/decal/cleanable/blood/footprints(T) + FP.blood_state = S.blood_state + FP.entered_dirs |= dir + FP.bloodiness = S.bloody_shoes[S.blood_state] + if(S.blood_DNA && S.blood_DNA.len) + FP.transfer_blood_dna(S.blood_DNA) + FP.update_icon() + update_inv_shoes() + //End bloody footprints + + S.step_action() +/mob/living/carbon/human/Moved() + . = ..() + if(buckled_mobs && buckled_mobs.len && riding_datum) + riding_datum.on_vehicle_move() + +/mob/living/carbon/human/Process_Spacemove(movement_dir = 0) //Temporary laziness thing. Will change to handles by species reee. + if(..()) + return 1 + return dna.species.space_move(src) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index d6d6084380..b2e88e278a 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -58,11 +58,16 @@ var/datum/gas_mixture/breath - if(health <= HEALTH_THRESHOLD_CRIT || (pulledby && pulledby.grab_state >= GRAB_KILL && !getorganslot("breathing_tube"))) - losebreath++ + if(!getorganslot("breathing_tube")) + if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL)) + losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath + + else if(health <= HEALTH_THRESHOLD_CRIT) + losebreath += 0.25 //You're having trouble breathing in soft crit, so you'll miss a breath one in four times + //Suffocate - if(losebreath > 0) + if(losebreath >= 1) //You've missed a breath, take oxy damage losebreath-- if(prob(10)) emote("gasp") @@ -150,7 +155,7 @@ else //Enough oxygen failed_last_breath = 0 - if(oxyloss) + if(health >= HEALTH_THRESHOLD_CRIT) adjustOxyLoss(-5) oxygen_used = breath_gases["o2"][MOLES] clear_alert("not_enough_oxy") diff --git a/code/modules/mob/living/carbon/monkey/punpun.dm b/code/modules/mob/living/carbon/monkey/punpun.dm index 711e771074..52ca8c8226 100644 --- a/code/modules/mob/living/carbon/monkey/punpun.dm +++ b/code/modules/mob/living/carbon/monkey/punpun.dm @@ -68,6 +68,6 @@ if(!ancestor_name) file_data["ancestor_name"] = name fdel(json_file) - WRITE_FILE(json_file, json_encode(json_file)) + WRITE_FILE(json_file, json_encode(file_data)) if(!dead) memory_saved = 1 \ No newline at end of file diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 70a8474165..29022da42d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -429,6 +429,7 @@ else return 0 + var/old_direction = dir var/atom/movable/pullee = pulling if(pullee && get_dist(src, pullee) > 1) stop_pulling() @@ -444,10 +445,6 @@ var/pull_dir = get_dir(src, pulling) if(get_dist(src, pulling) > 1 || ((pull_dir - 1) & pull_dir)) //puller and pullee more than one tile away or in diagonal position - if(isliving(pulling)) - var/mob/living/M = pulling - if(M.lying && !M.buckled && (prob(M.getBruteLoss()*200/M.maxHealth))) - M.makeTrail(T) pulling.Move(T, get_dir(pulling, T)) //the pullee tries to reach our previous position if(pulling && get_dist(src, pulling) > 1) //the pullee couldn't keep up stop_pulling() @@ -458,6 +455,10 @@ if (s_active && !(CanReach(s_active,view_only = TRUE))) s_active.close(src) + if(lying && !buckled && prob(getBruteLoss()*200/maxHealth)) + + makeTrail(newloc, T, old_direction) + /mob/living/movement_delay(ignorewalk = 0) . = ..() if(isopenturf(loc) && !is_flying()) @@ -474,31 +475,32 @@ if(MOVE_INTENT_WALK) . += config.walk_speed -/mob/living/proc/makeTrail(turf/target_turf) +/mob/living/proc/makeTrail(turf/target_turf, turf/start, direction) if(!has_gravity()) return var/blood_exists = FALSE - for(var/obj/effect/decal/cleanable/trail_holder/C in loc) //checks for blood splatter already on the floor + for(var/obj/effect/decal/cleanable/trail_holder/C in start) //checks for blood splatter already on the floor blood_exists = TRUE - if(isturf(loc)) + if(isturf(start)) var/trail_type = getTrail() if(trail_type) var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1) if(blood_volume && blood_volume > max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold blood_volume = max(blood_volume - max(1, brute_ratio * 2), 0) //that depends on our brute damage. - var/newdir = get_dir(target_turf, loc) - if(newdir != dir) - newdir = newdir | dir + var/newdir = get_dir(target_turf, start) + if(newdir != direction) + newdir = newdir | direction if(newdir == 3) //N + S newdir = NORTH else if(newdir == 12) //E + W newdir = EAST if((newdir in GLOB.cardinals) && (prob(50))) - newdir = turn(get_dir(target_turf, loc), 180) + newdir = turn(get_dir(target_turf, start), 180) if(!blood_exists) - new /obj/effect/decal/cleanable/trail_holder(loc) - for(var/obj/effect/decal/cleanable/trail_holder/TH in loc) + new /obj/effect/decal/cleanable/trail_holder(start) + + for(var/obj/effect/decal/cleanable/trail_holder/TH in start) if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled) TH.existing_dirs += newdir TH.add_overlay(image('icons/effects/blood.dmi', trail_type, dir = newdir)) @@ -690,6 +692,7 @@ who.equip_to_slot(what, where, TRUE) /mob/living/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_SIX) throw_at(S,14,3, spin=1) else diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index 52c06439fb..d41b6f5f29 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -35,9 +35,9 @@ if(nuking) set_security_level("red") nuking = FALSE - for(var/obj/item/pinpointer/P in GLOB.pinpointer_list) + for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list) P.switch_mode_to(TRACK_NUKE_DISK) //Party's over, back to work, everyone - P.nuke_warning = FALSE + P.alert = FALSE if(doomsday_device) doomsday_device.timing = FALSE diff --git a/code/modules/mob/living/silicon/ai/death.dm.rej b/code/modules/mob/living/silicon/ai/death.dm.rej new file mode 100644 index 0000000000..40b5625e8c --- /dev/null +++ b/code/modules/mob/living/silicon/ai/death.dm.rej @@ -0,0 +1,13 @@ +diff a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm (rejected hunks) +@@ -35,9 +35,9 @@ + if(nuking) + set_security_level("red") + nuking = FALSE +- for(var/obj/item/weapon/pinpointer/P in GLOB.pinpointer_list) ++ for(var/obj/item/weapon/pinpointer/nuke/P in GLOB.pinpointer_list) + P.switch_mode_to(TRACK_NUKE_DISK) //Party's over, back to work, everyone +- P.nuke_warning = FALSE ++ P.alert = FALSE + + if(doomsday_device) + doomsday_device.timing = FALSE diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 41fbc69180..af6f3daf92 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -599,7 +599,7 @@ /obj/item/gun/ballistic/revolver/grenadelauncher/cyborg, /obj/item/card/emag, /obj/item/crowbar/cyborg, - /obj/item/pinpointer/syndicate/cyborg) + /obj/item/pinpointer/syndicate_cyborg) ratvar_modules = list( /obj/item/clockwork/slab/cyborg/security, @@ -625,7 +625,7 @@ /obj/item/roller/robo, /obj/item/card/emag, /obj/item/crowbar/cyborg, - /obj/item/pinpointer/syndicate/cyborg, + /obj/item/pinpointer/syndicate_cyborg, /obj/item/stack/medical/gauze/cyborg, /obj/item/gun/medbeam) ratvar_modules = list( diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm.rej b/code/modules/mob/living/silicon/robot/robot_modules.dm.rej new file mode 100644 index 0000000000..b0f0812dc4 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm.rej @@ -0,0 +1,19 @@ +diff a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm (rejected hunks) +@@ -519,7 +519,7 @@ + /obj/item/weapon/gun/ballistic/revolver/grenadelauncher/cyborg, + /obj/item/weapon/card/emag, + /obj/item/weapon/crowbar/cyborg, +- /obj/item/weapon/pinpointer/syndicate/cyborg) ++ /obj/item/weapon/pinpointer/syndicate_cyborg) + + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/security, +@@ -545,7 +545,7 @@ + /obj/item/roller/robo, + /obj/item/weapon/card/emag, + /obj/item/weapon/crowbar/cyborg, +- /obj/item/weapon/pinpointer/syndicate/cyborg, ++ /obj/item/weapon/pinpointer/syndicate_cyborg, + /obj/item/stack/medical/gauze/cyborg, + /obj/item/weapon/gun/medbeam) + ratvar_modules = list( diff --git a/code/modules/mob/living/simple_animal/friendly/drone/verbs.dm b/code/modules/mob/living/simple_animal/friendly/drone/verbs.dm index 18bd4cc3b0..49faea14b3 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/verbs.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/verbs.dm @@ -15,6 +15,10 @@ /mob/living/simple_animal/drone/verb/toggle_light() set category = "Drone" set name = "Toggle drone light" + + if(stat == DEAD) + to_chat(src, "There's no light in your life... by that I mean you're dead.") + return if(light_on) set_light(0) else diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 76553d17fd..f768cbe2d2 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -587,7 +587,7 @@ var/turf/T = get_turf(client.eye) stat("Location:", COORD(T)) stat("CPU:", "[world.cpu]") - stat("Instances:", "[world.contents.len]") + stat("Instances:", "[num2text(world.contents.len, 10)]") GLOB.stat_entry() config.stat_entry() stat(null) @@ -950,18 +950,18 @@ /mob/vv_get_dropdown() . = ..() . += "---" - .["Gib"] = "?_src_=vars;gib=\ref[src]" - .["Give Spell"] = "?_src_=vars;give_spell=\ref[src]" - .["Remove Spell"] = "?_src_=vars;remove_spell=\ref[src]" - .["Give Disease"] = "?_src_=vars;give_disease=\ref[src]" - .["Toggle Godmode"] = "?_src_=vars;godmode=\ref[src]" - .["Drop Everything"] = "?_src_=vars;drop_everything=\ref[src]" - .["Regenerate Icons"] = "?_src_=vars;regenerateicons=\ref[src]" - .["Make Space Ninja"] = "?_src_=vars;ninja=\ref[src]" - .["Show player panel"] = "?_src_=vars;mob_player_panel=\ref[src]" - .["Toggle Build Mode"] = "?_src_=vars;build_mode=\ref[src]" - .["Assume Direct Control"] = "?_src_=vars;direct_control=\ref[src]" - .["Offer Control to Ghosts"] = "?_src_=vars;offer_control=\ref[src]" + .["Gib"] = "?_src_=vars;[HrefToken()];gib=\ref[src]" + .["Give Spell"] = "?_src_=vars;[HrefToken()];give_spell=\ref[src]" + .["Remove Spell"] = "?_src_=vars;[HrefToken()];remove_spell=\ref[src]" + .["Give Disease"] = "?_src_=vars;[HrefToken()];give_disease=\ref[src]" + .["Toggle Godmode"] = "?_src_=vars;[HrefToken()];godmode=\ref[src]" + .["Drop Everything"] = "?_src_=vars;[HrefToken()];drop_everything=\ref[src]" + .["Regenerate Icons"] = "?_src_=vars;[HrefToken()];regenerateicons=\ref[src]" + .["Make Space Ninja"] = "?_src_=vars;[HrefToken()];ninja=\ref[src]" + .["Show player panel"] = "?_src_=vars;[HrefToken()];mob_player_panel=\ref[src]" + .["Toggle Build Mode"] = "?_src_=vars;[HrefToken()];build_mode=\ref[src]" + .["Assume Direct Control"] = "?_src_=vars;[HrefToken()];direct_control=\ref[src]" + .["Offer Control to Ghosts"] = "?_src_=vars;[HrefToken()];offer_control=\ref[src]" /mob/vv_get_var(var_name) switch(var_name) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 3b070f1384..b0212e6155 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -874,9 +874,9 @@ occupier.loc = src.loc occupier.death() occupier.gib() - for(var/obj/item/pinpointer/P in GLOB.pinpointer_list) + for(var/obj/item/pinpointer/nuke/P in GLOB.pinpointer_list) P.switch_mode_to(TRACK_NUKE_DISK) //Pinpointers go back to tracking the nuke disk - P.nuke_warning = FALSE + P.alert = FALSE /obj/machinery/power/apc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/device/aicard/card) if(card.AI) diff --git a/code/modules/power/apc.dm.rej b/code/modules/power/apc.dm.rej new file mode 100644 index 0000000000..9e432f503c --- /dev/null +++ b/code/modules/power/apc.dm.rej @@ -0,0 +1,13 @@ +diff a/code/modules/power/apc.dm b/code/modules/power/apc.dm (rejected hunks) +@@ -867,9 +867,9 @@ + occupier.loc = src.loc + occupier.death() + occupier.gib() +- for(var/obj/item/weapon/pinpointer/P in GLOB.pinpointer_list) ++ for(var/obj/item/weapon/pinpointer/nuke/P in GLOB.pinpointer_list) + P.switch_mode_to(TRACK_NUKE_DISK) //Pinpointers go back to tracking the nuke disk +- P.nuke_warning = FALSE ++ P.alert = FALSE + + /obj/machinery/power/apc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/device/aicard/card) + if(card.AI) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index ebe478a7bf..a24c0d7c57 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -170,6 +170,7 @@ By design, d1 is the smallest direction and d2 is the highest return 0 /obj/structure/cable/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) deconstruct() diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index 9b22236a03..5e3bf0f749 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -1072,7 +1072,7 @@ All effects don't start immediately, but rather get worse over time; the rate is glass_desc = "Aromatic beverage served piping hot. According to folk tales it can almost wake the dead." /datum/reagent/consumable/ethanol/hearty_punch/on_mob_life(mob/living/M) - if(M.stat == UNCONSCIOUS && M.health <= 0) + if(M.health <= 0) M.adjustBruteLoss(-7, 0) M.adjustFireLoss(-7, 0) M.adjustToxLoss(-7, 0) diff --git a/code/modules/recycling/disposal-structures.dm b/code/modules/recycling/disposal-structures.dm index efe730d1c2..9015320c56 100644 --- a/code/modules/recycling/disposal-structures.dm +++ b/code/modules/recycling/disposal-structures.dm @@ -335,6 +335,7 @@ /obj/structure/disposalpipe/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) deconstruct() diff --git a/code/modules/recycling/disposal-unit.dm b/code/modules/recycling/disposal-unit.dm index 14ebec8128..517b4aa195 100644 --- a/code/modules/recycling/disposal-unit.dm +++ b/code/modules/recycling/disposal-unit.dm @@ -58,6 +58,7 @@ return ..() /obj/machinery/disposal/singularity_pull(S, current_size) + ..() if(current_size >= STAGE_FIVE) deconstruct() @@ -335,20 +336,18 @@ eject() . = TRUE -/obj/machinery/disposal/bin/CanPass(atom/movable/mover, turf/target) - if (isitem(mover) && mover.throwing) - var/obj/item/I = mover - if(istype(I, /obj/item/projectile)) - return + +/obj/machinery/disposal/bin/hitby(atom/movable/AM) + if(isitem(AM) && AM.CanEnterDisposals()) if(prob(75)) - I.forceMove(src) - visible_message("[I] lands in [src].") + AM.forceMove(src) + visible_message("[AM] lands in [src].") update_icon() else - visible_message("[I] bounces off of [src]'s rim!") - return 0 + visible_message("[AM] bounces off of [src]'s rim!") + return ..() else - return ..(mover, target) + return ..() /obj/machinery/disposal/bin/flush() ..() @@ -457,12 +456,12 @@ trunk.linked = src // link the pipe trunk to self /obj/machinery/disposal/deliveryChute/place_item_in_disposal(obj/item/I, mob/user) - if(I.disposalEnterTry()) + if(I.CanEnterDisposals()) ..() flush() /obj/machinery/disposal/deliveryChute/CollidedWith(atom/movable/AM) //Go straight into the chute - if(!AM.disposalEnterTry()) + if(!AM.CanEnterDisposals()) return switch(dir) if(NORTH) @@ -485,16 +484,16 @@ M.forceMove(src) flush() -/atom/movable/proc/disposalEnterTry() +/atom/movable/proc/CanEnterDisposals() return 1 -/obj/item/projectile/disposalEnterTry() +/obj/item/projectile/CanEnterDisposals() return -/obj/effect/disposalEnterTry() +/obj/effect/CanEnterDisposals() return -/obj/mecha/disposalEnterTry() +/obj/mecha/CanEnterDisposals() return /obj/machinery/disposal/deliveryChute/newHolderDestination(obj/structure/disposalholder/H) diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 525cb7cd74..89767a1c55 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -430,3 +430,23 @@ materials = list(MAT_METAL = 500, MAT_GLASS = 500) build_path = /obj/item/organ/liver/cybernetic/upgraded category = list("Medical Designs") + +/datum/design/cybernetic_lungs + name = "Cybernetic Lungs" + desc = "A pair of cybernetic lungs." + id = "cybernetic_lungs" + req_tech = list("biotech" = 4, "materials" = 4) + build_type = PROTOLATHE + materials = list(MAT_METAL = 500, MAT_GLASS = 500) + build_path = /obj/item/organ/lungs/cybernetic + category = list("Medical Designs") + +/datum/design/cybernetic_lungs_u + name = "Upgraded Cybernetic Lungs" + desc = "A pair of upgraded cybernetic lungs." + id = "cybernetic_lungs_u" + req_tech = list("biotech" = 5, "materials" = 5, "engineering" = 5) + build_type = PROTOLATHE + materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 500) + build_path = /obj/item/organ/lungs/cybernetic/upgraded + category = list("Medical Designs") \ No newline at end of file diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index e4927349d8..da965aae9d 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -46,71 +46,35 @@ desc = "The incomplete body of a golem. Add ten sheets of any mineral to finish." var/shell_type = /obj/effect/mob_spawn/human/golem var/has_owner = FALSE //if the resulting golem obeys someone - w_class = WEIGHT_CLASS_BULKY + w_class = WEIGHT_CLASS_BULKY /obj/item/golem_shell/attackby(obj/item/I, mob/user, params) ..() - var/species - if(istype(I, /obj/item/stack/)) + var/static/list/golem_shell_species_types = list( + /obj/item/stack/sheet/metal = /datum/species/golem, + /obj/item/stack/sheet/glass = /datum/species/golem/glass, + /obj/item/stack/sheet/plasteel = /datum/species/golem/plasteel, + /obj/item/stack/sheet/mineral/sandstone = /datum/species/golem/sand, + /obj/item/stack/sheet/mineral/plasma = /datum/species/golem/plasma, + /obj/item/stack/sheet/mineral/diamond = /datum/species/golem/diamond, + /obj/item/stack/sheet/mineral/gold = /datum/species/golem/gold, + /obj/item/stack/sheet/mineral/silver = /datum/species/golem/silver, + /obj/item/stack/sheet/mineral/uranium = /datum/species/golem/uranium, + /obj/item/stack/sheet/mineral/bananium = /datum/species/golem/bananium, + /obj/item/stack/sheet/mineral/titanium = /datum/species/golem/titanium, + /obj/item/stack/sheet/mineral/plastitanium = /datum/species/golem/plastitanium, + /obj/item/stack/sheet/mineral/abductor = /datum/species/golem/alloy, + /obj/item/stack/sheet/mineral/wood = /datum/species/golem/wood, + /obj/item/stack/sheet/bluespace_crystal = /datum/species/golem/bluespace, + /obj/item/stack/sheet/runed_metal = /datum/species/golem/runic, + /obj/item/stack/medical/gauze = /datum/species/golem/cloth, + /obj/item/stack/sheet/cloth = /datum/species/golem/cloth, + /obj/item/stack/sheet/mineral/adamantine = /datum/species/golem/adamantine, + /obj/item/stack/sheet/plastic = /datum/species/golem/plastic) + + if(istype(I, /obj/item/stack)) var/obj/item/stack/O = I - - if(istype(O, /obj/item/stack/sheet/metal)) - species = /datum/species/golem - - if(istype(O, /obj/item/stack/sheet/glass)) - species = /datum/species/golem/glass - - if(istype(O, /obj/item/stack/sheet/plasteel)) - species = /datum/species/golem/plasteel - - if(istype(O, /obj/item/stack/sheet/mineral/sandstone)) - species = /datum/species/golem/sand - - if(istype(O, /obj/item/stack/sheet/mineral/plasma)) - species = /datum/species/golem/plasma - - if(istype(O, /obj/item/stack/sheet/mineral/diamond)) - species = /datum/species/golem/diamond - - if(istype(O, /obj/item/stack/sheet/mineral/gold)) - species = /datum/species/golem/gold - - if(istype(O, /obj/item/stack/sheet/mineral/silver)) - species = /datum/species/golem/silver - - if(istype(O, /obj/item/stack/sheet/mineral/uranium)) - species = /datum/species/golem/uranium - - if(istype(O, /obj/item/stack/sheet/mineral/bananium)) - species = /datum/species/golem/bananium - - if(istype(O, /obj/item/stack/sheet/mineral/titanium)) - species = /datum/species/golem/titanium - - if(istype(O, /obj/item/stack/sheet/mineral/plastitanium)) - species = /datum/species/golem/plastitanium - - if(istype(O, /obj/item/stack/sheet/mineral/abductor)) - species = /datum/species/golem/alloy - - if(istype(O, /obj/item/stack/sheet/mineral/wood)) - species = /datum/species/golem/wood - - if(istype(O, /obj/item/stack/sheet/bluespace_crystal)) - species = /datum/species/golem/bluespace - - if(istype(O, /obj/item/stack/sheet/runed_metal)) - species = /datum/species/golem/runic - - if(istype(O, /obj/item/stack/medical/gauze) || istype(O, /obj/item/stack/sheet/cloth)) - species = /datum/species/golem/cloth - - if(istype(O, /obj/item/stack/sheet/mineral/adamantine)) - species = /datum/species/golem/adamantine - - if(istype(O, /obj/item/stack/sheet/plastic)) - species = /datum/species/golem/plastic - + var/species = golem_shell_species_types[O.merge_type] if(species) if(O.use(10)) to_chat(user, "You finish up the golem shell with ten sheets of [O].") diff --git a/code/modules/server_tools/server_tools.dm b/code/modules/server_tools/server_tools.dm index be8c80ac24..f16a56b2f9 100644 --- a/code/modules/server_tools/server_tools.dm +++ b/code/modules/server_tools/server_tools.dm @@ -4,6 +4,10 @@ GLOBAL_PROTECT(reboot_mode) /world/proc/RunningService() return params[SERVICE_WORLD_PARAM] +/proc/ServiceVersion() + if(world.RunningService()) + return world.params[SERVICE_VERSION_PARAM] + /world/proc/ExportService(command) return RunningService() && shell("python code/modules/server_tools/nudge.py \"[command]\"") == 0 diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 59a0a46ae8..77eec512d8 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -14,7 +14,8 @@ All ShuttleMove procs go here // Called from the new turf before anything has been moved // Only gets called if fromShuttleMove returns true first // returns the new move_mode (based on the old) -/turf/proc/toShuttleMove(turf/oldT, shuttle_dir, move_mode) +/turf/proc/toShuttleMove(turf/oldT, move_mode, obj/docking_port/mobile/shuttle) + var/shuttle_dir = shuttle.dir for(var/i in contents) var/atom/movable/thing = i if(ismob(thing)) @@ -383,4 +384,4 @@ All ShuttleMove procs go here /obj/effect/abstract/proximity_checker/onShuttleMove(turf/newT, turf/oldT, rotation, list/movement_force, move_dir, old_dock) //timer so it only happens once - addtimer(CALLBACK(monitor, /datum/proximity_monitor/proc/SetRange, monitor.current_range, TRUE), 0, TIMER_UNIQUE) \ No newline at end of file + addtimer(CALLBACK(monitor, /datum/proximity_monitor/proc/SetRange, monitor.current_range, TRUE), 0, TIMER_UNIQUE) diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index f035777df5..4326e6298e 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -570,7 +570,7 @@ move_mode = moving_atom.beforeShuttleMove(newT, rotation, move_mode) //atoms move_mode = oldT.fromShuttleMove(newT, underlying_turf_type, baseturf_cache, move_mode) //turfs - move_mode = newT.toShuttleMove(oldT, dir, move_mode) //turfs + move_mode = newT.toShuttleMove(oldT, move_mode , src) //turfs if(move_mode & MOVE_AREA) areas_to_move[old_area] = TRUE diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm index 6a972bff9b..0373ea5b53 100644 --- a/code/modules/shuttle/special.dm +++ b/code/modules/shuttle/special.dm @@ -215,23 +215,37 @@ /obj/effect/forcefield/luxury_shuttle var/threshold = 500 var/static/list/approved_passengers = list() + var/static/list/check_times = list() /obj/effect/forcefield/luxury_shuttle/CanPass(atom/movable/mover, turf/target) if(mover in approved_passengers) - return 1 + return TRUE if(!isliving(mover)) //No stowaways - return 0 + return FALSE + + return FALSE + + +#define LUXURY_MESSAGE_COOLDOWN 100 +/obj/effect/forcefield/luxury_shuttle/CollidedWith(atom/movable/AM) + if(!isliving(AM)) + return ..() + + if(check_times[AM] && check_times[AM] > world.time) //Let's not spam the message + return ..() + + check_times[AM] = world.time + LUXURY_MESSAGE_COOLDOWN var/total_cash = 0 var/list/counted_money = list() - for(var/obj/item/coin/C in mover.GetAllContents()) + for(var/obj/item/coin/C in AM.GetAllContents()) total_cash += C.value counted_money += C if(total_cash >= threshold) break - for(var/obj/item/stack/spacecash/S in mover.GetAllContents()) + for(var/obj/item/stack/spacecash/S in AM.GetAllContents()) total_cash += S.value * S.amount counted_money += S if(total_cash >= threshold) @@ -241,12 +255,13 @@ for(var/obj/I in counted_money) qdel(I) - to_chat(mover, "Thank you for your payment! Please enjoy your flight.") - approved_passengers += mover - return 1 + to_chat(AM, "Thank you for your payment! Please enjoy your flight.") + approved_passengers += AM + check_times -= AM + return else - to_chat(mover, "You don't have enough money to enter the main shuttle. You'll have to fly coach.") - return 0 + to_chat(AM, "You don't have enough money to enter the main shuttle. You'll have to fly coach.") + return ..() /mob/living/simple_animal/hostile/bear/fightpit name = "fight pit bear" diff --git a/code/modules/station_goals/station_goal.dm b/code/modules/station_goals/station_goal.dm index 4a9bc42438..98ec01f641 100644 --- a/code/modules/station_goals/station_goal.dm +++ b/code/modules/station_goals/station_goal.dm @@ -39,7 +39,7 @@ /datum/station_goal/Topic(href, href_list) ..() - if(!check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN) || !usr.client.holder.CheckAdminHref(href, href_list)) return if(href_list["announce"]) diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index f95e2cd996..834a99d1ec 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -122,7 +122,8 @@ H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) else H.failed_last_breath = FALSE - H.adjustOxyLoss(-5) + if(H.health >= HEALTH_THRESHOLD_CRIT) + H.adjustOxyLoss(-5) gas_breathed = breath_gases["o2"][MOLES] H.clear_alert("not_enough_oxy") @@ -149,7 +150,8 @@ H.throw_alert("nitro", /obj/screen/alert/not_enough_nitro) else H.failed_last_breath = FALSE - H.adjustOxyLoss(-5) + if(H.health >= HEALTH_THRESHOLD_CRIT) + H.adjustOxyLoss(-5) gas_breathed = breath_gases["n2"][MOLES] H.clear_alert("nitro") @@ -185,7 +187,8 @@ H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2) else H.failed_last_breath = FALSE - H.adjustOxyLoss(-5) + if(H.health >= HEALTH_THRESHOLD_CRIT) + H.adjustOxyLoss(-5) gas_breathed = breath_gases["co2"][MOLES] H.clear_alert("not_enough_co2") @@ -214,7 +217,8 @@ H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) else H.failed_last_breath = FALSE - H.adjustOxyLoss(-5) + if(H.health >= HEALTH_THRESHOLD_CRIT) + H.adjustOxyLoss(-5) gas_breathed = breath_gases["plasma"][MOLES] H.clear_alert("not_enough_tox") @@ -316,6 +320,29 @@ safe_toxins_min = 16 //We breath THIS! safe_toxins_max = 0 +/obj/item/organ/lungs/cybernetic + name = "cybernetic lungs" + desc = "A cybernetic version of the lungs found in traditional humanoid entities. It functions the same as an organic lung and is merely meant as a replacement." + icon_state = "lungs-c" + origin_tech = "biotech=4" + +/obj/item/organ/lungs/cybernetic/emp_act() + owner.losebreath = 20 + + +/obj/item/organ/lungs/cybernetic/upgraded + name = "upgraded cybernetic lungs" + desc = "A more advanced version of the stock cybernetic lungs. They are capable of filtering out lower levels of toxins and carbon-dioxide." + icon_state = "lungs-c-u" + origin_tech = "biotech=5" + + safe_toxins_max = 20 + safe_co2_max = 20 + + cold_level_1_threshold = 200 + cold_level_2_threshold = 140 + cold_level_3_threshold = 100 + #undef HUMAN_MAX_OXYLOSS #undef HUMAN_CRIT_MAX_OXYLOSS #undef HEAT_GAS_DAMAGE_LEVEL_1 diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index b69c1f4c5c..0113166d86 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -47,6 +47,7 @@ say_mod = "hisses" taste_sensitivity = 10 // combined nose + tongue, extra sensitive +/* /obj/item/organ/tongue/lizard/TongueSpeech(var/message) var/regex/lizard_hiss = new("s+", "g") var/regex/lizard_hiSS = new("S+", "g") @@ -54,6 +55,7 @@ message = lizard_hiss.Replace(message, "sss") message = lizard_hiSS.Replace(message, "SSS") return message + */ /obj/item/organ/tongue/fly name = "proboscis" diff --git a/config/config.txt b/config/config.txt index 70fe7812b2..652651e425 100644 --- a/config/config.txt +++ b/config/config.txt @@ -337,3 +337,6 @@ MINUTE_TOPIC_LIMIT 100 ## Send a message to IRC when starting a new game #IRC_ANNOUNCE_NEW_GAME + +## Allow admin hrefs that don't use the new token system, will eventually be removed +DEBUG_ADMIN_HREFS diff --git a/html/changelogs/AutoChangeLog-pr-2566.yml b/html/changelogs/AutoChangeLog-pr-2566.yml new file mode 100644 index 0000000000..6059f590d5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2566.yml @@ -0,0 +1,5 @@ +author: "Pubby" +delete-after: True +changes: + - rscadd: "Crew-tracking pinpointers to replace laggy crew monitoring console functionality" + - rscadd: "Crew-tracking pinpointers in medical vendors and the detective's office" diff --git a/html/changelogs/AutoChangeLog-pr-2600.yml b/html/changelogs/AutoChangeLog-pr-2600.yml new file mode 100644 index 0000000000..79380038d4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2600.yml @@ -0,0 +1,4 @@ +author: "Jay" +delete-after: True +changes: + - tweak: "Removes the lisp lizards have when talking" diff --git a/html/changelogs/AutoChangeLog-pr-2633.yml b/html/changelogs/AutoChangeLog-pr-2633.yml new file mode 100644 index 0000000000..4806cb12c5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2633.yml @@ -0,0 +1,4 @@ +author: "Xhuis" +delete-after: True +changes: + - imageadd: "The hand drill and jaws of life now have sprites on the toolbelt." diff --git a/html/changelogs/AutoChangeLog-pr-2641.yml b/html/changelogs/AutoChangeLog-pr-2641.yml new file mode 100644 index 0000000000..a888615c05 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2641.yml @@ -0,0 +1,4 @@ +author: "CitadelStationBot" +delete-after: True +changes: + - rscadd: "Admins may now show the variables interface to players to help contributors debug their new additions" diff --git a/html/changelogs/AutoChangeLog-pr-2650.yml b/html/changelogs/AutoChangeLog-pr-2650.yml new file mode 100644 index 0000000000..4e062b977c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2650.yml @@ -0,0 +1,4 @@ +author: "Naksu" +delete-after: True +changes: + - rscadd: "Added TGUI interfaces to various smartfridges of different kinds, drying racks and the disk compartmentalizer" diff --git a/html/changelogs/AutoChangeLog-pr-2674.yml b/html/changelogs/AutoChangeLog-pr-2674.yml new file mode 100644 index 0000000000..192d444569 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2674.yml @@ -0,0 +1,4 @@ +author: "VexingRaven" +delete-after: True +changes: + - bugfix: "Hearty Punch once again pulls people out of crit." diff --git a/html/changelogs/AutoChangeLog-pr-2678.yml b/html/changelogs/AutoChangeLog-pr-2678.yml new file mode 100644 index 0000000000..cf518a6179 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2678.yml @@ -0,0 +1,5 @@ +author: "Kor" +delete-after: True +changes: + - rscadd: "People in soft crit will take oxyloss more slowly than people in full crit if they remain still." + - rscadd: "People dragging themselves in critical condition will now leave blood trails. This will rapidly deal oxyloss to you." diff --git a/html/changelogs/AutoChangeLog-pr-2688.yml b/html/changelogs/AutoChangeLog-pr-2688.yml new file mode 100644 index 0000000000..5a58322884 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2688.yml @@ -0,0 +1,4 @@ +author: "Firecage" +delete-after: True +changes: + - rscadd: "The NanoTrasen Department for Cybernetics (NDC) would like to announce the creation of Cybernetic Lungs. Both a stock variety to replace traditional stock human lungs in emergencies, and a more enhanced variety allowing greater tolerance of breathing cold air, toxins, and CO2." diff --git a/html/changelogs/AutoChangeLog-pr-2695.yml b/html/changelogs/AutoChangeLog-pr-2695.yml new file mode 100644 index 0000000000..64cedc7d0d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2695.yml @@ -0,0 +1,7 @@ +author: "Poojawa" +delete-after: True +changes: + - rscadd: "Added Horse and Cow tails" + - rscadd: "Added Sergal ears and tails." + - tweak: "Fennec Ears a bit cleaner" + - tweak: "TRUE/FALSE in Citadel Sprites" diff --git a/html/changelogs/AutoChangeLog-pr-2706.yml b/html/changelogs/AutoChangeLog-pr-2706.yml new file mode 100644 index 0000000000..4db678dd9b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2706.yml @@ -0,0 +1,4 @@ +author: "Xhuis" +delete-after: True +changes: + - rscadd: "You can now use metal rods and departmental jumpsuits to craft departments for each banner." diff --git a/icons/mob/mam_bodyparts.dmi b/icons/mob/mam_bodyparts.dmi index 8adb82544e..b8491525a7 100644 Binary files a/icons/mob/mam_bodyparts.dmi and b/icons/mob/mam_bodyparts.dmi differ diff --git a/icons/mob/neck.dmi b/icons/mob/neck.dmi index 4618dee14b..e7c2d4025d 100644 Binary files a/icons/mob/neck.dmi and b/icons/mob/neck.dmi differ diff --git a/icons/obj/clothing/belt_overlays.dmi b/icons/obj/clothing/belt_overlays.dmi index bbd4df9bb0..5946966d24 100644 Binary files a/icons/obj/clothing/belt_overlays.dmi and b/icons/obj/clothing/belt_overlays.dmi differ diff --git a/icons/obj/clothing/neck.dmi b/icons/obj/clothing/neck.dmi index 0f3668ce10..40c4aa03bc 100644 Binary files a/icons/obj/clothing/neck.dmi and b/icons/obj/clothing/neck.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 808de69446..79f0a207d4 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/stationobjs.dmi b/icons/obj/stationobjs.dmi index 28dd158ff7..0f4cda5d8b 100644 Binary files a/icons/obj/stationobjs.dmi and b/icons/obj/stationobjs.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 961b2f163c..ab1e866b8d 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -282,6 +282,7 @@ #include "code\datums\antagonists\datum_traitor.dm" #include "code\datums\antagonists\devil.dm" #include "code\datums\antagonists\ninja.dm" +#include "code\datums\components\archaeology.dm" #include "code\datums\components\component.dm" #include "code\datums\components\slippery.dm" #include "code\datums\diseases\_disease.dm" @@ -797,6 +798,7 @@ #include "code\game\objects\items\mop.dm" #include "code\game\objects\items\paint.dm" #include "code\game\objects\items\paiwire.dm" +#include "code\game\objects\items\pinpointer.dm" #include "code\game\objects\items\pneumaticCannon.dm" #include "code\game\objects\items\powerfist.dm" #include "code\game\objects\items\RCD.dm" diff --git a/tgui/assets/tgui.css b/tgui/assets/tgui.css index f19852971d..ffe61666b9 100644 --- a/tgui/assets/tgui.css +++ b/tgui/assets/tgui.css @@ -1 +1 @@ -@charset "utf-8";body,html{box-sizing:border-box;height:100%;margin:0}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif;font-size:12px;color:#fff;background-color:#2a2a2a;background-image:linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2a2a2a',endColorstr='#ff202020',GradientType=0)}*,:after,:before{box-sizing:inherit}h1,h2,h3,h4{display:inline-block;margin:0;padding:6px 0}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4{font-size:12px}body.clockwork{background:linear-gradient(180deg,#b18b25 0,#5f380e);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb18b25',endColorstr='#ff5f380e',GradientType=0)}body.clockwork .normal{color:#b18b25}body.clockwork .good{color:#cfba47}body.clockwork .average{color:#896b19}body.clockwork .bad{color:#5f380e}body.clockwork .highlight{color:#b18b25}body.clockwork main{display:block;margin-top:32px;padding:2px 6px 0}body.clockwork hr{height:2px;background-color:#b18b25;border:none}body.clockwork .hidden{display:none}body.clockwork .bar .barText,body.clockwork span.button{color:#b18b25;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.clockwork .bold{font-weight:700}body.clockwork .italic{font-style:italic}body.clockwork [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.clockwork div[data-tooltip],body.clockwork span[data-tooltip]{position:relative}body.clockwork div[data-tooltip]:after,body.clockwork span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #170800;background-color:#2d1400}body.clockwork div[data-tooltip]:hover:after,body.clockwork span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.clockwork div[data-tooltip].tooltip-top:after,body.clockwork span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-top:hover:after,body.clockwork span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.clockwork div[data-tooltip].tooltip-bottom:after,body.clockwork span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.clockwork div[data-tooltip].tooltip-bottom:hover:after,body.clockwork span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-left:after,body.clockwork span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-left:hover:after,body.clockwork span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-right:after,body.clockwork span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-right:hover:after,body.clockwork span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #170800;background:#2d1400}body.clockwork .bar .barText{position:absolute;top:0;right:3px}body.clockwork .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#b18b25}body.clockwork .bar .barFill.good{background-color:#cfba47}body.clockwork .bar .barFill.average{background-color:#896b19}body.clockwork .bar .barFill.bad{background-color:#5f380e}body.clockwork span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #170800}body.clockwork span.button .fa{padding-right:2px}body.clockwork span.button.normal{transition:background-color .5s;background-color:#5f380e}body.clockwork span.button.normal.active:focus,body.clockwork span.button.normal.active:hover{transition:background-color .25s;background-color:#704211;outline:0}body.clockwork span.button.disabled{transition:background-color .5s;background-color:#2d1400}body.clockwork span.button.disabled.active:focus,body.clockwork span.button.disabled.active:hover{transition:background-color .25s;background-color:#441e00;outline:0}body.clockwork span.button.selected{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.selected.active:focus,body.clockwork span.button.selected.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.toggle{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.toggle.active:focus,body.clockwork span.button.toggle.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.caution{transition:background-color .5s;background-color:#be6209}body.clockwork span.button.caution.active:focus,body.clockwork span.button.caution.active:hover{transition:background-color .25s;background-color:#cd6a0a;outline:0}body.clockwork span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.clockwork span.button.danger.active:focus,body.clockwork span.button.danger.active:hover{transition:background-color .25s;background-color:#abaf00;outline:0}body.clockwork span.button.gridable{width:125px;margin:2px 0}body.clockwork span.button.gridable.center{text-align:center;width:75px}body.clockwork span.button+span:not(.button),body.clockwork span:not(.button)+span.button{margin-left:5px}body.clockwork div.display{width:100%;padding:4px;margin:6px 0;background-color:#2d1400;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400);background-color:rgba(45,20,0,.9);box-shadow:inset 0 0 5px rgba(0,0,0,.3)}body.clockwork div.display.tabular{padding:0;margin:0}body.clockwork div.display header,body.clockwork div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#cfba47;border-bottom:2px solid #b18b25}body.clockwork div.display header .buttonRight,body.clockwork div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.clockwork div.display article,body.clockwork div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.clockwork input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#b18b25;background-color:#cfba47;border:1px solid #272727}body.clockwork input.number{width:35px}body.clockwork input::-webkit-input-placeholder{color:#999}body.clockwork input::-moz-placeholder{color:#999}body.clockwork input:-ms-input-placeholder{color:#999}body.clockwork input::placeholder{color:#999}body.clockwork input::-ms-clear{display:none}body.clockwork svg.linegraph{overflow:hidden}body.clockwork div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#2d1400;font-weight:700;font-style:italic;background-color:#000;background-image:repeating-linear-gradient(-45deg,#000,#000 10px,#170800 0,#170800 20px)}body.clockwork div.notice .label{color:#2d1400}body.clockwork div.notice .content:only-of-type{padding:0}body.clockwork div.notice hr{background-color:#896b19}body.clockwork div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #5f380e;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.clockwork section .cell,body.clockwork section .content,body.clockwork section .label,body.clockwork section .line,body.nanotrasen section .cell,body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .cell,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.clockwork section{display:table-row;width:100%}body.clockwork section:not(:first-child){padding-top:4px}body.clockwork section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.clockwork section .label{width:1%;padding-right:32px;white-space:nowrap;color:#b18b25}body.clockwork section .content:not(:last-child){padding-right:16px}body.clockwork section .line{width:100%}body.clockwork section .cell:not(:first-child){text-align:center;padding-top:0}body.clockwork section .cell span.button{width:75px}body.clockwork section:not(:last-child){padding-right:4px}body.clockwork div.subdisplay{width:100%;margin:0}body.clockwork header.titlebar .close,body.clockwork header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#cfba47}body.clockwork header.titlebar .close:hover,body.clockwork header.titlebar .minimize:hover{color:#d1bd50}body.clockwork header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#5f380e;border-bottom:1px solid #170800;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.clockwork header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.clockwork header.titlebar .title{position:absolute;top:6px;left:46px;color:#cfba47;font-size:16px;white-space:nowrap}body.clockwork header.titlebar .minimize{position:absolute;top:6px;right:46px}body.clockwork header.titlebar .close{position:absolute;top:4px;right:12px}body.nanotrasen{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4wIiB2aWV3Qm94PSIwIDAgNDI1IDIwMCIgb3BhY2l0eT0iLjMzIj4NCiAgPHBhdGggZD0ibSAxNzguMDAzOTksMC4wMzg2OSAtNzEuMjAzOTMsMCBhIDYuNzYxMzQyMiw2LjAyNTU0OTUgMCAwIDAgLTYuNzYxMzQsNi4wMjU1NSBsIDAsMTg3Ljg3MTQ3IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA2Ljc2MTM0LDYuMDI1NTQgbCA1My4xMDcyLDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDYuNzYxMzUsLTYuMDI1NTQgbCAwLC0xMDEuNTQ0MDE4IDcyLjIxNjI4LDEwNC42OTkzOTggYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDUuNzYwMTUsMi44NzAxNiBsIDczLjU1NDg3LDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDYuNzYxMzUsLTYuMDI1NTQgbCAwLC0xODcuODcxNDcgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTM1LC02LjAyNTU1IGwgLTU0LjcxNjQ0LDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTMzLDYuMDI1NTUgbCAwLDEwMi42MTkzNSBMIDE4My43NjQxMywyLjkwODg2IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCAtNS43NjAxNCwtMi44NzAxNyB6IiAvPg0KICA8cGF0aCBkPSJNIDQuODQ0NjMzMywyMi4xMDg3NSBBIDEzLjQxMjAzOSwxMi41MDE4NDIgMCAwIDEgMTMuNDc3NTg4LDAuMDM5MjQgbCA2Ni4xMTgzMTUsMCBhIDUuMzY0ODE1OCw1LjAwMDczNyAwIDAgMSA1LjM2NDgyMyw1LjAwMDczIGwgMCw3OS44NzkzMSB6IiAvPg0KICA8cGF0aCBkPSJtIDQyMC4xNTUzNSwxNzcuODkxMTkgYSAxMy40MTIwMzgsMTIuNTAxODQyIDAgMCAxIC04LjYzMjk1LDIyLjA2OTUxIGwgLTY2LjExODMyLDAgYSA1LjM2NDgxNTIsNS4wMDA3MzcgMCAwIDEgLTUuMzY0ODIsLTUuMDAwNzQgbCAwLC03OS44NzkzMSB6IiAvPg0KPC9zdmc+DQo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4NCjwhLS0gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLyAtLT4NCg==") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2a2a2a',endColorstr='#ff202020',GradientType=0)}body.nanotrasen .normal{color:#40628a}body.nanotrasen .good{color:#537d29}body.nanotrasen .average{color:#be6209}body.nanotrasen .bad{color:#b00e0e}body.nanotrasen .highlight{color:#8ba5c4}body.nanotrasen main{display:block;margin-top:32px;padding:2px 6px 0}body.nanotrasen hr{height:2px;background-color:#40628a;border:none}body.nanotrasen .hidden{display:none}body.nanotrasen .bar .barText,body.nanotrasen span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.nanotrasen .bold{font-weight:700}body.nanotrasen .italic{font-style:italic}body.nanotrasen [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.nanotrasen div[data-tooltip],body.nanotrasen span[data-tooltip]{position:relative}body.nanotrasen div[data-tooltip]:after,body.nanotrasen span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.nanotrasen div[data-tooltip]:hover:after,body.nanotrasen span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.nanotrasen div[data-tooltip].tooltip-top:after,body.nanotrasen span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-top:hover:after,body.nanotrasen span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:after,body.nanotrasen span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:hover:after,body.nanotrasen span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-left:after,body.nanotrasen span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-left:hover:after,body.nanotrasen span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-right:after,body.nanotrasen span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-right:hover:after,body.nanotrasen span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #40628a;background:#272727}body.nanotrasen .bar .barText{position:absolute;top:0;right:3px}body.nanotrasen .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#40628a}body.nanotrasen .bar .barFill.good{background-color:#537d29}body.nanotrasen .bar .barFill.average{background-color:#be6209}body.nanotrasen .bar .barFill.bad{background-color:#b00e0e}body.nanotrasen span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.nanotrasen span.button .fa{padding-right:2px}body.nanotrasen span.button.normal{transition:background-color .5s;background-color:#40628a}body.nanotrasen span.button.normal.active:focus,body.nanotrasen span.button.normal.active:hover{transition:background-color .25s;background-color:#4f78aa;outline:0}body.nanotrasen span.button.disabled{transition:background-color .5s;background-color:#999}body.nanotrasen span.button.disabled.active:focus,body.nanotrasen span.button.disabled.active:hover{transition:background-color .25s;background-color:#a8a8a8;outline:0}body.nanotrasen span.button.selected{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.selected.active:focus,body.nanotrasen span.button.selected.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.toggle{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.toggle.active:focus,body.nanotrasen span.button.toggle.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.caution{transition:background-color .5s;background-color:#9a9d00}body.nanotrasen span.button.caution.active:focus,body.nanotrasen span.button.caution.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.nanotrasen span.button.danger{transition:background-color .5s;background-color:#9d0808}body.nanotrasen span.button.danger.active:focus,body.nanotrasen span.button.danger.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.nanotrasen span.button.gridable{width:125px;margin:2px 0}body.nanotrasen span.button.gridable.center{text-align:center;width:75px}body.nanotrasen span.button+span:not(.button),body.nanotrasen span:not(.button)+span.button{margin-left:5px}body.nanotrasen div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000);background-color:rgba(0,0,0,.33);box-shadow:inset 0 0 5px rgba(0,0,0,.5)}body.nanotrasen div.display.tabular{padding:0;margin:0}body.nanotrasen div.display header,body.nanotrasen div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #40628a}body.nanotrasen div.display header .buttonRight,body.nanotrasen div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.nanotrasen div.display article,body.nanotrasen div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.nanotrasen input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#000;background-color:#fff;border:1px solid #272727}body.nanotrasen input.number{width:35px}body.nanotrasen input::-webkit-input-placeholder{color:#999}body.nanotrasen input::-moz-placeholder{color:#999}body.nanotrasen input:-ms-input-placeholder{color:#999}body.nanotrasen input::placeholder{color:#999}body.nanotrasen input::-ms-clear{display:none}body.nanotrasen svg.linegraph{overflow:hidden}body.nanotrasen div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,#bb9b68,#bb9b68 10px,#b1905d 0,#b1905d 20px)}body.nanotrasen div.notice .label{color:#000}body.nanotrasen div.notice .content:only-of-type{padding:0}body.nanotrasen div.notice hr{background-color:#272727}body.nanotrasen div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.nanotrasen section .cell,body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .cell,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.nanotrasen section{display:table-row;width:100%}body.nanotrasen section:not(:first-child){padding-top:4px}body.nanotrasen section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.nanotrasen section .label{width:1%;padding-right:32px;white-space:nowrap;color:#8ba5c4}body.nanotrasen section .content:not(:last-child){padding-right:16px}body.nanotrasen section .line{width:100%}body.nanotrasen section .cell:not(:first-child){text-align:center;padding-top:0}body.nanotrasen section .cell span.button{width:75px}body.nanotrasen section:not(:last-child){padding-right:4px}body.nanotrasen div.subdisplay{width:100%;margin:0}body.nanotrasen header.titlebar .close,body.nanotrasen header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#8ba5c4}body.nanotrasen header.titlebar .close:hover,body.nanotrasen header.titlebar .minimize:hover{color:#9cb2cd}body.nanotrasen header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.nanotrasen header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.nanotrasen header.titlebar .title{position:absolute;top:6px;left:46px;color:#8ba5c4;font-size:16px;white-space:nowrap}body.nanotrasen header.titlebar .minimize{position:absolute;top:6px;right:46px}body.nanotrasen header.titlebar .close{position:absolute;top:4px;right:12px}body.syndicate{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4wIiB2aWV3Qm94PSIwIDAgMjAwIDI4OS43NDIiIG9wYWNpdHk9Ii4zMyI+DQogIDxwYXRoIGQ9Im0gOTMuNTM3Njc3LDAgYyAtMTguMTEzMTI1LDAgLTM0LjIyMDEzMywzLjExMTY0IC00OC4zMjM0ODQsOS4zMzQzNyAtMTMuOTY1MDkyLDYuMjIxNjcgLTI0LjYxMjQ0MiwxNS4wNzExNCAtMzEuOTQwNjUxLDI2LjU0NzEgLTcuMTg5OTM5OCwxMS4zMzc4OSAtMTAuMzAxMjI2NiwyNC43NDkxMSAtMTAuMzAxMjI2Niw0MC4yMzQ3OCAwLDEwLjY0NjYyIDIuNzI1MDAyNiwyMC40NjQ2NSA4LjE3NTExMTYsMjkuNDUyNTggNS42MTUyNzcsOC45ODY4NiAxNC4wMzgyNzcsMTcuMzUyMDQgMjUuMjY4ODIxLDI1LjA5NDM2IDExLjIzMDU0NCw3LjYwNTMxIDI2LjUwNzQyMSwxNS40MTgzNSA0NS44MzA1MTQsMjMuNDM3ODIgMTkuOTgzNzQ4LDguMjk1NTcgMzQuODQ4ODQ4LDE1LjU1NDcxIDQ0LjU5Mjk5OCwyMS43NzYzOCA5Ljc0NDE0LDYuMjIyNzMgMTYuNzYxNywxMi44NTg1IDIxLjA1NTcyLDE5LjkwOTUxIDQuMjk0MDQsNy4wNTIwOCA2LjQ0MTkzLDE1Ljc2NDA4IDYuNDQxOTMsMjYuMTM0NTkgMCwxNi4xNzcwMiAtNS4yMDE5NiwyOC40ODIyMiAtMTUuNjA2NzMsMzYuOTE2ODIgLTEwLjIzOTYsOC40MzQ3IC0yNS4wMjIwMywxMi42NTIzIC00NC4zNDUxNjksMTIuNjUyMyAtMTQuMDM4MTcxLDAgLTI1LjUxNTI0NywtMS42NTk0IC0zNC40MzM2MTgsLTQuOTc3NyAtOC45MTgzNywtMy40NTY2IC0xNi4xODU1NzIsLTguNzExMyAtMjEuODAwODM5LC0xNS43NjMzIC01LjYxNTI3NywtNy4wNTIxIC0xMC4wNzQ3OTUsLTE2LjY2MDg4IC0xMy4zNzc4OTksLTI4LjgyODEyIGwgLTI0Ljc3MzE2MjYyOTM5NDUsMCAwLDU2LjgyNjMyIEMgMzMuODU2NzY5LDI4Ni4wNzYwMSA2My43NDkwNCwyODkuNzQyMDEgODkuNjc4MzgzLDI4OS43NDIwMSBjIDE2LjAyMDAyNywwIDMwLjcxOTc4NywtMS4zODI3IDQ0LjA5NzMzNywtNC4xNDc5IDEzLjU0MjcyLC0yLjkwNDMgMjUuMTA0MSwtNy40Njc2IDM0LjY4MzA5LC0xMy42ODkzIDkuNzQ0MTMsLTYuMzU5NyAxNy4zNDA0MiwtMTQuNTE5NSAyMi43OTA1MiwtMjQuNDc0OCA1LjQ1MDEsLTEwLjA5MzMyIDguMTc1MTEsLTIyLjM5OTU5IDguMTc1MTEsLTM2LjkxNjgyIDAsLTEyLjk5NzY0IC0zLjMwMjEsLTI0LjMzNTM5IC05LjkwODI5LC0zNC4wMTQ2IC02LjQ0MTA1LC05LjgxNzI1IC0xNS41MjU0NSwtMTguNTI3MDcgLTI3LjI1MTQ2LC0yNi4xMzEzMyAtMTEuNTYwODUsLTcuNjA0MjcgLTI3LjkxMDgzLC0xNS44MzE0MiAtNDkuMDUwNjYsLTI0LjY4MDIyIC0xNy41MDY0NCwtNy4xOTAxMiAtMzAuNzE5NjY4LC0xMy42ODk0OCAtMzkuNjM4MDM4LC0xOS40OTcwMSAtOC45MTgzNzEsLTUuODA3NTIgLTE4LjYwNzQ3NCwtMTIuNDM0MDkgLTI0LjA5NjUyNCwtMTguODc0MTcgLTUuNDI2MDQzLC02LjM2NjE2IC05LjY1ODgyNiwtMTUuMDcwMDMgLTkuNjU4ODI2LC0yNC44ODcyOSAwLC05LjI2NDAxIDIuMDc1NDE0LC0xNy4yMTM0NSA2LjIyMzQ1NCwtMjMuODUwMzMgMTEuMDk4Mjk4LC0xNC4zOTc0OCA0MS4yODY2MzgsLTEuNzk1MDcgNDUuMDc1NjA5LDI0LjM0NzYyIDQuODM5MzkyLDYuNzc0OTEgOC44NDkzNSwxNi4yNDcyOSAxMi4wMjk1MTUsMjguNDE1NiBsIDIwLjUzMjM0LDAgMCwtNTUuOTk5NjcgYyAtNC40NzgyNSwtNS45MjQ0OCAtOS45NTQ4OCwtMTAuNjMyMjIgLTE1LjkwODM3LC0xNC4zNzQxMSAxLjY0MDU1LDAuNDc5MDUgMy4xOTAzOSwxLjAyMzc2IDQuNjM4NjUsMS42NDAyNCA2LjQ5ODYxLDIuNjI2MDcgMTIuMTY3OTMsNy4zMjc0NyAxNy4wMDczLDE0LjEwMzQ1IDQuODM5MzksNi43NzQ5MSA4Ljg0OTM1LDE2LjI0NTY3IDEyLjAyOTUyLDI4LjQxMzk3IDAsMCA4LjQ4MTI4LC0wLjEyODk0IDguNDg5NzgsLTAuMDAyIDAuNDE3NzYsNi40MTQ5NCAtMS43NTMzOSw5LjQ1Mjg2IC00LjEyMzQyLDEyLjU2MTA0IC0yLjQxNzQsMy4xNjk3OCAtNS4xNDQ4Niw2Ljc4OTczIC00LjAwMjc4LDEzLjAwMjkgMS41MDc4Niw4LjIwMzE4IDEwLjE4MzU0LDEwLjU5NjQyIDE0LjYyMTk0LDkuMzExNTQgLTMuMzE4NDIsLTAuNDk5MTEgLTUuMzE4NTUsLTEuNzQ5NDggLTUuMzE4NTUsLTEuNzQ5NDggMCwwIDEuODc2NDYsMC45OTg2OCA1LjY1MTE3LC0xLjM1OTgxIC0zLjI3Njk1LDAuOTU1NzEgLTEwLjcwNTI5LC0wLjc5NzM4IC0xMS44MDEyNSwtNi43NjMxMyAtMC45NTc1MiwtNS4yMDg2MSAwLjk0NjU0LC03LjI5NTE0IDMuNDAxMTMsLTEwLjUxNDgyIDIuNDU0NjIsLTMuMjE5NjggNS4yODQyNiwtNi45NTgzMSA0LjY4NDMsLTE0LjQ4ODI0IGwgMC4wMDMsMC4wMDIgOC45MjY3NiwwIDAsLTU1Ljk5OTY3IGMgLTE1LjA3MTI1LC0zLjg3MTY4IC0yNy42NTMxNCwtNi4zNjA0MiAtMzcuNzQ2NzEsLTcuNDY1ODYgLTkuOTU1MzEsLTEuMTA3NTUgLTIwLjE4ODIzLC0xLjY1OTgxIC0zMC42OTY2MTMsLTEuNjU5ODEgeiBtIDcwLjMyMTYwMywxNy4zMDg5MyAwLjIzODA1LDQwLjMwNDkgYyAxLjMxODA4LDEuMjI2NjYgMi40Mzk2NSwyLjI3ODE1IDMuMzQwODEsMy4xMDYwMiA0LjgzOTM5LDYuNzc0OTEgOC44NDkzNCwxNi4yNDU2NiAxMi4wMjk1MSwyOC40MTM5NyBsIDIwLjUzMjM0LDAgMCwtNTUuOTk5NjcgYyAtNi42NzczMSwtNC41OTM4MSAtMTkuODM2NDMsLTEwLjQ3MzA5IC0zNi4xNDA3MSwtMTUuODI1MjIgeiBtIC0yOC4xMjA0OSw1LjYwNTUxIDguNTY0NzksMTcuNzE2NTUgYyAtMTEuOTcwMzcsLTYuNDY2OTcgLTEzLjg0Njc4LC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk3MDUsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IG0gMTUuMjIxOTUsMjQuMDA4NDggOC41NjQ3OSwxNy43MTY1NSBjIC0xMS45NzAzOCwtNi40NjY5NyAtMTMuODQ2NzksLTkuNzE3MjYgLTguNTY0NzksLTE3LjcxNjU1IHogbSAyMi43OTcwNCwwIGMgMi43NzE1LDcuOTk5MjkgMS43ODc0MSwxMS4yNDk1OCAtNC40OTM1NCwxNy43MTY1NSBsIDQuNDkzNTQsLTE3LjcxNjU1IHogbSAtOTkuMTEzODQsMi4yMDc2NCA4LjU2NDc5LDE3LjcxNjU1IGMgLTExLjk3MDM4MiwtNi40NjY5NyAtMTMuODQ2NzgyLC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk1NDIsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IiAvPg0KPC9zdmc+DQo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4NCjwhLS0gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLyAtLT4NCg==") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#750000 0,#340404);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff750000',endColorstr='#ff340404',GradientType=0)}body.syndicate .normal{color:#40628a}body.syndicate .good{color:#73e573}body.syndicate .average{color:#be6209}body.syndicate .bad{color:#b00e0e}body.syndicate .highlight{color:#000}body.syndicate main{display:block;margin-top:32px;padding:2px 6px 0}body.syndicate hr{height:2px;background-color:#272727;border:none}body.syndicate .hidden{display:none}body.syndicate .bar .barText,body.syndicate span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.syndicate .bold{font-weight:700}body.syndicate .italic{font-style:italic}body.syndicate [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.syndicate div[data-tooltip],body.syndicate span[data-tooltip]{position:relative}body.syndicate div[data-tooltip]:after,body.syndicate span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.syndicate div[data-tooltip]:hover:after,body.syndicate span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.syndicate div[data-tooltip].tooltip-top:after,body.syndicate span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-top:hover:after,body.syndicate span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.syndicate div[data-tooltip].tooltip-bottom:after,body.syndicate span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.syndicate div[data-tooltip].tooltip-bottom:hover:after,body.syndicate span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-left:after,body.syndicate span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-left:hover:after,body.syndicate span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-right:after,body.syndicate span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-right:hover:after,body.syndicate span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #000;background:#272727}body.syndicate .bar .barText{position:absolute;top:0;right:3px}body.syndicate .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#000}body.syndicate .bar .barFill.good{background-color:#73e573}body.syndicate .bar .barFill.average{background-color:#be6209}body.syndicate .bar .barFill.bad{background-color:#b00e0e}body.syndicate span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.syndicate span.button .fa{padding-right:2px}body.syndicate span.button.normal{transition:background-color .5s;background-color:#397439}body.syndicate span.button.normal.active:focus,body.syndicate span.button.normal.active:hover{transition:background-color .25s;background-color:#4a964a;outline:0}body.syndicate span.button.disabled{transition:background-color .5s;background-color:#363636}body.syndicate span.button.disabled.active:focus,body.syndicate span.button.disabled.active:hover{transition:background-color .25s;background-color:#545454;outline:0}body.syndicate span.button.selected{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.selected.active:focus,body.syndicate span.button.selected.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.toggle{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.toggle.active:focus,body.syndicate span.button.toggle.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.caution{transition:background-color .5s;background-color:#be6209}body.syndicate span.button.caution.active:focus,body.syndicate span.button.caution.active:hover{transition:background-color .25s;background-color:#eb790b;outline:0}body.syndicate span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.syndicate span.button.danger.active:focus,body.syndicate span.button.danger.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.syndicate span.button.gridable{width:125px;margin:2px 0}body.syndicate span.button.gridable.center{text-align:center;width:75px}body.syndicate span.button+span:not(.button),body.syndicate span:not(.button)+span.button{margin-left:5px}body.syndicate div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000);background-color:rgba(0,0,0,.5);box-shadow:inset 0 0 5px rgba(0,0,0,.75)}body.syndicate div.display.tabular{padding:0;margin:0}body.syndicate div.display header,body.syndicate div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #272727}body.syndicate div.display header .buttonRight,body.syndicate div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.syndicate div.display article,body.syndicate div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.syndicate input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#fff;background-color:#9d0808;border:1px solid #272727}body.syndicate input.number{width:35px}body.syndicate input::-webkit-input-placeholder{color:#999}body.syndicate input::-moz-placeholder{color:#999}body.syndicate input:-ms-input-placeholder{color:#999}body.syndicate input::placeholder{color:#999}body.syndicate input::-ms-clear{display:none}body.syndicate svg.linegraph{overflow:hidden}body.syndicate div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#750000;background-image:repeating-linear-gradient(-45deg,#750000,#750000 10px,#910101 0,#910101 20px)}body.syndicate div.notice .label{color:#000}body.syndicate div.notice .content:only-of-type{padding:0}body.syndicate div.notice hr{background-color:#272727}body.syndicate div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.syndicate section .cell,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.syndicate section{display:table-row;width:100%}body.syndicate section:not(:first-child){padding-top:4px}body.syndicate section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.syndicate section .label{width:1%;padding-right:32px;white-space:nowrap;color:#fff}body.syndicate section .content:not(:last-child){padding-right:16px}body.syndicate section .line{width:100%}body.syndicate section .cell:not(:first-child){text-align:center;padding-top:0}body.syndicate section .cell span.button{width:75px}body.syndicate section:not(:last-child){padding-right:4px}body.syndicate div.subdisplay{width:100%;margin:0}body.syndicate header.titlebar .close,body.syndicate header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#e74242}body.syndicate header.titlebar .close:hover,body.syndicate header.titlebar .minimize:hover{color:#eb5e5e}body.syndicate header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.syndicate header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.syndicate header.titlebar .title{position:absolute;top:6px;left:46px;color:#e74242;font-size:16px;white-space:nowrap}body.syndicate header.titlebar .minimize{position:absolute;top:6px;right:46px}body.syndicate header.titlebar .close{position:absolute;top:4px;right:12px}.no-icons header.titlebar .statusicon{font-size:20px}.no-icons header.titlebar .statusicon:after{content:"O"}.no-icons header.titlebar .minimize{top:-2px;font-size:20px}.no-icons header.titlebar .minimize:after{content:"—"}.no-icons header.titlebar .close{font-size:20px}.no-icons header.titlebar .close:after{content:"X"} \ No newline at end of file +@charset "utf-8";body,html{box-sizing:border-box;height:100%;margin:0}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif;font-size:12px;color:#fff;background-color:#2a2a2a;background-image:linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#ff2a2a2a",endColorstr="#ff202020",GradientType=0)}*,:after,:before{box-sizing:inherit}h1,h2,h3,h4{display:inline-block;margin:0;padding:6px 0}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4{font-size:12px}body.clockwork{background:linear-gradient(180deg,#b18b25 0,#5f380e);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#ffb18b25",endColorstr="#ff5f380e",GradientType=0)}body.clockwork .normal{color:#b18b25}body.clockwork .good{color:#cfba47}body.clockwork .average{color:#896b19}body.clockwork .bad{color:#5f380e}body.clockwork .highlight{color:#b18b25}body.clockwork main{display:block;margin-top:32px;padding:2px 6px 0}body.clockwork hr{height:2px;background-color:#b18b25;border:none}body.clockwork .hidden{display:none}body.clockwork .bar .barText,body.clockwork span.button{color:#b18b25;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.clockwork .bold{font-weight:700}body.clockwork .italic{font-style:italic}body.clockwork [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.clockwork div[data-tooltip],body.clockwork span[data-tooltip]{position:relative}body.clockwork div[data-tooltip]:after,body.clockwork span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #170800;background-color:#2d1400}body.clockwork div[data-tooltip]:hover:after,body.clockwork span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.clockwork div[data-tooltip].tooltip-top:after,body.clockwork span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-top:hover:after,body.clockwork span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.clockwork div[data-tooltip].tooltip-bottom:after,body.clockwork span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.clockwork div[data-tooltip].tooltip-bottom:hover:after,body.clockwork span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-left:after,body.clockwork span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-left:hover:after,body.clockwork span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-right:after,body.clockwork span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-right:hover:after,body.clockwork span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #170800;background:#2d1400}body.clockwork .bar .barText{position:absolute;top:0;right:3px}body.clockwork .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#b18b25}body.clockwork .bar .barFill.good{background-color:#cfba47}body.clockwork .bar .barFill.average{background-color:#896b19}body.clockwork .bar .barFill.bad{background-color:#5f380e}body.clockwork span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #170800}body.clockwork span.button .fa{padding-right:2px}body.clockwork span.button.normal{transition:background-color .5s;background-color:#5f380e}body.clockwork span.button.normal.active:focus,body.clockwork span.button.normal.active:hover{transition:background-color .25s;background-color:#704211;outline:0}body.clockwork span.button.disabled{transition:background-color .5s;background-color:#2d1400}body.clockwork span.button.disabled.active:focus,body.clockwork span.button.disabled.active:hover{transition:background-color .25s;background-color:#441e00;outline:0}body.clockwork span.button.selected{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.selected.active:focus,body.clockwork span.button.selected.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.toggle{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.toggle.active:focus,body.clockwork span.button.toggle.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.caution{transition:background-color .5s;background-color:#be6209}body.clockwork span.button.caution.active:focus,body.clockwork span.button.caution.active:hover{transition:background-color .25s;background-color:#cd6a0a;outline:0}body.clockwork span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.clockwork span.button.danger.active:focus,body.clockwork span.button.danger.active:hover{transition:background-color .25s;background-color:#abaf00;outline:0}body.clockwork span.button.gridable{width:125px;margin:2px 0}body.clockwork span.button.gridable.center{text-align:center;width:75px}body.clockwork span.button+span:not(.button),body.clockwork span:not(.button)+span.button{margin-left:5px}body.clockwork div.display{width:100%;padding:4px;margin:6px 0;background-color:#2d1400;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400);background-color:rgba(45,20,0,.9);box-shadow:inset 0 0 5px rgba(0,0,0,.3)}body.clockwork div.display.tabular{padding:0;margin:0}body.clockwork div.display header,body.clockwork div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#cfba47;border-bottom:2px solid #b18b25}body.clockwork div.display header .buttonRight,body.clockwork div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.clockwork div.display article,body.clockwork div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.clockwork input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#b18b25;background-color:#cfba47;border:1px solid #272727}body.clockwork input.number{width:35px}body.clockwork input::-webkit-input-placeholder{color:#999}body.clockwork input:-ms-input-placeholder{color:#999}body.clockwork input::placeholder{color:#999}body.clockwork input::-ms-clear{display:none}body.clockwork svg.linegraph{overflow:hidden}body.clockwork div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#2d1400;font-weight:700;font-style:italic;background-color:#000;background-image:repeating-linear-gradient(-45deg,#000,#000 10px,#170800 0,#170800 20px)}body.clockwork div.notice .label{color:#2d1400}body.clockwork div.notice .content:only-of-type{padding:0}body.clockwork div.notice hr{background-color:#896b19}body.clockwork div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #5f380e;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.clockwork section .cell,body.clockwork section .content,body.clockwork section .label,body.clockwork section .line,body.nanotrasen section .cell,body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .cell,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.clockwork section{display:table-row;width:100%}body.clockwork section:not(:first-child){padding-top:4px}body.clockwork section.candystripe:nth-child(2n){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.clockwork section .label{width:1%;padding-right:32px;white-space:nowrap;color:#b18b25}body.clockwork section .content:not(:last-child){padding-right:16px}body.clockwork section .line{width:100%}body.clockwork section .cell:not(:first-child){text-align:center;padding-top:0}body.clockwork section .cell span.button{width:75px}body.clockwork section:not(:last-child){padding-right:4px}body.clockwork div.subdisplay{width:100%;margin:0}body.clockwork header.titlebar .close,body.clockwork header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#cfba47}body.clockwork header.titlebar .close:hover,body.clockwork header.titlebar .minimize:hover{color:#d1bd50}body.clockwork header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#5f380e;border-bottom:1px solid #170800;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.clockwork header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.clockwork header.titlebar .title{position:absolute;top:6px;left:46px;color:#cfba47;font-size:16px;white-space:nowrap}body.clockwork header.titlebar .minimize{position:absolute;top:6px;right:46px}body.clockwork header.titlebar .close{position:absolute;top:4px;right:12px}body.nanotrasen{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4wIiB2aWV3Qm94PSIwIDAgNDI1IDIwMCIgb3BhY2l0eT0iLjMzIj4NCiAgPHBhdGggZD0ibSAxNzguMDAzOTksMC4wMzg2OSAtNzEuMjAzOTMsMCBhIDYuNzYxMzQyMiw2LjAyNTU0OTUgMCAwIDAgLTYuNzYxMzQsNi4wMjU1NSBsIDAsMTg3Ljg3MTQ3IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA2Ljc2MTM0LDYuMDI1NTQgbCA1My4xMDcyLDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDYuNzYxMzUsLTYuMDI1NTQgbCAwLC0xMDEuNTQ0MDE4IDcyLjIxNjI4LDEwNC42OTkzOTggYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDUuNzYwMTUsMi44NzAxNiBsIDczLjU1NDg3LDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDYuNzYxMzUsLTYuMDI1NTQgbCAwLC0xODcuODcxNDcgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTM1LC02LjAyNTU1IGwgLTU0LjcxNjQ0LDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTMzLDYuMDI1NTUgbCAwLDEwMi42MTkzNSBMIDE4My43NjQxMywyLjkwODg2IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCAtNS43NjAxNCwtMi44NzAxNyB6IiAvPg0KICA8cGF0aCBkPSJNIDQuODQ0NjMzMywyMi4xMDg3NSBBIDEzLjQxMjAzOSwxMi41MDE4NDIgMCAwIDEgMTMuNDc3NTg4LDAuMDM5MjQgbCA2Ni4xMTgzMTUsMCBhIDUuMzY0ODE1OCw1LjAwMDczNyAwIDAgMSA1LjM2NDgyMyw1LjAwMDczIGwgMCw3OS44NzkzMSB6IiAvPg0KICA8cGF0aCBkPSJtIDQyMC4xNTUzNSwxNzcuODkxMTkgYSAxMy40MTIwMzgsMTIuNTAxODQyIDAgMCAxIC04LjYzMjk1LDIyLjA2OTUxIGwgLTY2LjExODMyLDAgYSA1LjM2NDgxNTIsNS4wMDA3MzcgMCAwIDEgLTUuMzY0ODIsLTUuMDAwNzQgbCAwLC03OS44NzkzMSB6IiAvPg0KPC9zdmc+DQo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4NCjwhLS0gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLyAtLT4NCg==") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#ff2a2a2a",endColorstr="#ff202020",GradientType=0)}body.nanotrasen .normal{color:#40628a}body.nanotrasen .good{color:#537d29}body.nanotrasen .average{color:#be6209}body.nanotrasen .bad{color:#b00e0e}body.nanotrasen .highlight{color:#8ba5c4}body.nanotrasen main{display:block;margin-top:32px;padding:2px 6px 0}body.nanotrasen hr{height:2px;background-color:#40628a;border:none}body.nanotrasen .hidden{display:none}body.nanotrasen .bar .barText,body.nanotrasen span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.nanotrasen .bold{font-weight:700}body.nanotrasen .italic{font-style:italic}body.nanotrasen [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.nanotrasen div[data-tooltip],body.nanotrasen span[data-tooltip]{position:relative}body.nanotrasen div[data-tooltip]:after,body.nanotrasen span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.nanotrasen div[data-tooltip]:hover:after,body.nanotrasen span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.nanotrasen div[data-tooltip].tooltip-top:after,body.nanotrasen span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-top:hover:after,body.nanotrasen span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:after,body.nanotrasen span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:hover:after,body.nanotrasen span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-left:after,body.nanotrasen span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-left:hover:after,body.nanotrasen span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-right:after,body.nanotrasen span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-right:hover:after,body.nanotrasen span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #40628a;background:#272727}body.nanotrasen .bar .barText{position:absolute;top:0;right:3px}body.nanotrasen .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#40628a}body.nanotrasen .bar .barFill.good{background-color:#537d29}body.nanotrasen .bar .barFill.average{background-color:#be6209}body.nanotrasen .bar .barFill.bad{background-color:#b00e0e}body.nanotrasen span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.nanotrasen span.button .fa{padding-right:2px}body.nanotrasen span.button.normal{transition:background-color .5s;background-color:#40628a}body.nanotrasen span.button.normal.active:focus,body.nanotrasen span.button.normal.active:hover{transition:background-color .25s;background-color:#4f78aa;outline:0}body.nanotrasen span.button.disabled{transition:background-color .5s;background-color:#999}body.nanotrasen span.button.disabled.active:focus,body.nanotrasen span.button.disabled.active:hover{transition:background-color .25s;background-color:#a8a8a8;outline:0}body.nanotrasen span.button.selected{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.selected.active:focus,body.nanotrasen span.button.selected.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.toggle{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.toggle.active:focus,body.nanotrasen span.button.toggle.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.caution{transition:background-color .5s;background-color:#9a9d00}body.nanotrasen span.button.caution.active:focus,body.nanotrasen span.button.caution.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.nanotrasen span.button.danger{transition:background-color .5s;background-color:#9d0808}body.nanotrasen span.button.danger.active:focus,body.nanotrasen span.button.danger.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.nanotrasen span.button.gridable{width:125px;margin:2px 0}body.nanotrasen span.button.gridable.center{text-align:center;width:75px}body.nanotrasen span.button+span:not(.button),body.nanotrasen span:not(.button)+span.button{margin-left:5px}body.nanotrasen div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000);background-color:rgba(0,0,0,.33);box-shadow:inset 0 0 5px rgba(0,0,0,.5)}body.nanotrasen div.display.tabular{padding:0;margin:0}body.nanotrasen div.display header,body.nanotrasen div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #40628a}body.nanotrasen div.display header .buttonRight,body.nanotrasen div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.nanotrasen div.display article,body.nanotrasen div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.nanotrasen input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#000;background-color:#fff;border:1px solid #272727}body.nanotrasen input.number{width:35px}body.nanotrasen input::-webkit-input-placeholder{color:#999}body.nanotrasen input:-ms-input-placeholder{color:#999}body.nanotrasen input::placeholder{color:#999}body.nanotrasen input::-ms-clear{display:none}body.nanotrasen svg.linegraph{overflow:hidden}body.nanotrasen div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,#bb9b68,#bb9b68 10px,#b1905d 0,#b1905d 20px)}body.nanotrasen div.notice .label{color:#000}body.nanotrasen div.notice .content:only-of-type{padding:0}body.nanotrasen div.notice hr{background-color:#272727}body.nanotrasen div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.nanotrasen section .cell,body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .cell,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.nanotrasen section{display:table-row;width:100%}body.nanotrasen section:not(:first-child){padding-top:4px}body.nanotrasen section.candystripe:nth-child(2n){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.nanotrasen section .label{width:1%;padding-right:32px;white-space:nowrap;color:#8ba5c4}body.nanotrasen section .content:not(:last-child){padding-right:16px}body.nanotrasen section .line{width:100%}body.nanotrasen section .cell:not(:first-child){text-align:center;padding-top:0}body.nanotrasen section .cell span.button{width:75px}body.nanotrasen section:not(:last-child){padding-right:4px}body.nanotrasen div.subdisplay{width:100%;margin:0}body.nanotrasen header.titlebar .close,body.nanotrasen header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#8ba5c4}body.nanotrasen header.titlebar .close:hover,body.nanotrasen header.titlebar .minimize:hover{color:#9cb2cd}body.nanotrasen header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.nanotrasen header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.nanotrasen header.titlebar .title{position:absolute;top:6px;left:46px;color:#8ba5c4;font-size:16px;white-space:nowrap}body.nanotrasen header.titlebar .minimize{position:absolute;top:6px;right:46px}body.nanotrasen header.titlebar .close{position:absolute;top:4px;right:12px}body.syndicate{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4wIiB2aWV3Qm94PSIwIDAgMjAwIDI4OS43NDIiIG9wYWNpdHk9Ii4zMyI+DQogIDxwYXRoIGQ9Im0gOTMuNTM3Njc3LDAgYyAtMTguMTEzMTI1LDAgLTM0LjIyMDEzMywzLjExMTY0IC00OC4zMjM0ODQsOS4zMzQzNyAtMTMuOTY1MDkyLDYuMjIxNjcgLTI0LjYxMjQ0MiwxNS4wNzExNCAtMzEuOTQwNjUxLDI2LjU0NzEgLTcuMTg5OTM5OCwxMS4zMzc4OSAtMTAuMzAxMjI2NiwyNC43NDkxMSAtMTAuMzAxMjI2Niw0MC4yMzQ3OCAwLDEwLjY0NjYyIDIuNzI1MDAyNiwyMC40NjQ2NSA4LjE3NTExMTYsMjkuNDUyNTggNS42MTUyNzcsOC45ODY4NiAxNC4wMzgyNzcsMTcuMzUyMDQgMjUuMjY4ODIxLDI1LjA5NDM2IDExLjIzMDU0NCw3LjYwNTMxIDI2LjUwNzQyMSwxNS40MTgzNSA0NS44MzA1MTQsMjMuNDM3ODIgMTkuOTgzNzQ4LDguMjk1NTcgMzQuODQ4ODQ4LDE1LjU1NDcxIDQ0LjU5Mjk5OCwyMS43NzYzOCA5Ljc0NDE0LDYuMjIyNzMgMTYuNzYxNywxMi44NTg1IDIxLjA1NTcyLDE5LjkwOTUxIDQuMjk0MDQsNy4wNTIwOCA2LjQ0MTkzLDE1Ljc2NDA4IDYuNDQxOTMsMjYuMTM0NTkgMCwxNi4xNzcwMiAtNS4yMDE5NiwyOC40ODIyMiAtMTUuNjA2NzMsMzYuOTE2ODIgLTEwLjIzOTYsOC40MzQ3IC0yNS4wMjIwMywxMi42NTIzIC00NC4zNDUxNjksMTIuNjUyMyAtMTQuMDM4MTcxLDAgLTI1LjUxNTI0NywtMS42NTk0IC0zNC40MzM2MTgsLTQuOTc3NyAtOC45MTgzNywtMy40NTY2IC0xNi4xODU1NzIsLTguNzExMyAtMjEuODAwODM5LC0xNS43NjMzIC01LjYxNTI3NywtNy4wNTIxIC0xMC4wNzQ3OTUsLTE2LjY2MDg4IC0xMy4zNzc4OTksLTI4LjgyODEyIGwgLTI0Ljc3MzE2MjYyOTM5NDUsMCAwLDU2LjgyNjMyIEMgMzMuODU2NzY5LDI4Ni4wNzYwMSA2My43NDkwNCwyODkuNzQyMDEgODkuNjc4MzgzLDI4OS43NDIwMSBjIDE2LjAyMDAyNywwIDMwLjcxOTc4NywtMS4zODI3IDQ0LjA5NzMzNywtNC4xNDc5IDEzLjU0MjcyLC0yLjkwNDMgMjUuMTA0MSwtNy40Njc2IDM0LjY4MzA5LC0xMy42ODkzIDkuNzQ0MTMsLTYuMzU5NyAxNy4zNDA0MiwtMTQuNTE5NSAyMi43OTA1MiwtMjQuNDc0OCA1LjQ1MDEsLTEwLjA5MzMyIDguMTc1MTEsLTIyLjM5OTU5IDguMTc1MTEsLTM2LjkxNjgyIDAsLTEyLjk5NzY0IC0zLjMwMjEsLTI0LjMzNTM5IC05LjkwODI5LC0zNC4wMTQ2IC02LjQ0MTA1LC05LjgxNzI1IC0xNS41MjU0NSwtMTguNTI3MDcgLTI3LjI1MTQ2LC0yNi4xMzEzMyAtMTEuNTYwODUsLTcuNjA0MjcgLTI3LjkxMDgzLC0xNS44MzE0MiAtNDkuMDUwNjYsLTI0LjY4MDIyIC0xNy41MDY0NCwtNy4xOTAxMiAtMzAuNzE5NjY4LC0xMy42ODk0OCAtMzkuNjM4MDM4LC0xOS40OTcwMSAtOC45MTgzNzEsLTUuODA3NTIgLTE4LjYwNzQ3NCwtMTIuNDM0MDkgLTI0LjA5NjUyNCwtMTguODc0MTcgLTUuNDI2MDQzLC02LjM2NjE2IC05LjY1ODgyNiwtMTUuMDcwMDMgLTkuNjU4ODI2LC0yNC44ODcyOSAwLC05LjI2NDAxIDIuMDc1NDE0LC0xNy4yMTM0NSA2LjIyMzQ1NCwtMjMuODUwMzMgMTEuMDk4Mjk4LC0xNC4zOTc0OCA0MS4yODY2MzgsLTEuNzk1MDcgNDUuMDc1NjA5LDI0LjM0NzYyIDQuODM5MzkyLDYuNzc0OTEgOC44NDkzNSwxNi4yNDcyOSAxMi4wMjk1MTUsMjguNDE1NiBsIDIwLjUzMjM0LDAgMCwtNTUuOTk5NjcgYyAtNC40NzgyNSwtNS45MjQ0OCAtOS45NTQ4OCwtMTAuNjMyMjIgLTE1LjkwODM3LC0xNC4zNzQxMSAxLjY0MDU1LDAuNDc5MDUgMy4xOTAzOSwxLjAyMzc2IDQuNjM4NjUsMS42NDAyNCA2LjQ5ODYxLDIuNjI2MDcgMTIuMTY3OTMsNy4zMjc0NyAxNy4wMDczLDE0LjEwMzQ1IDQuODM5MzksNi43NzQ5MSA4Ljg0OTM1LDE2LjI0NTY3IDEyLjAyOTUyLDI4LjQxMzk3IDAsMCA4LjQ4MTI4LC0wLjEyODk0IDguNDg5NzgsLTAuMDAyIDAuNDE3NzYsNi40MTQ5NCAtMS43NTMzOSw5LjQ1Mjg2IC00LjEyMzQyLDEyLjU2MTA0IC0yLjQxNzQsMy4xNjk3OCAtNS4xNDQ4Niw2Ljc4OTczIC00LjAwMjc4LDEzLjAwMjkgMS41MDc4Niw4LjIwMzE4IDEwLjE4MzU0LDEwLjU5NjQyIDE0LjYyMTk0LDkuMzExNTQgLTMuMzE4NDIsLTAuNDk5MTEgLTUuMzE4NTUsLTEuNzQ5NDggLTUuMzE4NTUsLTEuNzQ5NDggMCwwIDEuODc2NDYsMC45OTg2OCA1LjY1MTE3LC0xLjM1OTgxIC0zLjI3Njk1LDAuOTU1NzEgLTEwLjcwNTI5LC0wLjc5NzM4IC0xMS44MDEyNSwtNi43NjMxMyAtMC45NTc1MiwtNS4yMDg2MSAwLjk0NjU0LC03LjI5NTE0IDMuNDAxMTMsLTEwLjUxNDgyIDIuNDU0NjIsLTMuMjE5NjggNS4yODQyNiwtNi45NTgzMSA0LjY4NDMsLTE0LjQ4ODI0IGwgMC4wMDMsMC4wMDIgOC45MjY3NiwwIDAsLTU1Ljk5OTY3IGMgLTE1LjA3MTI1LC0zLjg3MTY4IC0yNy42NTMxNCwtNi4zNjA0MiAtMzcuNzQ2NzEsLTcuNDY1ODYgLTkuOTU1MzEsLTEuMTA3NTUgLTIwLjE4ODIzLC0xLjY1OTgxIC0zMC42OTY2MTMsLTEuNjU5ODEgeiBtIDcwLjMyMTYwMywxNy4zMDg5MyAwLjIzODA1LDQwLjMwNDkgYyAxLjMxODA4LDEuMjI2NjYgMi40Mzk2NSwyLjI3ODE1IDMuMzQwODEsMy4xMDYwMiA0LjgzOTM5LDYuNzc0OTEgOC44NDkzNCwxNi4yNDU2NiAxMi4wMjk1MSwyOC40MTM5NyBsIDIwLjUzMjM0LDAgMCwtNTUuOTk5NjcgYyAtNi42NzczMSwtNC41OTM4MSAtMTkuODM2NDMsLTEwLjQ3MzA5IC0zNi4xNDA3MSwtMTUuODI1MjIgeiBtIC0yOC4xMjA0OSw1LjYwNTUxIDguNTY0NzksMTcuNzE2NTUgYyAtMTEuOTcwMzcsLTYuNDY2OTcgLTEzLjg0Njc4LC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk3MDUsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IG0gMTUuMjIxOTUsMjQuMDA4NDggOC41NjQ3OSwxNy43MTY1NSBjIC0xMS45NzAzOCwtNi40NjY5NyAtMTMuODQ2NzksLTkuNzE3MjYgLTguNTY0NzksLTE3LjcxNjU1IHogbSAyMi43OTcwNCwwIGMgMi43NzE1LDcuOTk5MjkgMS43ODc0MSwxMS4yNDk1OCAtNC40OTM1NCwxNy43MTY1NSBsIDQuNDkzNTQsLTE3LjcxNjU1IHogbSAtOTkuMTEzODQsMi4yMDc2NCA4LjU2NDc5LDE3LjcxNjU1IGMgLTExLjk3MDM4MiwtNi40NjY5NyAtMTMuODQ2NzgyLC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk1NDIsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IiAvPg0KPC9zdmc+DQo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4NCjwhLS0gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLyAtLT4NCg==") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#750000 0,#340404);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#ff750000",endColorstr="#ff340404",GradientType=0)}body.syndicate .normal{color:#40628a}body.syndicate .good{color:#73e573}body.syndicate .average{color:#be6209}body.syndicate .bad{color:#b00e0e}body.syndicate .highlight{color:#000}body.syndicate main{display:block;margin-top:32px;padding:2px 6px 0}body.syndicate hr{height:2px;background-color:#272727;border:none}body.syndicate .hidden{display:none}body.syndicate .bar .barText,body.syndicate span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.syndicate .bold{font-weight:700}body.syndicate .italic{font-style:italic}body.syndicate [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.syndicate div[data-tooltip],body.syndicate span[data-tooltip]{position:relative}body.syndicate div[data-tooltip]:after,body.syndicate span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.syndicate div[data-tooltip]:hover:after,body.syndicate span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.syndicate div[data-tooltip].tooltip-top:after,body.syndicate span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-top:hover:after,body.syndicate span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.syndicate div[data-tooltip].tooltip-bottom:after,body.syndicate span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.syndicate div[data-tooltip].tooltip-bottom:hover:after,body.syndicate span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-left:after,body.syndicate span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-left:hover:after,body.syndicate span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-right:after,body.syndicate span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-right:hover:after,body.syndicate span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #000;background:#272727}body.syndicate .bar .barText{position:absolute;top:0;right:3px}body.syndicate .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#000}body.syndicate .bar .barFill.good{background-color:#73e573}body.syndicate .bar .barFill.average{background-color:#be6209}body.syndicate .bar .barFill.bad{background-color:#b00e0e}body.syndicate span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.syndicate span.button .fa{padding-right:2px}body.syndicate span.button.normal{transition:background-color .5s;background-color:#397439}body.syndicate span.button.normal.active:focus,body.syndicate span.button.normal.active:hover{transition:background-color .25s;background-color:#4a964a;outline:0}body.syndicate span.button.disabled{transition:background-color .5s;background-color:#363636}body.syndicate span.button.disabled.active:focus,body.syndicate span.button.disabled.active:hover{transition:background-color .25s;background-color:#545454;outline:0}body.syndicate span.button.selected{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.selected.active:focus,body.syndicate span.button.selected.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.toggle{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.toggle.active:focus,body.syndicate span.button.toggle.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.caution{transition:background-color .5s;background-color:#be6209}body.syndicate span.button.caution.active:focus,body.syndicate span.button.caution.active:hover{transition:background-color .25s;background-color:#eb790b;outline:0}body.syndicate span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.syndicate span.button.danger.active:focus,body.syndicate span.button.danger.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.syndicate span.button.gridable{width:125px;margin:2px 0}body.syndicate span.button.gridable.center{text-align:center;width:75px}body.syndicate span.button+span:not(.button),body.syndicate span:not(.button)+span.button{margin-left:5px}body.syndicate div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000);background-color:rgba(0,0,0,.5);box-shadow:inset 0 0 5px rgba(0,0,0,.75)}body.syndicate div.display.tabular{padding:0;margin:0}body.syndicate div.display header,body.syndicate div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #272727}body.syndicate div.display header .buttonRight,body.syndicate div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.syndicate div.display article,body.syndicate div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.syndicate input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#fff;background-color:#9d0808;border:1px solid #272727}body.syndicate input.number{width:35px}body.syndicate input::-webkit-input-placeholder{color:#999}body.syndicate input:-ms-input-placeholder{color:#999}body.syndicate input::placeholder{color:#999}body.syndicate input::-ms-clear{display:none}body.syndicate svg.linegraph{overflow:hidden}body.syndicate div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#750000;background-image:repeating-linear-gradient(-45deg,#750000,#750000 10px,#910101 0,#910101 20px)}body.syndicate div.notice .label{color:#000}body.syndicate div.notice .content:only-of-type{padding:0}body.syndicate div.notice hr{background-color:#272727}body.syndicate div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.syndicate section .cell,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.syndicate section{display:table-row;width:100%}body.syndicate section:not(:first-child){padding-top:4px}body.syndicate section.candystripe:nth-child(2n){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.syndicate section .label{width:1%;padding-right:32px;white-space:nowrap;color:#fff}body.syndicate section .content:not(:last-child){padding-right:16px}body.syndicate section .line{width:100%}body.syndicate section .cell:not(:first-child){text-align:center;padding-top:0}body.syndicate section .cell span.button{width:75px}body.syndicate section:not(:last-child){padding-right:4px}body.syndicate div.subdisplay{width:100%;margin:0}body.syndicate header.titlebar .close,body.syndicate header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#e74242}body.syndicate header.titlebar .close:hover,body.syndicate header.titlebar .minimize:hover{color:#eb5e5e}body.syndicate header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.syndicate header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.syndicate header.titlebar .title{position:absolute;top:6px;left:46px;color:#e74242;font-size:16px;white-space:nowrap}body.syndicate header.titlebar .minimize{position:absolute;top:6px;right:46px}body.syndicate header.titlebar .close{position:absolute;top:4px;right:12px}.no-icons header.titlebar .statusicon{font-size:20px}.no-icons header.titlebar .statusicon:after{content:"O"}.no-icons header.titlebar .minimize{top:-2px;font-size:20px}.no-icons header.titlebar .minimize:after{content:"—"}.no-icons header.titlebar .close{font-size:20px}.no-icons header.titlebar .close:after{content:"X"} \ No newline at end of file diff --git a/tgui/assets/tgui.js b/tgui/assets/tgui.js index 5d42acb654..a41d648fe1 100644 --- a/tgui/assets/tgui.js +++ b/tgui/assets/tgui.js @@ -1,16 +1,16 @@ -require=function t(e,n,a){function r(o,s){if(!n[o]){if(!e[o]){var u="function"==typeof require&&require;if(!s&&u)return u(o,!0);if(i)return i(o,!0);var p=Error("Cannot find module '"+o+"'");throw p.code="MODULE_NOT_FOUND",p}var c=n[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return r(n?n:t)},c,c.exports,t,e,n,a)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o2?p[2]:void 0,l=Math.min((void 0===c?o:r(c,o))-u,o-s),f=1;for(s>u&&u+l>s&&(f=-1,u+=l-1,s+=l-1);l-- >0;)u in n?n[s]=n[u]:delete n[s],s+=f,u+=f;return n}},{76:76,79:79,80:80}],6:[function(t,e,n){"use strict";var a=t(80),r=t(76),i=t(79);e.exports=[].fill||function(t){for(var e=a(this),n=i(e.length),o=arguments,s=o.length,u=r(s>1?o[1]:void 0,n),p=s>2?o[2]:void 0,c=void 0===p?n:r(p,n);c>u;)e[u++]=t;return e}},{76:76,79:79,80:80}],7:[function(t,e,n){var a=t(78),r=t(79),i=t(76);e.exports=function(t){return function(e,n,o){var s,u=a(e),p=r(u.length),c=i(o,p);if(t&&n!=n){for(;p>c;)if(s=u[c++],s!=s)return!0}else for(;p>c;c++)if((t||c in u)&&u[c]===n)return t||c;return!t&&-1}}},{76:76,78:78,79:79}],8:[function(t,e,n){var a=t(17),r=t(34),i=t(80),o=t(79),s=t(9);e.exports=function(t){var e=1==t,n=2==t,u=3==t,p=4==t,c=6==t,l=5==t||c;return function(f,d,h){for(var m,v,g=i(f),b=r(g),y=a(d,h,3),x=o(b.length),_=0,w=e?s(f,x):n?s(f,0):void 0;x>_;_++)if((l||_ in b)&&(m=b[_],v=y(m,_,g),t))if(e)w[_]=v;else if(v)switch(t){case 3:return!0;case 5:return m;case 6:return _;case 2:w.push(m)}else if(p)return!1;return c?-1:u||p?p:w}}},{17:17,34:34,79:79,80:80,9:9}],9:[function(t,e,n){var a=t(38),r=t(36),i=t(83)("species");e.exports=function(t,e){var n;return r(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!r(n.prototype)||(n=void 0),a(n)&&(n=n[i],null===n&&(n=void 0))),new(void 0===n?Array:n)(e)}},{36:36,38:38,83:83}],10:[function(t,e,n){var a=t(11),r=t(83)("toStringTag"),i="Arguments"==a(function(){return arguments}());e.exports=function(t){var e,n,o;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=(e=Object(t))[r])?n:i?a(e):"Object"==(o=a(e))&&"function"==typeof e.callee?"Arguments":o}},{11:11,83:83}],11:[function(t,e,n){var a={}.toString;e.exports=function(t){return a.call(t).slice(8,-1)}},{}],12:[function(t,e,n){"use strict";var a=t(46),r=t(31),i=t(60),o=t(17),s=t(69),u=t(18),p=t(27),c=t(42),l=t(44),f=t(82)("id"),d=t(30),h=t(38),m=t(65),v=t(19),g=Object.isExtensible||h,b=v?"_s":"size",y=0,x=function(t,e){if(!h(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!d(t,f)){if(!g(t))return"F";if(!e)return"E";r(t,f,++y)}return"O"+t[f]},_=function(t,e){var n,a=x(e);if("F"!==a)return t._i[a];for(n=t._f;n;n=n.n)if(n.k==e)return n};e.exports={getConstructor:function(t,e,n,r){var c=t(function(t,i){s(t,c,e),t._i=a.create(null),t._f=void 0,t._l=void 0,t[b]=0,void 0!=i&&p(i,n,t[r],t)});return i(c.prototype,{clear:function(){for(var t=this,e=t._i,n=t._f;n;n=n.n)n.r=!0,n.p&&(n.p=n.p.n=void 0),delete e[n.i];t._f=t._l=void 0,t[b]=0},"delete":function(t){var e=this,n=_(e,t);if(n){var a=n.n,r=n.p;delete e._i[n.i],n.r=!0,r&&(r.n=a),a&&(a.p=r),e._f==n&&(e._f=a),e._l==n&&(e._l=r),e[b]--}return!!n},forEach:function(t){for(var e,n=o(t,arguments.length>1?arguments[1]:void 0,3);e=e?e.n:this._f;)for(n(e.v,e.k,this);e&&e.r;)e=e.p},has:function(t){return!!_(this,t)}}),v&&a.setDesc(c.prototype,"size",{get:function(){return u(this[b])}}),c},def:function(t,e,n){var a,r,i=_(t,e);return i?i.v=n:(t._l=i={i:r=x(e,!0),k:e,v:n,p:a=t._l,n:void 0,r:!1},t._f||(t._f=i),a&&(a.n=i),t[b]++,"F"!==r&&(t._i[r]=i)),t},getEntry:_,setStrong:function(t,e,n){c(t,e,function(t,e){this._t=t,this._k=e,this._l=void 0},function(){for(var t=this,e=t._k,n=t._l;n&&n.r;)n=n.p;return t._t&&(t._l=n=n?n.n:t._t._f)?"keys"==e?l(0,n.k):"values"==e?l(0,n.v):l(0,[n.k,n.v]):(t._t=void 0,l(1))},n?"entries":"values",!n,!0),m(e)}}},{17:17,18:18,19:19,27:27,30:30,31:31,38:38,42:42,44:44,46:46,60:60,65:65,69:69,82:82}],13:[function(t,e,n){var a=t(27),r=t(10);e.exports=function(t){return function(){if(r(this)!=t)throw TypeError(t+"#toJSON isn't generic");var e=[];return a(this,!1,e.push,e),e}}},{10:10,27:27}],14:[function(t,e,n){"use strict";var a=t(31),r=t(60),i=t(4),o=t(38),s=t(69),u=t(27),p=t(8),c=t(30),l=t(82)("weak"),f=Object.isExtensible||o,d=p(5),h=p(6),m=0,v=function(t){return t._l||(t._l=new g)},g=function(){this.a=[]},b=function(t,e){return d(t.a,function(t){return t[0]===e})};g.prototype={get:function(t){var e=b(this,t);return e?e[1]:void 0},has:function(t){return!!b(this,t)},set:function(t,e){var n=b(this,t);n?n[1]=e:this.a.push([t,e])},"delete":function(t){var e=h(this.a,function(e){return e[0]===t});return~e&&this.a.splice(e,1),!!~e}},e.exports={getConstructor:function(t,e,n,a){var i=t(function(t,r){s(t,i,e),t._i=m++,t._l=void 0,void 0!=r&&u(r,n,t[a],t)});return r(i.prototype,{"delete":function(t){return o(t)?f(t)?c(t,l)&&c(t[l],this._i)&&delete t[l][this._i]:v(this)["delete"](t):!1},has:function(t){return o(t)?f(t)?c(t,l)&&c(t[l],this._i):v(this).has(t):!1}}),i},def:function(t,e,n){return f(i(e))?(c(e,l)||a(e,l,{}),e[l][t._i]=n):v(t).set(e,n),t},frozenStore:v,WEAK:l}},{27:27,30:30,31:31,38:38,4:4,60:60,69:69,8:8,82:82}],15:[function(t,e,n){"use strict";var a=t(29),r=t(22),i=t(61),o=t(60),s=t(27),u=t(69),p=t(38),c=t(24),l=t(43),f=t(66);e.exports=function(t,e,n,d,h,m){var v=a[t],g=v,b=h?"set":"add",y=g&&g.prototype,x={},_=function(t){var e=y[t];i(y,t,"delete"==t?function(t){return m&&!p(t)?!1:e.call(this,0===t?0:t)}:"has"==t?function(t){return m&&!p(t)?!1:e.call(this,0===t?0:t)}:"get"==t?function(t){return m&&!p(t)?void 0:e.call(this,0===t?0:t)}:"add"==t?function(t){return e.call(this,0===t?0:t),this}:function(t,n){return e.call(this,0===t?0:t,n),this})};if("function"==typeof g&&(m||y.forEach&&!c(function(){(new g).entries().next()}))){var w,k=new g,E=k[b](m?{}:-0,1)!=k,S=c(function(){k.has(1)}),C=l(function(t){new g(t)});C||(g=e(function(e,n){u(e,g,t);var a=new v;return void 0!=n&&s(n,h,a[b],a),a}),g.prototype=y,y.constructor=g),m||k.forEach(function(t,e){w=1/e===-(1/0)}),(S||w)&&(_("delete"),_("has"),h&&_("get")),(w||E)&&_(b),m&&y.clear&&delete y.clear}else g=d.getConstructor(e,t,h,b),o(g.prototype,n);return f(g,t),x[t]=g,r(r.G+r.W+r.F*(g!=v),x),m||d.setStrong(g,t,h),g}},{22:22,24:24,27:27,29:29,38:38,43:43,60:60,61:61,66:66,69:69}],16:[function(t,e,n){var a=e.exports={version:"1.2.6"};"number"==typeof __e&&(__e=a)},{}],17:[function(t,e,n){var a=t(2);e.exports=function(t,e,n){if(a(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,a){return t.call(e,n,a)};case 3:return function(n,a,r){return t.call(e,n,a,r)}}return function(){return t.apply(e,arguments)}}},{2:2}],18:[function(t,e,n){e.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},{}],19:[function(t,e,n){e.exports=!t(24)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},{24:24}],20:[function(t,e,n){var a=t(38),r=t(29).document,i=a(r)&&a(r.createElement);e.exports=function(t){return i?r.createElement(t):{}}},{29:29,38:38}],21:[function(t,e,n){var a=t(46);e.exports=function(t){var e=a.getKeys(t),n=a.getSymbols;if(n)for(var r,i=n(t),o=a.isEnum,s=0;i.length>s;)o.call(t,r=i[s++])&&e.push(r);return e}},{46:46}],22:[function(t,e,n){var a=t(29),r=t(16),i=t(31),o=t(61),s=t(17),u="prototype",p=function(t,e,n){var c,l,f,d,h=t&p.F,m=t&p.G,v=t&p.S,g=t&p.P,b=t&p.B,y=m?a:v?a[e]||(a[e]={}):(a[e]||{})[u],x=m?r:r[e]||(r[e]={}),_=x[u]||(x[u]={});m&&(n=e);for(c in n)l=!h&&y&&c in y,f=(l?y:n)[c],d=b&&l?s(f,a):g&&"function"==typeof f?s(Function.call,f):f,y&&!l&&o(y,c,f),x[c]!=f&&i(x,c,d),g&&_[c]!=f&&(_[c]=f)};a.core=r,p.F=1,p.G=2,p.S=4,p.P=8,p.B=16,p.W=32,e.exports=p},{16:16,17:17,29:29,31:31,61:61}],23:[function(t,e,n){var a=t(83)("match");e.exports=function(t){var e=/./;try{"/./"[t](e)}catch(n){try{return e[a]=!1,!"/./"[t](e)}catch(r){}}return!0}},{83:83}],24:[function(t,e,n){e.exports=function(t){try{return!!t()}catch(e){return!0}}},{}],25:[function(t,e,n){"use strict";var a=t(31),r=t(61),i=t(24),o=t(18),s=t(83);e.exports=function(t,e,n){var u=s(t),p=""[t];i(function(){var e={};return e[u]=function(){return 7},7!=""[t](e)})&&(r(String.prototype,t,n(o,u,p)),a(RegExp.prototype,u,2==e?function(t,e){return p.call(t,this,e)}:function(t){return p.call(t,this)}))}},{18:18,24:24,31:31,61:61,83:83}],26:[function(t,e,n){"use strict";var a=t(4);e.exports=function(){var t=a(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},{4:4}],27:[function(t,e,n){var a=t(17),r=t(40),i=t(35),o=t(4),s=t(79),u=t(84);e.exports=function(t,e,n,p){var c,l,f,d=u(t),h=a(n,p,e?2:1),m=0;if("function"!=typeof d)throw TypeError(t+" is not iterable!");if(i(d))for(c=s(t.length);c>m;m++)e?h(o(l=t[m])[0],l[1]):h(t[m]);else for(f=d.call(t);!(l=f.next()).done;)r(f,h,l.value,e)}},{17:17,35:35,4:4,40:40,79:79,84:84}],28:[function(t,e,n){var a=t(78),r=t(46).getNames,i={}.toString,o="object"==typeof window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],s=function(t){try{return r(t)}catch(e){return o.slice()}};e.exports.get=function(t){return o&&"[object Window]"==i.call(t)?s(t):r(a(t))}},{46:46,78:78}],29:[function(t,e,n){var a=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=a)},{}],30:[function(t,e,n){var a={}.hasOwnProperty;e.exports=function(t,e){return a.call(t,e)}},{}],31:[function(t,e,n){var a=t(46),r=t(59);e.exports=t(19)?function(t,e,n){return a.setDesc(t,e,r(1,n))}:function(t,e,n){return t[e]=n,t}},{19:19,46:46,59:59}],32:[function(t,e,n){e.exports=t(29).document&&document.documentElement},{29:29}],33:[function(t,e,n){e.exports=function(t,e,n){var a=void 0===n;switch(e.length){case 0:return a?t():t.call(n);case 1:return a?t(e[0]):t.call(n,e[0]);case 2:return a?t(e[0],e[1]):t.call(n,e[0],e[1]);case 3:return a?t(e[0],e[1],e[2]):t.call(n,e[0],e[1],e[2]);case 4:return a?t(e[0],e[1],e[2],e[3]):t.call(n,e[0],e[1],e[2],e[3])}return t.apply(n,e)}},{}],34:[function(t,e,n){var a=t(11);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==a(t)?t.split(""):Object(t)}},{11:11}],35:[function(t,e,n){var a=t(45),r=t(83)("iterator"),i=Array.prototype;e.exports=function(t){return void 0!==t&&(a.Array===t||i[r]===t)}},{45:45,83:83}],36:[function(t,e,n){var a=t(11);e.exports=Array.isArray||function(t){return"Array"==a(t)}},{11:11}],37:[function(t,e,n){var a=t(38),r=Math.floor;e.exports=function(t){return!a(t)&&isFinite(t)&&r(t)===t}},{38:38}],38:[function(t,e,n){e.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},{}],39:[function(t,e,n){var a=t(38),r=t(11),i=t(83)("match");e.exports=function(t){var e;return a(t)&&(void 0!==(e=t[i])?!!e:"RegExp"==r(t))}},{11:11,38:38,83:83}],40:[function(t,e,n){var a=t(4);e.exports=function(t,e,n,r){try{return r?e(a(n)[0],n[1]):e(n)}catch(i){var o=t["return"];throw void 0!==o&&a(o.call(t)),i}}},{4:4}],41:[function(t,e,n){"use strict";var a=t(46),r=t(59),i=t(66),o={};t(31)(o,t(83)("iterator"),function(){return this}),e.exports=function(t,e,n){t.prototype=a.create(o,{next:r(1,n)}),i(t,e+" Iterator")}},{31:31,46:46,59:59,66:66,83:83}],42:[function(t,e,n){"use strict";var a=t(48),r=t(22),i=t(61),o=t(31),s=t(30),u=t(45),p=t(41),c=t(66),l=t(46).getProto,f=t(83)("iterator"),d=!([].keys&&"next"in[].keys()),h="@@iterator",m="keys",v="values",g=function(){return this};e.exports=function(t,e,n,b,y,x,_){p(n,e,b);var w,k,E=function(t){if(!d&&t in A)return A[t];switch(t){case m:return function(){return new n(this,t)};case v:return function(){return new n(this,t)}}return function(){return new n(this,t)}},S=e+" Iterator",C=y==v,P=!1,A=t.prototype,O=A[f]||A[h]||y&&A[y],T=O||E(y);if(O){var R=l(T.call(new t));c(R,S,!0),!a&&s(A,h)&&o(R,f,g),C&&O.name!==v&&(P=!0,T=function(){return O.call(this)})}if(a&&!_||!d&&!P&&A[f]||o(A,f,T),u[e]=T,u[S]=g,y)if(w={values:C?T:E(v),keys:x?T:E(m),entries:C?E("entries"):T},_)for(k in w)k in A||i(A,k,w[k]);else r(r.P+r.F*(d||P),e,w);return w}},{22:22,30:30,31:31,41:41,45:45,46:46,48:48,61:61,66:66,83:83}],43:[function(t,e,n){var a=t(83)("iterator"),r=!1;try{var i=[7][a]();i["return"]=function(){r=!0},Array.from(i,function(){throw 2})}catch(o){}e.exports=function(t,e){if(!e&&!r)return!1;var n=!1;try{var i=[7],o=i[a]();o.next=function(){return{done:n=!0}},i[a]=function(){return o},t(i)}catch(s){}return n}},{83:83}],44:[function(t,e,n){e.exports=function(t,e){return{value:e,done:!!t}}},{}],45:[function(t,e,n){e.exports={}},{}],46:[function(t,e,n){var a=Object;e.exports={create:a.create,getProto:a.getPrototypeOf,isEnum:{}.propertyIsEnumerable,getDesc:a.getOwnPropertyDescriptor,setDesc:a.defineProperty,setDescs:a.defineProperties,getKeys:a.keys,getNames:a.getOwnPropertyNames,getSymbols:a.getOwnPropertySymbols,each:[].forEach}},{}],47:[function(t,e,n){var a=t(46),r=t(78);e.exports=function(t,e){for(var n,i=r(t),o=a.getKeys(i),s=o.length,u=0;s>u;)if(i[n=o[u++]]===e)return n}},{46:46,78:78}],48:[function(t,e,n){e.exports=!1},{}],49:[function(t,e,n){e.exports=Math.expm1||function(t){return 0==(t=+t)?t:t>-1e-6&&1e-6>t?t+t*t/2:Math.exp(t)-1}},{}],50:[function(t,e,n){e.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&1e-8>t?t-t*t/2:Math.log(1+t)}},{}],51:[function(t,e,n){e.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:0>t?-1:1}},{}],52:[function(t,e,n){var a,r,i,o=t(29),s=t(75).set,u=o.MutationObserver||o.WebKitMutationObserver,p=o.process,c=o.Promise,l="process"==t(11)(p),f=function(){var t,e,n;for(l&&(t=p.domain)&&(p.domain=null,t.exit());a;)e=a.domain,n=a.fn,e&&e.enter(),n(),e&&e.exit(),a=a.next;r=void 0,t&&t.enter()};if(l)i=function(){p.nextTick(f)};else if(u){var d=1,h=document.createTextNode("");new u(f).observe(h,{characterData:!0}),i=function(){h.data=d=-d}}else i=c&&c.resolve?function(){c.resolve().then(f)}:function(){s.call(o,f)};e.exports=function(t){var e={fn:t,next:void 0,domain:l&&p.domain};r&&(r.next=e),a||(a=e,i()),r=e}},{11:11,29:29,75:75}],53:[function(t,e,n){var a=t(46),r=t(80),i=t(34);e.exports=t(24)(function(){var t=Object.assign,e={},n={},a=Symbol(),r="abcdefghijklmnopqrst";return e[a]=7,r.split("").forEach(function(t){n[t]=t}),7!=t({},e)[a]||Object.keys(t({},n)).join("")!=r})?function(t,e){for(var n=r(t),o=arguments,s=o.length,u=1,p=a.getKeys,c=a.getSymbols,l=a.isEnum;s>u;)for(var f,d=i(o[u++]),h=c?p(d).concat(c(d)):p(d),m=h.length,v=0;m>v;)l.call(d,f=h[v++])&&(n[f]=d[f]);return n}:Object.assign},{24:24,34:34,46:46,80:80}],54:[function(t,e,n){var a=t(22),r=t(16),i=t(24);e.exports=function(t,e){var n=(r.Object||{})[t]||Object[t],o={};o[t]=e(n),a(a.S+a.F*i(function(){n(1)}),"Object",o)}},{16:16,22:22,24:24}],55:[function(t,e,n){var a=t(46),r=t(78),i=a.isEnum;e.exports=function(t){return function(e){for(var n,o=r(e),s=a.getKeys(o),u=s.length,p=0,c=[];u>p;)i.call(o,n=s[p++])&&c.push(t?[n,o[n]]:o[n]);return c}}},{46:46,78:78}],56:[function(t,e,n){var a=t(46),r=t(4),i=t(29).Reflect;e.exports=i&&i.ownKeys||function(t){var e=a.getNames(r(t)),n=a.getSymbols;return n?e.concat(n(t)):e}},{29:29,4:4,46:46}],57:[function(t,e,n){"use strict";var a=t(58),r=t(33),i=t(2);e.exports=function(){for(var t=i(this),e=arguments.length,n=Array(e),o=0,s=a._,u=!1;e>o;)(n[o]=arguments[o++])===s&&(u=!0);return function(){var a,i=this,o=arguments,p=o.length,c=0,l=0;if(!u&&!p)return r(t,n,i);if(a=n.slice(),u)for(;e>c;c++)a[c]===s&&(a[c]=o[l++]);for(;p>l;)a.push(o[l++]);return r(t,a,i)}}},{2:2,33:33,58:58}],58:[function(t,e,n){e.exports=t(29)},{29:29}],59:[function(t,e,n){e.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},{}],60:[function(t,e,n){var a=t(61);e.exports=function(t,e){for(var n in e)a(t,n,e[n]);return t}},{61:61}],61:[function(t,e,n){var a=t(29),r=t(31),i=t(82)("src"),o="toString",s=Function[o],u=(""+s).split(o);t(16).inspectSource=function(t){return s.call(t)},(e.exports=function(t,e,n,o){"function"==typeof n&&(n.hasOwnProperty(i)||r(n,i,t[e]?""+t[e]:u.join(e+"")),n.hasOwnProperty("name")||r(n,"name",e)),t===a?t[e]=n:(o||delete t[e],r(t,e,n))})(Function.prototype,o,function(){return"function"==typeof this&&this[i]||s.call(this)})},{16:16,29:29,31:31,82:82}],62:[function(t,e,n){e.exports=function(t,e){var n=e===Object(e)?function(t){return e[t]}:e;return function(e){return(e+"").replace(t,n)}}},{}],63:[function(t,e,n){e.exports=Object.is||function(t,e){return t===e?0!==t||1/t===1/e:t!=t&&e!=e}},{}],64:[function(t,e,n){var a=t(46).getDesc,r=t(38),i=t(4),o=function(t,e){if(i(t),!r(e)&&null!==e)throw TypeError(e+": can't set as prototype!")};e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,n,r){try{r=t(17)(Function.call,a(Object.prototype,"__proto__").set,2),r(e,[]),n=!(e instanceof Array)}catch(i){n=!0}return function(t,e){return o(t,e),n?t.__proto__=e:r(t,e),t}}({},!1):void 0),check:o}},{17:17,38:38,4:4,46:46}],65:[function(t,e,n){"use strict";var a=t(29),r=t(46),i=t(19),o=t(83)("species");e.exports=function(t){var e=a[t];i&&e&&!e[o]&&r.setDesc(e,o,{configurable:!0,get:function(){return this}})}},{19:19,29:29,46:46,83:83}],66:[function(t,e,n){var a=t(46).setDesc,r=t(30),i=t(83)("toStringTag");e.exports=function(t,e,n){t&&!r(t=n?t:t.prototype,i)&&a(t,i,{configurable:!0,value:e})}},{30:30,46:46,83:83}],67:[function(t,e,n){var a=t(29),r="__core-js_shared__",i=a[r]||(a[r]={});e.exports=function(t){return i[t]||(i[t]={})}},{29:29}],68:[function(t,e,n){var a=t(4),r=t(2),i=t(83)("species");e.exports=function(t,e){var n,o=a(t).constructor;return void 0===o||void 0==(n=a(o)[i])?e:r(n)}},{2:2,4:4,83:83}],69:[function(t,e,n){e.exports=function(t,e,n){if(!(t instanceof e))throw TypeError(n+": use the 'new' operator!");return t}},{}],70:[function(t,e,n){var a=t(77),r=t(18);e.exports=function(t){return function(e,n){var i,o,s=r(e)+"",u=a(n),p=s.length;return 0>u||u>=p?t?"":void 0:(i=s.charCodeAt(u),55296>i||i>56319||u+1===p||(o=s.charCodeAt(u+1))<56320||o>57343?t?s.charAt(u):i:t?s.slice(u,u+2):(i-55296<<10)+(o-56320)+65536)}}},{18:18,77:77}],71:[function(t,e,n){var a=t(39),r=t(18);e.exports=function(t,e,n){if(a(e))throw TypeError("String#"+n+" doesn't accept regex!");return r(t)+""}},{18:18,39:39}],72:[function(t,e,n){var a=t(79),r=t(73),i=t(18);e.exports=function(t,e,n,o){var s=i(t)+"",u=s.length,p=void 0===n?" ":n+"",c=a(e);if(u>=c)return s;""==p&&(p=" ");var l=c-u,f=r.call(p,Math.ceil(l/p.length));return f.length>l&&(f=f.slice(0,l)),o?f+s:s+f}},{18:18,73:73,79:79}],73:[function(t,e,n){"use strict";var a=t(77),r=t(18);e.exports=function(t){var e=r(this)+"",n="",i=a(t);if(0>i||i==1/0)throw RangeError("Count can't be negative");for(;i>0;(i>>>=1)&&(e+=e))1&i&&(n+=e);return n}},{18:18,77:77}],74:[function(t,e,n){var a=t(22),r=t(18),i=t(24),o=" \n\x0B\f\r  ᠎              \u2028\u2029\ufeff",s="["+o+"]",u="​…",p=RegExp("^"+s+s+"*"),c=RegExp(s+s+"*$"),l=function(t,e){var n={};n[t]=e(f),a(a.P+a.F*i(function(){return!!o[t]()||u[t]()!=u}),"String",n)},f=l.trim=function(t,e){return t=r(t)+"",1&e&&(t=t.replace(p,"")),2&e&&(t=t.replace(c,"")),t};e.exports=l},{18:18,22:22,24:24}],75:[function(t,e,n){var a,r,i,o=t(17),s=t(33),u=t(32),p=t(20),c=t(29),l=c.process,f=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,m=0,v={},g="onreadystatechange",b=function(){var t=+this;if(v.hasOwnProperty(t)){var e=v[t];delete v[t],e()}},y=function(t){b.call(t.data)};f&&d||(f=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return v[++m]=function(){s("function"==typeof t?t:Function(t),e)},a(m),m},d=function(t){delete v[t]},"process"==t(11)(l)?a=function(t){l.nextTick(o(b,t,1))}:h?(r=new h,i=r.port2,r.port1.onmessage=y,a=o(i.postMessage,i,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(a=function(t){c.postMessage(t+"","*")},c.addEventListener("message",y,!1)):a=g in p("script")?function(t){u.appendChild(p("script"))[g]=function(){u.removeChild(this),b.call(t)}}:function(t){setTimeout(o(b,t,1),0)}),e.exports={set:f,clear:d}},{11:11,17:17,20:20,29:29,32:32,33:33}],76:[function(t,e,n){var a=t(77),r=Math.max,i=Math.min;e.exports=function(t,e){return t=a(t),0>t?r(t+e,0):i(t,e)}},{77:77}],77:[function(t,e,n){var a=Math.ceil,r=Math.floor;e.exports=function(t){return isNaN(t=+t)?0:(t>0?r:a)(t)}},{}],78:[function(t,e,n){var a=t(34),r=t(18);e.exports=function(t){return a(r(t))}},{18:18,34:34}],79:[function(t,e,n){var a=t(77),r=Math.min;e.exports=function(t){return t>0?r(a(t),9007199254740991):0}},{77:77}],80:[function(t,e,n){var a=t(18);e.exports=function(t){return Object(a(t))}},{18:18}],81:[function(t,e,n){var a=t(38);e.exports=function(t,e){if(!a(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!a(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!a(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!a(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")}},{38:38}],82:[function(t,e,n){var a=0,r=Math.random();e.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++a+r).toString(36))}},{}],83:[function(t,e,n){var a=t(67)("wks"),r=t(82),i=t(29).Symbol;e.exports=function(t){return a[t]||(a[t]=i&&i[t]||(i||r)("Symbol."+t))}},{29:29,67:67,82:82}],84:[function(t,e,n){var a=t(10),r=t(83)("iterator"),i=t(45);e.exports=t(16).getIteratorMethod=function(t){return void 0!=t?t[r]||t["@@iterator"]||i[a(t)]:void 0}},{10:10,16:16,45:45,83:83}],85:[function(t,e,n){"use strict";var a,r=t(46),i=t(22),o=t(19),s=t(59),u=t(32),p=t(20),c=t(30),l=t(11),f=t(33),d=t(24),h=t(4),m=t(2),v=t(38),g=t(80),b=t(78),y=t(77),x=t(76),_=t(79),w=t(34),k=t(82)("__proto__"),E=t(8),S=t(7)(!1),C=Object.prototype,P=Array.prototype,A=P.slice,O=P.join,T=r.setDesc,R=r.getDesc,j=r.setDescs,M={};o||(a=!d(function(){return 7!=T(p("div"),"a",{get:function(){return 7}}).a}),r.setDesc=function(t,e,n){if(a)try{return T(t,e,n)}catch(r){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(h(t)[e]=n.value),t},r.getDesc=function(t,e){if(a)try{return R(t,e)}catch(n){}return c(t,e)?s(!C.propertyIsEnumerable.call(t,e),t[e]):void 0},r.setDescs=j=function(t,e){h(t);for(var n,a=r.getKeys(e),i=a.length,o=0;i>o;)r.setDesc(t,n=a[o++],e[n]);return t}),i(i.S+i.F*!o,"Object",{getOwnPropertyDescriptor:r.getDesc,defineProperty:r.setDesc,defineProperties:j});var L="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(","),D=L.concat("length","prototype"),N=L.length,F=function(){var t,e=p("iframe"),n=N,a=">";for(e.style.display="none",u.appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write("