diff --git a/SQL/database_changelog.txt b/SQL/database_changelog.txt index 5d3c3ccec2..7063273378 100644 --- a/SQL/database_changelog.txt +++ b/SQL/database_changelog.txt @@ -1,17 +1,152 @@ +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.4; The query to update the schema revision table is: + +INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 4); +or +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. +Also, added flags column to the player table. + +CREATE TABLE `role_time` ( `ckey` VARCHAR(32) NOT NULL , `job` VARCHAR(128) NOT NULL , `minutes` INT UNSIGNED NOT NULL, PRIMARY KEY (`ckey`, `job`) ) ENGINE = InnoDB; + +ALTER TABLE `player` ADD `flags` INT NOT NULL default '0' AFTER `accountjoindate`; + +Remember to add a prefix to the table name if you use them. + +---------------------------------------------------- + +28 June 2017, by oranges +Added schema_revision to store the current db revision, why start at 3.0? + +because: +15:09 <+MrStonedOne> 1.0 was erro, 2.0 was when i removed erro_, 3.0 was when jordie made all the strings that hold numbers numbers + +CREATE TABLE `schema_revision` ( +`major` TINYINT(3) UNSIGNED NOT NULL , +`minor` TINYINT(3) UNSIGNED NOT NULL , +`date` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, +PRIMARY KEY ( `major`,`minor` ) +) ENGINE = INNODB; + +INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 0); + +Remember to add a prefix to the table name if you use them. + +---------------------------------------------------- + +26 June 2017, by Jordie0608 + +Modified table 'poll_option', adding the column 'default_percentage_calc'. + +ALTER TABLE `poll_option` ADD COLUMN `default_percentage_calc` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1' AFTER `descmax` + +Remember to add a prefix to the table name if you use them. + +---------------------------------------------------- + +22 June 2017, by Jordie0608 + +Modified table 'poll_option', removing the column 'percentagecalc'. + +ALTER TABLE `poll_option` DROP COLUMN `percentagecalc` + +Remember to add a prefix to the table name if you use them. + +---------------------------------------------------- + +8 June 2017, by Jordie0608 + +Modified table 'death', adding column 'round_id', removing column 'gender' and replacing column 'coord' with the columns 'x_coord', 'y_coord' and 'z_coord'. + +START TRANSACTION; +ALTER TABLE `death` DROP COLUMN `gender`, ADD COLUMN `x_coord` SMALLINT(5) UNSIGNED NOT NULL AFTER `coord`, ADD COLUMN `y_coord` SMALLINT(5) UNSIGNED NOT NULL AFTER `x_coord`, ADD COLUMN `z_coord` SMALLINT(5) UNSIGNED NOT NULL AFTER `y_coord`, ADD COLUMN `round_id` INT(11) NOT NULL AFTER `server_port`; +SET SQL_SAFE_UPDATES = 0; +UPDATE `death` SET `x_coord` = SUBSTRING_INDEX(`coord`, ',', 1), `y_coord` = SUBSTRING_INDEX(SUBSTRING_INDEX(`coord`, ',', 2), ',', -1), `z_coord` = SUBSTRING_INDEX(`coord`, ',', -1); +SET SQL_SAFE_UPDATES = 1; +ALTER TABLE `death` DROP COLUMN `coord`; +COMMIT; + +Remember to add a prefix to the table name if you use them. + +--------------------------------------------------- + +30 May 2017, by MrStonedOne + +Z levels changed, this query allows you to convert old ss13 death records: + +UPDATE death SET coord = CONCAT(SUBSTRING_INDEX(coord, ',', 2), ', ', CASE TRIM(SUBSTRING_INDEX(coord, ',', -1)) WHEN 1 THEN 2 WHEN 2 THEN 1 ELSE TRIMSUBSTRING_INDEX(coord, ',', -1) END) + +--------------------------------------------------- + +26 May 2017, by Jordie0608 + +Modified table 'ban', adding the column 'round_id'. + +ALTER TABLE `ban` ADD COLUMN `round_id` INT(11) NOT NULL AFTER `server_port` + +Remember to add a prefix to the table name if you use them. + +---------------------------------------------------- + 20 May 2017, by Jordie0608 Created table `round` to replace tracking of the datapoints 'round_start', 'round_end', 'server_ip', 'game_mode', 'round_end_results', 'end_error', 'end_proper', 'emergency_shuttle', 'map_name' and 'station_renames' in the `feedback` table. Once created this table is populated with rows from the `feedback` table. START TRANSACTION; -CREATE TABLE `feedback`.`round` (`id` INT(11) NOT NULL AUTO_INCREMENT, `start_datetime` DATETIME NOT NULL, `end_datetime` DATETIME NULL, `server_ip` INT(10) UNSIGNED NOT NULL, `server_port` SMALLINT(5) UNSIGNED NOT NULL, `commit_hash` CHAR(40) NULL, `game_mode` VARCHAR(32) NULL, `game_mode_result` VARCHAR(64) NULL, `end_state` VARCHAR(64) NULL, `shuttle_name` VARCHAR(64) NULL, `map_name` VARCHAR(32) NULL, `station_name` VARCHAR(80) NULL, PRIMARY KEY (`id`)); -ALTER TABLE `feedback`.`feedback` ADD INDEX `tmp` (`round_id` ASC, `var_name` ASC); -INSERT INTO `feedback`.`round` +CREATE TABLE `round` (`id` INT(11) NOT NULL AUTO_INCREMENT, `start_datetime` DATETIME NOT NULL, `end_datetime` DATETIME NULL, `server_ip` INT(10) UNSIGNED NOT NULL, `server_port` SMALLINT(5) UNSIGNED NOT NULL, `commit_hash` CHAR(40) NULL, `game_mode` VARCHAR(32) NULL, `game_mode_result` VARCHAR(64) NULL, `end_state` VARCHAR(64) NULL, `shuttle_name` VARCHAR(64) NULL, `map_name` VARCHAR(32) NULL, `station_name` VARCHAR(80) NULL, PRIMARY KEY (`id`)); +ALTER TABLE `feedback` ADD INDEX `tmp` (`round_id` ASC, `var_name` ASC); +INSERT INTO `round` (`id`, `start_datetime`, `end_datetime`, `server_ip`, `server_port`, `commit_hash`, `game_mode`, `game_mode_result`, `end_state`, `shuttle_name`, `map_name`, `station_name`) SELECT DISTINCT ri.round_id, IFNULL(STR_TO_DATE(st.details,'%a %b %e %H:%i:%s %Y'), TIMESTAMP(0)), STR_TO_DATE(et.details,'%a %b %e %H:%i:%s %Y'), IFNULL(INET_ATON(SUBSTRING_INDEX(IF(si.details = '', '0', IF(SUBSTRING_INDEX(si.details, ':', 1) LIKE '%_._%', si.details, '0')), ':', 1)), INET_ATON(0)), IFNULL(IF(si.details LIKE '%:_%', CAST(SUBSTRING_INDEX(si.details, ':', -1) AS UNSIGNED), '0'), '0'), ch.details, gm.details, mr.details, IFNULL(es.details, ep.details), ss.details, mn.details, sn.details -FROM `feedback`.`feedback`AS ri -LEFT JOIN `feedback`.`feedback` AS st ON ri.round_id = st.round_id AND st.var_name = "round_start" LEFT JOIN `feedback`.`feedback` AS et ON ri.round_id = et.round_id AND et.var_name = "round_end" LEFT JOIN `feedback`.`feedback` AS si ON ri.round_id = si.round_id AND si.var_name = "server_ip" LEFT JOIN `feedback`.`feedback` AS ch ON ri.round_id = ch.round_id AND ch.var_name = "revision" LEFT JOIN `feedback`.`feedback` AS gm ON ri.round_id = gm.round_id AND gm.var_name = "game_mode" LEFT JOIN `feedback`.`feedback` AS mr ON ri.round_id = mr.round_id AND mr.var_name = "round_end_result" LEFT JOIN `feedback`.`feedback` AS es ON ri.round_id = es.round_id AND es.var_name = "end_state" LEFT JOIN `feedback`.`feedback` AS ep ON ri.round_id = ep.round_id AND ep.var_name = "end_proper" LEFT JOIN `feedback`.`feedback` AS ss ON ri.round_id = ss.round_id AND ss.var_name = "emergency_shuttle" LEFT JOIN `feedback`.`feedback` AS mn ON ri.round_id = mn.round_id AND mn.var_name = "map_name" LEFT JOIN `feedback`.`feedback` AS sn ON ri.round_id = sn.round_id AND sn.var_name = "station_renames"; -ALTER TABLE `feedback`.`feedback` DROP INDEX `tmp`; +FROM `feedback`AS ri +LEFT JOIN `feedback` AS st ON ri.round_id = st.round_id AND st.var_name = "round_start" LEFT JOIN `feedback` AS et ON ri.round_id = et.round_id AND et.var_name = "round_end" LEFT JOIN `feedback` AS si ON ri.round_id = si.round_id AND si.var_name = "server_ip" LEFT JOIN `feedback` AS ch ON ri.round_id = ch.round_id AND ch.var_name = "revision" LEFT JOIN `feedback` AS gm ON ri.round_id = gm.round_id AND gm.var_name = "game_mode" LEFT JOIN `feedback` AS mr ON ri.round_id = mr.round_id AND mr.var_name = "round_end_result" LEFT JOIN `feedback` AS es ON ri.round_id = es.round_id AND es.var_name = "end_state" LEFT JOIN `feedback` AS ep ON ri.round_id = ep.round_id AND ep.var_name = "end_proper" LEFT JOIN `feedback` AS ss ON ri.round_id = ss.round_id AND ss.var_name = "emergency_shuttle" LEFT JOIN `feedback` AS mn ON ri.round_id = mn.round_id AND mn.var_name = "map_name" LEFT JOIN `feedback` AS sn ON ri.round_id = sn.round_id AND sn.var_name = "station_renames"; +ALTER TABLE `feedback` DROP INDEX `tmp`; COMMIT; It's not necessary to delete the rows from the `feedback` table but henceforth these datapoints will be in the `round` table. @@ -24,19 +159,18 @@ Remember to add a prefix to the table names if you use them Modified table 'player', adding the column 'accountjoindate', removing the column 'id' and making the column 'ckey' the primary key. -ALTER TABLE `feedback`.`player` DROP COLUMN `id`, ADD COLUMN `accountjoindate` DATE NULL AFTER `lastadminrank`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`), DROP INDEX `ckey`; +ALTER TABLE `player` DROP COLUMN `id`, ADD COLUMN `accountjoindate` DATE NULL AFTER `lastadminrank`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`), DROP INDEX `ckey`; Remember to add a prefix to the table name if you use them. ---------------------------------------------------- - 10 March 2017, by Jordie0608 Modified table 'death', adding the columns 'toxloss', 'cloneloss', and 'staminaloss' and table 'legacy_population', adding the columns 'server_ip' and 'server_port'. -ALTER TABLE `feedback`.`death` ADD COLUMN `toxloss` SMALLINT(5) UNSIGNED NOT NULL AFTER `oxyloss`, ADD COLUMN `cloneloss` SMALLINT(5) UNSIGNED NOT NULL AFTER `toxloss`, ADD COLUMN `staminaloss` SMALLINT(5) UNSIGNED NOT NULL AFTER `cloneloss`; +ALTER TABLE `death` ADD COLUMN `toxloss` SMALLINT(5) UNSIGNED NOT NULL AFTER `oxyloss`, ADD COLUMN `cloneloss` SMALLINT(5) UNSIGNED NOT NULL AFTER `toxloss`, ADD COLUMN `staminaloss` SMALLINT(5) UNSIGNED NOT NULL AFTER `cloneloss`; -ALTER TABLE `feedback`.`legacy_population` ADD COLUMN `server_ip` INT(10) UNSIGNED NOT NULL AFTER `time`, ADD COLUMN `server_port` SMALLINT(5) UNSIGNED NOT NULL AFTER `server_ip`; +ALTER TABLE `legacy_population` ADD COLUMN `server_ip` INT(10) UNSIGNED NOT NULL AFTER `time`, ADD COLUMN `server_port` SMALLINT(5) UNSIGNED NOT NULL AFTER `server_ip`; Remember to add a prefix to the table name if you use them. @@ -68,18 +202,18 @@ Created table 'messages' to supersede the 'notes', 'memos', and 'watchlist' tabl To create this new table run the following command: -CREATE TABLE `feedback`.`messages` (`id` INT(11) NOT NULL AUTO_INCREMENT , `type` VARCHAR(32) NOT NULL , `targetckey` VARCHAR(32) NOT NULL , `adminckey` VARCHAR(32) NOT NULL , `text` TEXT NOT NULL , `timestamp` DATETIME NOT NULL , `server` VARCHAR(32) NULL , `secret` TINYINT(1) NULL DEFAULT 1 , `lasteditor` VARCHAR(32) NULL , `edits` TEXT NULL , PRIMARY KEY (`id`) ) +CREATE TABLE `messages` (`id` INT(11) NOT NULL AUTO_INCREMENT , `type` VARCHAR(32) NOT NULL , `targetckey` VARCHAR(32) NOT NULL , `adminckey` VARCHAR(32) NOT NULL , `text` TEXT NOT NULL , `timestamp` DATETIME NOT NULL , `server` VARCHAR(32) NULL , `secret` TINYINT(1) NULL DEFAULT 1 , `lasteditor` VARCHAR(32) NULL , `edits` TEXT NULL , PRIMARY KEY (`id`) ) To copy the contents of the 'notes', 'memos', and 'watchlist' tables to this new table run the following commands: -INSERT INTO `feedback`.`messages` -(`id`,`type`,`targetckey`,`adminckey`,`text`,`timestamp`,`server`,`secret`,`lasteditor`,`edits`) SELECT `id`, "note", `ckey`, `adminckey`, `notetext`, `timestamp`, `server`, `secret`, `last_editor`, `edits` FROM `feedback`.`notes` +INSERT INTO `messages` +(`id`,`type`,`targetckey`,`adminckey`,`text`,`timestamp`,`server`,`secret`,`lasteditor`,`edits`) SELECT `id`, "note", `ckey`, `adminckey`, `notetext`, `timestamp`, `server`, `secret`, `last_editor`, `edits` FROM `notes` -INSERT INTO `feedback`.`messages` -(`type`,`targetckey`,`adminckey`,`text`,`timestamp`,`lasteditor`,`edits`) SELECT "memo", `ckey`, `ckey`, `memotext`, `timestamp`, `last_editor`, `edits` FROM `feedback`.`memo` +INSERT INTO `messages` +(`type`,`targetckey`,`adminckey`,`text`,`timestamp`,`lasteditor`,`edits`) SELECT "memo", `ckey`, `ckey`, `memotext`, `timestamp`, `last_editor`, `edits` FROM `memo` -INSERT INTO `feedback`.`messages` -(`type`,`targetckey`,`adminckey`,`text`,`timestamp`,`lasteditor`,`edits`) SELECT "watchlist entry", `ckey`, `adminckey`, `reason`, `timestamp`, `last_editor`, `edits` FROM `feedback`.`watch` +INSERT INTO `messages` +(`type`,`targetckey`,`adminckey`,`text`,`timestamp`,`lasteditor`,`edits`) SELECT "watchlist entry", `ckey`, `adminckey`, `reason`, `timestamp`, `last_editor`, `edits` FROM `watch` It's not necessary to delete the 'notes', 'memos', and 'watchlist' tables but they will no longer be used. @@ -91,7 +225,7 @@ Remember to add a prefix to the table names if you use them Modified table 'notes', adding column 'secret'. -ALTER TABLE `feedback`.`notes` ADD COLUMN `secret` TINYINT(1) NOT NULL DEFAULT '1' AFTER `server` +ALTER TABLE `notes` ADD COLUMN `secret` TINYINT(1) NOT NULL DEFAULT '1' AFTER `server` Remember to add a prefix to the table name if you use them @@ -101,7 +235,7 @@ Remember to add a prefix to the table name if you use them Changed appearance bans to be jobbans. -UPDATE 'feedback'.`ban` SET `job` = "appearance", `bantype` = "JOB_PERMABAN" WHERE `bantype` = "APPEARANCE_PERMABAN" +UPDATE `ban` SET `job` = "appearance", `bantype` = "JOB_PERMABAN" WHERE `bantype` = "APPEARANCE_PERMABAN" Remember to add a prefix to the table name if you use them @@ -111,7 +245,7 @@ Remember to add a prefix to the table name if you use them Modified table 'poll_question', adding column 'dontshow' which was recently added to the server schema. -ALTER TABLE `feedback`.`poll_question` ADD COLUMN `dontshow` TINYINT(1) NOT NULL DEFAULT '0' AFTER `for_trialmin` +ALTER TABLE `poll_question` ADD COLUMN `dontshow` TINYINT(1) NOT NULL DEFAULT '0' AFTER `for_trialmin` Remember to add a prefix to the table name if you use them @@ -121,7 +255,7 @@ Remember to add a prefix to the table name if you use them Added ipintel table, only required if ip intel is enabled in the config -CREATE TABLE `ipintel` ( +CREATE TABLE `ipintel` ( `ip` INT UNSIGNED NOT NULL , `date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL , `intel` REAL NOT NULL DEFAULT '0', @@ -134,7 +268,7 @@ PRIMARY KEY ( `ip` ) Modified table 'poll_question', adding columns 'createdby_ckey', 'createdby_ip' and 'for_trialmin' to bring it inline with the schema used by the tg servers. -ALTER TABLE `feedback`.`poll_question` ADD COLUMN `createdby_ckey` VARCHAR(45) NULL DEFAULT NULL AFTER `multiplechoiceoptions`, ADD COLUMN `createdby_ip` VARCHAR(45) NULL DEFAULT NULL AFTER `createdby_ckey`, ADD COLUMN `for_trialmin` VARCHAR(45) NULL DEFAULT NULL AFTER `createdby_ip` +ALTER TABLE `poll_question` ADD COLUMN `createdby_ckey` VARCHAR(45) NULL DEFAULT NULL AFTER `multiplechoiceoptions`, ADD COLUMN `createdby_ip` VARCHAR(45) NULL DEFAULT NULL AFTER `createdby_ckey`, ADD COLUMN `for_trialmin` VARCHAR(45) NULL DEFAULT NULL AFTER `createdby_ip` Remember to add a prefix to the table name if you use them @@ -144,7 +278,7 @@ Remember to add a prefix to the table name if you use them Modified table 'watch', removing 'id' column, making 'ckey' primary and adding the columns 'timestamp', 'adminckey', 'last_editor' and 'edits'. -ALTER TABLE `feedback`.`watch` DROP COLUMN `id`, ADD COLUMN `timestamp` datetime NOT NULL AFTER `reason`, ADD COLUMN `adminckey` varchar(32) NOT NULL AFTER `timestamp`, ADD COLUMN `last_editor` varchar(32) NULL AFTER `adminckey`, ADD COLUMN `edits` text NULL AFTER `last_editor`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`) +ALTER TABLE `watch` DROP COLUMN `id`, ADD COLUMN `timestamp` datetime NOT NULL AFTER `reason`, ADD COLUMN `adminckey` varchar(32) NOT NULL AFTER `timestamp`, ADD COLUMN `last_editor` varchar(32) NULL AFTER `adminckey`, ADD COLUMN `edits` text NULL AFTER `last_editor`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`) Remember to add a prefix to the table name if you use them. @@ -166,7 +300,7 @@ Remember to add prefix to the table name if you use them. Modified table 'memo', removing 'id' column and making 'ckey' primary. -ALTER TABLE `feedback`.`memo` DROP COLUMN `id`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`) +ALTER TABLE `memo` DROP COLUMN `id`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`) Remember to add prefix to the table name if you use them. @@ -230,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 4c76e1f275..1edd5ad12b 100644 --- a/SQL/tgstation_schema.sql +++ b/SQL/tgstation_schema.sql @@ -1,6 +1,3 @@ -CREATE DATABASE IF NOT EXISTS `feedback` /*!40100 DEFAULT CHARACTER SET latin1 */; -USE `feedback`; - /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; @@ -74,6 +71,7 @@ CREATE TABLE `ban` ( `bantime` datetime NOT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) NOT NULL, `bantype` enum('PERMABAN','TEMPBAN','JOB_PERMABAN','JOB_TEMPBAN','ADMIN_PERMABAN','ADMIN_TEMPBAN') NOT NULL, `reason` varchar(2048) NOT NULL, `job` varchar(32) DEFAULT NULL, @@ -112,6 +110,7 @@ CREATE TABLE `connection_log` ( `datetime` datetime DEFAULT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) unsigned NOT NULL, `ckey` varchar(45) DEFAULT NULL, `ip` int(10) unsigned NOT NULL, `computerid` varchar(45) DEFAULT NULL, @@ -119,7 +118,6 @@ CREATE TABLE `connection_log` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `death` -- @@ -136,7 +134,7 @@ CREATE TABLE `death` ( `mapname` varchar(32) NOT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, - `round_id` int(11) NOT NULL + `round_id` int(11) NOT NULL, `tod` datetime NOT NULL COMMENT 'Time of death', `job` varchar(32) NOT NULL, `special` varchar(32) DEFAULT NULL, @@ -151,11 +149,12 @@ CREATE TABLE `death` ( `toxloss` smallint(5) unsigned NOT NULL, `cloneloss` smallint(5) unsigned NOT NULL, `staminaloss` smallint(5) unsigned NOT NULL, + `last_words` varchar(255) DEFAULT NULL, + `suicide` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `feedback` -- @@ -204,6 +203,7 @@ CREATE TABLE `legacy_population` ( `time` datetime NOT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -224,6 +224,7 @@ CREATE TABLE `library` ( `ckey` varchar(32) NOT NULL DEFAULT 'LEGACY', `datetime` datetime NOT NULL, `deleted` tinyint(1) unsigned DEFAULT NULL, + `round_id_created` int(11) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `deleted_idx` (`deleted`), KEY `idx_lib_id_del` (`id`,`deleted`), @@ -247,16 +248,35 @@ CREATE TABLE `messages` ( `text` varchar(2048) NOT NULL, `timestamp` datetime NOT NULL, `server` varchar(32) DEFAULT NULL, + `server_ip` int(10) unsigned NOT NULL, + `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) unsigned NOT NULL, `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 */; +-- +-- Table structure for table `role_time` +-- + +DROP TABLE IF EXISTS `role_time`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; + +CREATE TABLE `role_time` +( `ckey` VARCHAR(32) NOT NULL , + `job` VARCHAR(32) NOT NULL , + `minutes` INT UNSIGNED NOT NULL, + PRIMARY KEY (`ckey`, `job`) + ) ENGINE = InnoDB; + -- -- Table structure for table `player` -- @@ -267,11 +287,14 @@ DROP TABLE IF EXISTS `player`; CREATE TABLE `player` ( `ckey` varchar(32) NOT NULL, `firstseen` datetime NOT NULL, + `firstseen_round_id` int(11) unsigned NOT NULL, `lastseen` datetime NOT NULL, + `lastseen_round_id` int(11) unsigned NOT NULL, `ip` int(10) unsigned NOT NULL, `computerid` varchar(32) NOT NULL, `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', `accountjoindate` DATE DEFAULT NULL, + `flags` smallint(5) unsigned DEFAULT '0' NOT NULL, PRIMARY KEY (`ckey`), KEY `idx_player_cid_ckey` (`computerid`,`ckey`), KEY `idx_player_ip_ckey` (`ip`,`ckey`) @@ -289,12 +312,12 @@ CREATE TABLE `poll_option` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pollid` int(11) NOT NULL, `text` varchar(255) NOT NULL, - `percentagecalc` tinyint(1) NOT NULL DEFAULT '1', `minval` int(3) DEFAULT NULL, `maxval` int(3) DEFAULT NULL, `descmin` varchar(32) DEFAULT NULL, `descmid` varchar(32) DEFAULT NULL, `descmax` varchar(32) DEFAULT NULL, + `default_percentage_calc` tinyint(1) unsigned NOT NULL DEFAULT '1', PRIMARY KEY (`id`), KEY `idx_pop_pollid` (`pollid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; @@ -391,10 +414,21 @@ CREATE TABLE `round` ( /*!40101 SET character_set_client = @saved_cs_client */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; +-- +-- Table structure for table `schema_revision` +-- +DROP TABLE IF EXISTS `schema_revision`; +CREATE TABLE `schema_revision` ( + `major` TINYINT(3) unsigned NOT NULL, + `minor` TINYINT(3) unsigned NOT NULL, + `date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`major`, `minor`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!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 b/SQL/tgstation_schema_prefixed.sql index f717bf96ad..72045e50fb 100644 --- a/SQL/tgstation_schema_prefixed.sql +++ b/SQL/tgstation_schema_prefixed.sql @@ -1,6 +1,3 @@ -CREATE DATABASE IF NOT EXISTS `feedback` /*!40100 DEFAULT CHARACTER SET latin1 */; -USE `feedback`; - /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; @@ -34,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` ( @@ -74,6 +71,7 @@ CREATE TABLE `SS13_ban` ( `bantime` datetime NOT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) NOT NULL, `bantype` enum('PERMABAN','TEMPBAN','JOB_PERMABAN','JOB_TEMPBAN','ADMIN_PERMABAN','ADMIN_TEMPBAN') NOT NULL, `reason` varchar(2048) NOT NULL, `job` varchar(32) DEFAULT NULL, @@ -112,6 +110,7 @@ CREATE TABLE `SS13_connection_log` ( `datetime` datetime DEFAULT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) unsigned NOT NULL, `ckey` varchar(45) DEFAULT NULL, `ip` int(10) unsigned NOT NULL, `computerid` varchar(45) DEFAULT NULL, @@ -129,10 +128,13 @@ DROP TABLE IF EXISTS `SS13_death`; CREATE TABLE `SS13_death` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pod` varchar(50) NOT NULL, - `coord` varchar(32) NOT NULL, + `x_coord` smallint(5) unsigned NOT NULL, + `y_coord` smallint(5) unsigned NOT NULL, + `z_coord` smallint(5) unsigned NOT NULL, `mapname` varchar(32) NOT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) NOT NULL, `tod` datetime NOT NULL COMMENT 'Time of death', `job` varchar(32) NOT NULL, `special` varchar(32) DEFAULT NULL, @@ -140,7 +142,6 @@ CREATE TABLE `SS13_death` ( `byondkey` varchar(32) NOT NULL, `laname` varchar(96) DEFAULT NULL, `lakey` varchar(32) DEFAULT NULL, - `gender` enum('neuter','male','female','plural') NOT NULL, `bruteloss` smallint(5) unsigned NOT NULL, `brainloss` smallint(5) unsigned NOT NULL, `fireloss` smallint(5) unsigned NOT NULL, @@ -148,6 +149,8 @@ CREATE TABLE `SS13_death` ( `toxloss` smallint(5) unsigned NOT NULL, `cloneloss` smallint(5) unsigned NOT NULL, `staminaloss` smallint(5) unsigned NOT NULL, + `last_words` varchar(255) DEFAULT NULL, + `suicide` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -200,6 +203,7 @@ CREATE TABLE `SS13_legacy_population` ( `time` datetime NOT NULL, `server_ip` int(10) unsigned NOT NULL, `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; @@ -220,6 +224,7 @@ CREATE TABLE `SS13_library` ( `ckey` varchar(32) NOT NULL DEFAULT 'LEGACY', `datetime` datetime NOT NULL, `deleted` tinyint(1) unsigned DEFAULT NULL, + `round_id_created` int(11) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `deleted_idx` (`deleted`), KEY `idx_lib_id_del` (`id`,`deleted`), @@ -243,16 +248,35 @@ CREATE TABLE `SS13_messages` ( `text` varchar(2048) NOT NULL, `timestamp` datetime NOT NULL, `server` varchar(32) DEFAULT NULL, + `server_ip` int(10) unsigned NOT NULL, + `server_port` smallint(5) unsigned NOT NULL, + `round_id` int(11) unsigned NOT NULL, `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 */; +-- +-- Table structure for table `SS13_role_time` +-- + +DROP TABLE IF EXISTS `SS13_role_time`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; + +CREATE TABLE `SS13_role_time` +( `ckey` VARCHAR(32) NOT NULL , + `job` VARCHAR(32) NOT NULL , + `minutes` INT UNSIGNED NOT NULL, + PRIMARY KEY (`ckey`, `job`) + ) ENGINE = InnoDB; + -- -- Table structure for table `SS13_player` -- @@ -263,11 +287,14 @@ DROP TABLE IF EXISTS `SS13_player`; CREATE TABLE `SS13_player` ( `ckey` varchar(32) NOT NULL, `firstseen` datetime NOT NULL, + `firstseen_round_id` int(11) unsigned NOT NULL, `lastseen` datetime NOT NULL, + `lastseen_round_id` int(11) unsigned NOT NULL, `ip` int(10) unsigned NOT NULL, `computerid` varchar(32) NOT NULL, `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', `accountjoindate` DATE DEFAULT NULL, + `flags` smallint(5) unsigned DEFAULT '0' NOT NULL, PRIMARY KEY (`ckey`), KEY `idx_player_cid_ckey` (`computerid`,`ckey`), KEY `idx_player_ip_ckey` (`ip`,`ckey`) @@ -285,12 +312,12 @@ CREATE TABLE `SS13_poll_option` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pollid` int(11) NOT NULL, `text` varchar(255) NOT NULL, - `percentagecalc` tinyint(1) NOT NULL DEFAULT '1', `minval` int(3) DEFAULT NULL, `maxval` int(3) DEFAULT NULL, `descmin` varchar(32) DEFAULT NULL, `descmid` varchar(32) DEFAULT NULL, `descmax` varchar(32) DEFAULT NULL, + `default_percentage_calc` tinyint(1) unsigned NOT NULL DEFAULT '1', PRIMARY KEY (`id`), KEY `idx_pop_pollid` (`pollid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; @@ -387,10 +414,21 @@ CREATE TABLE `SS13_round` ( /*!40101 SET character_set_client = @saved_cs_client */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; +-- +-- Table structure for table `SS13_schema_revision` +-- +DROP TABLE IF EXISTS `SS13_schema_revision`; +CREATE TABLE `SS13_schema_revision` ( + `major` TINYINT(3) unsigned NOT NULL, + `minor` TINYINT(3) unsigned NOT NULL, + `date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`major`,`minor`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!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/_maps/RandomRuins/LavaRuins/lavaland_biodome_clown_planet.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_clown_planet.dmm index 1008d51d11..0324d65554 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_clown_planet.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_clown_planet.dmm @@ -378,9 +378,7 @@ invisibility = 101 }, /obj/structure/table, -/obj/item/paper/crumpled/bloody{ - info = "If you dare not continue down this path of madness, escape can be found through the chute in this room. " - }, +/obj/item/paper/crumpled/bloody/ruins/lavaland/clown_planet/escape, /obj/item/pen/fourcolor, /turf/open/indestructible{ icon_state = "white" @@ -925,9 +923,7 @@ /turf/open/lava/smooth, /area/ruin/powered/clownplanet) "CB" = ( -/obj/item/paper/crumpled/bloody{ - info = "Abandon hope, all ye who enter here." - }, +/obj/item/paper/crumpled/bloody/ruins/lavaland/clown_planet/hope, /obj/effect/decal/cleanable/blood/old, /turf/open/floor/noslip{ initial_gas_mix = "o2=14;n2=23;TEMP=300" diff --git a/_maps/RandomRuins/SpaceRuins/TheDerelict.dmm b/_maps/RandomRuins/SpaceRuins/TheDerelict.dmm index 49da58eace..f9144cd3ef 100644 --- a/_maps/RandomRuins/SpaceRuins/TheDerelict.dmm +++ b/_maps/RandomRuins/SpaceRuins/TheDerelict.dmm @@ -3717,11 +3717,7 @@ /obj/item/reagent_containers/glass/beaker{ list_reagents = list("sacid" = 50) }, -/obj/item/paper/crumpled/bloody{ - desc = "Looks like someone started shakily writing a will in space common, but were interrupted by something bloody..."; - info = "I, Victor Belyakov, do hereby leave my _- "; - name = "unfinished paper scrap" - }, +/obj/item/paper/crumpled/bloody/ruins/thederelict/unfinished, /obj/item/pen, /turf/open/floor/plasteel/airless, /area/ruin/space/derelict/hallway/primary) diff --git a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm index 21755f1e2f..e44e4a9d2d 100644 --- a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm +++ b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm @@ -258,9 +258,7 @@ name = "critter crate - mr.tiggles"; opened = 1 }, -/obj/item/paper/crumpled/ruins/snowdin{ - info = "A crumpled piece of manifest paper, out of the barely legible pen writing, you can see something about a warning involving whatever was originally in the crate." - }, +/obj/item/paper/crumpled/ruins/bigderelict1/manifest, /obj/structure/alien/weeds{ color = "#4BAE56"; desc = "A thick gelatinous surface covers the floor. Someone get the golashes."; @@ -454,10 +452,7 @@ name = "Tradeport Officer"; random = 1 }, -/obj/item/paper/crumpled/ruins/snowdin{ - icon_state = "scrap_bloodied"; - info = "If anyone finds this, please, don't let my kids know I died a coward.." - }, +/obj/item/paper/crumpled/ruins/bigderelict1/coward, /obj/effect/decal/cleanable/blood/old{ name = "dried blood splatter"; pixel_x = -29 diff --git a/_maps/RandomRuins/SpaceRuins/gondolaasteroid.dmm b/_maps/RandomRuins/SpaceRuins/gondolaasteroid.dmm new file mode 100644 index 0000000000..b7a5910b1e --- /dev/null +++ b/_maps/RandomRuins/SpaceRuins/gondolaasteroid.dmm @@ -0,0 +1,1382 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/template_noop, +/area/template_noop) +"b" = ( +/turf/closed/mineral/random, +/area/ruin/space/has_grav) +"c" = ( +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"d" = ( +/obj/structure/marker_beacon{ + light_color = "#FFE8AA"; + light_range = 20 + }, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"e" = ( +/obj/structure/flora/ausbushes/fullgrass, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"f" = ( +/obj/structure/flora/ausbushes/ywflowers, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"g" = ( +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav{ + dynamic_lighting = 1 + }) +"h" = ( +/mob/living/simple_animal/pet/gondola, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"i" = ( +/obj/structure/flora/ausbushes/sparsegrass, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"j" = ( +/obj/effect/overlay/coconut, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"k" = ( +/obj/effect/overlay/palmtree_l, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"l" = ( +/obj/structure/flora/ausbushes/stalkybush, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"m" = ( +/obj/structure/flora/ausbushes/grassybush, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"n" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"o" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"p" = ( +/obj/structure/flora/ausbushes/brflowers, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"q" = ( +/obj/structure/flora/ausbushes/fernybush, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"r" = ( +/obj/effect/overlay/palmtree_r, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"s" = ( +/obj/structure/flora/junglebush/large, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"t" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) +"u" = ( +/obj/machinery/door/airlock/survival_pod/vertical, +/turf/open/floor/plating/asteroid/airless, +/area/ruin/space/has_grav) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +b +b +b +b +b +b +a +a +a +"} +(2,1,1) = {" +a +a +a +a +b +b +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +b +b +b +b +b +b +b +b +b +a +a +"} +(3,1,1) = {" +a +a +b +b +b +b +b +a +a +a +a +a +a +a +a +a +a +a +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +c +"} +(4,1,1) = {" +a +b +b +b +b +a +a +a +a +a +a +a +a +a +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +c +b +b +b +b +b +"} +(5,1,1) = {" +a +b +b +b +a +a +a +a +a +a +a +a +a +a +b +b +b +b +b +b +b +b +b +b +b +c +c +c +c +c +c +c +b +b +b +"} +(6,1,1) = {" +a +a +a +a +a +a +a +a +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +c +c +c +c +f +c +c +c +h +c +b +b +"} +(7,1,1) = {" +a +a +a +a +a +a +a +a +b +b +b +b +b +b +b +b +b +b +b +b +b +c +c +c +c +o +c +r +c +b +b +b +b +b +c +"} +(8,1,1) = {" +a +a +a +a +a +a +b +b +b +b +b +b +b +b +b +b +c +b +c +c +c +c +c +c +c +c +j +c +c +c +c +b +b +b +c +"} +(9,1,1) = {" +a +a +a +a +a +b +b +b +b +b +b +b +b +c +k +c +c +q +c +c +j +c +c +k +c +c +c +c +m +c +c +b +b +b +c +"} +(10,1,1) = {" +a +a +a +b +b +b +b +b +b +b +b +b +b +b +b +c +c +c +c +c +s +c +c +c +c +c +c +i +c +c +c +b +b +b +c +"} +(11,1,1) = {" +a +a +b +b +b +b +b +b +b +b +b +b +b +b +c +i +n +f +c +c +d +c +c +j +c +h +c +l +c +d +c +b +b +b +c +"} +(12,1,1) = {" +a +a +b +b +b +b +b +b +b +b +b +b +c +c +c +c +o +o +c +h +c +c +c +c +c +c +c +i +o +c +c +b +b +b +c +"} +(13,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +c +c +c +c +i +c +q +c +c +c +c +c +c +s +c +c +c +c +b +b +b +b +c +"} +(14,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +h +c +c +c +c +l +c +c +c +m +i +c +c +c +c +c +c +c +b +b +b +b +b +c +"} +(15,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +c +c +c +c +c +i +o +c +c +c +c +c +c +c +c +b +b +b +b +c +"} +(16,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +c +c +c +o +p +c +c +c +c +r +c +c +c +c +b +b +b +c +"} +(17,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +c +e +n +c +c +c +c +c +c +c +c +c +c +c +b +b +c +"} +(18,1,1) = {" +a +b +b +b +b +b +b +b +b +b +c +e +c +b +b +b +b +b +c +c +c +c +c +c +i +t +c +c +c +i +c +c +b +b +b +"} +(19,1,1) = {" +a +b +b +b +b +b +b +b +b +g +c +c +c +c +c +b +b +b +b +c +r +j +c +c +c +f +c +c +c +c +c +c +u +c +u +"} +(20,1,1) = {" +a +c +b +b +b +b +b +b +b +h +c +c +d +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +b +b +b +b +"} +(21,1,1) = {" +a +c +c +b +b +b +b +b +b +c +c +c +k +c +i +i +c +c +q +c +c +c +c +c +d +c +h +c +c +c +c +c +b +b +b +"} +(22,1,1) = {" +a +c +c +b +b +b +b +b +b +c +c +c +c +c +c +l +c +c +c +c +c +c +c +c +c +c +c +c +c +r +c +c +b +b +b +"} +(23,1,1) = {" +a +c +c +b +b +b +b +b +b +c +c +j +c +c +c +c +c +c +c +c +c +h +s +c +c +c +s +c +c +c +c +c +b +b +b +"} +(24,1,1) = {" +a +a +c +c +b +b +b +b +e +c +c +c +c +c +h +c +c +c +c +c +c +c +c +c +m +c +c +c +c +c +c +c +b +b +b +"} +(25,1,1) = {" +a +a +c +c +b +b +b +c +c +c +c +c +c +c +c +c +i +l +c +c +c +c +c +c +n +m +c +c +c +c +c +b +b +b +a +"} +(26,1,1) = {" +a +a +c +c +b +b +b +c +c +c +c +c +c +c +c +m +p +i +c +f +c +c +c +c +c +c +k +c +c +j +c +b +b +b +a +"} +(27,1,1) = {" +a +a +c +c +b +b +b +b +c +c +c +c +c +c +c +c +c +c +c +d +m +i +c +c +c +c +c +c +c +c +b +b +b +a +a +"} +(28,1,1) = {" +a +a +a +c +c +b +b +b +c +c +c +c +c +e +c +c +c +c +c +c +i +l +p +c +c +c +c +c +c +b +b +b +b +a +a +"} +(29,1,1) = {" +a +a +a +c +c +b +b +c +c +c +e +c +c +c +c +c +c +c +c +c +c +c +c +c +c +i +c +c +c +b +b +b +c +c +a +"} +(30,1,1) = {" +a +a +a +c +b +b +c +d +f +c +i +c +c +c +b +c +c +c +c +c +j +c +c +c +c +c +c +c +b +b +b +c +c +c +a +"} +(31,1,1) = {" +a +a +a +b +b +b +b +c +c +c +c +c +c +b +b +b +c +c +c +r +c +c +c +c +c +c +b +b +b +b +c +c +c +b +b +"} +(32,1,1) = {" +a +a +a +b +b +b +c +c +c +c +c +c +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +c +c +b +b +b +"} +(33,1,1) = {" +a +a +a +b +b +c +c +c +c +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +a +b +b +b +a +"} +(34,1,1) = {" +a +a +a +b +b +b +b +b +b +b +b +b +b +b +c +c +c +c +c +b +b +b +b +b +a +a +a +a +a +a +a +b +b +b +a +"} +(35,1,1) = {" +a +a +a +a +b +b +b +b +b +b +b +b +b +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/RandomRuins/SpaceRuins/originalcontent.dmm b/_maps/RandomRuins/SpaceRuins/originalcontent.dmm index 73bf43342e..e55cee1581 100644 --- a/_maps/RandomRuins/SpaceRuins/originalcontent.dmm +++ b/_maps/RandomRuins/SpaceRuins/originalcontent.dmm @@ -541,12 +541,7 @@ /area/ruin/powered) "bB" = ( /obj/structure/easel, -/obj/item/paper/pamphlet{ - icon = 'icons/obj/fluff.dmi'; - icon_state = "painting4"; - info = "This picture depicts a crudely-drawn stickman firing a crudely-drawn gun."; - name = "Painting - 'BANG' " - }, +/obj/item/paper/pamphlet/ruin/originalcontent/stickman, /turf/open/indestructible/paper, /area/ruin/powered) "bC" = ( @@ -609,12 +604,7 @@ dir = 1 }, /obj/structure/easel, -/obj/item/paper/pamphlet{ - icon = 'icons/obj/fluff.dmi'; - icon_state = "painting1"; - info = "This picture depicts a sunny day on a lush hillside, set under a shaded tree."; - name = "Painting - 'Treeside' " - }, +/obj/item/paper/pamphlet/ruin/originalcontent/treeside, /turf/open/indestructible/paper, /area/ruin/powered) "bK" = ( @@ -716,12 +706,7 @@ dir = 8 }, /obj/structure/easel, -/obj/item/paper/pamphlet{ - icon = 'icons/obj/fluff.dmi'; - icon_state = "painting3"; - info = "This picture depicts a smiling clown. Something doesn't feel right about this.."; - name = "Painting - 'Pennywise' " - }, +/obj/item/paper/pamphlet/ruin/originalcontent/pennywise, /turf/open/indestructible/paper, /area/ruin/powered) "bX" = ( @@ -869,12 +854,7 @@ "cr" = ( /obj/structure/fluff/paper, /obj/structure/easel, -/obj/item/paper/pamphlet{ - icon = 'icons/obj/fluff.dmi'; - icon_state = "painting2"; - info = "This picture depicts a man yelling on a bridge for no apparent reason."; - name = "Painting - 'Hands-On-Face' " - }, +/obj/item/paper/pamphlet/ruin/originalcontent/yelling, /turf/open/indestructible/paper, /area/ruin/powered) "cs" = ( diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm index 22f0169437..2edb5dcc12 100644 --- a/_maps/RandomZLevels/moonoutpost19.dmm +++ b/_maps/RandomZLevels/moonoutpost19.dmm @@ -5156,22 +5156,10 @@ }) "gW" = ( /obj/structure/filingcabinet/filingcabinet, -/obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_hivemind{ - info = "Researcher: Dr. Mark Douglas
Date: 17/06/2554

Report:
Earlier today we have observed a new phenomenon with our subjects. While feeding them our last monkey subject and throwing out the box, the aliens merely looked at us instead of infecting the monkey right away. They looked to be collectively distressed as they would no longer be given hosts, where instead we would move to the next phase of the experiment. When I glanced at the gas tanks and piping leading to their cell, I looked back to see all of them were up against the glass, even the queen! It was as if they all understood what was going to happen, even though we knew only the queen had the cognitive capability to do so.

The only explanation for this is a form of communication between the aliens, but we have seen no such action take place anywhere in the cell until now. We also know that regular drone and hunter xenomorphs have no personality or instinct to survive by themselves. Perhaps the queen has a direct link to them? A form of a commander or overseer that controls their every move? A hivemind?"; - name = "The Hivemind Hypothesis" - }, -/obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_behavior{ - info = "Researcher: Dr. Sakuma Sano
Date: 08/06/2554

Report:
The xenomorphs we have come to study here are a remarkable species. They are almost universally aggressive across all castes, showing no remorse or guilt or pause before or after acts of violence. They appear to be a species entirely designed to kill. Oddly enough, even their method of reproduction is a brutal two-for-one method of birthing a new xenomorph and killing its host.

The lone xenomorph we studied only five days ago showed little sign of intelligence. Only a simple drone that flung itself at the safety glass and shields repeatedly and thankfully without success. Once the drone molted into a queen, it became much more calm and calculating, merely looking at us and waiting while building its nest. As the hive grew in size and in numbers, so too did the intelligence of the common hunter and drone. We are still researching how they can communicate with one another and the relationship between the different castes and the queen. We will continue to update our research as we learn more about the species."; - name = "A Preliminary Study of Alien Behavior" - }, -/obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_castes{ - info = "Researcher: Dr. Mark Douglas
Date: 06/06/2554

Report:
While observing the growing number of aliens in the containment cell, we began to notice subtle differences that were consistently repeating. Like ants, these creatures clearly have different specialized variations that determine their roles in the hive. We have dubbed the three currently observed castes as Hunters, Drones, and Sentinels.

Hunters have been observed to be by far the most aggressive and agile of the three, constantly running on every surface and frequently swiping at the windows. They are also remarkably good at camouflaging themselves in darkness and on their resin structures, appearing almost invisible to the unwary observer. They are always the first to reach the monkeys we send in leading us to believe that this caste is primarily used for finding and retrieving hosts.

Drones on the other hand are much more docile and seem more shy by comparison, though not any less aggressive than the other castes. They have been observed to have a much wider head and lack dorsal tubes. They have shown to be less agile and visibly more fragile than any other caste. The drone however has never been observed to interact with the monkeys directly and instead preferring maintenance of the hive by building walls of resin and moving eggs around the nest. As far as we know, we have only ever observed a drone become a queen, and we have no way of knowing if the other castes have that capability.

Lastly, we have the Sentinels, which appear at first glance to be the guards of the hive. They have so far been only observed to remain near the queen and the eggs, frequently curled up against the walls. We have only observed one instance where they have interacted with a monkey who strayed too closely to the queen, and was pounced and held down immediately until it was applied with a facehugger. Their lack of movement makes it difficult to determine their exact purpose as guards, sentries, or other role."; - name = "The Xenomorph 'Castes'" - }, -/obj/item/paper/fluff/awaymissions/moonoutpost19/research/larva_autopsy{ - info = "Researcher: Dr. Mark Douglas
Date: 04/06/2554

Report:
After an extremely dangerous, time consuming and costly dissection, we have managed to record and identify several of the organs inside of the first stage of the xenomorph cycle: the larva. This procedure took an extensive amount of time because these creatures have incredibly, almost-comically acidic blood that can melt through almost anything in a few moments. We had to use over a dozen scalpels and retractors to complete the autopsy.

The larva seems to possess far fewer and quite different organs than that of a human. There is a stomach, with no digestive tract, a heart, which seems to lack any blood-oxygen circulation purpose, and an elongated brain, even though its as dumb as any large cat. It also lacks any liver, kidneys, or other basic organs.

We can't determine the exact nature of how these creatures grow, nor if they gain organs as they become adults. The larger breeds of xenomorph are too dangerous to kill and capture to give us an accurate answer to these questions. All that we can conclude is that being able to function with so little and yet be so deadly means that these creatures are highly evolved and likely to be extremely durable to various hazards that would otherwise be lethal to humans."; - name = "Larva Xenomorph Autopsy Report" - }, +/obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_hivemind, +/obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_behavior, +/obj/item/paper/fluff/awaymissions/moonoutpost19/research/xeno_castes, +/obj/item/paper/fluff/awaymissions/moonoutpost19/research/larva_autopsy, /obj/structure/alien/weeds, /turf/open/floor/plasteel/white, /area/awaycontent/a2{ diff --git a/_maps/RandomZLevels/wildwest.dmm b/_maps/RandomZLevels/wildwest.dmm index 61d5c4ee49..237fa11a45 100644 --- a/_maps/RandomZLevels/wildwest.dmm +++ b/_maps/RandomZLevels/wildwest.dmm @@ -171,9 +171,7 @@ /turf/open/floor/engine/cult, /area/awaymission/wwvault) "aL" = ( -/obj/item/paper/fluff/awaymissions/wildwest/grinder{ - info = "meat grinder requires sacri" - }, +/obj/item/paper/fluff/awaymissions/wildwest/grinder, /turf/open/floor/plasteel/cult{ name = "engraved floor" }, diff --git a/_maps/basemap.dm b/_maps/basemap.dm index d11b527aa3..8a838879b8 100644 --- a/_maps/basemap.dm +++ b/_maps/basemap.dm @@ -9,10 +9,10 @@ #include "map_files\Deltastation\DeltaStation2.dmm" #include "map_files\MetaStation\MetaStation.dmm" #include "map_files\OmegaStation\OmegaStation.dmm" -#include "map_files\PubbyStation\PubbyStation.dmm" +// #include "map_files\PubbyStation\PubbyStation.dmm" #include "map_files\BoxStation\BoxStation.dmm" #ifdef TRAVISBUILDING #include "templates.dm" #endif -#endif \ No newline at end of file +#endif 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 9a3b2b9454..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 @@ -18893,7 +18893,6 @@ }, /obj/structure/cable{ icon_state = "0-2"; - pixel_y = 1; d2 = 2 }, /obj/effect/decal/cleanable/dirt, @@ -21313,14 +21312,14 @@ /obj/machinery/power/smes{ charge = 5e+006 }, -/obj/structure/cable{ - icon_state = "0-2"; - d2 = 2 - }, /obj/structure/sign/electricshock{ pixel_y = 32 }, /obj/effect/turf_decal/bot, +/obj/structure/cable{ + icon_state = "0-2"; + d2 = 2 + }, /turf/open/floor/plasteel, /area/engine/gravity_generator) "aJo" = ( @@ -21935,35 +21934,25 @@ /turf/open/floor/plasteel, /area/engine/gravity_generator) "aKu" = ( -/obj/structure/cable{ - d1 = 1; - d2 = 4; - icon_state = "1-4" - }, /obj/machinery/holopad, /obj/effect/decal/cleanable/dirt, -/obj/structure/cable{ - d1 = 2; - d2 = 4; - icon_state = "2-4" - }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 9 }, /obj/structure/cable{ - d1 = 4; + d1 = 2; d2 = 8; - icon_state = "4-8" + icon_state = "2-8" + }, +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2" }, /turf/open/floor/plasteel/neutral, /area/engine/gravity_generator) "aKv" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, /obj/machinery/atmospherics/components/unary/vent_pump/on, /obj/structure/cable/white{ d1 = 1; @@ -21977,17 +21966,17 @@ /area/engine/gravity_generator) "aKw" = ( /obj/effect/decal/cleanable/dirt, -/obj/structure/cable{ - d1 = 1; - d2 = 8; - icon_state = "1-8" - }, /obj/structure/cable/white{ d1 = 4; d2 = 8; icon_state = "4-8" }, /obj/effect/turf_decal/delivery, +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, /turf/open/floor/plasteel, /area/engine/gravity_generator) "aKx" = ( @@ -22671,6 +22660,11 @@ icon_state = "1-2" }, /obj/effect/turf_decal/stripes/line, +/obj/structure/cable{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, /turf/open/floor/plasteel, /area/engine/gravity_generator) "aLJ" = ( @@ -22678,6 +22672,11 @@ /obj/effect/turf_decal/stripes/line{ dir = 6 }, +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, /turf/open/floor/plasteel, /area/engine/gravity_generator) "aLK" = ( @@ -22693,6 +22692,11 @@ dir = 4 }, /obj/effect/turf_decal/bot, +/obj/structure/cable{ + d1 = 1; + d2 = 8; + icon_state = "1-8" + }, /turf/open/floor/plasteel, /area/engine/gravity_generator) "aLL" = ( diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index 7f55c286c7..66a9216882 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -3595,8 +3595,8 @@ /area/security/armory) "aij" = ( /obj/structure/closet/secure_closet/contraband/armory, -/obj/item/book/codex_gigas, /obj/item/poster/random_contraband, +/obj/item/clothing/suit/security/officer/russian, /obj/item/grenade/smokebomb, /turf/open/floor/plasteel/black, /area/security/armory) @@ -4110,7 +4110,11 @@ /area/crew_quarters/heads/hos) "ajt" = ( /obj/structure/table, -/obj/machinery/chem_dispenser/drinks/beer, +/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass{ + pixel_x = 4; + pixel_y = 7 + }, +/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass, /turf/open/floor/plating, /area/maintenance/department/crew_quarters/dorms) "aju" = ( @@ -4445,10 +4449,7 @@ /turf/open/floor/plasteel/black, /area/crew_quarters/heads/hos) "akf" = ( -/obj/structure/extinguisher_cabinet{ - pixel_x = -24 - }, -/turf/open/floor/plating, +/turf/open/floor/carpet, /area/maintenance/department/crew_quarters/dorms) "akg" = ( /obj/structure/mineral_door/wood, @@ -4512,12 +4513,6 @@ /obj/machinery/door/airlock/glass{ name = "space-bridge access" }, -/obj/machinery/button/door{ - id = "supplybridge"; - name = "Space Bridge Control"; - pixel_y = 27; - req_access_txt = "0" - }, /obj/effect/turf_decal/stripes/line{ dir = 4 }, @@ -4564,12 +4559,6 @@ /obj/machinery/door/airlock/glass{ name = "space-bridge access" }, -/obj/machinery/button/door{ - id = "supplybridge"; - name = "Space Bridge Control"; - pixel_y = 27; - req_access_txt = "0" - }, /obj/structure/cable{ d1 = 4; d2 = 8; @@ -4923,12 +4912,7 @@ /turf/open/space/basic, /area/space) "alb" = ( -/obj/structure/table, -/obj/item/lighter, -/obj/machinery/light/small{ - dir = 8 - }, -/turf/open/floor/plating, +/turf/open/floor/wood, /area/maintenance/department/crew_quarters/dorms) "alc" = ( /obj/structure/table, @@ -7456,9 +7440,7 @@ /area/security/brig) "aqB" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on, -/turf/open/floor/plasteel/darkred/side{ - dir = 1 - }, +/turf/open/floor/plasteel/black, /area/security/brig) "aqC" = ( /turf/open/floor/plasteel/black, @@ -7467,9 +7449,7 @@ /obj/machinery/ai_status_display{ pixel_y = 32 }, -/turf/open/floor/plasteel/darkred/side{ - dir = 1 - }, +/turf/open/floor/plasteel/black, /area/security/brig) "aqE" = ( /obj/structure/cable{ @@ -8976,13 +8956,13 @@ /area/security/brig) "atI" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plasteel/darkred/side, +/turf/open/floor/plasteel/black, /area/security/brig) "atJ" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 1 }, -/turf/open/floor/plasteel/darkred/side, +/turf/open/floor/plasteel/black, /area/security/brig) "atK" = ( /obj/structure/cable{ @@ -13937,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 @@ -20047,6 +20027,7 @@ /obj/structure/chair/wood/normal{ dir = 8 }, +/obj/item/clothing/under/janimaid, /turf/open/floor/wood{ icon_state = "wood-broken" }, @@ -20402,9 +20383,9 @@ /obj/structure/table, /obj/item/book/manual/hydroponics_pod_people, /obj/item/paper/guides/jobs/hydroponics, -/obj/item/reagent_containers/dropper, /obj/item/reagent_containers/glass/bottle/mutagen, /obj/item/reagent_containers/dropper, +/obj/item/reagent_containers/dropper, /turf/open/floor/plasteel/hydrofloor, /area/hydroponics) "aSH" = ( @@ -22960,6 +22941,10 @@ /obj/structure/sign/botany{ pixel_y = 32 }, +/obj/machinery/light{ + dir = 1; + light_color = "#e8eaff" + }, /turf/open/floor/plasteel/green/side{ dir = 1 }, @@ -23326,8 +23311,9 @@ }, /area/hydroponics) "aYQ" = ( -/obj/machinery/light{ - dir = 4 +/obj/structure/sink{ + dir = 4; + pixel_x = 11 }, /turf/open/floor/plasteel, /area/hydroponics) @@ -29196,14 +29182,16 @@ /turf/open/floor/plating, /area/maintenance/department/engine) "bmg" = ( -/obj/machinery/door/airlock/maintenance{ - req_access_txt = "12" - }, /obj/structure/cable{ d1 = 4; d2 = 8; icon_state = "4-8" }, +/obj/machinery/door/airlock/maintenance{ + name = "Genetics Maintenance"; + req_access_txt = "0"; + req_one_access_txt = "12;45;5;9" + }, /turf/open/floor/plating, /area/maintenance/department/engine) "bmh" = ( @@ -39848,8 +39836,8 @@ }, /area/medical/virology) "bHZ" = ( -/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 }, /turf/open/floor/plasteel/whitegreen/corner{ dir = 1 @@ -40425,10 +40413,12 @@ /turf/open/floor/plasteel/white, /area/medical/virology) "bJn" = ( -/obj/machinery/atmospherics/pipe/simple/cyan/hidden, /obj/machinery/light{ dir = 4 }, +/obj/machinery/atmospherics/pipe/manifold/cyan/hidden{ + dir = 4 + }, /turf/open/floor/plasteel/white, /area/medical/virology) "bJo" = ( @@ -40927,15 +40917,10 @@ /turf/open/floor/plasteel/white, /area/medical/virology) "bKs" = ( -/obj/machinery/atmospherics/pipe/manifold/cyan/hidden{ - dir = 8 - }, +/obj/machinery/atmospherics/pipe/simple/cyan/hidden, /turf/open/floor/plasteel/white, /area/medical/virology) "bKt" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on{ - dir = 8 - }, /obj/machinery/computer/pandemic, /turf/open/floor/plasteel/white, /area/medical/virology) @@ -41475,7 +41460,7 @@ layer = 3.1 }, /obj/item/stack/sheet/mineral/plasma{ - amount = 1; + amount = 2; layer = 3 }, /turf/open/floor/plasteel/whitegreen/side{ @@ -41950,7 +41935,10 @@ }, /area/medical/virology) "bMK" = ( -/obj/machinery/vending/cigarette, +/obj/item/soap/nanotrasen, +/obj/item/clothing/neck/stethoscope, +/obj/item/gun/syringe, +/obj/structure/table/glass, /turf/open/floor/plasteel/whiteblue/side{ dir = 10 }, @@ -50077,7 +50065,9 @@ /area/chapel/office) "ceF" = ( /obj/structure/filingcabinet, -/turf/open/floor/plasteel/black, +/turf/open/floor/plasteel/vault{ + dir = 8 + }, /area/chapel/office) "ceG" = ( /obj/machinery/suit_storage_unit/standard_unit, @@ -53368,7 +53358,7 @@ /obj/machinery/light{ dir = 8 }, -/turf/open/floor/plasteel/darkred/side/telecomms, +/turf/open/floor/plasteel/black/telecomms, /area/tcommsat/server) "cmS" = ( /turf/open/floor/plasteel/whitepurple/side/telecomms, @@ -53380,7 +53370,7 @@ /obj/machinery/light{ dir = 4 }, -/turf/open/floor/plasteel/whitepurple/side/telecomms, +/turf/open/floor/plasteel/black/telecomms, /area/tcommsat/server) "cmV" = ( /obj/machinery/telecomms/server/presets/security, @@ -54317,6 +54307,7 @@ /area/crew_quarters/kitchen) "cpA" = ( /obj/structure/rack, +/obj/item/reagent_containers/food/snacks/mint, /obj/item/storage/box/drinkingglasses, /turf/open/floor/plasteel/cafeteria, /area/crew_quarters/kitchen) @@ -55197,17 +55188,6 @@ /turf/open/floor/plating/airless, /area/maintenance/department/engine) "csn" = ( -/obj/structure/closet{ - name = "chaplain closet" - }, -/obj/item/clothing/under/rank/chaplain, -/obj/item/clothing/shoes/sneakers/black, -/obj/item/storage/backpack/cultpack, -/obj/item/storage/fancy/candle_box, -/obj/item/storage/fancy/candle_box, -/obj/item/clothing/suit/nun, -/obj/item/clothing/head/nun_hood, -/obj/item/clothing/suit/holidaypriest, /obj/item/device/radio/intercom{ dir = 0; name = "Station Intercom (General)"; @@ -55218,7 +55198,10 @@ departmentType = 2; pixel_x = -32 }, -/turf/open/floor/plasteel/black, +/obj/structure/closet/wardrobe/chaplain_black, +/turf/open/floor/plasteel/vault{ + dir = 8 + }, /area/chapel/office) "cso" = ( /obj/structure/table/wood, @@ -55513,7 +55496,9 @@ /obj/machinery/newscaster{ pixel_x = -32 }, -/turf/open/floor/plasteel/black, +/turf/open/floor/plasteel/vault{ + dir = 8 + }, /area/chapel/office) "csZ" = ( /obj/structure/chair/wood/normal{ @@ -56007,7 +55992,8 @@ /obj/structure/extinguisher_cabinet{ pixel_x = 24 }, -/obj/item/storage/bag/plants, +/obj/item/storage/bag/plants/portaseeder, +/obj/item/storage/bag/plants/portaseeder, /turf/open/floor/plasteel/hydrofloor, /area/chapel/main/monastery) "cuv" = ( @@ -58192,6 +58178,206 @@ /obj/structure/ore_box, /turf/open/floor/plating, /area/maintenance/department/engine) +"cBk" = ( +/obj/machinery/vending/boozeomat{ + products = list(/obj/item/reagent_containers/food/drinks/bottle/whiskey = 1, /obj/item/reagent_containers/food/drinks/bottle/absinthe = 1, /obj/item/reagent_containers/food/drinks/bottle/limejuice = 1, /obj/item/reagent_containers/food/drinks/bottle/cream = 1, /obj/item/reagent_containers/food/drinks/soda_cans/tonic = 1, /obj/item/reagent_containers/food/drinks/drinkingglass = 10, /obj/item/reagent_containers/food/drinks/ice = 3, /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass = 6, /obj/item/reagent_containers/food/drinks/flask = 1); + req_access_txt = "0" + }, +/turf/closed/wall, +/area/maintenance/department/crew_quarters/dorms) +"cBl" = ( +/obj/structure/table, +/obj/machinery/chem_dispenser/drinks/beer, +/obj/structure/sign/poster/contraband/random{ + pixel_x = 32 + }, +/turf/open/floor/plating, +/area/maintenance/department/crew_quarters/dorms) +"cBm" = ( +/obj/item/cigbutt/cigarbutt, +/turf/open/floor/plating{ + burnt = 1; + icon_state = "panelscorched" + }, +/area/maintenance/department/crew_quarters/dorms) +"cBn" = ( +/obj/structure/extinguisher_cabinet{ + pixel_x = 24 + }, +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/maintenance/department/crew_quarters/dorms) +"cBo" = ( +/obj/structure/table, +/obj/item/lighter, +/obj/structure/light_construct/small{ + tag = "icon-bulb-construct-stage1 (WEST)"; + icon_state = "bulb-construct-stage1"; + dir = 8 + }, +/turf/open/floor/plating, +/area/maintenance/department/crew_quarters/dorms) +"cBp" = ( +/obj/structure/table, +/obj/item/reagent_containers/food/drinks/bottle/gin{ + pixel_y = 8 + }, +/obj/machinery/light/small{ + dir = 4 + }, +/turf/open/floor/plating, +/area/maintenance/department/crew_quarters/dorms) +"cBq" = ( +/obj/effect/landmark/xeno_spawn, +/turf/open/floor/plating, +/area/maintenance/department/crew_quarters/dorms) +"cBr" = ( +/obj/structure/chair/comfy, +/turf/open/floor/carpet, +/area/maintenance/department/crew_quarters/dorms) +"cBs" = ( +/obj/structure/table/wood/poker, +/obj/item/toy/cards/deck, +/obj/machinery/light/small{ + dir = 8 + }, +/turf/open/floor/carpet, +/area/maintenance/department/crew_quarters/dorms) +"cBt" = ( +/turf/open/floor/wood, +/area/maintenance/department/crew_quarters/dorms) +"cBu" = ( +/obj/structure/chair/comfy{ + dir = 1 + }, +/turf/open/floor/carpet, +/area/maintenance/department/crew_quarters/dorms) +"cBv" = ( +/obj/item/cigbutt/roach, +/turf/open/floor/carpet, +/area/maintenance/department/crew_quarters/dorms) +"cBw" = ( +/turf/open/floor/wood{ + icon_state = "wood-broken7" + }, +/area/maintenance/department/crew_quarters/dorms) +"cBx" = ( +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/button/door{ + id = "supplybridge"; + name = "Space Bridge Control"; + pixel_y = 27; + req_access_txt = "0" + }, +/turf/open/floor/plating, +/area/maintenance/department/crew_quarters/dorms) +"cBy" = ( +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/button/door{ + id = "supplybridge"; + name = "Space Bridge Control"; + pixel_y = 27; + req_access_txt = "0" + }, +/turf/open/floor/plating, +/area/maintenance/department/crew_quarters/dorms) +"cBz" = ( +/turf/open/floor/plating{ + broken = 1; + icon_state = "platingdmg1" + }, +/area/maintenance/department/crew_quarters/dorms) +"cBA" = ( +/obj/structure/grille/broken, +/turf/open/floor/plating{ + burnt = 1; + icon_state = "panelscorched" + }, +/area/maintenance/department/crew_quarters/dorms) +"cBB" = ( +/obj/machinery/light/small{ + brightness = 3; + dir = 8 + }, +/turf/open/floor/plating{ + broken = 1; + icon_state = "platingdmg3" + }, +/area/maintenance/department/crew_quarters/dorms) +"cBC" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/shuttle/escape) +"cBD" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/shuttle/escape) +"cBE" = ( +/turf/closed/wall, +/area/shuttle/escape) +"cBF" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/space) +"cBG" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/space) +"cBH" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/shuttle/escape) +"cBI" = ( +/obj/effect/spawner/structure/window/reinforced, +/turf/open/floor/plating, +/area/shuttle/escape) +"cBJ" = ( +/turf/closed/wall, +/area/shuttle/escape) +"cBK" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 4 + }, +/turf/open/floor/plasteel/white, +/area/medical/virology) +"cBL" = ( +/obj/machinery/door/airlock/maintenance{ + name = "Medbay Maintenance"; + req_access_txt = "0"; + req_one_access_txt = "12;45;5;9" + }, +/turf/open/floor/plating, +/area/maintenance/department/engine) +"cBM" = ( +/obj/structure/sign/securearea{ + desc = "Under the painting a plaque reads: 'While the meat grinder may not have spared you, fear not. Not one part of you has gone to waste... You were delicious.'"; + icon_state = "monkey_painting"; + name = "Mr. Deempisi portrait"; + pixel_y = 28 + }, +/turf/open/floor/plasteel/black, +/area/chapel/office) (1,1,1) = {" aaa @@ -69669,7 +69855,7 @@ bZY bZY bZY bZY -crv +cBM csd cdo csB @@ -71922,11 +72108,11 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa +bVq +cBF +bVq +cBG +bVq aaa aaa aaa @@ -72179,11 +72365,11 @@ aFT aFT aFS aKs -aFS -aKs -aFS -aFS -aFS +cBC +aOc +aPj +aOc +cBH aaa aaa aaa @@ -72436,11 +72622,11 @@ aNZ aNZ aNZ aMI -aNZ -aMI -aNZ -aVJ -aWD +cBD +aOd +aPk +aQq +cBI aaa aaa aaa @@ -72693,11 +72879,11 @@ aIx aIx aIx aIx -aIx -aIx -aIx -aVJ -aWD +cBE +aOc +aPl +aOc +cBJ aaa aaa aaa @@ -83010,8 +83196,8 @@ bDm bvc bFK bGQ -bHW -bGW +bHX +bJm bKn bKn bKn @@ -83782,7 +83968,7 @@ bEs bEr bGS bHZ -bJm +bGW bKq bKn bKn @@ -84039,7 +84225,7 @@ bEt bFM bGT bIa -bGW +cBK bKr bLC bMH @@ -84231,10 +84417,10 @@ anQ aiR aph ajM -coa +aqC arw asI -cof +aqC auH avI auH @@ -84491,7 +84677,7 @@ ajM aqD arx asJ -cof +aqC auH avJ awS @@ -84745,10 +84931,10 @@ anS aiR api ajM -coa +aqC ary asK -cof +aqC auH avK auH @@ -85002,7 +85188,7 @@ anT aiR aph ajM -coa +aqC arz asL atJ @@ -85383,22 +85569,22 @@ aaa aaa adR adR -clO adR adR adR adR -clO adR adR adR adR -clO adR adR adR adR -clO +adR +adR +adR +adR adR adR aaa @@ -85916,7 +86102,7 @@ cmB cmB cmB cny -clO +adR abI aaa aaa @@ -86167,7 +86353,7 @@ cmL cmR cmV cna -cnf +cmR cnj cmK cnr @@ -86421,10 +86607,10 @@ clG cmD cmJ cmM -cmS +cmK cmW cnb -cng +cmK cnk cmK cns @@ -86612,8 +86798,8 @@ bIh bkh bKz bnH -bML -bjc +boN +cBL bOG bPB bQq @@ -86944,7 +87130,7 @@ cmK cnu cmB abI -clO +adR abI aaa aaa @@ -87449,10 +87635,10 @@ clG cmG cmK cmP -cmS +cmK cmY cnd -cnh +cmK cnn cmK cnw @@ -87709,7 +87895,7 @@ cmQ cmU cmZ cne -cni +cmU cno cmK cnx @@ -87972,7 +88158,7 @@ cmB cmB cmB cnz -clO +adR abI aaa aaa @@ -88467,22 +88653,22 @@ aaa aaa adR adR -clO adR adR adR adR -clO adR adR adR adR -clO adR adR adR adR -clO +adR +adR +adR +adR adR adR aaa @@ -94498,11 +94684,11 @@ aaa aaa aaa aaa -aaa -aaa -aaa -aaa -aaa +aiS +aiT +aiS +aiT +aiS aaa aaa aaa @@ -94755,12 +94941,12 @@ aaa aaa aaa aaa -aiS -aiS aiT +cBr +cBs +cBu aiT -aiS -aiS +aaa aaa aaa aaa @@ -94774,13 +94960,13 @@ anX anX anX awd -awd +axi +aaa +aaa aaa aaa -aht aaa aaa -aht aaa aaa aaa @@ -95009,23 +95195,23 @@ aaa aaa aaa aaa -aaa -aaa -abI +aiS +aiS +aiS aiS akf -aju -alP -alP -aiS -aiS -aiT -aiS +akf +cBv aiS aaa aaa aaa aaa +aaa +aaa +aaa +aaa +aaa aht aaa aht @@ -95034,10 +95220,10 @@ aaa aht aaa aaa -aht aaa aaa -aht +aaa +aaa aaa aaa aaa @@ -95266,18 +95452,18 @@ aaa aaa aaa aaa -aaa -aaa +aiT +cBk +aju +cBo +alQ +cBt +cBw aiS aiS -akg +aiT aiS aiS -akg -aiS -anY -apt -aiS aiS aiS aiS @@ -95523,20 +95709,20 @@ aaa aaa aaa aaa -aaa -aaa -aiT -ajt +aiU +ajv aju -alb +ajt alQ -amE +alb +alb aiS -anZ -ajv +anY +apt aiS -ajv -ajv +cBz +alP +cBB apu ale ale @@ -95780,19 +95966,19 @@ aaa aaa aaa aaa -aaa -aaa -aiU +aiT aju akh alc alR amF +alb anm -aoa -ajv ajv ajv +aju +cBA +aiS aiS akn apX @@ -96037,14 +96223,14 @@ aaa aaa aaa aaa -aaa -aaa aiT -ajv -aki -ald +cBl +cBm +cBp alS amG +alb +aiS aiS aiS aiS @@ -96294,16 +96480,16 @@ aaa aaa aaa aaa -aaa -aaa -aiS -ajw aiS aiS +akg aiS aiS +akg +aiS aiS aob +ajv aoD ajv cnX @@ -96551,13 +96737,13 @@ aaa aaa aaa aaa -aaa -aaa -aiS -aiS +aht aiS +cBn +cBq ajv ajv +aiS akj ale ale @@ -96809,13 +96995,13 @@ aaa aaa aaa aaa -aaa -aaa -aaa +aiS aiS aiT +aiT aiS -akk +aiS +cBx aiS aiS aiS @@ -97071,9 +97257,9 @@ aaa aaa aaa aaa -aiS +aiT akl -aiS +aiT aaa aaa aaa @@ -98613,9 +98799,9 @@ aaa aaa aaa aaa -aiS +aiT akp -aiS +aiT aaa aaa aaa @@ -98870,8 +99056,8 @@ aaa aaa aaa aaa -aiT -akm +aiS +cBy aiS aiS aiS diff --git a/tools/toolsmerge-upstream-pull-request.sh b/citadel/tools/merge-upstream-pull-request.sh similarity index 66% rename from tools/toolsmerge-upstream-pull-request.sh rename to citadel/tools/merge-upstream-pull-request.sh index 5207606ea4..a034b5c450 100755 --- a/tools/toolsmerge-upstream-pull-request.sh +++ b/citadel/tools/merge-upstream-pull-request.sh @@ -1,4 +1,11 @@ #!/usr/bin/env bash +source ~/.discordauth + +# ~/.discordauth contains: +# CHANNELID=x +# TOKEN=x +# CHANNELID being the Discord Channel ID +# TOKEN being the bot token set -u # don't expand unbound variable set -f # disable pathname expansion @@ -25,11 +32,26 @@ type curl >/dev/null 2>&1 || { echo >&2 "Error: This script requires curl, pleas # Ensure jq exists and is available in the current context type jq >/dev/null 2>&1 || { echo >&2 "Error: This script requires jq, please ensure jq is installed and exists in the current PATH"; exit 1; } +containsElement () { + local e match="$1" + shift + for e; do [[ "$e" == "$match" ]] && return 0; done + return 1 +} + # Make sure we have our upstream remote if ! git remote | grep tgstation > /dev/null; then git remote add tgstation https://github.com/tgstation/tgstation.git fi +curl -v \ +-H "Authorization: Bot $TOKEN" \ +-H "User-Agent: myBotThing (http://some.url, v0.1)" \ +-H "Content-Type: application/json" \ +-X POST \ +-d "{\"content\":\"Mirroring [$1] from /tg/ to Citadel\"}" \ +https://discordapp.com/api/channels/$CHANNELID/messages + # We need to make sure we are always on a clean master when creating the new branch. # So we forcefully reset, clean and then checkout the master branch git fetch --all @@ -47,30 +69,42 @@ git checkout -b "$BASE_BRANCH_NAME$1" readonly MERGE_SHA=$(curl --silent "$BASE_PULL_URL/$1" | jq '.merge_commit_sha' -r) # Get the commits -readonly COMMITS=$(curl "$BASE_PULL_URL/$1/commits" | jq '.[].sha' -r) +readonly COMMITS=$(curl --silent "$BASE_PULL_URL/$1/commits" | jq '.[].sha' -r) # Cherry pick onto the new branch +echo "Cherry picking onto branch" CHERRY_PICK_OUTPUT=$(git cherry-pick -m 1 "$MERGE_SHA" 2>&1) echo "$CHERRY_PICK_OUTPUT" # If it's a squash commit, you can't use -m 1, you need to remove it # You also can't use -m 1 if it's a rebase and merge... -if echo "$CHERRY_PICK_OUTPUT" | grep 'error: mainline was specified but commit'; then +if echo "$CHERRY_PICK_OUTPUT" | grep -i 'error: mainline was specified but commit'; then echo "Commit was a squash, retrying" - for commit in $COMMITS; do - echo "Cherry-picking: $commit" - git cherry-pick "$commit" + if containsElement "$MERGE_SHA" "${COMMITS[@]}"; then + for commit in $COMMITS; do + echo "Cherry-picking: $commit" + git cherry-pick "$commit" + # Add all files onto this branch + git add -A . + git cherry-pick --continue + done + else + echo "Cherry-picking: $MERGE_SHA" + git cherry-pick "$MERGE_SHA" # Add all files onto this branch git add -A . git cherry-pick --continue - done + fi else # Add all files onto this branch + echo "Adding files to branch:" git add -A . fi # Commit these changes +echo "Commiting changes" git commit --allow-empty -m "$2" # Push them onto the branch +echo "Pushing changes" git push -u origin "$BASE_BRANCH_NAME$1" 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/combat.dm b/code/__DEFINES/combat.dm index c088e81c4a..3f45fb170b 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -49,6 +49,7 @@ //Health Defines #define HEALTH_THRESHOLD_CRIT 0 +#define HEALTH_THRESHOLD_FULLCRIT -30 #define HEALTH_THRESHOLD_DEAD -100 //Actual combat defines @@ -73,6 +74,10 @@ #define GRAB_NECK 2 #define GRAB_KILL 3 +//slowdown when in softcrit +#define SOFTCRIT_MIN_SLOWDOWN 8 +#define SOFTCRIT_ADD_SLOWDOWN 6 + //Attack types for checking shields/hit reactions #define MELEE_ATTACK 1 #define UNARMED_ATTACK 2 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/construction.dm b/code/__DEFINES/construction.dm index 46589408a2..ae3f9f6fe9 100644 --- a/code/__DEFINES/construction.dm +++ b/code/__DEFINES/construction.dm @@ -101,6 +101,7 @@ #define CAT_ROBOT "Robots" #define CAT_MISC "Misc" #define CAT_PRIMAL "Tribal" +#define CAT_CLOTHING "Clothing" #define CAT_FOOD "Foods" #define CAT_BREAD "Breads" #define CAT_BURGER "Burgers" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 16b2a8f4fb..3f85c93a00 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -1,5 +1,7 @@ // simple is_type and similar inline helpers +#define isdatum(D) (istype(D, /datum)) + #define islist(L) (istype(L, /list)) #define in_range(source, user) (get_dist(source, user) <= 1) @@ -25,6 +27,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 a380bf523c..b34af475d3 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -315,9 +315,16 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE #define APPEARANCE_CONSIDER_ALPHA ~RESET_ALPHA #define APPEARANCE_LONG_GLIDE LONG_GLIDE -// Consider these images/atoms as part of the UI/HUD -#define APPEARANCE_UI_IGNORE_ALPHA RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA -#define APPEARANCE_UI RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR +#ifndef PIXEL_SCALE +#define PIXEL_SCALE 0 +#if DM_VERSION >= 512 +#error HEY, PIXEL_SCALE probably exists now, remove this gross ass shim. +#endif +#endif + + // Consider these images/atoms as part of the UI/HUD +#define APPEARANCE_UI_IGNORE_ALPHA RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA|PIXEL_SCALE +#define APPEARANCE_UI RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR //Just space #define SPACE_ICON_STATE "[((x + y) ^ ~(x * y) + z) % 25]" @@ -440,3 +447,13 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE #define MOUSE_OPACITY_TRANSPARENT 0 #define MOUSE_OPACITY_ICON 1 #define MOUSE_OPACITY_OPAQUE 2 + +//world/proc/shelleo +#define SHELLEO_ERRORLEVEL 1 +#define SHELLEO_STDOUT 2 +#define SHELLEO_STDERR 3 + +//server security mode +#define SECURITY_SAFE 1 +#define SECURITY_ULTRASAFE 2 +#define SECURITY_TRUSTED 3 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 52e324bd76..c2d3256e59 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -154,3 +154,5 @@ #define JUDGE_IGNOREMONKEYS 16 #define MEGAFAUNA_DEFAULT_RECOVERY_TIME 5 + +#define SHADOW_SPECIES_LIGHT_THRESHOLD 0.2 \ No newline at end of file diff --git a/code/__DEFINES/pinpointers.dm b/code/__DEFINES/pinpointers.dm index 80403e54de..75f0452ea9 100644 --- a/code/__DEFINES/pinpointers.dm +++ b/code/__DEFINES/pinpointers.dm @@ -2,6 +2,3 @@ #define TRACK_NUKE_DISK 1 //We track the nuclear authentication disk, either to protect it or steal it #define TRACK_MALF_AI 2 //We track the malfunctioning AI, so we can prevent it from blowing us all up #define TRACK_INFILTRATOR 3 //We track the Syndicate infiltrator, so we can get back to ship when the nuke's armed -#define TRACK_OPERATIVES 4 //We track the closest operative, so we can regroup when we need to -#define TRACK_ATOM 5 //We track a specified atom, so admins can make us function for events -#define TRACK_COORDINATES 6 //We point towards the specified coordinates on our z-level, so we can navigate diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index f26b750492..b6133df287 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -47,4 +47,22 @@ #define SEC_DEPT_ENGINEERING "Engineering" #define SEC_DEPT_MEDICAL "Medical" #define SEC_DEPT_SCIENCE "Science" -#define SEC_DEPT_SUPPLY "Supply" \ No newline at end of file +#define SEC_DEPT_SUPPLY "Supply" + +// Playtime tracking system, see jobs_exp.dm +#define EXP_TYPE_LIVING "Living" +#define EXP_TYPE_CREW "Crew" +#define EXP_TYPE_COMMAND "Command" +#define EXP_TYPE_ENGINEERING "Engineering" +#define EXP_TYPE_MEDICAL "Medical" +#define EXP_TYPE_SCIENCE "Science" +#define EXP_TYPE_SUPPLY "Supply" +#define EXP_TYPE_SECURITY "Security" +#define EXP_TYPE_SILICON "Silicon" +#define EXP_TYPE_SERVICE "Service" +#define EXP_TYPE_ANTAG "Antag" +#define EXP_TYPE_SPECIAL "Special" +#define EXP_TYPE_GHOST "Ghost" + +//Flags in the players table in the db +#define DB_FLAG_EXEMPT 1 \ No newline at end of file diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index 041a6283e7..8749218847 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -10,6 +10,11 @@ //if TESTING is enabled, qdel will call this object's find_references() verb. //defines for the gc_destroyed var +#define GC_QUEUE_PREQUEUE 1 +#define GC_QUEUE_CHECK 2 +#define GC_QUEUE_HARDDELETE 3 +#define GC_QUEUE_COUNT 3 //increase this when adding more steps. + #define GC_QUEUED_FOR_QUEUING -1 #define GC_QUEUED_FOR_HARD_DEL -2 #define GC_CURRENTLY_BEING_QDELETED -3 diff --git a/code/__DEFINES/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/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm index bee6508609..b4fd6719b2 100644 --- a/code/__DEFINES/shuttles.dm +++ b/code/__DEFINES/shuttles.dm @@ -53,9 +53,14 @@ #define ENGINE_COEFF_MAX 2 #define ENGINE_DEFAULT_MAXSPEED_ENGINES 5 -//Docking error flags_1 +//Docking error flags #define DOCKING_SUCCESS 0 -#define DOCKING_COMPLETE 1 -#define DOCKING_BLOCKED 2 -#define DOCKING_IMMOBILIZED 4 -#define DOCKING_AREA_EMPTY 8 \ No newline at end of file +#define DOCKING_BLOCKED 1 +#define DOCKING_IMMOBILIZED 2 +#define DOCKING_AREA_EMPTY 4 + + +//Docking turf movements +#define MOVE_TURF 1 +#define MOVE_AREA 2 +#define MOVE_CONTENTS 4 \ No newline at end of file diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 620d8bed10..d3867e5257 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -10,12 +10,13 @@ #define CHANNEL_BICYCLE 1016 //Citadel code -#define CHANNEL_PRED 1018 +#define CHANNEL_PRED 1015 +#define CHANNEL_PREYLOOP 1014 //THIS SHOULD ALWAYS BE THE LOWEST ONE! //KEEP IT UPDATED -#define CHANNEL_HIGHEST_AVAILABLE 1015 +#define CHANNEL_HIGHEST_AVAILABLE 1013 #define CHANNEL_HIGHEST_AVAILABLE 1017 #define SOUND_MINIMUM_PRESSURE 10 diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm index e2cf5d5dd7..5dbe99a9c5 100644 --- a/code/__DEFINES/stat.dm +++ b/code/__DEFINES/stat.dm @@ -4,8 +4,9 @@ //mob/var/stat things #define CONSCIOUS 0 -#define UNCONSCIOUS 1 -#define DEAD 2 +#define SOFT_CRIT 1 +#define UNCONSCIOUS 2 +#define DEAD 3 //mob disabilities stat @@ -21,9 +22,8 @@ // bitflags for machine stat variable #define BROKEN 1 #define NOPOWER 2 -#define POWEROFF 4 // tbd -#define MAINT 8 // under maintaince -#define EMPED 16 // temporary broken by EMP pulse +#define MAINT 4 // under maintaince +#define EMPED 8 // temporary broken by EMP pulse //ai power requirement defines #define POWER_REQ_NONE 0 diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index f7322abe6c..9ec26b7506 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -56,7 +56,6 @@ #define INIT_ORDER_TIMER 1 #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_AIR -1 -#define INIT_ORDER_SHUTTLE -2 #define INIT_ORDER_MINIMAP -3 #define INIT_ORDER_ASSETS -4 #define INIT_ORDER_ICON_SMOOTHING -5 @@ -64,6 +63,7 @@ #define INIT_ORDER_XKEYSCORE -10 #define INIT_ORDER_STICKY_BAN -10 #define INIT_ORDER_LIGHTING -20 +#define INIT_ORDER_SHUTTLE -21 #define INIT_ORDER_SQUEAK -40 #define INIT_ORDER_PERSISTENCE -100 diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 956935258b..28017de85c 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -13,3 +13,11 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using #define FRIDAY "Fri" #define SATURDAY "Sat" #define SUNDAY "Sun" + +#define SECONDS *10 + +#define MINUTES SECONDS*60 + +#define HOURS MINUTES*60 + +#define TICKS *world.tick_lag \ No newline at end of file diff --git a/code/__DEFINES/voreconstants.dm b/code/__DEFINES/voreconstants.dm index bf6cf2e257..ea4c26d79e 100644 --- a/code/__DEFINES/voreconstants.dm +++ b/code/__DEFINES/voreconstants.dm @@ -2,6 +2,7 @@ #define DM_HOLD "Hold" #define DM_DIGEST "Digest" #define DM_HEAL "Heal" +#define DM_NOISY "Noisy" #define VORE_STRUGGLE_EMOTE_CHANCE 40 diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index cde5e0b4e7..4b5342031f 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -96,6 +96,9 @@ if (config.log_pda) WRITE_FILE(GLOB.world_game_log, "\[[time_stamp()]]CHAT: [text]") +/proc/log_qdel(text) + WRITE_FILE(GLOB.world_qdel_log, "\[[time_stamp()]]QDEL: [text]") + /proc/log_sql(text) WRITE_FILE(GLOB.sql_error_log, "\[[time_stamp()]]SQL: [text]") diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 171d776d0d..1c9c33f21a 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -49,3 +49,12 @@ GLOBAL_VAR_INIT(cmp_field, "name") /proc/cmp_ruincost_priority(datum/map_template/ruin/A, datum/map_template/ruin/B) return initial(A.cost) - initial(B.cost) + +/proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B) + . = B.hard_delete_time - A.hard_delete_time + if (!.) + . = B.destroy_time - A.destroy_time + if (!.) + . = B.failures - A.failures + if (!.) + . = B.qdels - A.qdels diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index d1d330fb01..265db91339 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -9,10 +9,8 @@ #define CULT_POLL_WAIT 2400 /proc/get_area(atom/A) - if (!istype(A)) - return - for(A, A && !isarea(A), A=A.loc); //semicolon is for the empty statement - return A + var/turf/T = get_turf(A) + return T ? T.loc : null /proc/get_area_name(atom/X) var/area/Y = get_area(X) diff --git a/code/__HELPERS/shell.dm b/code/__HELPERS/shell.dm new file mode 100644 index 0000000000..8b615eac0a --- /dev/null +++ b/code/__HELPERS/shell.dm @@ -0,0 +1,57 @@ +//Runs the command in the system's shell, returns a list of (error code, stdout, stderr) + +#define SHELLEO_NAME "data/shelleo." +#define SHELLEO_ERR ".err" +#define SHELLEO_OUT ".out" +/world/proc/shelleo(command) + var/static/list/shelleo_ids = list() + var/stdout = "" + var/stderr = "" + var/errorcode = 1 + var/shelleo_id + var/out_file = "" + var/err_file = "" + var/static/list/interpreters = list("[MS_WINDOWS]" = "cmd /c", "[UNIX]" = "sh -c") + var/interpreter = interpreters["[world.system_type]"] + if(interpreter) + for(var/seo_id in shelleo_ids) + if(!shelleo_ids[seo_id]) + shelleo_ids[seo_id] = TRUE + shelleo_id = "[seo_id]" + break + if(!shelleo_id) + shelleo_id = "[shelleo_ids.len + 1]" + shelleo_ids += shelleo_id + shelleo_ids[shelleo_id] = TRUE + out_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_OUT]" + err_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_ERR]" + errorcode = shell("[interpreter] \"[command]\" > [out_file] 2> [err_file]") + if(fexists(out_file)) + stdout = file2text(out_file) + fdel(out_file) + if(fexists(err_file)) + stderr = file2text(err_file) + fdel(err_file) + shelleo_ids[shelleo_id] = FALSE + else + CRASH("Operating System: [world.system_type] not supported") // If you encounter this error, you are encouraged to update this proc with support for the new operating system + . = list(errorcode, stdout, stderr) +#undef SHELLEO_NAME +#undef SHELLEO_ERR +#undef SHELLEO_OUT + +/proc/shell_url_scrub(url) + var/static/regex/bad_chars_regex = regex("\[^#%&./:=?\\w]*", "g") + var/scrubbed_url = "" + var/bad_match = "" + var/last_good = 1 + var/bad_chars = 1 + do + bad_chars = bad_chars_regex.Find(url) + scrubbed_url += copytext(url, last_good, bad_chars) + if(bad_chars) + bad_match = url_encode(bad_chars_regex.match) + scrubbed_url += bad_match + last_good = bad_chars + length(bad_match) + while(bad_chars) + . = scrubbed_url diff --git a/code/__HELPERS/type2type_vr.dm b/code/__HELPERS/type2type_vr.dm index e6df531806..09ea0a158a 100644 --- a/code/__HELPERS/type2type_vr.dm +++ b/code/__HELPERS/type2type_vr.dm @@ -1,5 +1,5 @@ /* -// Contains VOREStation type2type functions +// Contains VOREStation based vore description type2type functions // list2text - takes delimiter and returns text // text2list - takes delimiter, and creates list // diff --git a/code/_compile_options.dm b/code/_compile_options.dm index c1f02856a7..ccab4a645b 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -72,4 +72,4 @@ //Update this whenever the db schema changes //make sure you add an update to the schema_version stable in the db changelog #define DB_MAJOR_VERSION 3 -#define DB_MINOR_VERSION 0 +#define DB_MINOR_VERSION 3 diff --git a/code/_compile_options.dm.rej b/code/_compile_options.dm.rej new file mode 100644 index 0000000000..842c24b746 --- /dev/null +++ b/code/_compile_options.dm.rej @@ -0,0 +1,11 @@ +diff a/code/_compile_options.dm b/code/_compile_options.dm (rejected hunks) +@@ -69,7 +69,7 @@ + #error You need version 511 or higher + #endif + +-//Update this whenever the db schema changes ++//Update this whenever the db schema changes + //make sure you add an update to the schema_version stable in the db changelog + #define DB_MAJOR_VERSION 3 +-#define DB_MINOR_VERSION 2 ++#define DB_MINOR_VERSION 3 diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index e477299308..7acb5bf729 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -105,53 +105,17 @@ GLOBAL_LIST_INIT(TAGGERLOCATIONS, list("Disposals", GLOBAL_LIST_INIT(guitar_notes, flist("sound/guitar/")) -GLOBAL_LIST_INIT(station_prefixes, list("", "Imperium", "Heretical", "Cuban", - "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", - "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", - "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", - "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", - "Automated", "Experimental", "Augmented")) +GLOBAL_LIST_INIT(station_prefixes, world.file2list("strings/station_prefixes.txt") + "") -GLOBAL_LIST_INIT(station_names, list("", "Stanford", "Dorf", "Alium", - "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", - "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", - "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", - "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", - "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", - "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist", - "Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", - "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", - "System", "Mining", "Neckbeard", "Research", "Supply", "Military", - "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", - "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", - "Robot", "Hats", "Pizza")) +GLOBAL_LIST_INIT(station_names, world.file2list("strings/station_names.txt" + "")) -GLOBAL_LIST_INIT(station_suffixes, list("Station", "Frontier", - "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", - "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", - "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", - "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", - "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", - "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", - "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", - "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp", - "Airstrip")) +GLOBAL_LIST_INIT(station_suffixes, world.file2list("strings/station_suffixes.txt")) -GLOBAL_LIST_INIT(greek_letters, list("Alpha", "Beta", "Gamma", "Delta", - "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", - "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", - "Chi", "Psi", "Omega")) +GLOBAL_LIST_INIT(greek_letters, world.file2list("strings/greek_letters.txt")) -GLOBAL_LIST_INIT(phonetic_alphabet, list("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")) +GLOBAL_LIST_INIT(phonetic_alphabet, world.file2list("strings/phonetic_alphabet.txt")) -GLOBAL_LIST_INIT(numbers_as_words, list("One", "Two", "Three", "Four", - "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", - "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", - "Eighteen", "Nineteen")) +GLOBAL_LIST_INIT(numbers_as_words, world.file2list("strings/numbers_as_words.txt")) /proc/generate_number_strings() var/list/L[198] diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 5be6a8dbaa..e600619d45 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -4,6 +4,8 @@ GLOBAL_VAR(world_game_log) GLOBAL_PROTECT(world_game_log) GLOBAL_VAR(world_runtime_log) GLOBAL_PROTECT(world_runtime_log) +GLOBAL_VAR(world_qdel_log) +GLOBAL_PROTECT(world_qdel_log) GLOBAL_VAR(world_attack_log) GLOBAL_PROTECT(world_attack_log) GLOBAL_VAR(world_href_log) diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index 765e659c32..11ab21d21f 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -25,7 +25,7 @@ * If you are diagonally adjacent, ensure you can pass through at least one of the mutually adjacent square. * Passing through in this case ignores anything with the LETPASSTHROW pass flag, such as tables, racks, and morgue trays. */ -/turf/Adjacent(atom/neighbor, atom/target = null, atom/movable/mover = null) +/turf/Adjacent(atom/neighbor, atom/target = null, atom/movable/mover = null) var/turf/T0 = get_turf(neighbor) if(T0 == src) //same turf @@ -37,7 +37,7 @@ // Non diagonal case if(T0.x == x || T0.y == y) // Check for border blockages - return T0.ClickCross(get_dir(T0,src), border_only = 1, target_atom = target, mover = mover) && src.ClickCross(get_dir(src,T0), border_only = 1, target_atom = target, mover = mover) + return T0.ClickCross(get_dir(T0,src), border_only = 1, target_atom = target, mover = mover) && src.ClickCross(get_dir(src,T0), border_only = 1, target_atom = target, mover = mover) // Diagonal case var/in_dir = get_dir(T0,src) // eg. northwest (1+8) = 9 (00001001) @@ -45,16 +45,16 @@ var/d2 = in_dir&12 // eg. west (1+8)&12 (0000 1100) = 8 (0000 1000) for(var/d in list(d1,d2)) - if(!T0.ClickCross(d, border_only = 1, target_atom = target, mover = mover)) + if(!T0.ClickCross(d, border_only = 1, target_atom = target, mover = mover)) continue // could not leave T0 in that direction var/turf/T1 = get_step(T0,d) - if(!T1 || T1.density) - continue - if(!T1.ClickCross(get_dir(T1,src), border_only = 0, target_atom = target, mover = mover) || !T1.ClickCross(get_dir(T1,T0), border_only = 0, target_atom = target, mover = mover)) + if(!T1 || T1.density) + continue + if(!T1.ClickCross(get_dir(T1,src), border_only = 0, target_atom = target, mover = mover) || !T1.ClickCross(get_dir(T1,T0), border_only = 0, target_atom = target, mover = mover)) continue // couldn't enter or couldn't leave T1 - if(!src.ClickCross(get_dir(src,T1), border_only = 1, target_atom = target, mover = mover)) + if(!src.ClickCross(get_dir(src,T1), border_only = 1, target_atom = target, mover = mover)) continue // could not enter src return 1 // we don't care about our own density @@ -70,7 +70,7 @@ return TRUE if(!isturf(loc)) return FALSE - if(loc.Adjacent(neighbor,target = neighbor, mover = src)) + if(loc.Adjacent(neighbor,target = neighbor, mover = src)) return TRUE return FALSE @@ -85,20 +85,19 @@ /* This checks if you there is uninterrupted airspace between that turf and this one. - This is defined as any dense ON_BORDER_1 object, or any dense object without LETPASSTHROW. + This is defined as any dense ON_BORDER_1 object, or any dense object without LETPASSTHROW. The border_only flag allows you to not objects (for source and destination squares) */ -/turf/proc/ClickCross(target_dir, border_only, target_atom = null, atom/movable/mover = null) +/turf/proc/ClickCross(target_dir, border_only, target_atom = null, atom/movable/mover = null) for(var/obj/O in src) - if((mover && O.CanPass(mover,get_step(src,target_dir))) || (!mover && !O.density)) - continue - if(O == target_atom || (O.pass_flags & LETPASSTHROW)) //check if there's a dense object present on the turf + if((mover && O.CanPass(mover,get_step(src,target_dir))) || (!mover && !O.density)) + continue + if(O == target_atom || O == mover || (O.pass_flags & LETPASSTHROW)) //check if there's a dense object present on the turf continue // LETPASSTHROW is used for anything you can click through (or the firedoor special case, see above) - if( O.flags_1&ON_BORDER_1) // windows are on border, check them first + if( O.flags_1&ON_BORDER_1) // windows are on border, check them first if( O.dir & target_dir || O.dir & (O.dir-1) ) // full tile windows are just diagonals mechanically return 0 //O.dir&(O.dir-1) is false for any cardinal direction, but true for diagonal ones - else if( !border_only ) // dense, not on border, cannot pass over return 0 return 1 diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 69deb2cb58..638b4a31cf 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -633,6 +633,7 @@ so as to remain in compliance with the most up-to-date laws." /obj/screen/alert/restrained/buckled name = "Buckled" desc = "You've been buckled to something. Click the alert to unbuckle unless you're handcuffed." + icon_state = "buckled" /obj/screen/alert/restrained/handcuffed name = "Handcuffed" @@ -698,4 +699,3 @@ so as to remain in compliance with the most up-to-date laws." severity = 0 master = null screen_loc = "" - diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 6c18ad0e07..12253cc550 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -67,7 +67,7 @@ screen_loc = "CENTER-7,CENTER-7" layer = FULLSCREEN_LAYER plane = FULLSCREEN_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT + mouse_opacity = MOUSE_OPACITY_TRANSPARENT var/severity = 0 var/show_when_dead = FALSE @@ -95,16 +95,20 @@ layer = CRIT_LAYER plane = FULLSCREEN_PLANE +/obj/screen/fullscreen/crit/vision + icon_state = "oxydamageoverlay" + layer = BLIND_LAYER + /obj/screen/fullscreen/blind icon_state = "blackimageoverlay" layer = BLIND_LAYER plane = FULLSCREEN_PLANE -/obj/screen/fullscreen/curse - icon_state = "curse" - layer = CURSE_LAYER - plane = FULLSCREEN_PLANE - +/obj/screen/fullscreen/curse + icon_state = "curse" + layer = CURSE_LAYER + plane = FULLSCREEN_PLANE + /obj/screen/fullscreen/impaired icon_state = "impairedoverlay" @@ -168,4 +172,4 @@ plane = LIGHTING_PLANE layer = LIGHTING_LAYER blend_mode = BLEND_ADD - show_when_dead = TRUE + show_when_dead = TRUE diff --git a/code/citadel/cit_reagents.dm b/code/citadel/cit_reagents.dm index 4a0e91479b..30b4da7ccf 100644 --- a/code/citadel/cit_reagents.dm +++ b/code/citadel/cit_reagents.dm @@ -46,7 +46,7 @@ name = "Female Ejaculate" id = "femcum" description = "Vaginal lubricant found in most mammals and other animals of similar nature. Where you found this is your own business." - taste_description = "female arousal" + taste_description = "something with a tang" // wew coders who haven't eaten out a girl. taste_mult = 2 data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null) reagent_state = LIQUID @@ -90,7 +90,7 @@ //aphrodisiac & anaphrodisiac -/datum/reagent/aphrodisiac +/datum/reagent/drug/aphrodisiac name = "Crocin" id = "aphro" description = "Naturally found in the crocus and gardenia flowers, this drug acts as a natural and safe aphrodisiac." @@ -98,7 +98,7 @@ taste_mult = 2 //Hide the roofies in stronger flavors color = "#FFADFF"//PINK, rgb(255, 173, 255) -/datum/reagent/aphrodisiac/on_mob_life(mob/living/M) +/datum/reagent/drug/aphrodisiac/on_mob_life(mob/living/M) if(prob(33)) M.adjustArousalLoss(2) if(prob(5)) @@ -108,7 +108,7 @@ to_chat(M, "[aroused_message]") ..() -/datum/reagent/aphrodisiacplus +/datum/reagent/drug/aphrodisiacplus name = "Hexacrocin" id = "aphro+" description = "Chemically condensed form of basic crocin. This aphrodisiac is extremely powerful and addictive in most animals.\ @@ -119,7 +119,7 @@ addiction_threshold = 20 overdose_threshold = 20 -/datum/reagent/aphrodisiacplus/on_mob_life(mob/living/M) +/datum/reagent/drug/aphrodisiacplus/on_mob_life(mob/living/M) if(prob(33)) M.adjustArousalLoss(6)//not quite six times as powerful, but still considerably more powerful. if(prob(5)) @@ -135,21 +135,21 @@ aroused_message = pick("You feel a bit hot.", "You feel strong sexual urges.", "You feel in the mood.", "You're ready to go down on someone.") to_chat(M, "[aroused_message]") -/datum/reagent/aphrodisiacplus/addiction_act_stage2(mob/living/M) +/datum/reagent/drug/aphrodisiacplus/addiction_act_stage2(mob/living/M) if(prob(30)) M.adjustBrainLoss(2) ..() -/datum/reagent/aphrodisiacplus/addiction_act_stage3(mob/living/M) +/datum/reagent/drug/aphrodisiacplus/addiction_act_stage3(mob/living/M) if(prob(30)) M.adjustBrainLoss(3) ..() -/datum/reagent/aphrodisiacplus/addiction_act_stage4(mob/living/M) +/datum/reagent/drug/aphrodisiacplus/addiction_act_stage4(mob/living/M) if(prob(30)) M.adjustBrainLoss(4) ..() -/datum/reagent/aphrodisiacplus/overdose_process(mob/living/M) +/datum/reagent/drug/aphrodisiacplus/overdose_process(mob/living/M) if(prob(33)) if(M.getArousalLoss() >= 100 && ishuman(M) && M.has_dna()) var/mob/living/carbon/human/H = M @@ -163,7 +163,7 @@ M.adjustArousalLoss(2) ..() -/datum/reagent/anaphrodisiac +/datum/reagent/drug/anaphrodisiac name = "Camphor" id = "anaphro" description = "Naturally found in some species of evergreen trees, camphor is a waxy substance. When injested by most animals, it acts as an anaphrodisiac\ @@ -173,12 +173,12 @@ color = "#D9D9D9"//rgb(217, 217, 217) reagent_state = SOLID -/datum/reagent/anaphrodisiac/on_mob_life(mob/living/M) +/datum/reagent/drug/anaphrodisiac/on_mob_life(mob/living/M) if(prob(33)) M.adjustArousalLoss(-2) ..() -/datum/reagent/anaphrodisiacplus +/datum/reagent/drug/anaphrodisiacplus name = "Hexacamphor" id = "anaphro+" description = "Chemically condensed camphor. Causes an extreme reduction in libido and a permanent one if overdosed. Non-addictive." @@ -187,12 +187,12 @@ reagent_state = SOLID overdose_threshold = 20 -/datum/reagent/anaphrodisiacplus/on_mob_life(mob/living/M) +/datum/reagent/drug/anaphrodisiacplus/on_mob_life(mob/living/M) if(prob(33)) M.adjustArousalLoss(-4) ..() -/datum/reagent/anaphrodisiacplus/overdose_process(mob/living/M) +/datum/reagent/drug/anaphrodisiacplus/overdose_process(mob/living/M) if(prob(33)) if(M.min_arousal > 0) M.min_arousal -= 1 diff --git a/code/citadel/custom_loadout/custom_items.dm b/code/citadel/custom_loadout/custom_items.dm new file mode 100644 index 0000000000..da81dfb37c --- /dev/null +++ b/code/citadel/custom_loadout/custom_items.dm @@ -0,0 +1,56 @@ + +//For custom items. + +/obj/item/custom/ceb_soap + name = "Cebutris' Soap" + desc = "A generic bar of soap that doesn't really seem to work right." + gender = PLURAL + icon = 'icons/obj/custom.dmi' + icon_state = "cebu" + 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." + icon = 'icons/obj/clothing/cloaks.dmi' + icon_state = "infcloak" + item_state = "infcloak" + 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.'" + icon = 'icons/obj/cigarettes.dmi' + icon_state = "gold_zippo" + item_state = "gold_zippo" + w_class = WEIGHT_CLASS_TINY + flags_1 = CONDUCT_1 + slot_flags = SLOT_BELT + heat = 1500 + resistance_flags = FIRE_PROOF + 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/citadel/custom_loadout/load_to_mob.dm b/code/citadel/custom_loadout/load_to_mob.dm new file mode 100644 index 0000000000..64eec2ba06 --- /dev/null +++ b/code/citadel/custom_loadout/load_to_mob.dm @@ -0,0 +1,61 @@ + +//Proc that does the actual loading of items to mob +/*Itemlists are formatted as +"[typepath]" = number_of_it_to_spawn +*/ + +#define DROP_TO_FLOOR 0 +#define LOADING_TO_HUMAN 1 + +/proc/handle_roundstart_items(mob/living/M, ckey_override, job_override, special_override) + if(!istype(M) || (!M.ckey && !ckey_override) || (!M.mind && (!job_override || !special_override))) + return FALSE + return load_itemlist_to_mob(M, parse_custom_roundstart_items(ckey_override? ckey_override : M.ckey, M.name, job_override? job_override : M.mind.assigned_role, special_override? special_override : M.mind.special_role), TRUE, TRUE, FALSE) + +//Just incase there's extra mob selections in the future..... +/proc/load_itemlist_to_mob(mob/living/L, list/itemlist, drop_on_floor_if_full = TRUE, load_to_all_slots = TRUE, replace_slots = FALSE) + if(!istype(L) || !islist(itemlist)) + return FALSE + var/loading_mode = DROP_TO_FLOOR + var/turf/current_turf = get_turf(L) + if(ishuman(L)) + loading_mode = LOADING_TO_HUMAN + switch(loading_mode) + if(DROP_TO_FLOOR) + for(var/I in itemlist) + var/typepath = text2path(I) + if(!typepath) + continue + for(var/i = 0, i < itemlist[I], i++) + new typepath(current_turf) + return TRUE + if(LOADING_TO_HUMAN) + return load_itemlist_to_human(L, itemlist, drop_on_floor_if_full, load_to_all_slots, replace_slots) + +/proc/load_itemlist_to_human(mob/living/carbon/human/H, list/itemlist, drop_on_floor_if_full = TRUE, load_to_all_slots = TRUE, replace_slots = FALSE) + if(!istype(H) || !islist(itemlist)) + return FALSE + var/turf/T = get_turf(H) + for(var/item in itemlist) + var/path = item + if(!ispath(path)) + path = text2path(path) + if(!path) + continue + var/amount = itemlist[item] + for(var/i in 1 to amount) + var/atom/movable/loaded_atom = new path + if(!istype(loaded_atom)) + QDEL_NULL(loaded_atom) + continue + if(!istype(loaded_atom, /obj/item)) + loaded_atom.forceMove(T) + continue + var/obj/item/loaded = loaded_atom + var/obj/item/storage/S = H.get_item_by_slot(slot_back) + if(istype(S)) + S.handle_item_insertion(loaded, TRUE, H) //Force it into their backpack + continue + if(!H.put_in_hands(loaded)) //They don't have one/somehow that failed, put it in their hands + loaded.forceMove(T) //Guess we're just dumping it on the floor! + return TRUE diff --git a/code/citadel/custom_loadout/read_from_file.dm b/code/citadel/custom_loadout/read_from_file.dm new file mode 100644 index 0000000000..0ed38e8d41 --- /dev/null +++ b/code/citadel/custom_loadout/read_from_file.dm @@ -0,0 +1,71 @@ + +GLOBAL_LIST(custom_item_list) +//Layered list in form of custom_item_list[ckey][job][items][amounts] +//ckey is key, job is specific jobs, or "ALL" for all jobs, items for items, amounts for amount of item. + +//File should be in the format of ckey|exact job name/exact job name/or put ALL instead of any job names|/path/to/item=amount;/path/to/item=amount +//Each ckey should be in a different line +//if there's multiple entries of a single ckey the later ones will add to the earlier definitions. + +/proc/reload_custom_roundstart_items_list(custom_filelist) + if(!custom_filelist) + custom_filelist = "config/custom_roundstart_items.txt" + GLOB.custom_item_list = list() + var/list/file_lines = world.file2list(custom_filelist) + for(var/line in file_lines) + if(length(line) == 0) //Emptyline, no one cares. + continue + if(copytext(line,1,3) == "//") //Commented line, ignore. + continue + var/ckey_str_sep = findtext(line, "|") //Process our stuff.. + var/char_str_sep = findtext(line, "|", ckey_str_sep+1) + var/job_str_sep = findtext(line, "|", char_str_sep+1) + var/item_str_sep = findtext(line, "|", job_str_sep+1) + var/ckey_str = ckey(copytext(line, 1, ckey_str_sep)) + var/char_str = copytext(line, ckey_str_sep+1, char_str_sep) + var/job_str = copytext(line, char_str_sep+1, job_str_sep) + var/item_str = copytext(line, job_str_sep+1, item_str_sep) + if(!ckey_str || !char_str || !job_str || !item_str || !length(ckey_str) || !length(char_str) || !length(job_str) || !length(item_str)) + log_admin("Errored custom_items_whitelist line: [line] - Component/separator missing!") + if(!islist(GLOB.custom_item_list[ckey_str])) + GLOB.custom_item_list[ckey_str] = list() //Initialize list for this ckey if it isn't initialized.. + var/list/characters = splittext(char_str, "/") + for(var/character in characters) + if(!islist(GLOB.custom_item_list[ckey_str][character])) + GLOB.custom_item_list[ckey_str][character] = list() + var/list/jobs = splittext(job_str, "/") + for(var/job in jobs) + for(var/character in characters) + if(!islist(GLOB.custom_item_list[ckey_str][character][job])) + GLOB.custom_item_list[ckey_str][character][job] = list() //Initialize item list for this job of this ckey if not already initialized. + var/list/item_strings = splittext(item_str, ";") //Get item strings in format of /path/to/item=amount + for(var/item_string in item_strings) + var/path_str_sep = findtext(item_string, "=") + var/path = copytext(item_string, 1, path_str_sep) //Path to spawn + var/amount = copytext(item_string, path_str_sep+1) //Amount to spawn + //world << "DEBUG: Item string [item_string] processed" + amount = text2num(amount) + path = text2path(path) + if(!ispath(path) || !isnum(amount)) + log_admin("Errored custom_items_whitelist line: [line] - Path/number for item missing or invalid.") + for(var/character in characters) + for(var/job in jobs) + if(!GLOB.custom_item_list[ckey_str][character][job][path]) //Doesn't exist, make it exist! + GLOB.custom_item_list[ckey_str][character][job][path] = amount + else + GLOB.custom_item_list[ckey_str][character][job][path] += amount //Exists, we want more~ + return GLOB.custom_item_list + +/proc/parse_custom_roundstart_items(ckey, char_name = "ALL", job_name = "ALL", special_role) + var/list/ret = list() + if(GLOB.custom_item_list[ckey]) + for(var/char in GLOB.custom_item_list[ckey]) + if((char_name == char) || (char_name == "ALL") || (char == "ALL")) + for(var/job in GLOB.custom_item_list[ckey][char]) + if((job_name == job) || (job == "ALL") || (job_name == "ALL") || (special_role && (job == special_role))) + for(var/item_path in GLOB.custom_item_list[ckey][char][job]) + if(ret[item_path]) + ret[item_path] += GLOB.custom_item_list[ckey][char][job][item_path] + else + ret[item_path] = GLOB.custom_item_list[ckey][char][job][item_path] + return ret diff --git a/code/citadel/dogborgstuff.dm b/code/citadel/dogborgstuff.dm index 4f3eae0fb7..20f6cfd8c3 100644 --- a/code/citadel/dogborgstuff.dm +++ b/code/citadel/dogborgstuff.dm @@ -23,7 +23,6 @@ attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed") w_class = 3 sharpness = IS_SHARP - var/emagged = 0 /obj/item/dogborg/jaws/attack(atom/A, mob/living/silicon/robot/user) ..() @@ -173,7 +172,6 @@ icon_state = "synthtongue" hitsound = 'sound/effects/attackblob.ogg' cleanspeed = 80 - var/emagged = 0 /obj/item/soap/tongue/New() ..() diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index c4d8335890..180d14853e 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -5,6 +5,10 @@ #define SECURITY_HAS_MAINT_ACCESS 2 #define EVERYONE_HAS_MAINT_ACCESS 4 +GLOBAL_VAR_INIT(config_dir, "config/") +GLOBAL_PROTECT(config_dir) + + /datum/configuration/can_vv_get(var_name) var/static/list/banned_gets = list("autoadmin", "autoadmin_rank") if (var_name in banned_gets) @@ -12,7 +16,7 @@ return ..() /datum/configuration/vv_edit_var(var_name, var_value) - var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank") + var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank", "invoke_youtubedl") if(var_name in banned_edits) return FALSE return ..() @@ -90,6 +94,8 @@ var/panic_server_name var/panic_address //Reconnect a player this linked server if this server isn't accepting new players + var/invoke_youtubedl + //IP Intel vars var/ipintel_email var/ipintel_rating_bad = 1 @@ -102,6 +108,15 @@ 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 + var/use_exp_restrictions_heads_hours = 0 + var/use_exp_restrictions_heads_department = FALSE + var/use_exp_restrictions_other = FALSE + var/use_exp_restrictions_admin_bypass = FALSE //Population cap vars var/soft_popcap = 0 @@ -168,6 +183,7 @@ var/rename_cyborg = 0 var/ooc_during_round = 0 var/emojis = 0 + var/no_credits_round_end = FALSE //Used for modifying movement speed for mobs. //Unversal modifiers @@ -266,6 +282,8 @@ 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) @@ -287,18 +305,20 @@ Reload() /datum/configuration/proc/Reload() - load("config/config.txt") - load("config/comms.txt", "comms") - load("config/game_options.txt","game_options") - load("config/policies.txt", "policies") - loadsql("config/dbconfig.txt") + load("config.txt") + load("comms.txt", "comms") + load("game_options.txt","game_options") + load("policies.txt", "policies") + loadsql("dbconfig.txt") + reload_custom_roundstart_items_list() if (maprotation) - loadmaplist("config/maps.txt") + loadmaplist("maps.txt") // apply some settings from config.. GLOB.abandon_allowed = respawn /datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist + filename = "[GLOB.config_dir][filename]" var/list/Lines = world.file2list(filename) for(var/t in Lines) @@ -336,6 +356,18 @@ use_age_restriction_for_jobs = 1 if("use_account_age_for_jobs") use_account_age_for_jobs = 1 + if("use_exp_tracking") + use_exp_tracking = TRUE + if("use_exp_restrictions_heads") + use_exp_restrictions_heads = TRUE + if("use_exp_restrictions_heads_hours") + use_exp_restrictions_heads_hours = text2num(value) + if("use_exp_restrictions_heads_department") + use_exp_restrictions_heads_department = TRUE + if("use_exp_restrictions_other") + use_exp_restrictions_other = TRUE + if("use_exp_restrictions_admin_bypass") + use_exp_restrictions_admin_bypass = TRUE if("lobby_countdown") lobby_countdown = text2num(value) if("round_end_countdown") @@ -450,10 +482,16 @@ if("panic_server_address") if(value != "byond://address:port") panic_address = value + if("invoke_youtubedl") + invoke_youtubedl = value if("show_irc_name") 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") @@ -533,6 +571,8 @@ 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]'") @@ -556,6 +596,8 @@ ooc_during_round = 1 if("emojis") emojis = 1 + if("no_credits_round_end") + no_credits_round_end = TRUE if("run_delay") run_speed = text2num(value) if("walk_delay") @@ -799,6 +841,7 @@ WRITE_FILE(GLOB.config_error_log, "Unknown setting in configuration: '[name]'") /datum/configuration/proc/loadmaplist(filename) + filename = "[GLOB.config_dir][filename]" var/list/Lines = world.file2list(filename) var/datum/map_config/currentmap = null @@ -851,6 +894,7 @@ /datum/configuration/proc/loadsql(filename) + filename = "[GLOB.config_dir][filename]" var/list/Lines = world.file2list(filename) for(var/t in Lines) if(!t) diff --git a/code/controllers/configuration.dm.rej b/code/controllers/configuration.dm.rej new file mode 100644 index 0000000000..5d5c2c5055 --- /dev/null +++ b/code/controllers/configuration.dm.rej @@ -0,0 +1,24 @@ +diff a/code/controllers/configuration.dm b/code/controllers/configuration.dm (rejected hunks) +@@ -337,17 +337,17 @@ + if("use_account_age_for_jobs") + use_account_age_for_jobs = 1 + if("use_exp_tracking") +- use_exp_tracking = 1 ++ use_exp_tracking = TRUE + if("use_exp_restrictions_heads") +- use_exp_restrictions_heads = 1 ++ use_exp_restrictions_heads = TRUE + if("use_exp_restrictions_heads_hours") + use_exp_restrictions_heads_hours = text2num(value) + if("use_exp_restrictions_heads_department") +- use_exp_restrictions_heads_department = 1 ++ use_exp_restrictions_heads_department = TRUE + if("use_exp_restrictions_other") +- use_exp_restrictions_other = 1 ++ use_exp_restrictions_other = TRUE + if("use_exp_restrictions_admin_bypass") +- use_exp_restrictions_admin_bypass = 1 ++ use_exp_restrictions_admin_bypass = TRUE + if("lobby_countdown") + lobby_countdown = text2num(value) + if("round_end_countdown") diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index d5298f7dde..2ca208642c 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -1,102 +1,102 @@ - /** - * Failsafe - * - * Pretty much pokes the MC to make sure it's still alive. - **/ - -GLOBAL_REAL(Failsafe, /datum/controller/failsafe) - -/datum/controller/failsafe // This thing pretty much just keeps poking the master controller - name = "Failsafe" - - // The length of time to check on the MC (in deciseconds). - // Set to 0 to disable. - var/processing_interval = 20 - // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. - var/defcon = 5 - //the world.time of the last check, so the mc can restart US if we hang. - // (Real friends look out for *eachother*) - var/lasttick = 0 - - // Track the MC iteration to make sure its still on track. - var/master_iteration = 0 - var/running = TRUE - -/datum/controller/failsafe/New() - // Highlander-style: there can only be one! Kill off the old and replace it with the new. - if(Failsafe != src) - if(istype(Failsafe)) - qdel(Failsafe) - Failsafe = src - Initialize() - -/datum/controller/failsafe/Initialize() - set waitfor = 0 - Failsafe.Loop() - if(!QDELETED(src)) - qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us - -/datum/controller/failsafe/Destroy() - running = FALSE - ..() - return QDEL_HINT_HARDDEL_NOW - -/datum/controller/failsafe/proc/Loop() - while(running) - lasttick = world.time - if(!Master) - // Replace the missing Master! This should never, ever happen. - new /datum/controller/master() - // Only poke it if overrides are not in effect. - if(processing_interval > 0) - if(Master.processing && Master.iteration) - // Check if processing is done yet. - if(Master.iteration == master_iteration) - switch(defcon) - if(4,5) - --defcon - if(3) - to_chat(GLOB.admins, "Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.") - --defcon - if(2) - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") - --defcon - if(1) - - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...") - --defcon - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else if(rtn < 0) - log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") - to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") - //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again - //no need to handle that specially when defcon 0 can handle it - if(0) //DEFCON 0! (mc failed to restart) - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else - defcon = min(defcon + 1,5) - master_iteration = Master.iteration - if (defcon <= 1) - sleep(processing_interval*2) - else - sleep(processing_interval) - else - defcon = 5 - sleep(initial(processing_interval)) - -/datum/controller/failsafe/proc/defcon_pretty() - return defcon - -/datum/controller/failsafe/stat_entry() - if(!statclick) - statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - - stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) + /** + * Failsafe + * + * Pretty much pokes the MC to make sure it's still alive. + **/ + +GLOBAL_REAL(Failsafe, /datum/controller/failsafe) + +/datum/controller/failsafe // This thing pretty much just keeps poking the master controller + name = "Failsafe" + + // The length of time to check on the MC (in deciseconds). + // Set to 0 to disable. + var/processing_interval = 20 + // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. + var/defcon = 5 + //the world.time of the last check, so the mc can restart US if we hang. + // (Real friends look out for *eachother*) + var/lasttick = 0 + + // Track the MC iteration to make sure its still on track. + var/master_iteration = 0 + var/running = TRUE + +/datum/controller/failsafe/New() + // Highlander-style: there can only be one! Kill off the old and replace it with the new. + if(Failsafe != src) + if(istype(Failsafe)) + qdel(Failsafe) + Failsafe = src + Initialize() + +/datum/controller/failsafe/Initialize() + set waitfor = 0 + Failsafe.Loop() + if(!QDELETED(src)) + qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us + +/datum/controller/failsafe/Destroy() + running = FALSE + ..() + return QDEL_HINT_HARDDEL_NOW + +/datum/controller/failsafe/proc/Loop() + while(running) + lasttick = world.time + if(!Master) + // Replace the missing Master! This should never, ever happen. + new /datum/controller/master() + // Only poke it if overrides are not in effect. + if(processing_interval > 0) + if(Master.processing && Master.iteration) + // Check if processing is done yet. + if(Master.iteration == master_iteration) + switch(defcon) + if(4,5) + --defcon + if(3) + message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.") + --defcon + if(2) + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") + --defcon + if(1) + + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...") + --defcon + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else if(rtn < 0) + log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") + to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") + //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again + //no need to handle that specially when defcon 0 can handle it + if(0) //DEFCON 0! (mc failed to restart) + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else + defcon = min(defcon + 1,5) + master_iteration = Master.iteration + if (defcon <= 1) + sleep(processing_interval*2) + else + sleep(processing_interval) + else + defcon = 5 + sleep(initial(processing_interval)) + +/datum/controller/failsafe/proc/defcon_pretty() + return defcon + +/datum/controller/failsafe/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) + + stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index 74536c7f0f..301ceeac21 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -1,269 +1,281 @@ -SUBSYSTEM_DEF(blackbox) - name = "Blackbox" - wait = 6000 - flags = SS_NO_TICK_CHECK | SS_NO_INIT - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - init_order = INIT_ORDER_BLACKBOX - - var/list/msg_common = list() - var/list/msg_science = list() - var/list/msg_command = list() - var/list/msg_medical = list() - var/list/msg_engineering = list() - var/list/msg_security = list() - var/list/msg_deathsquad = list() - var/list/msg_syndicate = list() - var/list/msg_service = list() - var/list/msg_cargo = list() - var/list/msg_other = list() - - var/list/feedback = list() //list of datum/feedback_variable - - var/sealed = FALSE //time to stop tracking stats? - -//poll population -/datum/controller/subsystem/blackbox/fire() - if(!SSdbcore.Connect()) - return - var/playercount = 0 - for(var/mob/M in GLOB.player_list) - if(M.client) - playercount += 1 - var/admincount = GLOB.admins.len - var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')") - query_record_playercount.Execute() - -/datum/controller/subsystem/blackbox/Recover() - msg_common = SSblackbox.msg_common - msg_science = SSblackbox.msg_science - msg_command = SSblackbox.msg_command - msg_medical = SSblackbox.msg_medical - msg_engineering = SSblackbox.msg_engineering - msg_security = SSblackbox.msg_security - msg_deathsquad = SSblackbox.msg_deathsquad - msg_syndicate = SSblackbox.msg_syndicate - msg_service = SSblackbox.msg_service - msg_cargo = SSblackbox.msg_cargo - msg_other = SSblackbox.msg_other - - feedback = SSblackbox.feedback - - sealed = SSblackbox.sealed - -//no touchie -/datum/controller/subsystem/blackbox/can_vv_get(var_name) - if(var_name == "feedback") - return FALSE - return ..() - -/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value) - return FALSE - -/datum/controller/subsystem/blackbox/Shutdown() - sealed = FALSE - set_val("ahelp_unresolved", GLOB.ahelp_tickets.active_tickets.len) - - var/pda_msg_amt = 0 - var/rc_msg_amt = 0 - - for (var/obj/machinery/message_server/MS in GLOB.message_servers) - if (MS.pda_msgs.len > pda_msg_amt) - pda_msg_amt = MS.pda_msgs.len - if (MS.rc_msgs.len > rc_msg_amt) - rc_msg_amt = MS.rc_msgs.len - - set_details("radio_usage","") - - add_details("radio_usage","COM-[msg_common.len]") - add_details("radio_usage","SCI-[msg_science.len]") - add_details("radio_usage","HEA-[msg_command.len]") - add_details("radio_usage","MED-[msg_medical.len]") - add_details("radio_usage","ENG-[msg_engineering.len]") - add_details("radio_usage","SEC-[msg_security.len]") - add_details("radio_usage","DTH-[msg_deathsquad.len]") - add_details("radio_usage","SYN-[msg_syndicate.len]") - add_details("radio_usage","SRV-[msg_service.len]") - add_details("radio_usage","CAR-[msg_cargo.len]") - add_details("radio_usage","OTH-[msg_other.len]") - add_details("radio_usage","PDA-[pda_msg_amt]") - add_details("radio_usage","RC-[rc_msg_amt]") - - if (!SSdbcore.Connect()) - return - - var/list/sqlrowlist = list() - - for (var/datum/feedback_variable/FV in feedback) - sqlrowlist += list(list("time" = "Now()", "round_id" = GLOB.round_id, "var_name" = "'[sanitizeSQL(FV.get_variable())]'", "var_value" = FV.get_value(), "details" = "'[sanitizeSQL(FV.get_details())]'")) - - if (!length(sqlrowlist)) - return - - SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE) - - -/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq) - if(sealed) - return - switch(freq) - if(1459) - msg_common += blackbox_msg - if(1351) - msg_science += blackbox_msg - if(1353) - msg_command += blackbox_msg - if(1355) - msg_medical += blackbox_msg - if(1357) - msg_engineering += blackbox_msg - if(1359) - msg_security += blackbox_msg - if(1441) - msg_deathsquad += blackbox_msg - if(1213) - msg_syndicate += blackbox_msg - if(1349) - msg_service += blackbox_msg - if(1347) - msg_cargo += blackbox_msg - else - msg_other += blackbox_msg - -/datum/controller/subsystem/blackbox/proc/find_feedback_datum(variable) - for(var/datum/feedback_variable/FV in feedback) - if(FV.get_variable() == variable) - return FV - - var/datum/feedback_variable/FV = new(variable) - feedback += FV - return FV - -/datum/controller/subsystem/blackbox/proc/set_val(variable, value) - if(sealed) - return - var/datum/feedback_variable/FV = find_feedback_datum(variable) - FV.set_value(value) - -/datum/controller/subsystem/blackbox/proc/inc(variable, value) - if(sealed) - return - var/datum/feedback_variable/FV = find_feedback_datum(variable) - FV.inc(value) - -/datum/controller/subsystem/blackbox/proc/dec(variable,value) - if(sealed) - return - var/datum/feedback_variable/FV = find_feedback_datum(variable) - FV.dec(value) - -/datum/controller/subsystem/blackbox/proc/set_details(variable,details) - if(sealed) - return - var/datum/feedback_variable/FV = find_feedback_datum(variable) - FV.set_details(details) - -/datum/controller/subsystem/blackbox/proc/add_details(variable,details) - if(sealed) - return - var/datum/feedback_variable/FV = find_feedback_datum(variable) - FV.add_details(details) - -/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L) - if(sealed) - return - if(!SSdbcore.Connect()) - return - if(!L || !L.key || !L.mind) - return - var/turf/T = get_turf(L) - var/area/placeofdeath = get_area(T.loc) - var/sqlname = sanitizeSQL(L.real_name) - var/sqlkey = sanitizeSQL(L.ckey) - var/sqljob = sanitizeSQL(L.mind.assigned_role) - var/sqlspecial = sanitizeSQL(L.mind.special_role) - var/sqlpod = sanitizeSQL(placeofdeath.name) - var/laname - var/lakey - if(L.lastattacker && ismob(L.lastattacker)) - var/mob/LA = L.lastattacker - laname = sanitizeSQL(LA.real_name) - lakey = sanitizeSQL(LA.key) - var/sqlbrute = sanitizeSQL(L.getBruteLoss()) - var/sqlfire = sanitizeSQL(L.getFireLoss()) - var/sqlbrain = sanitizeSQL(L.getBrainLoss()) - var/sqloxy = sanitizeSQL(L.getOxyLoss()) - var/sqltox = sanitizeSQL(L.getToxLoss()) - var/sqlclone = sanitizeSQL(L.getCloneLoss()) - var/sqlstamina = sanitizeSQL(L.getStaminaLoss()) - var/x_coord = sanitizeSQL(L.x) - var/y_coord = sanitizeSQL(L.y) - var/z_coord = sanitizeSQL(L.z) - var/map = sanitizeSQL(SSmapping.config.map_name) - var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina])") - query_report_death.Execute() - -/datum/controller/subsystem/blackbox/proc/Seal() - if(sealed) - return - if(IsAdminAdvancedProcCall()) - var/msg = "[key_name_admin(usr)] sealed the blackbox!" - message_admins(msg) - log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].") - sealed = TRUE - -//feedback variable datum, for storing all kinds of data -/datum/feedback_variable - var/variable - var/value - var/details - -/datum/feedback_variable/New(param_variable, param_value = 0) - variable = param_variable - value = param_value - -/datum/feedback_variable/proc/inc(num = 1) - if (isnum(value)) - value += num - else - value = text2num(value) - if (isnum(value)) - value += num - else - value = num - -/datum/feedback_variable/proc/dec(num = 1) - if (isnum(value)) - value -= num - else - value = text2num(value) - if (isnum(value)) - value -= num - else - value = -num - -/datum/feedback_variable/proc/set_value(num) - if (isnum(num)) - value = num - -/datum/feedback_variable/proc/get_value() - if (!isnum(value)) - return 0 - return value - -/datum/feedback_variable/proc/get_variable() - return variable - +SUBSYSTEM_DEF(blackbox) + name = "Blackbox" + wait = 6000 + flags = SS_NO_TICK_CHECK + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + init_order = INIT_ORDER_BLACKBOX + + var/list/msg_common = list() + var/list/msg_science = list() + var/list/msg_command = list() + var/list/msg_medical = list() + var/list/msg_engineering = list() + var/list/msg_security = list() + var/list/msg_deathsquad = list() + var/list/msg_syndicate = list() + var/list/msg_service = list() + var/list/msg_cargo = list() + var/list/msg_other = list() + + var/list/feedback = list() //list of datum/feedback_variable + var/triggertime = 0 + var/sealed = FALSE //time to stop tracking stats? + + +/datum/controller/subsystem/blackbox/Initialize() + triggertime = world.time + . = ..() + +//poll population +/datum/controller/subsystem/blackbox/fire() + if(!SSdbcore.Connect()) + return + var/playercount = 0 + for(var/mob/M in GLOB.player_list) + if(M.client) + playercount += 1 + var/admincount = GLOB.admins.len + var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port, round_id) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]')") + query_record_playercount.Execute() + + if(config.use_exp_tracking) + if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check + update_exp(10,FALSE) + + +/datum/controller/subsystem/blackbox/Recover() + msg_common = SSblackbox.msg_common + msg_science = SSblackbox.msg_science + msg_command = SSblackbox.msg_command + msg_medical = SSblackbox.msg_medical + msg_engineering = SSblackbox.msg_engineering + msg_security = SSblackbox.msg_security + msg_deathsquad = SSblackbox.msg_deathsquad + msg_syndicate = SSblackbox.msg_syndicate + msg_service = SSblackbox.msg_service + msg_cargo = SSblackbox.msg_cargo + msg_other = SSblackbox.msg_other + + feedback = SSblackbox.feedback + + sealed = SSblackbox.sealed + +//no touchie +/datum/controller/subsystem/blackbox/can_vv_get(var_name) + if(var_name == "feedback") + return FALSE + return ..() + +/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value) + return FALSE + +/datum/controller/subsystem/blackbox/Shutdown() + sealed = FALSE + set_val("ahelp_unresolved", GLOB.ahelp_tickets.active_tickets.len) + + var/pda_msg_amt = 0 + var/rc_msg_amt = 0 + + for (var/obj/machinery/message_server/MS in GLOB.message_servers) + if (MS.pda_msgs.len > pda_msg_amt) + pda_msg_amt = MS.pda_msgs.len + if (MS.rc_msgs.len > rc_msg_amt) + rc_msg_amt = MS.rc_msgs.len + + set_details("radio_usage","") + + add_details("radio_usage","COM-[msg_common.len]") + add_details("radio_usage","SCI-[msg_science.len]") + add_details("radio_usage","HEA-[msg_command.len]") + add_details("radio_usage","MED-[msg_medical.len]") + add_details("radio_usage","ENG-[msg_engineering.len]") + add_details("radio_usage","SEC-[msg_security.len]") + add_details("radio_usage","DTH-[msg_deathsquad.len]") + add_details("radio_usage","SYN-[msg_syndicate.len]") + add_details("radio_usage","SRV-[msg_service.len]") + add_details("radio_usage","CAR-[msg_cargo.len]") + add_details("radio_usage","OTH-[msg_other.len]") + add_details("radio_usage","PDA-[pda_msg_amt]") + add_details("radio_usage","RC-[rc_msg_amt]") + + if (!SSdbcore.Connect()) + return + + var/list/sqlrowlist = list() + + for (var/datum/feedback_variable/FV in feedback) + sqlrowlist += list(list("time" = "Now()", "round_id" = GLOB.round_id, "var_name" = "'[sanitizeSQL(FV.get_variable())]'", "var_value" = FV.get_value(), "details" = "'[sanitizeSQL(FV.get_details())]'")) + + if (!length(sqlrowlist)) + return + + SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE) + + +/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq) + if(sealed) + return + switch(freq) + if(1459) + msg_common += blackbox_msg + if(1351) + msg_science += blackbox_msg + if(1353) + msg_command += blackbox_msg + if(1355) + msg_medical += blackbox_msg + if(1357) + msg_engineering += blackbox_msg + if(1359) + msg_security += blackbox_msg + if(1441) + msg_deathsquad += blackbox_msg + if(1213) + msg_syndicate += blackbox_msg + if(1349) + msg_service += blackbox_msg + if(1347) + msg_cargo += blackbox_msg + else + msg_other += blackbox_msg + +/datum/controller/subsystem/blackbox/proc/find_feedback_datum(variable) + for(var/datum/feedback_variable/FV in feedback) + if(FV.get_variable() == variable) + return FV + + var/datum/feedback_variable/FV = new(variable) + feedback += FV + return FV + +/datum/controller/subsystem/blackbox/proc/set_val(variable, value) + if(sealed) + return + var/datum/feedback_variable/FV = find_feedback_datum(variable) + FV.set_value(value) + +/datum/controller/subsystem/blackbox/proc/inc(variable, value) + if(sealed) + return + var/datum/feedback_variable/FV = find_feedback_datum(variable) + FV.inc(value) + +/datum/controller/subsystem/blackbox/proc/dec(variable,value) + if(sealed) + return + var/datum/feedback_variable/FV = find_feedback_datum(variable) + FV.dec(value) + +/datum/controller/subsystem/blackbox/proc/set_details(variable,details) + if(sealed) + return + var/datum/feedback_variable/FV = find_feedback_datum(variable) + FV.set_details(details) + +/datum/controller/subsystem/blackbox/proc/add_details(variable,details) + if(sealed) + return + var/datum/feedback_variable/FV = find_feedback_datum(variable) + FV.add_details(details) + +/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L) + if(sealed) + return + if(!SSdbcore.Connect()) + return + if(!L || !L.key || !L.mind) + return + var/turf/T = get_turf(L) + var/area/placeofdeath = get_area(T.loc) + var/sqlname = sanitizeSQL(L.real_name) + var/sqlkey = sanitizeSQL(L.ckey) + var/sqljob = sanitizeSQL(L.mind.assigned_role) + var/sqlspecial = sanitizeSQL(L.mind.special_role) + var/sqlpod = sanitizeSQL(placeofdeath.name) + var/laname + var/lakey + if(L.lastattacker && ismob(L.lastattacker)) + var/mob/LA = L.lastattacker + laname = sanitizeSQL(LA.real_name) + lakey = sanitizeSQL(LA.key) + var/sqlbrute = sanitizeSQL(L.getBruteLoss()) + var/sqlfire = sanitizeSQL(L.getFireLoss()) + var/sqlbrain = sanitizeSQL(L.getBrainLoss()) + var/sqloxy = sanitizeSQL(L.getOxyLoss()) + var/sqltox = sanitizeSQL(L.getToxLoss()) + var/sqlclone = sanitizeSQL(L.getCloneLoss()) + var/sqlstamina = sanitizeSQL(L.getStaminaLoss()) + var/x_coord = sanitizeSQL(L.x) + var/y_coord = sanitizeSQL(L.y) + var/z_coord = sanitizeSQL(L.z) + var/last_words = sanitizeSQL(L.last_words) + var/suicide = sanitizeSQL(L.suiciding) + var/map = sanitizeSQL(SSmapping.config.map_name) + var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, last_words, suicide) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[last_words]', [suicide])") + query_report_death.Execute() + +/datum/controller/subsystem/blackbox/proc/Seal() + if(sealed) + return + if(IsAdminAdvancedProcCall()) + var/msg = "[key_name_admin(usr)] sealed the blackbox!" + message_admins(msg) + log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].") + sealed = TRUE + +//feedback variable datum, for storing all kinds of data +/datum/feedback_variable + var/variable + var/value + var/list/details + +/datum/feedback_variable/New(param_variable, param_value = 0) + variable = param_variable + value = param_value + +/datum/feedback_variable/proc/inc(num = 1) + if (isnum(value)) + value += num + else + value = text2num(value) + if (isnum(value)) + value += num + else + value = num + +/datum/feedback_variable/proc/dec(num = 1) + if (isnum(value)) + value -= num + else + value = text2num(value) + if (isnum(value)) + value -= num + else + value = -num + +/datum/feedback_variable/proc/set_value(num) + if (isnum(num)) + value = num + +/datum/feedback_variable/proc/get_value() + if (!isnum(value)) + return 0 + return value + +/datum/feedback_variable/proc/get_variable() + return variable + /datum/feedback_variable/proc/set_details(deets) - details = "\"[deets]\"" + details = list("\"[deets]\"") /datum/feedback_variable/proc/add_details(deets) if (!details) set_details(deets) else - details += " | \"[deets]\"" - -/datum/feedback_variable/proc/get_details() - return details - -/datum/feedback_variable/proc/get_parsed() - return list(variable,value,details) \ No newline at end of file + details += "\"[deets]\"" + +/datum/feedback_variable/proc/get_details() + return details.Join(" | ") + +/datum/feedback_variable/proc/get_parsed() + return list(variable,value,details.Join(" | ")) diff --git a/code/controllers/subsystem/blackbox.dm.rej b/code/controllers/subsystem/blackbox.dm.rej new file mode 100644 index 0000000000..5bd713172b --- /dev/null +++ b/code/controllers/subsystem/blackbox.dm.rej @@ -0,0 +1,10 @@ +diff a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm (rejected hunks) +@@ -40,7 +40,7 @@ SUBSYSTEM_DEF(blackbox) + + if(config.use_exp_tracking) + if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check +- SSblackbox.update_exp(10,FALSE) ++ update_exp(10,FALSE) + + + /datum/controller/subsystem/blackbox/Recover() diff --git a/code/controllers/subsystem/disease.dm b/code/controllers/subsystem/disease.dm index c75063c256..327ba95196 100644 --- a/code/controllers/subsystem/disease.dm +++ b/code/controllers/subsystem/disease.dm @@ -14,3 +14,10 @@ SUBSYSTEM_DEF(disease) /datum/controller/subsystem/disease/stat_entry(msg) ..("P:[active_diseases.len]") + +/datum/controller/subsystem/disease/proc/get_disease_name(id) + var/datum/disease/advance/A = archive_diseases[id] + if(A.name) + return A.name + else + return "Unknown" diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index c41f422575..7816a6bdf8 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -116,6 +116,8 @@ SUBSYSTEM_DEF(events) //allows a client to trigger an event //aka Badmin Central +// > Not in modules/admin +// REEEEEEEEE /client/proc/forceEvent() set name = "Trigger Event" set category = "Fun" @@ -131,7 +133,7 @@ SUBSYSTEM_DEF(events) var/magic = "" var/holiday = "" for(var/datum/round_event_control/E in SSevents.control) - dat = "
[E]" + dat = "
[E]" if(E.holidayID) holiday += dat else if(E.wizardevent) diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 8502280aaf..d202c08d39 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -1,40 +1,44 @@ SUBSYSTEM_DEF(garbage) name = "Garbage" priority = 15 - wait = 20 + wait = 2 SECONDS flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY - var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object - var/delslasttick = 0 // number of del()'s we've done this tick - var/gcedlasttick = 0 // number of things that gc'ed last tick + var/list/collection_timeout = list(0, 2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level + + //Stat tracking + var/delslasttick = 0 // number of del()'s we've done this tick + var/gcedlasttick = 0 // number of things that gc'ed last tick var/totaldels = 0 var/totalgcs = 0 var/highest_del_time = 0 var/highest_del_tickusage = 0 - var/list/queue = list() // list of refID's of things that should be garbage collected - // refID's are associated with the time at which they time out and need to be manually del() - // we do this so we aren't constantly locating them and preventing them from being gc'd + var/list/pass_counts + var/list/fail_counts - var/list/tobequeued = list() //We store the references of things to be added to the queue separately so we can spread out GC overhead over a few ticks + var/list/items = list() // Holds our qdel_item statistics datums - var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened. - // the types are stored as strings - var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call + //Queue + var/list/queues - var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT - // all types that did not respect qdel(A, force=TRUE) and returned one - // of the immortality qdel hints - var/list/noforcerespect = list() -#ifdef TESTING - var/list/qdel_list = list() // list of all types that have been qdel()eted -#endif +/datum/controller/subsystem/garbage/PreInit() + queues = new(GC_QUEUE_COUNT) + pass_counts = new(GC_QUEUE_COUNT) + fail_counts = new(GC_QUEUE_COUNT) + for(var/i in 1 to GC_QUEUE_COUNT) + queues[i] = list() + pass_counts[i] = 0 + fail_counts[i] = 0 /datum/controller/subsystem/garbage/stat_entry(msg) - msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|" + var/list/counts = list() + for (var/list/L in queues) + counts += length(L) + msg += "Q:[counts.Join(",")]|D:[delslasttick]|G:[gcedlasttick]|" msg += "GR:" if (!(delslasttick+gcedlasttick)) msg += "n/a|" @@ -46,116 +50,179 @@ SUBSYSTEM_DEF(garbage) msg += "n/a|" else msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%" + msg += " P:[pass_counts.Join(",")]" + msg += "|F:[fail_counts.Join(",")]" ..(msg) /datum/controller/subsystem/garbage/Shutdown() - //Adds the del() log to world.log in a format condensable by the runtime condenser found in tools - if(didntgc.len || sleptDestroy.len) - var/list/dellog = list() - for(var/path in didntgc) - dellog += "Path : [path] \n" - dellog += "Failures : [didntgc[path]] \n" - if(path in sleptDestroy) - dellog += "Sleeps : [sleptDestroy[path]] \n" - sleptDestroy -= path - for(var/path in sleptDestroy) - dellog += "Path : [path] \n" - dellog += "Sleeps : [sleptDestroy[path]] \n" - text2file(dellog.Join(), "[GLOB.log_directory]/qdel.log") + //Adds the del() log to the qdel log file + var/list/dellog = list() + + //sort by how long it's wasted hard deleting + sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) + for(var/path in items) + var/datum/qdel_item/I = items[path] + dellog += "Path: [path]" + if (I.failures) + dellog += "\tFailures: [I.failures]" + dellog += "\tqdel() Count: [I.qdels]" + dellog += "\tDestroy() Cost: [I.destroy_time]ms" + if (I.hard_deletes) + dellog += "\tTotal Hard Deletes [I.hard_deletes]" + dellog += "\tTime Spent Hard Deleting: [I.hard_delete_time]ms" + if (I.slept_destroy) + dellog += "\tSleeps: [I.slept_destroy]" + if (I.no_respect_force) + dellog += "\tIgnored force: [I.no_respect_force] times" + if (I.no_hint) + dellog += "\tNo hint: [I.no_hint] times" + log_qdel(dellog.Join("\n")) /datum/controller/subsystem/garbage/fire() - HandleToBeQueued() - if(state == SS_RUNNING) - HandleQueue() - + //the fact that this resets its processing each fire (rather then resume where it left off) is intentional. + var/queue = GC_QUEUE_PREQUEUE + + while (state == SS_RUNNING) + switch (queue) + if (GC_QUEUE_PREQUEUE) + HandlePreQueue() + queue = GC_QUEUE_PREQUEUE+1 + if (GC_QUEUE_CHECK) + HandleQueue(GC_QUEUE_CHECK) + queue = GC_QUEUE_CHECK+1 + if (GC_QUEUE_HARDDELETE) + HandleQueue(GC_QUEUE_HARDDELETE) + break + if (state == SS_PAUSED) //make us wait again before the next run. - state = SS_RUNNING + state = SS_RUNNING //If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond. //Don't attempt to optimize, not worth the effort. -/datum/controller/subsystem/garbage/proc/HandleToBeQueued() - var/list/tobequeued = src.tobequeued - var/starttime = world.time - var/starttimeofday = world.timeofday - while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday) - if (MC_TICK_CHECK) - break - var/ref = tobequeued[1] - Queue(ref) - tobequeued.Cut(1, 2) +/datum/controller/subsystem/garbage/proc/HandlePreQueue() + var/list/tobequeued = queues[GC_QUEUE_PREQUEUE] + var/static/count = 0 + if (count) + var/c = count + count = 0 //so if we runtime on the Cut, we don't try again. + tobequeued.Cut(1,c+1) -/datum/controller/subsystem/garbage/proc/HandleQueue() - delslasttick = 0 - gcedlasttick = 0 - var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del() - var/list/queue = src.queue - var/starttime = world.time - var/starttimeofday = world.timeofday - while(queue.len && starttime == world.time && starttimeofday == world.timeofday) + for (var/ref in tobequeued) + count++ + Queue(ref, GC_QUEUE_PREQUEUE+1) if (MC_TICK_CHECK) break - var/refID = queue[1] + if (count) + tobequeued.Cut(1,count+1) + count = 0 + +/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_CHECK) + if (level == GC_QUEUE_CHECK) + delslasttick = 0 + gcedlasttick = 0 + var/cut_off_time = world.time - collection_timeout[level] //ignore entries newer then this + var/list/queue = queues[level] + var/static/lastlevel + var/static/count = 0 + if (count) //runtime last run before we could do this. + var/c = count + count = 0 //so if we runtime on the Cut, we don't try again. + var/list/lastqueue = queues[lastlevel] + lastqueue.Cut(1, c+1) + + lastlevel = level + + for (var/refID in queue) if (!refID) - queue.Cut(1, 2) + count++ + if (MC_TICK_CHECK) + break continue var/GCd_at_time = queue[refID] - if(GCd_at_time > time_to_kill) + if(GCd_at_time > cut_off_time) break // Everything else is newer, skip them - queue.Cut(1, 2) - var/datum/A - A = locate(refID) - if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake - #ifdef GC_FAILURE_HARD_LOOKUP - A.find_references() - #endif + count++ - // Something's still referring to the qdel'd object. Kill it. - var/type = A.type - testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --") - didntgc["[type]"]++ - - HardDelete(A) + var/datum/D + D = locate(refID) - ++delslasttick - ++totaldels - else + if (!D || D.gc_destroyed != GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake ++gcedlasttick ++totalgcs + pass_counts[level]++ + if (MC_TICK_CHECK) + break + continue -/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A) - if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) - tobequeued += A - A.gc_destroyed = GC_QUEUED_FOR_QUEUING + // Something's still referring to the qdel'd object. + fail_counts[level]++ + switch (level) + if (GC_QUEUE_CHECK) + #ifdef GC_FAILURE_HARD_LOOKUP + D.find_references() + #endif + var/type = D.type + var/datum/qdel_item/I = items[type] + testing("GC: -- \ref[D] | [type] was unable to be GC'd --") + I.failures++ + if (GC_QUEUE_HARDDELETE) + HardDelete(D) + if (MC_TICK_CHECK) + break + continue -/datum/controller/subsystem/garbage/proc/Queue(datum/A) - if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0)) + Queue(D, level+1) + + if (MC_TICK_CHECK) + break + if (count) + queue.Cut(1,count+1) + count = 0 + +/datum/controller/subsystem/garbage/proc/PreQueue(datum/D) + if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + queues[GC_QUEUE_PREQUEUE] += D + D.gc_destroyed = GC_QUEUED_FOR_QUEUING + +/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_CHECK) + if (isnull(D)) return - if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL) - HardDelete(A) + if (D.gc_destroyed == GC_QUEUED_FOR_HARD_DEL) + level = GC_QUEUE_HARDDELETE + if (level > GC_QUEUE_COUNT) + HardDelete(D) return var/gctime = world.time - var/refid = "\ref[A]" - - A.gc_destroyed = gctime + var/refid = "\ref[D]" + D.gc_destroyed = gctime + var/list/queue = queues[level] if (queue[refid]) queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list. queue[refid] = gctime -//this is purely to separate things profile wise. -/datum/controller/subsystem/garbage/proc/HardDelete(datum/A) +//this is mainly to separate things profile wise. +/datum/controller/subsystem/garbage/proc/HardDelete(datum/D) var/time = world.timeofday var/tick = TICK_USAGE var/ticktime = world.time - - var/type = A.type - var/refID = "\ref[A]" - - del(A) - + ++delslasttick + ++totaldels + var/type = D.type + var/refID = "\ref[D]" + + del(D) + tick = (TICK_USAGE-tick+((world.time-ticktime)/world.tick_lag*100)) + + var/datum/qdel_item/I = items[type] + + I.hard_deletes++ + I.hard_delete_time += TICK_DELTA_TO_MS(tick) + + if (tick > highest_del_tickusage) highest_del_tickusage = tick time = world.timeofday - time @@ -166,18 +233,33 @@ SUBSYSTEM_DEF(garbage) if (time > 10) log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)") message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).") - postpone(time/5) - -/datum/controller/subsystem/garbage/proc/HardQueue(datum/A) - if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) - tobequeued += A - A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL + postpone(time) + +/datum/controller/subsystem/garbage/proc/HardQueue(datum/D) + if (D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + queues[GC_QUEUE_PREQUEUE] += D + D.gc_destroyed = GC_QUEUED_FOR_HARD_DEL /datum/controller/subsystem/garbage/Recover() - if (istype(SSgarbage.queue)) - queue |= SSgarbage.queue - if (istype(SSgarbage.tobequeued)) - tobequeued |= SSgarbage.tobequeued + if (istype(SSgarbage.queues)) + for (var/i in 1 to SSgarbage.queues.len) + queues[i] |= SSgarbage.queues[i] + + +/datum/qdel_item + var/name = "" + var/qdels = 0 //Total number of times it's passed thru qdel. + var/destroy_time = 0 //Total amount of milliseconds spent processing this type's Destroy() + var/failures = 0 //Times it was queued for soft deletion but failed to soft delete. + var/hard_deletes = 0 //Different from failures because it also includes QDEL_HINT_HARDDEL deletions + var/hard_delete_time = 0//Total amount of milliseconds spent hard deleting this type. + var/no_respect_force = 0//Number of times it's not respected force=TRUE + var/no_hint = 0 //Number of times it's not even bother to give a qdel hint + var/slept_destroy = 0 //Number of times it's slept in its destroy + +/datum/qdel_item/New(mytype) + name = "[mytype]" + // Should be treated as a replacement for the 'del' keyword. // Datums passed to this will be given a chance to clean up references to allow the GC to collect them. @@ -185,21 +267,27 @@ SUBSYSTEM_DEF(garbage) if(!istype(D)) del(D) return -#ifdef TESTING - SSgarbage.qdel_list += "[D.type]" -#endif + var/datum/qdel_item/I = SSgarbage.items[D.type] + if (!I) + I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type) + I.qdels++ + + if(isnull(D.gc_destroyed)) D.SendSignal(COMSIG_PARENT_QDELETED) D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED var/start_time = world.time + var/start_tick = world.tick_usage var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up. if(world.time != start_time) - SSgarbage.sleptDestroy["[D.type]"]++ + I.slept_destroy++ + else + I.destroy_time += TICK_USAGE_TO_MS(start_tick) if(!D) return switch(hint) if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion. - SSgarbage.QueueForQueuing(D) + SSgarbage.PreQueue(D) if (QDEL_HINT_IWILLGC) D.gc_destroyed = world.time return @@ -209,28 +297,33 @@ SUBSYSTEM_DEF(garbage) return // Returning LETMELIVE after being told to force destroy // indicates the objects Destroy() does not respect force - if(!SSgarbage.noforcerespect["[D.type]"]) - SSgarbage.noforcerespect["[D.type]"] = "[D.type]" + #ifdef TESTING + if(!I.no_respect_force) testing("WARNING: [D.type] has been force deleted, but is \ returning an immortal QDEL_HINT, indicating it does \ not respect the force flag for qdel(). It has been \ placed in the queue, further instances of this type \ will also be queued.") - SSgarbage.QueueForQueuing(D) + #endif + I.no_respect_force++ + + SSgarbage.PreQueue(D) if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate() SSgarbage.HardQueue(D) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. SSgarbage.HardDelete(D) if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. - SSgarbage.QueueForQueuing(D) + SSgarbage.PreQueue(D) #ifdef TESTING D.find_references() #endif else - if(!SSgarbage.noqdelhint["[D.type]"]) - SSgarbage.noqdelhint["[D.type]"] = "[D.type]" + #ifdef TESTING + if(!I.no_hint) testing("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") - SSgarbage.QueueForQueuing(D) + #endif + I.no_hint++ + SSgarbage.PreQueue(D) else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") @@ -281,15 +374,6 @@ SUBSYSTEM_DEF(garbage) SSgarbage.can_fire = 1 SSgarbage.next_fire = world.time + world.tick_lag -/client/verb/purge_all_destroyed_objects() - set category = "Debug" - while(SSgarbage.queue.len) - var/datum/o = locate(SSgarbage.queue[1]) - if(istype(o) && o.gc_destroyed) - del(o) - SSgarbage.totaldels++ - SSgarbage.queue.Cut(1, 2) - /datum/verb/qdel_then_find_references() set category = "Debug" set name = "qdel() then Find References" @@ -300,24 +384,6 @@ SUBSYSTEM_DEF(garbage) if(!running_find_references) find_references(TRUE) -/client/verb/show_qdeleted() - set category = "Debug" - set name = "Show qdel() Log" - set desc = "Render the qdel() log and display it" - - var/dat = "List of things that have been qdel()eted this round

" - - var/tmplist = list() - for(var/elem in SSgarbage.qdel_list) - if(!(elem in tmplist)) - tmplist[elem] = 0 - tmplist[elem]++ - - for(var/path in tmplist) - dat += "[path] - [tmplist[path]] times
" - - usr << browse(dat, "window=qdeletedlog") - /datum/proc/DoSearchVar(X, Xname) if(usr && usr.client && !usr.client.running_find_references) return if(istype(X, /datum)) diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index f28373ef22..d64739698a 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -73,6 +73,8 @@ SUBSYSTEM_DEF(job) return 0 if(!job.player_old_enough(player.client)) return 0 + if(job.required_playtime_remaining(player.client)) + return 0 var/position_limit = job.total_positions if(!latejoin) position_limit = job.spawn_positions @@ -95,6 +97,9 @@ SUBSYSTEM_DEF(job) if(!job.player_old_enough(player.client)) Debug("FOC player not old enough, Player: [player]") continue + if(job.required_playtime_remaining(player.client)) + Debug("FOC player not enough xp, Player: [player]") + continue if(flag && (!(flag in player.client.prefs.be_special))) Debug("FOC flag failed, Player: [player], Flag: [flag], ") continue @@ -130,6 +135,10 @@ SUBSYSTEM_DEF(job) Debug("GRJ player not old enough, Player: [player]") continue + if(job.required_playtime_remaining(player.client)) + Debug("GRJ player not enough xp, Player: [player]") + continue + if(player.mind && job.title in player.mind.restricted_roles) Debug("GRJ incompatible with antagonist role, Player: [player], Job: [job.title]") continue @@ -300,6 +309,10 @@ SUBSYSTEM_DEF(job) Debug("DO player not old enough, Player: [player], Job:[job.title]") continue + if(job.required_playtime_remaining(player.client)) + Debug("DO player not enough xp, Player: [player], Job:[job.title]") + continue + if(player.mind && job.title in player.mind.restricted_roles) Debug("DO incompatible with antagonist role, Player: [player], Job:[job.title]") continue @@ -407,7 +420,7 @@ SUBSYSTEM_DEF(job) if(job && H) job.after_spawn(H, M) - + handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role) return H @@ -463,6 +476,9 @@ SUBSYSTEM_DEF(job) if(!job.player_old_enough(player.client)) level6++ continue + if(job.required_playtime_remaining(player.client)) + level6++ + continue if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) level1++ else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index 05062a6314..8b8bc81ad2 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -2,15 +2,15 @@ SUBSYSTEM_DEF(persistence) name = "Persistence" init_order = INIT_ORDER_PERSISTENCE flags = SS_NO_FIRE - var/savefile/secret_satchels + var/secret_satchels var/list/satchel_blacklist = list() //this is a typecache var/list/new_secret_satchels = list() //these are objects - var/old_secret_satchels = "" + var/list/old_secret_satchels = "" var/list/obj/structure/chisel_message/chisel_messages = list() var/list/saved_messages = list() - var/savefile/trophy_sav + var/trophy_sav var/list/saved_trophies = list() /datum/controller/subsystem/persistence/Initialize() @@ -21,67 +21,53 @@ SUBSYSTEM_DEF(persistence) ..() /datum/controller/subsystem/persistence/proc/LoadSatchels() - secret_satchels = new /savefile("data/npc_saves/SecretSatchels.sav") + secret_satchels = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json") + if(!fexists(secret_satchels)) + return satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar)) - secret_satchels[SSmapping.config.map_name] >> old_secret_satchels - - var/list/expanded_old_satchels = list() - var/placed_satchels = 0 - - if(!isnull(old_secret_satchels)) - expanded_old_satchels = splittext(old_secret_satchels,"#") - if(PlaceSecretSatchel(expanded_old_satchels)) - placed_satchels++ - else - expanded_old_satchels.len = 0 + var/list/json = list() + json = json_decode(file2text(secret_satchels)) + old_secret_satchels = json["data"] + var/placed_satchel = 0 + if(old_secret_satchels.len) + if(old_secret_satchels.len >= 20) //guards against low drop pools assuring that one player cannot reliably find his own gear. + var/pos = rand(1, old_secret_satchels.len) + old_secret_satchels.Cut(pos, pos+1) + var/obj/item/storage/backpack/satchel/flat/F = new() + F.x = old_secret_satchels[pos]["x"] + F.y = old_secret_satchels[pos]["y"] + F.z = ZLEVEL_STATION + var/path = text2path(old_secret_satchels[pos]["saved_obj"]) + if(!ispath(path)) + return + if(isfloorturf(F.loc) && !istype(F.loc, /turf/open/floor/plating/)) + F.hide(1) + new path(F) + placed_satchel++ var/list/free_satchels = list() for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,ZLEVEL_STATION), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,ZLEVEL_STATION)))) //Nontrivially expensive but it's roundstart only if(isfloorturf(T) && !istype(T, /turf/open/floor/plating/)) free_satchels += new /obj/item/storage/backpack/satchel/flat/secret(T) - if(!isemptylist(free_satchels) && ((free_satchels.len + placed_satchels) >= (50 - expanded_old_satchels.len) * 0.1)) //up to six tiles, more than enough to kill anything that moves + if(!isemptylist(free_satchels) && ((free_satchels.len + placed_satchel) >= (50 - old_secret_satchels.len) * 0.1)) //up to six tiles, more than enough to kill anything that moves break -/datum/controller/subsystem/persistence/proc/PlaceSecretSatchel(list/expanded_old_satchels) - var/satchel_string - - if(expanded_old_satchels.len >= 20) //guards against low drop pools assuring that one player cannot reliably find his own gear. - satchel_string = pick_n_take(expanded_old_satchels) - - old_secret_satchels = jointext(expanded_old_satchels,"#") - WRITE_FILE(secret_satchels[SSmapping.config.map_name], old_secret_satchels) - - var/list/chosen_satchel = splittext(satchel_string,"|") - if(!chosen_satchel || isemptylist(chosen_satchel) || chosen_satchel.len != 3) //Malformed - return 0 - - var/path = text2path(chosen_satchel[3]) //If the item no longer exist, this returns null - if(!path) - return 0 - - var/obj/item/storage/backpack/satchel/flat/F = new() - F.x = text2num(chosen_satchel[1]) - F.y = text2num(chosen_satchel[2]) - F.z = ZLEVEL_STATION - if(isfloorturf(F.loc) && !istype(F.loc, /turf/open/floor/plating/)) - F.hide(1) - new path(F) - return 1 - /datum/controller/subsystem/persistence/proc/LoadPoly() for(var/mob/living/simple_animal/parrot/Poly/P in GLOB.living_mob_list) twitterize(P.speech_buffer, "polytalk") break //Who's been duping the bird?! /datum/controller/subsystem/persistence/proc/LoadChiselMessages() - var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav") - var/saved_json - chisel_messages_sav[SSmapping.config.map_name] >> saved_json + var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json") + if(!fexists(json_file)) + return + var/list/json + json = json_decode(file2text(json_file)) - if(!saved_json) + if(!json) return - var/list/saved_messages = json_decode(saved_json) + var/list/saved_messages = json["data"] for(var/item in saved_messages) if(!islist(item)) @@ -109,19 +95,16 @@ SUBSYSTEM_DEF(persistence) log_world("Loaded [saved_messages.len] engraved messages on map [SSmapping.config.map_name]") /datum/controller/subsystem/persistence/proc/LoadTrophies() - trophy_sav = new /savefile("data/npc_saves/TrophyItems.sav") - var/saved_json - trophy_sav >> saved_json + trophy_sav = file("data/npc_saves/TrophyItems.json") + if(!fexists(trophy_sav)) + return + var/list/json = list() + json = json_decode(file2text(trophy_sav)) - if(!saved_json) + if(!json) return - var/decoded_json = json_decode(saved_json) - - if(!islist(decoded_json)) - return - - saved_trophies = decoded_json + saved_trophies = json["data"] SetUpTrophies(saved_trophies.Copy()) @@ -156,6 +139,7 @@ SUBSYSTEM_DEF(persistence) CollectTrophies() /datum/controller/subsystem/persistence/proc/CollectSecretSatchels() + var/list/satchels = list() for(var/A in new_secret_satchels) var/obj/item/storage/backpack/satchel/flat/F = A if(QDELETED(F) || F.z != ZLEVEL_STATION || F.invisibility != INVISIBILITY_MAXIMUM) @@ -170,25 +154,37 @@ SUBSYSTEM_DEF(persistence) savable_obj += O.type if(isemptylist(savable_obj)) continue - old_secret_satchels += "[F.x]|[F.y]|[pick(savable_obj)]#" - WRITE_FILE(secret_satchels[SSmapping.config.map_name], old_secret_satchels) + var/list/data = list() + data["x"] = F.x + data["y"] = F.y + data["saved_obj"] = pick(savable_obj) + satchels += list(data) + var/list/file_data = list() + file_data["data"] = satchels + fdel(secret_satchels) + WRITE_FILE(secret_satchels, json_encode(file_data)) /datum/controller/subsystem/persistence/proc/CollectChiselMessages() - var/savefile/chisel_messages_sav = new /savefile("data/npc_saves/ChiselMessages.sav") + var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json") for(var/obj/structure/chisel_message/M in chisel_messages) saved_messages += list(M.pack()) log_world("Saved [saved_messages.len] engraved messages on map [SSmapping.config.map_name]") - - WRITE_FILE(chisel_messages_sav[SSmapping.config.map_name], json_encode(saved_messages)) + var/list/file_data = list() + file_data["data"] = saved_messages + fdel(json_file) + WRITE_FILE(json_file, json_encode(file_data)) /datum/controller/subsystem/persistence/proc/SaveChiselMessage(obj/structure/chisel_message/M) saved_messages += list(M.pack()) // dm eats one list /datum/controller/subsystem/persistence/proc/CollectTrophies() - WRITE_FILE(trophy_sav, json_encode(saved_trophies)) + var/list/file_data = list() + file_data["data"] = saved_trophies + fdel(trophy_sav) + WRITE_FILE(trophy_sav, json_encode(file_data)) /datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T) if(!T.added_roundstart && T.showpiece) @@ -196,4 +192,4 @@ SUBSYSTEM_DEF(persistence) data["path"] = T.showpiece.type data["message"] = T.trophy_message data["placer_key"] = T.placer_key - saved_trophies += list(data) + saved_trophies += list(data) \ No newline at end of file diff --git a/code/controllers/subsystem/server_maint.dm.rej b/code/controllers/subsystem/server_maint.dm.rej new file mode 100644 index 0000000000..486375b505 --- /dev/null +++ b/code/controllers/subsystem/server_maint.dm.rej @@ -0,0 +1,30 @@ +diff a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm (rejected hunks) +@@ -6,18 +6,16 @@ SUBSYSTEM_DEF(server_maint) + flags = SS_POST_FIRE_TIMING|SS_FIRE_IN_LOBBY + priority = 10 + var/list/currentrun +- var/triggertime = null + + /datum/controller/subsystem/server_maint/Initialize(timeofday) + if (config.hub) + world.visibility = 1 +- triggertime = REALTIMEOFDAY + ..() + + /datum/controller/subsystem/server_maint/fire(resumed = FALSE) + if(!resumed) + src.currentrun = GLOB.clients.Copy() +- ++ + var/list/currentrun = src.currentrun + var/round_started = SSticker.HasRoundStarted() + +@@ -39,8 +37,3 @@ SUBSYSTEM_DEF(server_maint) + return + + #undef PING_BUFFER_TIME +- if(config.sql_enabled) +- sql_poll_population() +- if(config.use_exp_tracking) +- if(REALTIMEOFDAY > (triggertime +3000)) //server maint fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire +- update_exp(10,0) diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index ea91e8a688..f9774f8cb5 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -341,7 +341,7 @@ SUBSYSTEM_DEF(shuttle) if(M.request(getDock(destination))) return 2 else - if(M.dock(getDock(destination))) + if(M.dock(getDock(destination)) != DOCKING_SUCCESS) return 2 return 0 //dock successful @@ -356,7 +356,7 @@ SUBSYSTEM_DEF(shuttle) if(M.request(D)) return 2 else - if(M.dock(D)) + if(M.dock(D) != DOCKING_SUCCESS) return 2 return 0 //dock successful diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 79972e7017..cc82822847 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -465,6 +465,12 @@ SUBSYSTEM_DEF(ticker) to_chat(world, "


The round has ended.") +/* var/nocredits = config.no_credits_round_end + for(var/client/C in GLOB.clients) + if(!C.credits && !nocredits) + C.RollCredits() + C.playtitlemusic(40)*/ + //Player status report for(var/mob/Player in GLOB.mob_list) if(Player.mind && !isnewplayer(Player)) @@ -513,7 +519,7 @@ SUBSYSTEM_DEF(ticker) //Silicon laws report for (var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) - if (aiPlayer.stat != 2 && aiPlayer.mind) + if (aiPlayer.stat != DEAD && aiPlayer.mind) to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws at the end of the round were:") aiPlayer.show_laws(1) else if (aiPlayer.mind) //if the dead ai has a mind, use its key instead @@ -533,7 +539,7 @@ SUBSYSTEM_DEF(ticker) for (var/mob/living/silicon/robot/robo in GLOB.mob_list) if (!robo.connected_ai && robo.mind) - if (robo.stat != 2) + if (robo.stat != DEAD) to_chat(world, "[robo.name] (Played by: [robo.mind.key]) survived as an AI-less borg! Its laws were:") else to_chat(world, "[robo.name] (Played by: [robo.mind.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index c8d5b69e61..804e430ef9 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -69,7 +69,7 @@ SUBSYSTEM_DEF(timer) if (ctime_timer.timeToRun <= REALTIMEOFDAY) --clienttime_timers.len var/datum/callback/callBack = ctime_timer.callBack - ctime_timer.spent = TRUE + ctime_timer.spent = REALTIMEOFDAY callBack.InvokeAsync() qdel(ctime_timer) else @@ -105,11 +105,11 @@ SUBSYSTEM_DEF(timer) if (!callBack) qdel(timer) bucket_resolution = null //force bucket recreation - CRASH("Invalid timer: [timer] timer.timeToRun=[timer.timeToRun]||QDELETED(timer)=[QDELETED(timer)]||world.time=[world.time]||head_offset=[head_offset]||practical_offset=[practical_offset]||timer.spent=[timer.spent]") + CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]") if (!timer.spent) spent += timer - timer.spent = TRUE + timer.spent = world.time callBack.InvokeAsync() last_invoke_tick = world.time @@ -135,9 +135,11 @@ SUBSYSTEM_DEF(timer) . = "Timer: [TE]" . += "Prev: [TE.prev ? TE.prev : "NULL"], Next: [TE.next ? TE.next : "NULL"]" if(TE.spent) - . += ", SPENT" + . += ", SPENT([TE.spent])" if(QDELETED(TE)) . += ", QDELETED" + if(!TE.callBack) + . += ", NO CALLBACK" /datum/controller/subsystem/timer/proc/shift_buckets() var/list/bucket_list = src.bucket_list @@ -216,7 +218,7 @@ SUBSYSTEM_DEF(timer) var/timeToRun var/hash var/list/flags - var/spent = FALSE //set to true right before running. + var/spent = 0 //time we ran the timer. var/name //for easy debugging. //cicular doublely linked list var/datum/timedevent/next @@ -243,6 +245,9 @@ SUBSYSTEM_DEF(timer) name = "Timer: " + num2text(id, 8) + ", TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])" + if (spent) + CRASH("HOLY JESUS. WHAT IS THAT? WHAT THE FUCK IS THAT?") + if (callBack.object != GLOBAL_PROC) LAZYADD(callBack.object.active_timers, src) diff --git a/code/datums/action.dm b/code/datums/action.dm index 10cb2a239f..8e32e8968f 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -172,6 +172,7 @@ /datum/action/item_action/rcl name = "Change Cable Color" + icon_icon = 'icons/mob/actions/actions_items.dmi' button_icon_state = "rcl_rainbow" /datum/action/item_action/startchainsaw @@ -405,27 +406,24 @@ /datum/action/item_action/initialize_ninja_suit name = "Toggle ninja suit" -/datum/action/item_action/ninjajaunt - name = "Phase Jaunt (10E)" - desc = "Utilizes the internal VOID-shift device to rapidly transit in direction facing." - icon_icon = 'icons/mob/actions/actions_items.dmi' - button_icon_state = "ninja_phase" - /datum/action/item_action/ninjasmoke name = "Smoke Bomb" desc = "Blind your enemies momentarily with a well-placed smoke bomb." button_icon_state = "smoke" + icon_icon = 'icons/mob/actions/actions_spells.dmi' /datum/action/item_action/ninjaboost - check_flags = AB_CHECK_RESTRAINED|AB_CHECK_CONSCIOUS + check_flags = NONE name = "Adrenaline Boost" desc = "Inject a secret chemical that will counteract all movement-impairing effect." button_icon_state = "repulse" + icon_icon = 'icons/mob/actions/actions_spells.dmi' /datum/action/item_action/ninjapulse name = "EM Burst (25E)" desc = "Disable any nearby technology with a electro-magnetic pulse." button_icon_state = "emp" + icon_icon = 'icons/mob/actions/actions_spells.dmi' /datum/action/item_action/ninjastar name = "Create Throwing Stars (1E)" @@ -448,8 +446,8 @@ /datum/action/item_action/ninja_stealth name = "Toggle Stealth" desc = "Toggles stealth mode on and off." - icon_icon = 'icons/mob/actions/actions_items.dmi' button_icon_state = "ninja_cloak" + icon_icon = 'icons/mob/actions/actions_minor_antag.dmi' /datum/action/item_action/toggle_glove name = "Toggle interaction" @@ -491,7 +489,7 @@ S.action = src name = S.name desc = S.desc - button_icon = S.action_icon + icon_icon = S.action_icon button_icon_state = S.action_icon_state background_icon_state = S.action_background_icon_state button.name = name diff --git a/code/datums/antagonists/devil.dm b/code/datums/antagonists/devil.dm index 9839dfe4ac..14c92f3263 100644 --- a/code/datums/antagonists/devil.dm +++ b/code/datums/antagonists/devil.dm @@ -164,7 +164,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", update_hud() switch(SOULVALUE) if(0) - to_chat(owner.current, "Your hellish powers have been restored.") + to_chat(owner.current, "Your hellish powers have been restored.") give_appropriate_spells() if(BLOOD_THRESHOLD) increase_blood_lizard() @@ -189,10 +189,10 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", regress_humanoid() if(SOULVALUE < 0) give_appropriate_spells() - to_chat(owner.current, "As punishment for your failures, all of your powers except contract creation have been revoked.") + to_chat(owner.current, "As punishment for your failures, all of your powers except contract creation have been revoked.") /datum/antagonist/devil/proc/regress_humanoid() - to_chat(owner.current, "Your powers weaken, have more contracts be signed to regain power.") + to_chat(owner.current, "Your powers weaken, have more contracts be signed to regain power.") if(ishuman(owner.current)) var/mob/living/carbon/human/H = owner.current H.set_species(/datum/species/human, 1) @@ -204,7 +204,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", /datum/antagonist/devil/proc/regress_blood_lizard() var/mob/living/carbon/true_devil/D = owner.current - to_chat(D, "Your powers weaken, have more contracts be signed to regain power.") + to_chat(D, "Your powers weaken, have more contracts be signed to regain power.") D.oldform.loc = D.loc owner.transfer_to(D.oldform) give_appropriate_spells() @@ -214,7 +214,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", /datum/antagonist/devil/proc/increase_blood_lizard() - to_chat(owner.current, "You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.") + to_chat(owner.current, "You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.") sleep(50) if(ishuman(owner.current)) var/mob/living/carbon/human/H = owner.current @@ -232,7 +232,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", /datum/antagonist/devil/proc/increase_true_devil() - to_chat(owner.current, "You feel as though your current form is about to shed. You will soon turn into a true devil.") + to_chat(owner.current, "You feel as though your current form is about to shed. You will soon turn into a true devil.") sleep(50) var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(owner.current.loc) A.faction |= "hell" @@ -248,7 +248,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", if(!ascendable) return var/mob/living/carbon/true_devil/D = owner.current - to_chat(D, "You feel as though your form is about to ascend.") + to_chat(D, "You feel as though your form is about to ascend.") sleep(50) if(!D) return diff --git a/code/datums/antagonists/ninja.dm b/code/datums/antagonists/ninja.dm index bd7bb13a1d..230a4d64e4 100644 --- a/code/datums/antagonists/ninja.dm +++ b/code/datums/antagonists/ninja.dm @@ -41,7 +41,6 @@ H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/space_ninja(H), slot_wear_mask) H.equip_to_slot_or_del(new /obj/item/clothing/glasses/night(H), slot_glasses) H.equip_to_slot_or_del(EK, slot_belt) - H.equip_to_slot_or_del(new /obj/item/device/flashlight(H), slot_r_store) H.equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(H), slot_l_store) H.equip_to_slot_or_del(new /obj/item/tank/internals/emergency_oxygen(H), slot_s_store) H.equip_to_slot_or_del(new /obj/item/tank/jetpack/carbondioxide(H), slot_back) 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/diseases/advance/presets.dm b/code/datums/diseases/advance/presets.dm index 07809d3555..1a197f0f34 100644 --- a/code/datums/diseases/advance/presets.dm +++ b/code/datums/diseases/advance/presets.dm @@ -34,20 +34,20 @@ ..(process, D, copy) -// Hullucigen +// Hallucigen -/datum/disease/advance/hullucigen/New(var/process = TRUE, var/datum/disease/advance/D, var/copy = FALSE) +/datum/disease/advance/hallucigen/New(var/process = TRUE, var/datum/disease/advance/D, var/copy = FALSE) if(!D) - name = "Reality Impairment" + name = "Second Sight" symptoms = list(new/datum/symptom/hallucigen) ..(process, D, copy) // Sensory Restoration -/datum/disease/advance/sensory_restoration/New(var/process = TRUE, var/datum/disease/advance/D, var/copy = FALSE) +/datum/disease/advance/mind_restoration/New(var/process = TRUE, var/datum/disease/advance/D, var/copy = FALSE) if(!D) - name = "Reality Enhancer" - symptoms = list(new/datum/symptom/sensory_restoration) + name = "Intelligence Booster" + symptoms = list(new/datum/symptom/mind_restoration) ..(process, D, copy) // Sensory Destruction diff --git a/code/datums/diseases/advance/symptoms/beard.dm b/code/datums/diseases/advance/symptoms/beard.dm index 2f8635301e..aa3919f3cf 100644 --- a/code/datums/diseases/advance/symptoms/beard.dm +++ b/code/datums/diseases/advance/symptoms/beard.dm @@ -17,6 +17,7 @@ BONUS /datum/symptom/beard name = "Facial Hypertrichosis" + desc = "The virus increases hair production significantly, causing rapid beard growth." stealth = -3 resistance = -1 stage_speed = -3 diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index a46ef690ef..f7f998f2b1 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/choking name = "Choking" + desc = "The virus causes inflammation of the host's air conduits, leading to intermittent choking." stealth = -3 resistance = -2 stage_speed = -2 @@ -27,6 +28,8 @@ Bonus base_message_chance = 15 symptom_delay_min = 10 symptom_delay_max = 30 + threshold_desc = "Stage Speed 8: Causes choking more frequently.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/choking/Start(datum/disease/advance/A) ..() @@ -84,6 +87,7 @@ Bonus /datum/symptom/asphyxiation name = "Acute respiratory distress syndrome" + desc = "The virus causes shrinking of the host's lungs, causing severe asphyxiation. May also lead to heart attacks." stealth = -2 resistance = -0 stage_speed = -1 diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm index 45bf5d6182..2e252267c4 100644 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ b/code/datums/diseases/advance/symptoms/confusion.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/confusion name = "Confusion" + desc = "The virus interferes with the proper function of the neural system, leading to bouts of confusion and erratic movement." stealth = 1 resistance = -1 stage_speed = -3 @@ -28,6 +29,9 @@ Bonus symptom_delay_min = 10 symptom_delay_max = 30 var/brain_damage = FALSE + threshold_desc = "Resistance 6: Causes brain damage over time.
    \ + Transmission 6: Increases confusion duration.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/confusion/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index a05d1d5e88..95577fe351 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -18,6 +18,7 @@ BONUS /datum/symptom/cough name = "Cough" + desc = "The virus irritates the throat of the host, causing occasional coughing." stealth = -1 resistance = 3 stage_speed = 1 @@ -28,6 +29,11 @@ BONUS symptom_delay_min = 2 symptom_delay_max = 15 var/infective = FALSE + threshold_desc = "Resistance 3: Host will drop small items when coughing.
    \ + Resistance 10: Occasionally causes coughing fits that stun the host.
    \ + Stage Speed 6: Increases cough frequency.
    \ + If Airborne: Coughing will infect bystanders.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/cough/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index b6163a72a1..c43970563d 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/deafness name = "Deafness" + desc = "The virus causes inflammation of the eardrums, causing intermittent deafness." stealth = -1 resistance = -2 stage_speed = -1 @@ -27,6 +28,8 @@ Bonus base_message_chance = 100 symptom_delay_min = 25 symptom_delay_max = 80 + threshold_desc = "Resistance 9: Causes permanent deafness, instead of intermittent.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/deafness/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm index 60e9989d4a..cb1bf11e63 100644 --- a/code/datums/diseases/advance/symptoms/dizzy.dm +++ b/code/datums/diseases/advance/symptoms/dizzy.dm @@ -18,7 +18,7 @@ Bonus /datum/symptom/dizzy // Not the egg name = "Dizziness" - stealth = 2 + desc = "The virus causes inflammation of the vestibular system, leading to bouts of dizziness." resistance = -2 stage_speed = -3 transmittable = -1 @@ -27,6 +27,8 @@ Bonus base_message_chance = 50 symptom_delay_min = 15 symptom_delay_max = 40 + threshold_desc = "Transmission 6: Also causes druggy vision.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/dizzy/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index e69c3bf0a2..673835b0ed 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -16,8 +16,8 @@ Bonus */ /datum/symptom/fever - name = "Fever" + desc = "The virus causes a febrile response from the host, raising its body temperature." stealth = 0 resistance = 3 stage_speed = 3 @@ -28,6 +28,8 @@ Bonus symptom_delay_min = 10 symptom_delay_max = 30 var/unsafe = FALSE //over the heat threshold + threshold_desc = "Resistance 5: Increases fever intensity, fever can overheat and harm the host.
    \ + Resistance 10: Further increases fever intensity." /datum/symptom/fever/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/fire.dm b/code/datums/diseases/advance/symptoms/fire.dm index b8d80b8023..e12e705350 100644 --- a/code/datums/diseases/advance/symptoms/fire.dm +++ b/code/datums/diseases/advance/symptoms/fire.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/fire name = "Spontaneous Combustion" + desc = "The virus turns fat into an extremely flammable compound, and raises the body's temperature, making the host burst into flames spontaneously." stealth = 1 resistance = -4 stage_speed = -4 @@ -28,6 +29,10 @@ Bonus symptom_delay_min = 20 symptom_delay_max = 75 var/infective = FALSE + threshold_desc = "Stage Speed 4: Increases the intensity of the flames.
    \ + Stage Speed 8: Further increases flame intensity.
    \ + Transmission 8: Host will spread the virus through skin flakes when bursting into flame.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/fire/Start(datum/disease/advance/A) ..() @@ -94,6 +99,7 @@ Bonus /datum/symptom/alkali name = "Alkali perspiration" + desc = "The virus attaches to sudoriparous glands, synthesizing a chemical that bursts into flames when reacting with water, leading to self-immolation." stealth = 2 resistance = -2 stage_speed = -2 @@ -105,6 +111,9 @@ Bonus symptom_delay_max = 90 var/chems = FALSE var/explosion_power = 1 + threshold_desc = "Resistance 9: Doubles the intensity of the effect, but reduces its frequency.
    \ + Stage Speed 8: Increases explosion radius when the host is wet.
    \ + Transmission 8: Additionally synthesizes chlorine trifluoride and napalm inside the host." /datum/symptom/alkali/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index 838d4481b5..16e2b5c065 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/flesh_eating name = "Necrotizing Fasciitis" + desc = "The virus aggressively attacks body cells, necrotizing tissues and organs." stealth = -3 resistance = -4 stage_speed = 0 @@ -29,6 +30,8 @@ Bonus symptom_delay_max = 60 var/bleed = FALSE var/pain = FALSE + threshold_desc = "Resistance 7: Host will bleed profusely during necrosis.
    \ + Transmission 8: Causes extreme pain to the host, weakening it." /datum/symptom/flesh_eating/Start(datum/disease/advance/A) ..() @@ -80,6 +83,7 @@ Bonus /datum/symptom/flesh_death name = "Autophagocytosis Necrosis" + desc = "The virus rapidly consumes infected cells, leading to heavy and widespread damage." stealth = -2 resistance = -2 stage_speed = 1 @@ -91,6 +95,8 @@ Bonus symptom_delay_max = 6 var/chems = FALSE var/zombie = FALSE + threshold_desc = "Stage Speed 7: Synthesizes Heparin and Lipolicide inside the host, causing increased bleeding and hunger.
    \ + Stealth 5: The symptom remains hidden until active." /datum/symptom/flesh_death/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm index 0b6ea5f808..1f654f2e97 100644 --- a/code/datums/diseases/advance/symptoms/genetics.dm +++ b/code/datums/diseases/advance/symptoms/genetics.dm @@ -16,8 +16,8 @@ Bonus */ /datum/symptom/genetic_mutation - name = "Deoxyribonucleic Acid Saboteur" + desc = "The virus bonds with the DNA of the host, causing damaging mutations until removed." stealth = -2 resistance = -3 stage_speed = 0 @@ -30,6 +30,9 @@ Bonus symptom_delay_min = 60 symptom_delay_max = 120 var/no_reset = FALSE + threshold_desc = "Resistance 8: Causes two harmful mutations at once.
    \ + Stage Speed 10: Increases mutation frequency.
    \ + Stealth 5: The mutations persist even if the virus is cured." /datum/symptom/genetic_mutation/Activate(datum/disease/advance/A) if(!..()) diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm index 9bfd5b0baf..d4cda525ad 100644 --- a/code/datums/diseases/advance/symptoms/hallucigen.dm +++ b/code/datums/diseases/advance/symptoms/hallucigen.dm @@ -16,8 +16,8 @@ Bonus */ /datum/symptom/hallucigen - name = "Hallucigen" + desc = "The virus stimulates the brain, causing occasional hallucinations." stealth = -2 resistance = -3 stage_speed = -3 @@ -28,6 +28,8 @@ Bonus symptom_delay_min = 25 symptom_delay_max = 90 var/fake_healthy = FALSE + threshold_desc = "Stage Speed 7: Increases the amount of hallucinations.
    \ + Stealth 4: The virus mimics positive symptoms.." /datum/symptom/hallucigen/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index 3baeedea19..b0665e0870 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -19,6 +19,7 @@ BONUS /datum/symptom/headache name = "Headache" + desc = "The virus causes inflammation inside the brain, causing constant headaches." stealth = -1 resistance = 4 stage_speed = 2 @@ -28,6 +29,9 @@ BONUS base_message_chance = 100 symptom_delay_min = 15 symptom_delay_max = 30 + threshold_desc = "Stage Speed 6: Headaches will cause severe pain, that weakens the host.
    \ + Stage Speed 9: Headaches become less frequent but far more intense, preventing any action from the host.
    \ + Stealth 4: Reduces headache frequency until later stages." /datum/symptom/headache/Start(datum/disease/advance/A) ..() @@ -45,11 +49,11 @@ BONUS return var/mob/living/M = A.affected_mob if(power < 2) - if(prob(base_message_chance)) + if(prob(base_message_chance) || A.stage >=4) to_chat(M, "[pick("Your head hurts.", "Your head pounds.")]") - if(power >= 2) + if(power >= 2 && A.stage >= 4) to_chat(M, "[pick("Your head hurts a lot.", "Your head pounds incessantly.")]") M.adjustStaminaLoss(25) - if(power >= 3) + if(power >= 3 && A.stage >= 5) to_chat(M, "[pick("Your head hurts!", "You feel a burning knife inside your brain!", "A wave of pain fills your head!")]") M.Stun(35) \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 7b31588240..5167b17e0d 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -1,5 +1,6 @@ /datum/symptom/heal name = "Basic Healing (does nothing)" //warning for adminspawn viruses + desc = "You should not be seeing this." stealth = 1 resistance = -4 stage_speed = -4 @@ -9,6 +10,9 @@ symptom_delay_min = 1 symptom_delay_max = 1 var/hide_healing = FALSE + threshold_desc = "Stage Speed 6: Doubles healing speed.
    \ + Stage Speed 11: Triples healing speed.
    \ + Stealth 4: Healing will no longer be visible to onlookers." /datum/symptom/heal/Start(datum/disease/advance/A) ..() @@ -51,6 +55,7 @@ Bonus /datum/symptom/heal/toxin name = "Toxic Filter" + desc = "The virus synthesizes regenerative chemicals in the bloodstream, repairing damage caused by toxins." stealth = 1 resistance = -4 stage_speed = -4 @@ -87,6 +92,7 @@ Bonus stage_speed = -2 transmittable = -2 level = 8 + desc = "The virus stimulates production of special stem cells in the bloodstream, causing rapid reparation of any damage caused by toxins." /datum/symptom/heal/toxin/plus/Heal(mob/living/M, datum/disease/advance/A) var/heal_amt = 2 * power @@ -115,6 +121,7 @@ Bonus /datum/symptom/heal/brute name = "Regeneration" + desc = "The virus stimulates the regenerative process in the host, causing faster wound healing." stealth = 1 resistance = -4 stage_speed = -4 @@ -158,6 +165,7 @@ Bonus /datum/symptom/heal/brute/plus name = "Flesh Mending" + desc = "The virus rapidly mutates into body cells, effectively allowing it to quickly fix the host's wounds." stealth = 0 resistance = 0 stage_speed = -2 @@ -207,6 +215,7 @@ Bonus /datum/symptom/heal/burn name = "Tissue Regrowth" + desc = "The virus recycles dead and burnt tissues, speeding up the healing of damage caused by burns." stealth = 1 resistance = -4 stage_speed = -4 @@ -248,7 +257,8 @@ Bonus /datum/symptom/heal/burn/plus - name = "Heat Resistance" + name = "Temperature Adaptation" + desc = "The virus quickly balances body heat, while also replacing tissues damaged by external sources." stealth = 0 resistance = 0 stage_speed = -2 @@ -297,6 +307,7 @@ Bonus /datum/symptom/heal/dna name = "Deoxyribonucleic Acid Restoration" + desc = "The virus repairs the host's genome, purging negative mutations." stealth = -1 resistance = -1 stage_speed = 0 @@ -304,9 +315,11 @@ Bonus level = 5 symptom_delay_min = 3 symptom_delay_max = 8 + threshold_desc = "Stage Speed 6: Additionally heals brain damage.
    \ + Stage Speed 11: Increases brain damage healing." /datum/symptom/heal/dna/Heal(mob/living/carbon/M, datum/disease/advance/A) - var/amt_healed = 2 * power + var/amt_healed = 2 * (power - 1) M.adjustBrainLoss(-amt_healed) //Non-power mutations, excluding race, so the virus does not force monkey -> human transformations. var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) - GLOB.mutations_list[RACEMUT] diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index 880ac1f3e6..119b04b48a 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -19,6 +19,7 @@ BONUS /datum/symptom/itching name = "Itching" + desc = "The virus irritates the skin, causing itching." stealth = 0 resistance = 3 stage_speed = 3 @@ -28,6 +29,8 @@ BONUS symptom_delay_min = 5 symptom_delay_max = 25 var/scratch = FALSE + threshold_desc = "Transmission 6: Increases frequency of itching.
    \ + Stage Speed 7: The host will scrath itself when itching, causing superficial damage." /datum/symptom/itching/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/narcolepsy.dm b/code/datums/diseases/advance/symptoms/narcolepsy.dm index a5a2bd7a4c..d850d257cb 100644 --- a/code/datums/diseases/advance/symptoms/narcolepsy.dm +++ b/code/datums/diseases/advance/symptoms/narcolepsy.dm @@ -14,6 +14,7 @@ Bonus */ /datum/symptom/narcolepsy name = "Narcolepsy" + desc = "The virus causes a hormone imbalance, making the host sleepy and narcoleptic." stealth = -1 resistance = -2 stage_speed = -3 @@ -25,6 +26,8 @@ Bonus var/sleep_level = 0 var/sleepy_ticks = 0 var/stamina = FALSE + threshold_desc = "Transmission 7: Also relaxes the muscles, weakening and slowing the host.
    \ + Resistance 10: Causes narcolepsy more often, increasing the chance of the host falling asleep." /datum/symptom/narcolepsy/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/oxygen.dm b/code/datums/diseases/advance/symptoms/oxygen.dm index 5949e84420..da085ab153 100644 --- a/code/datums/diseases/advance/symptoms/oxygen.dm +++ b/code/datums/diseases/advance/symptoms/oxygen.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/oxygen name = "Self-Respiration" + desc = "The virus rapidly synthesizes oxygen, effectively removing the need for breathing." stealth = 1 resistance = -3 stage_speed = -3 @@ -27,6 +28,7 @@ Bonus symptom_delay_min = 1 symptom_delay_max = 1 var/regenerate_blood = FALSE + threshold_desc = "Resistance 8:Additionally regenerates lost blood.
    " /datum/symptom/oxygen/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 3063ee06f6..dd417e50ed 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -15,8 +15,9 @@ Bonus ////////////////////////////////////// */ -/datum/symptom/sensory_restoration - name = "Sensory Restoration" +/datum/symptom/mind_restoration + name = "Mind Restoration" + desc = "The virus strengthens the bonds between neurons, reducing the duration of any ailments of the mind." stealth = -1 resistance = -4 stage_speed = -4 @@ -27,15 +28,17 @@ Bonus symptom_delay_max = 10 var/purge_alcohol = FALSE var/brain_heal = FALSE + threshold_desc = "Resistance 6: Heals brain damage.
    \ + Transmission 8: Purges alcohol in the bloodstream." -/datum/symptom/sensory_restoration/Start(datum/disease/advance/A) +/datum/symptom/mind_restoration/Start(datum/disease/advance/A) ..() if(A.properties["resistance"] >= 6) //heal brain damage brain_heal = TRUE if(A.properties["transmittable"] >= 8) //purge alcohol purge_alcohol = TRUE -/datum/symptom/sensory_restoration/Activate(var/datum/disease/advance/A) +/datum/symptom/mind_restoration/Activate(var/datum/disease/advance/A) if(!..()) return var/mob/living/M = A.affected_mob diff --git a/code/datums/diseases/advance/symptoms/shedding.dm b/code/datums/diseases/advance/symptoms/shedding.dm index cf88fcf6db..a578289e17 100644 --- a/code/datums/diseases/advance/symptoms/shedding.dm +++ b/code/datums/diseases/advance/symptoms/shedding.dm @@ -15,8 +15,8 @@ BONUS */ /datum/symptom/shedding - name = "Alopecia" + desc = "The virus causes rapid shedding of head and body hair." stealth = 0 resistance = 1 stage_speed = -1 diff --git a/code/datums/diseases/advance/symptoms/shivering.dm b/code/datums/diseases/advance/symptoms/shivering.dm index 4c9ec94a2b..f40fd151d9 100644 --- a/code/datums/diseases/advance/symptoms/shivering.dm +++ b/code/datums/diseases/advance/symptoms/shivering.dm @@ -16,8 +16,8 @@ Bonus */ /datum/symptom/shivering - name = "Shivering" + desc = "The virus inhibits the body's thermoregulation, cooling the body down." stealth = 0 resistance = 2 stage_speed = 2 @@ -27,6 +27,8 @@ Bonus symptom_delay_min = 10 symptom_delay_max = 30 var/unsafe = FALSE //over the cold threshold + threshold_desc = "Stage Speed 5: Increases cooling speed; the host can fall below safe temperature levels.
    \ + Stage Speed 10: Further increases cooling speed." /datum/symptom/fever/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm index 09eea77520..014607eb44 100644 --- a/code/datums/diseases/advance/symptoms/skin.dm +++ b/code/datums/diseases/advance/symptoms/skin.dm @@ -17,6 +17,7 @@ BONUS /datum/symptom/vitiligo name = "Vitiligo" + desc = "The virus destroys skin pigment cells, causing rapid loss of pigmentation in the host." stealth = -3 resistance = -1 stage_speed = -1 @@ -61,6 +62,7 @@ BONUS /datum/symptom/revitiligo name = "Revitiligo" + desc = "The virus causes increased production of skin pigment cells, making the host's skin grow darker over time." stealth = -3 resistance = -1 stage_speed = -1 diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index fda1fc765c..085b5ff592 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/sneeze name = "Sneezing" + desc = "The virus causes irritation of the nasal cavity, making the host sneeze occasionally." stealth = -2 resistance = 3 stage_speed = 0 @@ -26,6 +27,8 @@ Bonus severity = 1 symptom_delay_min = 5 symptom_delay_max = 35 + threshold_desc = "Transmission 9: Increases sneezing range, spreading the virus over a larger area.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/sneeze/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 4bcb1b502f..8557375dbd 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -3,6 +3,8 @@ /datum/symptom // Buffs/Debuffs the symptom has to the overall engineered disease. var/name = "" + var/desc = "If you see this something went very wrong." //Basic symptom description + var/threshold_desc = "" //Description of threshold effects var/stealth = 0 var/resistance = 0 var/stage_speed = 0 @@ -25,6 +27,7 @@ var/power = 1 //A neutered symptom has no effect, and only affects statistics. var/neutered = FALSE + var/list/thresholds /datum/symptom/New() var/list/S = SSdisease.list_symptoms @@ -37,7 +40,6 @@ // Called when processing of the advance disease, which holds this symptom, starts. /datum/symptom/proc/Start(datum/disease/advance/A) next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10) //so it doesn't instantly activate on infection - return // Called when the advance disease is going to be deleted or when the advance disease stops processing. /datum/symptom/proc/End(datum/disease/advance/A) @@ -58,3 +60,6 @@ new_symp.id = id new_symp.neutered = neutered return new_symp + +/datum/symptom/proc/generate_threshold_desc() + return diff --git a/code/datums/diseases/advance/symptoms/viral.dm b/code/datums/diseases/advance/symptoms/viral.dm index 49bb2d674c..539c57c92e 100644 --- a/code/datums/diseases/advance/symptoms/viral.dm +++ b/code/datums/diseases/advance/symptoms/viral.dm @@ -15,6 +15,7 @@ BONUS */ /datum/symptom/viraladaptation name = "Viral self-adaptation" + desc = "The virus mimics the function of normal body cells, becoming harder to spot and to eradicate, but reducing its speed." stealth = 3 resistance = 5 stage_speed = -3 @@ -38,6 +39,8 @@ BONUS */ /datum/symptom/viralevolution name = "Viral evolutionary acceleration" + desc = "The virus quickly adapts to spread as fast as possible both outside and inside a host. \ + This, however, makes the virus easier to spot, and less able to fight off a cure." stealth = -2 resistance = -3 stage_speed = 5 @@ -65,6 +68,8 @@ Bonus /datum/symptom/viralreverse name = "Viral aggressive metabolism" + desc = "The virus sacrifices its long term survivability to gain a near-instant spread when inside a host. \ + The virus will start at the lastest stage, but will eventually decay and die off by itself." stealth = -2 resistance = 1 stage_speed = 3 @@ -73,6 +78,8 @@ Bonus symptom_delay_min = 1 symptom_delay_max = 1 var/time_to_cure + threshold_desc = "Resistance/Stage Speed: Highest between these determines the amount of time before self-curing.
    \ + Stealth 4: Doubles the time before the virus self-cures." /datum/symptom/viralreverse/Activate(datum/disease/advance/A) if(!..()) diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index 9148139e50..04f5d72ba6 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/visionloss name = "Hyphema" + desc = "The virus causes inflammation of the retina, leading to eye damage and eventually blindness." stealth = -1 resistance = -4 stage_speed = -4 @@ -28,6 +29,8 @@ Bonus symptom_delay_min = 25 symptom_delay_max = 80 var/remove_eyes = FALSE + threshold_desc = "Resistance 12: Weakens extraocular muscles, eventually leading to complete detachment of the eyes.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/visionloss/Start(datum/disease/advance/A) ..() @@ -88,6 +91,7 @@ Bonus /datum/symptom/visionaid name = "Ocular Restoration" + desc = "The virus stimulates the production and replacement of eye cells, causing the host to regenerate its eyes when damaged." stealth = -1 resistance = -3 stage_speed = -2 diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm index 3abeb42f03..bdeb6321bc 100644 --- a/code/datums/diseases/advance/symptoms/voice_change.dm +++ b/code/datums/diseases/advance/symptoms/voice_change.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/voice_change name = "Voice Change" + desc = "The virus alters the pitch and tone of the host's vocal cords, changing how their voice sounds." stealth = -1 resistance = -2 stage_speed = -2 @@ -30,6 +31,9 @@ Bonus var/scramble_language = FALSE var/datum/language/current_language var/datum/language_holder/original_language + threshold_desc = "Transmission 14: The host's language center of the brain is damaged, leading to complete inability to speak or understand any language.
    \ + Stage Speed 7: Changes voice more often.
    \ + Stealth 3: The symptom remains hidden until active." /datum/symptom/voice_change/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm index e2be924d6a..983d20a66d 100644 --- a/code/datums/diseases/advance/symptoms/vomit.dm +++ b/code/datums/diseases/advance/symptoms/vomit.dm @@ -22,6 +22,7 @@ Bonus /datum/symptom/vomit name = "Vomiting" + desc = "The virus causes nausea and irritates the stomach, causing occasional vomit." stealth = -2 resistance = -1 stage_speed = 0 @@ -33,6 +34,9 @@ Bonus symptom_delay_max = 80 var/vomit_blood = FALSE var/proj_vomit = 0 + threshold_desc = "Resistance 7: Host will vomit blood, causing internal damage.
    \ + Transmission 7: Host will projectile vomit, increasing vomiting range.
    \ + Stealth 4: The symptom remains hidden until active." /datum/symptom/vomit/Start(datum/disease/advance/A) ..() diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm index ec371f1167..ea2577b800 100644 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -18,6 +18,7 @@ Bonus /datum/symptom/weight_gain name = "Weight Gain" + desc = "The virus mutates the host's metabolism, making it gain weight much faster than normal." stealth = -3 resistance = -3 stage_speed = -2 @@ -27,6 +28,7 @@ Bonus base_message_chance = 100 symptom_delay_min = 15 symptom_delay_max = 45 + threshold_desc = "Stealth 4: The symptom is less noticeable." /datum/symptom/weight_gain/Start(datum/disease/advance/A) ..() @@ -66,6 +68,7 @@ Bonus /datum/symptom/weight_loss name = "Weight Loss" + desc = "The virus mutates the host's metabolism, making it almost unable to gain nutrition from food." stealth = -3 resistance = -2 stage_speed = -2 @@ -75,6 +78,7 @@ Bonus base_message_chance = 100 symptom_delay_min = 15 symptom_delay_max = 45 + threshold_desc = "Stealth 4: The symptom is less noticeable." /datum/symptom/weight_loss/Start(datum/disease/advance/A) ..() @@ -116,6 +120,7 @@ Bonus /datum/symptom/weight_even name = "Weight Even" + desc = "The virus alters the host's metabolism, making it far more efficient then normal, and synthesizing nutrients from normally unedible sources." stealth = -3 resistance = -2 stage_speed = -2 diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm index 9793313354..6be34684e7 100644 --- a/code/datums/diseases/advance/symptoms/youth.dm +++ b/code/datums/diseases/advance/symptoms/youth.dm @@ -18,6 +18,8 @@ BONUS /datum/symptom/youth name = "Eternal Youth" + desc = "The virus becomes symbiotically connected to the cells in the host's body, preventing and reversing aging. \ + The virus, in turn, becomes more resistant, spreads faster, and is harder to spot, although it doesn't thrive as well without a host." stealth = 3 resistance = 4 stage_speed = 4 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/map_config.dm b/code/datums/map_config.dm index 4c0a5fb11e..1ff83aeac8 100644 --- a/code/datums/map_config.dm +++ b/code/datums/map_config.dm @@ -4,142 +4,142 @@ // -Cyberboss /datum/map_config - var/config_filename = "_maps/boxstation.json" - var/map_name = "Box Station" - var/map_path = "map_files/BoxStation" - var/map_file = "BoxStation.dmm" + var/config_filename = "_maps/boxstation.json" + var/map_name = "Box Station" + var/map_path = "map_files/BoxStation" + var/map_file = "BoxStation.dmm" - var/minetype = "lavaland" + var/minetype = "lavaland" - var/list/transition_config = list(CENTCOM = SELFLOOPING, + var/list/transition_config = list(CENTCOM = SELFLOOPING, MAIN_STATION = CROSSLINKED, - EMPTY_AREA_1 = CROSSLINKED, - EMPTY_AREA_2 = CROSSLINKED, - MINING = SELFLOOPING, - EMPTY_AREA_3 = CROSSLINKED, - EMPTY_AREA_4 = CROSSLINKED, - EMPTY_AREA_5 = CROSSLINKED, - EMPTY_AREA_6 = CROSSLINKED, - EMPTY_AREA_7 = CROSSLINKED, - EMPTY_AREA_8 = CROSSLINKED) - var/defaulted = TRUE //if New failed + EMPTY_AREA_1 = CROSSLINKED, + EMPTY_AREA_2 = CROSSLINKED, + MINING = SELFLOOPING, + EMPTY_AREA_3 = CROSSLINKED, + EMPTY_AREA_4 = CROSSLINKED, + EMPTY_AREA_5 = CROSSLINKED, + EMPTY_AREA_6 = CROSSLINKED, + EMPTY_AREA_7 = CROSSLINKED, + EMPTY_AREA_8 = CROSSLINKED) + var/defaulted = TRUE //if New failed - var/config_max_users = 0 - var/config_min_users = 0 - var/voteweight = 1 - var/allow_custom_shuttles = "yes" + var/config_max_users = 0 + var/config_min_users = 0 + var/voteweight = 1 + var/allow_custom_shuttles = "yes" /datum/map_config/New(filename = "data/next_map.json", default_to_box, delete_after) - if(default_to_box) - return - LoadConfig(filename) - if(delete_after) - fdel(filename) + if(default_to_box) + return + LoadConfig(filename) + if(delete_after) + fdel(filename) /datum/map_config/proc/LoadConfig(filename) - if(!fexists(filename)) - log_world("map_config not found: [filename]") - return + if(!fexists(filename)) + log_world("map_config not found: [filename]") + return - var/json = file(filename) - if(!json) - log_world("Could not open map_config: [filename]") - return + var/json = file(filename) + if(!json) + log_world("Could not open map_config: [filename]") + return - json = file2text(json) - if(!json) - log_world("map_config is not text: [filename]") - return + json = file2text(json) + if(!json) + log_world("map_config is not text: [filename]") + return - json = json_decode(json) - if(!json) - log_world("map_config is not json: [filename]") - return + json = json_decode(json) + if(!json) + log_world("map_config is not json: [filename]") + return - if(!ValidateJSON(json)) - log_world("map_config failed to validate for above reason: [filename]") - return + if(!ValidateJSON(json)) + log_world("map_config failed to validate for above reason: [filename]") + return - config_filename = filename + config_filename = filename - map_name = json["map_name"] - map_path = json["map_path"] - map_file = json["map_file"] + map_name = json["map_name"] + map_path = json["map_path"] + map_file = json["map_file"] - minetype = json["minetype"] - allow_custom_shuttles = json["allow_custom_shuttles"] + minetype = json["minetype"] + allow_custom_shuttles = json["allow_custom_shuttles"] - var/list/jtcl = json["transition_config"] + var/list/jtcl = json["transition_config"] - if(jtcl != "default") - transition_config.Cut() + if(jtcl != "default") + transition_config.Cut() - for(var/I in jtcl) - transition_config[TransitionStringToEnum(I)] = TransitionStringToEnum(jtcl[I]) + for(var/I in jtcl) + transition_config[TransitionStringToEnum(I)] = TransitionStringToEnum(jtcl[I]) - defaulted = FALSE + defaulted = FALSE #define CHECK_EXISTS(X) if(!istext(json[X])) { log_world(X + "missing from json!"); return; } /datum/map_config/proc/ValidateJSON(list/json) - CHECK_EXISTS("map_name") - CHECK_EXISTS("map_path") - CHECK_EXISTS("map_file") - CHECK_EXISTS("minetype") - CHECK_EXISTS("transition_config") - CHECK_EXISTS("allow_custom_shuttles") + CHECK_EXISTS("map_name") + CHECK_EXISTS("map_path") + CHECK_EXISTS("map_file") + CHECK_EXISTS("minetype") + CHECK_EXISTS("transition_config") + CHECK_EXISTS("allow_custom_shuttles") - var/path = GetFullMapPath(json["map_path"], json["map_file"]) - if(!fexists(path)) - log_world("Map file ([path]) does not exist!") - return + var/path = GetFullMapPath(json["map_path"], json["map_file"]) + if(!fexists(path)) + log_world("Map file ([path]) does not exist!") + return - if(json["transition_config"] != "default") - if(!islist(json["transition_config"])) - log_world("transition_config is not a list!") - return + if(json["transition_config"] != "default") + if(!islist(json["transition_config"])) + log_world("transition_config is not a list!") + return - var/list/jtcl = json["transition_config"] - for(var/I in jtcl) - if(isnull(TransitionStringToEnum(I))) - log_world("Invalid transition_config option: [I]!") - if(isnull(TransitionStringToEnum(jtcl[I]))) - log_world("Invalid transition_config option: [I]!") + var/list/jtcl = json["transition_config"] + for(var/I in jtcl) + if(isnull(TransitionStringToEnum(I))) + log_world("Invalid transition_config option: [I]!") + if(isnull(TransitionStringToEnum(jtcl[I]))) + log_world("Invalid transition_config option: [I]!") - return TRUE + return TRUE #undef CHECK_EXISTS /datum/map_config/proc/TransitionStringToEnum(string) - switch(string) - if("CROSSLINKED") - return CROSSLINKED - if("SELFLOOPING") - return SELFLOOPING - if("UNAFFECTED") - return UNAFFECTED - if("MAIN_STATION") - return MAIN_STATION - if("CENTCOM") - return CENTCOM - if("MINING") - return MINING - if("EMPTY_AREA_1") - return EMPTY_AREA_1 - if("EMPTY_AREA_2") - return EMPTY_AREA_2 - if("EMPTY_AREA_3") - return EMPTY_AREA_3 - if("EMPTY_AREA_4") - return EMPTY_AREA_4 - if("EMPTY_AREA_5") - return EMPTY_AREA_5 - if("EMPTY_AREA_6") - return EMPTY_AREA_6 - if("EMPTY_AREA_7") - return EMPTY_AREA_7 - if("EMPTY_AREA_8") - return EMPTY_AREA_8 + switch(string) + if("CROSSLINKED") + return CROSSLINKED + if("SELFLOOPING") + return SELFLOOPING + if("UNAFFECTED") + return UNAFFECTED + if("MAIN_STATION") + return MAIN_STATION + if("CENTCOM") + return CENTCOM + if("MINING") + return MINING + if("EMPTY_AREA_1") + return EMPTY_AREA_1 + if("EMPTY_AREA_2") + return EMPTY_AREA_2 + if("EMPTY_AREA_3") + return EMPTY_AREA_3 + if("EMPTY_AREA_4") + return EMPTY_AREA_4 + if("EMPTY_AREA_5") + return EMPTY_AREA_5 + if("EMPTY_AREA_6") + return EMPTY_AREA_6 + if("EMPTY_AREA_7") + return EMPTY_AREA_7 + if("EMPTY_AREA_8") + return EMPTY_AREA_8 /datum/map_config/proc/GetFullMapPath(mp = map_path, mf = map_file) - return "_maps/[mp]/[mf]" + return "_maps/[mp]/[mf]" /datum/map_config/proc/MakeNextMap() - return config_filename == "data/next_map.json" || fcopy(config_filename, "data/next_map.json") + return config_filename == "data/next_map.json" || fcopy(config_filename, "data/next_map.json") diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 0634d40806..cee55375f0 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -302,7 +302,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]).") @@ -358,6 +358,9 @@ if(!SSticker.HasRoundStarted()) alert("Not before round-start!", "Alert") return + if(QDELETED(src) || QDELETED(current)) + alert("This mind doesn't have a mob, or is deleted! For some reason!", "Edit Memory") + return var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
    " out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
    " @@ -365,193 +368,40 @@ out += "Faction and special role: [special_role]
    " var/list/sections = list( - "revolution", - "cult", - "wizard", + "traitor", // "traitorchan", "changeling", "nuclear", - "traitor", // "traitorchan", - "monkey", + "wizard", + "revolution", + "cult", "clockcult", + "abductor", "devil", - "ninja" + "ninja", + "monkey" ) var/text = "" - if(ishuman(current)) - /** REVOLUTION ***/ - text = "revolution" - if (SSticker.mode.config_tag=="revolution") - text = uppertext(text) - text = "[text]: " - if (assigned_role in GLOB.command_positions) - text += "HEAD|loyal|employee|headrev|rev" - else if (src in SSticker.mode.head_revolutionaries) - var/last_healthy_headrev = TRUE - for(var/I in SSticker.mode.head_revolutionaries) - if(I == src) - continue - var/mob/M = I - if(M.z == ZLEVEL_STATION && !M.stat) - last_healthy_headrev = FALSE - break - text += "head|loyal|employee|[last_healthy_headrev ? "LAST " : ""]HEADREV|rev" - text += "
    Flash: give" - - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - if (flash) - if(!flash.crit_fail) - text += "|take." - else - text += "|take|repair." - else - text += "." - - text += " Reequip (gives traitor uplink)." - if (objectives.len==0) - text += "
    Objectives are empty! Set to kill all heads." - else if(current.isloyal()) - text += "head|LOYAL|employee|headrev|rev" - else if (src in SSticker.mode.revolutionaries) - text += "head|loyal|employee|headrev|REV" - else - text += "head|loyal|EMPLOYEE|headrev|rev" - - if(current && current.client && (ROLE_REV in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["revolution"] = text - - /** Abductors **/ - text = "Abductor" - if(SSticker.mode.config_tag == "abductor") - text = uppertext(text) - text = "[text]: " - if(src in SSticker.mode.abductors) - text += "Abductor|human" - text += "|undress|equip" - else - text += "Abductor|human" - - if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["abductor"] = text - - /** NUCLEAR ***/ - text = "nuclear" - if (SSticker.mode.config_tag=="nuclear") - text = uppertext(text) - text = "[text]: " - if (src in SSticker.mode.syndicates) - text += "OPERATIVE|nanotrasen" - text += "
    To shuttle, undress, dress up." - var/code - for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines) - if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if (code) - text += " Code is [code]. tell the code." - else - text += "operative|NANOTRASEN" - - if(current && current.client && (ROLE_OPERATIVE in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["nuclear"] = text - - /** WIZARD ***/ - text = "wizard" - if (SSticker.mode.config_tag=="wizard") - text = uppertext(text) - text = "[text]: " - if ((src in SSticker.mode.wizards) || (src in SSticker.mode.apprentices)) - text += "YES|no" - text += "
    To lair, undress, dress up, let choose name." - if (objectives.len==0) - text += "
    Objectives are empty! Randomize!" - else - text += "yes|NO" - - if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["wizard"] = text - - /** CULT ***/ - text = "cult" - if (SSticker.mode.config_tag=="cult") - text = uppertext(text) - text = "[text]: " - if(iscultist(current)) - text += "loyal|employee|CULTIST" - text += "
    Give tome|amulet." - - else if(current.isloyal()) - text += "LOYAL|employee|cultist" - else if(is_convertable_to_cult(current)) - text += "loyal|EMPLOYEE|cultist" - else - text += "loyal|EMPLOYEE|cannot serve Nar-Sie" - - if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["cult"] = text - - /** CLOCKWORK CULT **/ - text = "clockwork cult" - if(SSticker.mode.config_tag == "clockwork cult") - text = uppertext(text) - text = "[text]: " - if(is_servant_of_ratvar(current)) - text += "loyal|employee|SERVANT" - text += "
    Give slab" - else if(current.isloyal()) - text += "LOYAL|employee|servant" - else if(is_eligible_servant(current)) - text += "loyal|EMPLOYEE|servant" - else - text += "loyal|EMPLOYEE|cannot serve Ratvar" - - if(current && current.client && (ROLE_SERVANT_OF_RATVAR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["clockcult"] = text - /** TRAITOR ***/ text = "traitor" if (SSticker.mode.config_tag=="traitor" || SSticker.mode.config_tag=="traitorchan") text = uppertext(text) text = "[text]: " if (src in SSticker.mode.traitors) - text += "TRAITOR|loyal" + text += "TRAITOR | loyal" if (objectives.len==0) text += "
    Objectives are empty! Randomize!" else - text += "traitor|LOYAL" + text += "traitor | LOYAL" if(current && current.client && (ROLE_TRAITOR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" + text += " | Enabled in Prefs" else - text += "|Disabled in Prefs" + text += " | Disabled in Prefs" sections["traitor"] = text + if(ishuman(current) || ismonkey(current)) /** CHANGELING ***/ @@ -560,34 +410,35 @@ text = uppertext(text) text = "[text]: " if ((src in SSticker.mode.changelings) && special_role) - text += "YES|no" + text += "YES | no" if (objectives.len==0) text += "
    Objectives are empty! Randomize!" if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) text += "
    Transform to initial appearance." else if(src in SSticker.mode.changelings) //Station Aligned Changeling - text += "YES (but not an antag)|no" + text += "YES (but not an antag) | no" if (objectives.len==0) text += "
    Objectives are empty! Randomize!" if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) text += "
    Transform to initial appearance." else - text += "yes|NO" + text += "yes | NO" if(current && current.client && (ROLE_CHANGELING in current.client.prefs.be_special)) - text += "|Enabled in Prefs" + text += " | Enabled in Prefs" else - text += "|Disabled in Prefs" + text += " | Disabled in Prefs" sections["changeling"] = text + /** MONKEY ***/ text = "monkey" if (SSticker.mode.config_tag=="monkey") text = uppertext(text) text = "[text]: " if (ishuman(current)) - text += "healthy|infected|HUMAN|other" + text += "healthy | infected | HUMAN | other" else if(ismonkey(current)) var/found = FALSE for(var/datum/disease/transformation/jungle_fever/JF in current.viruses) @@ -595,63 +446,222 @@ break if(found) - text += "healthy|INFECTED|human|other" + text += "healthy | INFECTED | human | other" else - text += "HEALTHY|infected|human|other" + text += "HEALTHY | infected | human | other" else - text += "healthy|infected|human|OTHER" + text += "healthy | infected | human | OTHER" if(current && current.client && (ROLE_MONKEY in current.client.prefs.be_special)) - text += "|Enabled in Prefs" + text += " | Enabled in Prefs" else - text += "|Disabled in Prefs" + text += " | Disabled in Prefs" sections["monkey"] = text - /** devil ***/ - text = "devil" - if(SSticker.mode.config_tag == "devil") - text = uppertext(text) - text = "[text]: " - var/datum/antagonist/devil/devilinfo = has_antag_datum(ANTAG_DATUM_DEVIL) - if(devilinfo) - if(!devilinfo.ascendable) - text += "DEVIL|Ascendable Devil|sintouched|human" + if(ishuman(current)) + + /** NUCLEAR ***/ + text = "nuclear" + if (SSticker.mode.config_tag=="nuclear") + text = uppertext(text) + text = "[text]: " + if (src in SSticker.mode.syndicates) + text += "OPERATIVE | nanotrasen" + text += "
    To shuttle, undress, dress up." + var/code + for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if (code) + text += " Code is [code]. tell the code." else - text += "DEVIL|ASCENDABLE DEVIL|sintouched|human" - else if(src in SSticker.mode.sintouched) - text += "devil|Ascendable Devil|SINTOUCHED|human" - else - text += "devil|Ascendable Devil|sintouched|HUMAN" + text += "operative | NANOTRASEN" - if(current && current.client && (ROLE_DEVIL in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - sections["devil"] = text - -/** NINJA ***/ - text = "ninja" - if(SSticker.mode.config_tag == "ninja") - text = uppertext(text) - text = "[text]: " - var/datum/antagonist/ninja/ninjainfo = has_antag_datum(ANTAG_DATUM_NINJA) - if(ninjainfo) - if(ninjainfo.helping_station) - text += "employee | syndicate | NANOTRASEN | EQUIP" + if(current && current.client && (ROLE_OPERATIVE in current.client.prefs.be_special)) + text += " | Enabled in Prefs" else - text += "employee | SYNDICATE | nanotrasen | EQUIP" - else - text += "EMPLOYEE | syndicate | nanotrasen | random allegiance" - if(current && current.client && (ROLE_NINJA in current.client.prefs.be_special)) - text += " | Enabled in Prefs" - else - text += " | Disabled in Prefs" - sections["ninja"] = text + text += " | Disabled in Prefs" + + sections["nuclear"] = text -/** SILICON ***/ + /** WIZARD ***/ + text = "wizard" + if (SSticker.mode.config_tag=="wizard") + text = uppertext(text) + text = "[text]: " + if ((src in SSticker.mode.wizards) || (src in SSticker.mode.apprentices)) + text += "YES | no" + text += "
    To lair, undress, dress up, let choose name." + if (objectives.len==0) + text += "
    Objectives are empty! Randomize!" + else + text += "yes | NO" + + if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special)) + text += " | Enabled in Prefs" + else + text += " | Disabled in Prefs" + + sections["wizard"] = text + + + /** REVOLUTION ***/ + text = "revolution" + if (SSticker.mode.config_tag=="revolution") + text = uppertext(text) + text = "[text]: " + if (assigned_role in GLOB.command_positions) + text += "HEAD | not mindshielded | employee | headrev | rev" + else if (src in SSticker.mode.head_revolutionaries) + var/last_healthy_headrev = TRUE + for(var/I in SSticker.mode.head_revolutionaries) + if(I == src) + continue + var/mob/M = I + if(M.z == ZLEVEL_STATION && !M.stat) + last_healthy_headrev = FALSE + break + text += "head | not mindshielded | employee | [last_healthy_headrev ? "LAST " : ""]HEADREV | rev" + text += "
    Flash: give" + + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + if (flash) + if(!flash.crit_fail) + text += " | take." + else + text += " | take | repair." + else + text += "." + + text += " Reequip (gives traitor uplink)." + if (objectives.len==0) + text += "
    Objectives are empty! Set to kill all heads." + else if(current.isloyal()) + text += "head | MINDSHIELDED | employee | headrev | rev" + else if (src in SSticker.mode.revolutionaries) + text += "head | not mindshielded | employee | headrev | REV" + else + text += "head | not mindshielded | EMPLOYEE | headrev | rev" + + if(current && current.client && (ROLE_REV in current.client.prefs.be_special)) + text += " | Enabled in Prefs" + else + text += " | Disabled in Prefs" + + sections["revolution"] = text + + /** Abductors **/ + text = "Abductor" + if(SSticker.mode.config_tag == "abductor") + text = uppertext(text) + text = "[text]: " + if(src in SSticker.mode.abductors) + text += "Abductor | human" + text += " | undress | equip" + else + text += "abductor | human" + + if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special)) + text += " | Enabled in Prefs" + else + text += " | Disabled in Prefs" + + sections["abductor"] = text + + + /** DEVIL ***/ + text = "devil" + if(SSticker.mode.config_tag == "devil") + text = uppertext(text) + text = "[text]: " + var/datum/antagonist/devil/devilinfo = has_antag_datum(ANTAG_DATUM_DEVIL) + if(devilinfo) + if(!devilinfo.ascendable) + text += "DEVIL | ascendable devil | sintouched | human" + else + text += "DEVIL | ASCENDABLE DEVIL | sintouched | human" + else if(src in SSticker.mode.sintouched) + text += "devil | ascendable devil | SINTOUCHED | human" + else + text += "devil | ascendable devil | sintouched | HUMAN" + + if(current && current.client && (ROLE_DEVIL in current.client.prefs.be_special)) + text += " | Enabled in Prefs" + else + text += " | Disabled in Prefs" + sections["devil"] = text + + + /** NINJA ***/ + text = "ninja" + if(SSticker.mode.config_tag == "ninja") + text = uppertext(text) + text = "[text]: " + var/datum/antagonist/ninja/ninjainfo = has_antag_datum(ANTAG_DATUM_NINJA) + if(ninjainfo) + if(ninjainfo.helping_station) + text += "employee | syndicate | NANOTRASEN | EQUIP" + else + text += "employee | SYNDICATE | nanotrasen | EQUIP" + else + text += "EMPLOYEE | syndicate | nanotrasen | random allegiance" + if(current && current.client && (ROLE_NINJA in current.client.prefs.be_special)) + text += " | Enabled in Prefs" + else + text += " | Disabled in Prefs" + sections["ninja"] = text + + + if(!issilicon(current)) + /** CULT ***/ + text = "cult" + if (SSticker.mode.config_tag=="cult") + text = uppertext(text) + text = "[text]: " + if(iscultist(current)) + text += "not mindshielded | employee | CULTIST" + text += "
    Give tome | amulet." + else if(is_convertable_to_cult(current)) + text += "not mindshielded | EMPLOYEE | cultist" + else + text += "[!current.isloyal() ? "not mindshielded" : "MINDSHIELDED"] | EMPLOYEE | cannot serve Nar-Sie" + + if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special)) + text += " | Enabled in Prefs" + else + text += " | Disabled in Prefs" + + sections["cult"] = text + + + if(ishuman(current) || issilicon(current)) + /** CLOCKWORK CULT **/ + text = "clockwork cult" + if(SSticker.mode.config_tag == "clockwork cult") + text = uppertext(text) + text = "[text]: " + if(is_servant_of_ratvar(current)) + text += "not mindshielded | employee | SERVANT" + text += "
    Give slab" + else if(is_eligible_servant(current)) + text += "not mindshielded | EMPLOYEE | servant" + else + text += "[!current.isloyal() ? "not mindshielded" : "MINDSHIELDED"] | EMPLOYEE | cannot serve Ratvar" + + if(current && current.client && (ROLE_SERVANT_OF_RATVAR in current.client.prefs.be_special)) + text += " | Enabled in Prefs" + else + text += " | Disabled in Prefs" + + sections["clockcult"] = text + + + /** SILICON ***/ if(issilicon(current)) text = "silicon" var/mob/living/silicon/robot/robot = current @@ -685,7 +695,7 @@ text = "Uplink: give" var/obj/item/device/uplink/U = find_syndicate_uplink() if(U) - text += "|take" + text += " | take" if (check_rights(R_FUN, 0)) text += ", [U.telecrystals] TC" else @@ -710,7 +720,10 @@ out += "Announce objectives

    " - usr << browse(out, "window=edit_memory[src];size=500x600") + var/datum/browser/popup = new(usr, "edit_memory", "", 600, 600) + popup.set_content(out) + popup.open() + //usr << browse(out, "window=edit_memory[src];size=575x600") /datum/mind/Topic(href, href_list) @@ -837,7 +850,7 @@ if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) def_num = objective.target_amount - var/target_number = input("Input target number:", "Objective", def_num) as num|null + var/target_number = input("Input target number:", "Objective", def_num) as num | null if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. return @@ -1267,7 +1280,7 @@ sleep(0) //because deleting of virus is doing throught spawn(0) //What log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") - H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) + H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) if(H) src = H.mind @@ -1301,7 +1314,7 @@ if(check_rights(R_FUN, 0)) var/obj/item/device/uplink/U = find_syndicate_uplink() if(U) - var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.telecrystals) as null|num + var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.telecrystals) as null | num if(!isnull(crystals)) U.telecrystals = crystals message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].") @@ -1348,6 +1361,7 @@ if(!(src in SSticker.mode.syndicates)) SSticker.mode.syndicates += src SSticker.mode.update_synd_icons_added(src) + assigned_role = "Syndicate" special_role = "Syndicate" SSticker.mode.forge_syndicate_objectives(src) SSticker.mode.greet_syndicate(src) @@ -1585,7 +1599,7 @@ /mob/living/carbon/human/mind_initialize() ..() if(!mind.assigned_role) - mind.assigned_role = "Assistant" //defualt + mind.assigned_role = "Unassigned" //default //XENO /mob/living/carbon/alien/mind_initialize() diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm index 88beec629f..b3af867bb4 100644 --- a/code/datums/ruins/space.dm +++ b/code/datums/ruins/space.dm @@ -18,6 +18,7 @@ name = "Asteroid 1" description = "I-spy with my little eye, something beginning with R." + /datum/map_template/ruin/space/asteroid2 id = "asteroid2" suffix = "asteroid2.dmm" @@ -247,4 +248,10 @@ id = "miracle" suffix = "miracle.dmm" name = "Ordinary Space Tile" - description = "Absolutely nothing strange going on here please move along, plenty more space to see right this way!" \ No newline at end of file + description = "Absolutely nothing strange going on here please move along, plenty more space to see right this way!" + +/datum/map_template/ruin/space/gondoland + id = "gondolaasteroid" + suffix = "gondolaasteroid.dmm" + name = "Gondoland" + description = "Just an ordinary rock- wait, what's that thing?" diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm index ff92b67978..688c921a73 100644 --- a/code/datums/status_effects/gas.dm +++ b/code/datums/status_effects/gas.dm @@ -4,6 +4,7 @@ status_type = STATUS_EFFECT_UNIQUE alert_type = /obj/screen/alert/status_effect/freon var/icon/cube + var/can_melt = TRUE /obj/screen/alert/status_effect/freon name = "Frozen Solid" @@ -20,7 +21,7 @@ /datum/status_effect/freon/tick() owner.update_canmove() - if(owner && owner.bodytemperature >= 310.055) + if(can_melt && owner.bodytemperature >= 310.055) qdel(src) /datum/status_effect/freon/on_remove() @@ -29,3 +30,7 @@ owner.cut_overlay(cube) owner.bodytemperature += 100 owner.update_canmove() + +/datum/status_effect/freon/watcher + duration = 8 + can_melt = FALSE diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 272ec34d94..c399f7c00d 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -29,6 +29,7 @@ var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. var/datum/proximity_monitor/proximity_monitor + var/buckle_message_cooldown = 0 /atom/New(loc, ...) //atom creation method that preloads variables at creation @@ -291,7 +292,10 @@ else to_chat(user, "Nothing.") -/atom/proc/relaymove() +/atom/proc/relaymove(mob/user) + if(buckle_message_cooldown <= world.time) + buckle_message_cooldown = world.time + 50 + to_chat(user, "You can't move while buckled to [src]!") return /atom/proc/contents_explosion(severity, target) @@ -300,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 @@ -464,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 @@ -609,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 b4e6db1ebe..cee9fc6262 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,11 +1,4 @@ -#ifndef PIXEL_SCALE -#define PIXEL_SCALE 0 -#if DM_VERSION >= 512 -#error HEY, PIXEL_SCALE probably exists now, remove this gross ass shim. -#endif -#endif - /atom/movable layer = OBJ_LAYER var/last_move = null @@ -222,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 @@ -504,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/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm index a485bc2d1a..ab9c041efe 100644 --- a/code/game/gamemodes/antag_spawner.dm +++ b/code/game/gamemodes/antag_spawner.dm @@ -108,6 +108,7 @@ new_objective.explanation_text = "Protect [usr.real_name], the wizard." M.mind.objectives += new_objective SSticker.mode.apprentices += M.mind + M.mind.assigned_role = "Apprentice" M.mind.special_role = "apprentice" SSticker.mode.update_wiz_icons_added(M.mind) SEND_SOUND(M, sound('sound/effects/magic.ogg')) diff --git a/code/game/gamemodes/antag_spawner.dm.rej b/code/game/gamemodes/antag_spawner.dm.rej new file mode 100644 index 0000000000..ea9a00132a --- /dev/null +++ b/code/game/gamemodes/antag_spawner.dm.rej @@ -0,0 +1,9 @@ +diff a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm (rejected hunks) +@@ -108,6 +108,7 @@ + new_objective.explanation_text = "Protect [usr.real_name], the wizard." + M.mind.objectives += new_objective + SSticker.mode.apprentices += M.mind ++ M.mind.assigned_role = "Apprentice" + M.mind.special_role = "apprentice" + SSticker.mode.update_wiz_icons_added(M.mind) + M << sound('sound/effects/magic.ogg') diff --git a/code/game/gamemodes/blob/blobs/shield.dm b/code/game/gamemodes/blob/blobs/shield.dm index 173166ddf3..fedc2eb6ab 100644 --- a/code/game/gamemodes/blob/blobs/shield.dm +++ b/code/game/gamemodes/blob/blobs/shield.dm @@ -7,11 +7,9 @@ brute_resist = 0.25 explosion_block = 3 point_return = 4 - atmosblock = 1 + atmosblock = TRUE armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 90, acid = 90) - - /obj/structure/blob/shield/scannerreport() if(atmosblock) return "Will prevent the spread of atmospheric changes." @@ -26,10 +24,10 @@ icon_state = "blob_shield_damaged" name = "weakened strong blob" desc = "A wall of twitching tendrils." - atmosblock = 0 + atmosblock = FALSE else icon_state = initial(icon_state) name = initial(name) desc = initial(desc) - atmosblock = 1 - air_update_turf(1) + atmosblock = TRUE + air_update_turf(1) \ No newline at end of file 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 f6cdd64bb6..8443f3d6f1 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -8,6 +8,7 @@ opacity = 0 anchored = TRUE layer = BELOW_MOB_LAYER + CanAtmosPass = ATMOS_PASS_PROC var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. max_integrity = 30 armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 70) @@ -16,19 +17,9 @@ var/heal_timestamp = 0 //we got healed when? var/brute_resist = 0.5 //multiplies brute damage by this var/fire_resist = 1 //multiplies burn damage by this - var/atmosblock = 0 //if the blob blocks atmos and heat spread + var/atmosblock = FALSE //if the blob blocks atmos and heat spread var/mob/camera/blob/overmind -/obj/structure/blob/attack_hand(mob/M) - . = ..() - M.changeNext_move(CLICK_CD_MELEE) - var/a = pick("gently stroke", "nuzzle", "affectionatly pet", "cuddle") - M.visible_message("[M] [a]s [src]!", "You [a] [src]!") - to_chat(overmind, "[M] [a]s you!") - playsound(src, 'sound/effects/blobattack.ogg', 50, 1) //SQUISH SQUISH - - - /obj/structure/blob/Initialize() var/area/Ablob = get_area(loc) if(Ablob.blob_allowed) //Is this area allowed for winning as blob? @@ -37,17 +28,16 @@ setDir(pick(GLOB.cardinals)) update_icon() .= ..() - ConsumeTile() if(atmosblock) - CanAtmosPass = ATMOS_PASS_NO air_update_turf(1) + ConsumeTile() /obj/structure/blob/proc/creation_action() //When it's created by the overmind, do this. return /obj/structure/blob/Destroy() if(atmosblock) - atmosblock = 0 + atmosblock = FALSE air_update_turf(1) GLOB.blobs_legit -= src //if it was in the legit blobs list, it isn't now GLOB.blobs -= src //it's no longer in the all blobs list either @@ -79,6 +69,9 @@ return 1 return 0 +/obj/structure/blob/CanAtmosPass(turf/T) + return !atmosblock + /obj/structure/blob/CanAStarPass(ID, dir, caller) . = 0 if(ismovableatom(caller)) @@ -97,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 @@ -353,4 +348,4 @@ icon_state = "blob" name = "blob" desc = "A thick wall of writhing tendrils." - brute_resist = 0.25 + brute_resist = 0.25 \ No newline at end of file diff --git a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm index 6cf5d55d2d..b2ddd022a4 100644 --- a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm +++ b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm @@ -1,4 +1,4 @@ -//Augmented Eyesight: Gives you thermal and night vision - bye bye, flashlights. Also, high DNA cost because of how powerful it is. +//Augmented Eyesight: Gives you x-ray vision or protection from flashes. Also, high DNA cost because of how powerful it is. //Possible todo: make a custom message for directing a penlight/flashlight at the eyes - not sure what would display though. /obj/effect/proc_holder/changeling/augmented_eyesight @@ -7,21 +7,31 @@ helptext = "Grants us thermal vision or flash protection. We will become a lot more vulnerable to flash-based devices while thermal vision is active." chemical_cost = 0 dna_cost = 2 //Would be 1 without thermal vision - active = 0 //Whether or not vision is enhanced + active = FALSE + +/obj/effect/proc_holder/changeling/augmented_eyesight/on_purchase(mob/user) //The ability starts inactive, so we should be protected from flashes. + var/obj/item/organ/eyes/E = user.getorganslot("eye_sight") + if (E) + E.flash_protect = 2 //Adjust the user's eyes' flash protection + to_chat(user, "We adjust our eyes to protect them from bright lights.") + else + to_chat(user, "We can't adjust our eyes if we don't have any!") /obj/effect/proc_holder/changeling/augmented_eyesight/sting_action(mob/living/carbon/human/user) if(!istype(user)) return var/obj/item/organ/eyes/E = user.getorganslot("eye_sight") if(E) - if(E.flash_protect) - E.sight_flags |= SEE_MOBS - E.flash_protect = -1 + if(!active) + E.sight_flags |= SEE_MOBS | SEE_OBJS | SEE_TURFS //Add sight flags to the user's eyes + E.flash_protect = -1 //Adjust the user's eyes' flash protection to_chat(user, "We adjust our eyes to sense prey through walls.") + active = TRUE //Defined in code/modules/spells/spell.dm else - E.sight_flags -= SEE_MOBS - E.flash_protect = 2 + E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS //Remove sight flags from the user's eyes + E.flash_protect = 2 //Adjust the user's eyes' flash protection to_chat(user, "We adjust our eyes to protect them from bright lights.") + active = FALSE user.update_sight() else to_chat(user, "We can't adjust our eyes if we don't have any!") @@ -31,7 +41,11 @@ return 1 -/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user) +/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user) //Get rid of x-ray vision and flash protection when the user refunds this ability var/obj/item/organ/eyes/E = user.getorganslot("eye_sight") if(E) - E.sight_flags -= SEE_MOBS \ No newline at end of file + if (active) + E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS + else + E.flash_protect = 0 + user.update_sight() \ No newline at end of file diff --git a/code/game/gamemodes/changeling/powers/mutations.dm b/code/game/gamemodes/changeling/powers/mutations.dm index 443720fefc..addca91629 100644 --- a/code/game/gamemodes/changeling/powers/mutations.dm +++ b/code/game/gamemodes/changeling/powers/mutations.dm @@ -247,7 +247,7 @@ /obj/item/gun/magic/tentacle/shoot_with_empty_chamber(mob/living/user as mob|obj) - to_chat(user, "The [name] is not ready yet.") + to_chat(user, "The [name] is not ready yet.") /obj/item/gun/magic/tentacle/suicide_act(mob/user) user.visible_message("[user] coils [src] tightly around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!") @@ -343,10 +343,10 @@ on_hit(I) //grab the item as if you had hit it directly with the tentacle return 1 else - to_chat(firer, "You can't seem to pry [I] off [C]'s hands!") + to_chat(firer, "You can't seem to pry [I] off [C]'s hands!") return 0 else - to_chat(firer, "[C] has nothing in hand to disarm!") + to_chat(firer, "[C] has nothing in hand to disarm!") return 0 if(INTENT_GRAB) diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index 6a680f27d6..ca2fd6f03e 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -73,7 +73,7 @@ if(!selected_dna) return if(NOTRANSSTING in selected_dna.dna.species.species_traits) - to_chat(user, "That DNA is not compatible with changeling retrovirus!") + to_chat(user, "That DNA is not compatible with changeling retrovirus!") return ..() 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/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 06407079ce..624fa83a57 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -144,7 +144,7 @@ to_chat(mob, "Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).") return 0 else - to_chat(mob, "You have a [item_name] in your [where].") + to_chat(mob, "You have a [item_name] in your [where].") if(where == "backpack") var/obj/item/storage/B = mob.back B.orient2hud(mob) diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm index 647b47e7fa..283376b055 100644 --- a/code/game/gamemodes/cult/cult_comms.dm +++ b/code/game/gamemodes/cult/cult_comms.dm @@ -118,7 +118,7 @@ if(B.current) B.current.update_action_buttons_icon() if(!B.current.incapacitated()) - to_chat(B.current,"[Nominee] has died in the process of attempting to win the cult's support!") + to_chat(B.current,"[Nominee] has died in the process of attempting to win the cult's support!") return FALSE if(!Nominee.mind) GLOB.cult_vote_called = FALSE @@ -126,7 +126,7 @@ if(B.current) B.current.update_action_buttons_icon() if(!B.current.incapacitated()) - to_chat(B.current,"[Nominee] has gone catatonic in the process of attempting to win the cult's support!") + to_chat(B.current,"[Nominee] has gone catatonic in the process of attempting to win the cult's support!") return FALSE if(LAZYLEN(yes_voters) <= LAZYLEN(asked_cultists) * 0.5) GLOB.cult_vote_called = FALSE @@ -134,7 +134,7 @@ if(B.current) B.current.update_action_buttons_icon() if(!B.current.incapacitated()) - to_chat(B.current, "[Nominee] could not win the cult's support and shall continue to serve as an acolyte.") + to_chat(B.current, "[Nominee] could not win the cult's support and shall continue to serve as an acolyte.") return FALSE GLOB.cult_mastered = TRUE SSticker.mode.remove_cultist(Nominee.mind, TRUE) @@ -144,7 +144,7 @@ for(var/datum/action/innate/cult/mastervote/vote in B.current.actions) vote.Remove(B.current) if(!B.current.incapacitated()) - to_chat(B.current,"[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!") + to_chat(B.current,"[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!") return TRUE /datum/action/innate/cult/master/IsAvailable() diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index 06f0910c19..a9255e89da 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -258,7 +258,7 @@ This file contains the arcane tome files. return FALSE if(T.z != ZLEVEL_STATION && T.z != ZLEVEL_MINING) - to_chat(user, "The veil is not weak enough here.") + to_chat(user, "The veil is not weak enough here.") return FALSE return TRUE diff --git a/code/game/gamemodes/gang/recaller.dm b/code/game/gamemodes/gang/recaller.dm index 86f68029d0..120e3a6345 100644 --- a/code/game/gamemodes/gang/recaller.dm +++ b/code/game/gamemodes/gang/recaller.dm @@ -3,7 +3,9 @@ name = "suspicious device" desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it." icon_state = "gangtool-white" - item_state = "walkietalkie" + item_state = "radio" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' throwforce = 0 w_class = WEIGHT_CLASS_TINY throw_speed = 3 diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 5dfd8f7396..339a445db6 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) @@ -534,7 +533,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( if(AA.z != ZLEVEL_STATION) continue AA.emagged = TRUE - to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") + to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) @@ -672,7 +671,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( var/obj/machinery/transformer/conveyor = new(T) conveyor.masterAI = owner playsound(T, 'sound/effects/phasein.ogg', 100, 1) - owner_AI.can_shunt = TRUE + owner_AI.can_shunt = FALSE to_chat(owner, "You are no longer able to shunt your core to APCs.") adjust_uses(-1) 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/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index 63a22c4ea1..2a7760dfc5 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -257,10 +257,15 @@ /obj/machinery/door/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) var/isonshuttle = istype(get_area(src), /area/shuttle) for(var/turf/T in range(1, src)) - if(isspaceturf(T) || (!isonshuttle && (istype(T.loc, /area/shuttle) || istype(T.loc, /area/space))) || (isonshuttle && !istype(T.loc, /area/shuttle))) + var/area/A = get_area(T) + if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle))) to_chat(S, "Destroying this object has the potential to cause a hull breach. Aborting.") S.target = null return FALSE + else if(istype(A, /area/engine/supermatter)) + to_chat(S, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.") + S.target = null + return FALSE S.DisIntegrate(src) return TRUE @@ -340,19 +345,29 @@ /turf/closed/wall/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) var/isonshuttle = istype(loc, /area/shuttle) for(var/turf/T in range(1, src)) - if(isspaceturf(T) || (!isonshuttle && (istype(T.loc, /area/shuttle) || istype(T.loc, /area/space))) || (isonshuttle && !istype(T.loc, /area/shuttle))) + var/area/A = get_area(T) + if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle))) to_chat(S, "Destroying this object has the potential to cause a hull breach. Aborting.") S.target = null return TRUE + else if(istype(A, /area/engine/supermatter)) + to_chat(S, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.") + S.target = null + return TRUE return ..() /obj/structure/window/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) var/isonshuttle = istype(get_area(src), /area/shuttle) for(var/turf/T in range(1, src)) - if(isspaceturf(T) || (!isonshuttle && (istype(T.loc, /area/shuttle) || istype(T.loc, /area/space))) || (isonshuttle && !istype(T.loc, /area/shuttle))) + var/area/A = get_area(T) + if(isspaceturf(T) || (!isonshuttle && (istype(A, /area/shuttle) || istype(A, /area/space))) || (isonshuttle && !istype(A, /area/shuttle))) to_chat(S, "Destroying this object has the potential to cause a hull breach. Aborting.") S.target = null return TRUE + else if(istype(A, /area/engine/supermatter)) + to_chat(S, "Disrupting the containment of a supermatter crystal would not be to our benefit. Aborting.") + S.target = null + return TRUE return ..() /obj/item/stack/cable_coil/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)//Wiring would be too effective as a resource diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm index acef4ec8ac..f422dcfcc5 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm @@ -1,8 +1,8 @@ /datum/round_event_control/spawn_swarmer name = "Spawn Swarmer Shell" typepath = /datum/round_event/spawn_swarmer - weight = 7 - max_occurrences = 1 //Only once okay fam + weight = 0 + max_occurrences = 0 //Only once okay fam earliest_start = 18000 //30 minutes min_players = 15 diff --git a/code/game/gamemodes/miniantags/monkey/monkey.dm b/code/game/gamemodes/miniantags/monkey/monkey.dm index f84e4e72b6..03793d9f76 100644 --- a/code/game/gamemodes/miniantags/monkey/monkey.dm +++ b/code/game/gamemodes/miniantags/monkey/monkey.dm @@ -46,7 +46,7 @@ /datum/game_mode/monkey/proc/greet_carrier(datum/mind/carrier) - to_chat(carrier.current, "You are the Jungle Fever patient zero!!") + to_chat(carrier.current, "You are the Jungle Fever patient zero!!") to_chat(carrier.current, "You have been planted onto this station by the Animal Rights Consortium.") to_chat(carrier.current, "Soon the disease will transform you into an ape. Afterwards, you will be able spread the infection to others with a bite.") to_chat(carrier.current, "While your infection strain is undetectable by scanners, any other infectees will show up on medical equipment.") 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/nuclear_challenge.dm b/code/game/gamemodes/nuclear/nuclear_challenge.dm index f671d38395..7b2a20a297 100644 --- a/code/game/gamemodes/nuclear/nuclear_challenge.dm +++ b/code/game/gamemodes/nuclear/nuclear_challenge.dm @@ -6,7 +6,9 @@ /obj/item/device/nuclear_challenge name = "Declaration of War (Challenge Mode)" icon_state = "gangtool-red" - item_state = "walkietalkie" + item_state = "radio" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' desc = "Use to send a declaration of hostilities to the target, delaying your shuttle departure for 20 minutes while they prepare for your assault. \ Such a brazen move will attract the attention of powerful benefactors within the Syndicate, who will supply your team with a massive amount of bonus telecrystals. \ Must be used within five minutes, or your benefactors will lose interest." 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.dm b/code/game/gamemodes/objective.dm index b33500db22..4bc43a6acb 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -34,7 +34,7 @@ /datum/objective/proc/find_target() var/list/possible_targets = list() for(var/datum/mind/possible_target in get_crewmember_minds()) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && is_unique_objective(possible_target)) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && is_unique_objective(possible_target)) possible_targets += possible_target if(possible_targets.len > 0) target = pick(possible_targets) diff --git a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm index 3cfede5f93..c0aee8bb76 100644 --- a/code/game/gamemodes/objective_items.dm +++ b/code/game/gamemodes/objective_items.dm @@ -132,7 +132,7 @@ /datum/objective_item/steal/functionalai/check_special_completion(obj/item/device/aicard/C) for(var/mob/living/silicon/ai/A in C) - if(isAI(A) && A.stat != 2) //See if any AI's are alive inside that card. + if(isAI(A) && A.stat != DEAD) //See if any AI's are alive inside that card. return 1 return 0 @@ -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/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 51a2c6c486..f393dc5338 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -335,7 +335,7 @@ /datum/game_mode/revolution/proc/check_heads_victory() for(var/datum/mind/rev_mind in head_revolutionaries) var/turf/T = get_turf(rev_mind.current) - if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != 2) && T && (T.z == ZLEVEL_STATION)) + if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != DEAD) && T && (T.z == ZLEVEL_STATION)) if(ishuman(rev_mind.current)) return 0 return 1 diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 16485c1c7b..28beb990de 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -13,7 +13,7 @@ density = FALSE anchored = TRUE state_open = TRUE - circuit = /obj/item/circuitboard/machine/sleeper + circuit = /obj/item/circuitboard/machine/sleeper var/efficiency = 1 var/min_health = -25 var/list/available_chems @@ -27,8 +27,8 @@ var/list/chem_buttons //Used when emagged to scramble which chem is used, eg: antitoxin -> morphine var/scrambled_chems = FALSE //Are chem buttons scrambled? used as a warning -/obj/machinery/sleeper/Initialize() - . = ..() +/obj/machinery/sleeper/Initialize() + . = ..() update_icon() reset_chem_buttons() @@ -95,7 +95,7 @@ return return ..() -/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ +/obj/machinery/sleeper/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) if(controls_inside && state == GLOB.notcontained_state) @@ -120,7 +120,19 @@ var/mob/living/mob_occupant = occupant if(mob_occupant) data["occupant"]["name"] = mob_occupant.name - data["occupant"]["stat"] = mob_occupant.stat + switch(mob_occupant.stat) + if(CONSCIOUS) + data["occupant"]["stat"] = "Conscious" + data["occupant"]["statstate"] = "good" + if(SOFT_CRIT) + data["occupant"]["stat"] = "Conscious" + data["occupant"]["statstate"] = "average" + if(UNCONSCIOUS) + data["occupant"]["stat"] = "Unconscious" + data["occupant"]["statstate"] = "average" + if(DEAD) + data["occupant"]["stat"] = "Dead" + data["occupant"]["statstate"] = "bad" data["occupant"]["health"] = mob_occupant.health data["occupant"]["maxHealth"] = mob_occupant.maxHealth data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 1002b1f6dc..b070de1b1c 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -35,13 +35,12 @@ Class Variables: Next uid value in sequence stat (bitflag) - Machine status bit flags_1. - Possible bit flags_1: - BROKEN:1 -- Machine is broken - NOPOWER:2 -- No power is being supplied to machine. - POWEROFF:4 -- tbd - MAINT:8 -- machine is currently under going maintenance. - EMPED:16 -- temporary broken by EMP pulse + Machine status bit flags. + Possible bit flags: + BROKEN -- Machine is broken + NOPOWER -- No power is being supplied to machine. + MAINT -- machine is currently under going maintenance. + EMPED -- temporary broken by EMP pulse Class Procs: Initialize() 'game/machinery/machine.dm' diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 10e10e878a..7c1904d2fe 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -105,8 +105,8 @@ /obj/machinery/button/emag_act(mob/user) if(emagged) return - req_access = null - req_one_access = null + req_access = list() + req_one_access = list() playsound(src, "sparks", 100, 1) emagged = TRUE diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 6f205a870d..864ab2bff2 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -24,7 +24,7 @@ name = "motion-sensitive security camera" /obj/machinery/camera/motion/Initialize() - ..() + . = ..() upgradeMotion() // ALL UPGRADES diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 1df8fc11e6..036b6f1f61 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -2,7 +2,7 @@ track.cameras.Cut() - if(src.stat == 2) + if(src.stat == DEAD) return var/list/L = list() @@ -45,7 +45,7 @@ track.humans.Cut() track.others.Cut() - if(usr.stat == 2) + if(usr.stat == DEAD) return list() for(var/mob/living/M in GLOB.mob_list) @@ -58,12 +58,12 @@ human = 1 var/name = M.name - while(name in track.names) + while(name in track.names) track.namecounts[name]++ name = text("[] ([])", name, track.namecounts[name]) - track.names.Add(name) - track.namecounts[name] = 1 - + track.names.Add(name) + track.namecounts[name] = 1 + if(human) track.humans[name] = M else @@ -136,9 +136,9 @@ /proc/near_camera(mob/living/M) if (!isturf(M.loc)) return 0 - if(issilicon(M)) - var/mob/living/silicon/S = M - if((!QDELETED(S.builtInCamera) || !S.builtInCamera.can_use()) && !GLOB.cameranet.checkCameraVis(M)) + if(issilicon(M)) + var/mob/living/silicon/S = M + if((!QDELETED(S.builtInCamera) || !S.builtInCamera.can_use()) && !GLOB.cameranet.checkCameraVis(M)) return 0 else if(!GLOB.cameranet.checkCameraVis(M)) return 0 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/computer/_computer.dm b/code/game/machinery/computer/_computer.dm index 85d8f02970..0c4c6c4bbe 100644 --- a/code/game/machinery/computer/_computer.dm +++ b/code/game/machinery/computer/_computer.dm @@ -90,6 +90,7 @@ playsound(loc, 'sound/effects/glassbr3.ogg', 100, 1) stat |= BROKEN update_icon() + set_light(0) /obj/machinery/computer/emp_act(severity) switch(severity) diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 18a94a6a94..d9371d5678 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -1,7 +1,7 @@ /obj/machinery/computer/aifixer name = "\improper AI system integrity restorer" desc = "Used with intelliCards containing nonfunctioning AIs to restore them to working order." - req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) + req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) var/mob/living/silicon/ai/occupier = null var/active = 0 circuit = /obj/item/circuitboard/computer/aifixer @@ -56,7 +56,7 @@ dat += "Laws:
    [laws]
    " - if (src.occupier.stat == 2) + if (src.occupier.stat == DEAD) dat += "AI non-functional" else dat += "AI functional" diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index 14fc50ee68..457abb3259 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -1,86 +1,86 @@ -/obj/machinery/computer/atmos_alert - name = "atmospheric alert console" - desc = "Used to monitor the station's air alarms." - circuit = /obj/item/circuitboard/computer/atmos_alert - icon_screen = "alert:0" - icon_keyboard = "atmos_key" - var/list/priority_alarms = list() - var/list/minor_alarms = list() - var/receive_frequency = 1437 - var/datum/radio_frequency/radio_connection - - light_color = LIGHT_COLOR_CYAN - -/obj/machinery/computer/atmos_alert/Initialize() - ..() - set_frequency(receive_frequency) - -/obj/machinery/computer/atmos_alert/Destroy() +/obj/machinery/computer/atmos_alert + name = "atmospheric alert console" + desc = "Used to monitor the station's air alarms." + circuit = /obj/item/circuitboard/computer/atmos_alert + icon_screen = "alert:0" + icon_keyboard = "atmos_key" + var/list/priority_alarms = list() + var/list/minor_alarms = list() + var/receive_frequency = 1437 + var/datum/radio_frequency/radio_connection + + light_color = LIGHT_COLOR_CYAN + +/obj/machinery/computer/atmos_alert/Initialize() + . = ..() + set_frequency(receive_frequency) + +/obj/machinery/computer/atmos_alert/Destroy() SSradio.remove_object(src, receive_frequency) - return ..() - + return ..() + /obj/machinery/computer/atmos_alert/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, "atmos_alert", name, 350, 300, master_ui, state) - ui.open() - -/obj/machinery/computer/atmos_alert/ui_data(mob/user) - var/list/data = list() - - data["priority"] = list() - for(var/zone in priority_alarms) - data["priority"] += zone - data["minor"] = list() - for(var/zone in minor_alarms) - data["minor"] += zone - - return data - -/obj/machinery/computer/atmos_alert/ui_act(action, params) - if(..()) - return - switch(action) - if("clear") - var/zone = params["zone"] - if(zone in priority_alarms) - to_chat(usr, "Priority alarm for [zone] cleared.") - priority_alarms -= zone - . = TRUE - if(zone in minor_alarms) - to_chat(usr, "Minor alarm for [zone] cleared.") - minor_alarms -= zone - . = TRUE - update_icon() - -/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency) - SSradio.remove_object(src, receive_frequency) - receive_frequency = new_frequency - radio_connection = SSradio.add_object(src, receive_frequency, GLOB.RADIO_ATMOSIA) - -/obj/machinery/computer/atmos_alert/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/zone = signal.data["zone"] - var/severity = signal.data["alert"] - - if(!zone || !severity) return - - minor_alarms -= zone - priority_alarms -= zone - if(severity == "severe") - priority_alarms += zone - else if (severity == "minor") - minor_alarms += zone - update_icon() - return - -/obj/machinery/computer/atmos_alert/update_icon() - ..() - if(stat & (NOPOWER|BROKEN)) - return - if(priority_alarms.len) - add_overlay("alert:2") - else if(minor_alarms.len) - add_overlay("alert:1") + 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, "atmos_alert", name, 350, 300, master_ui, state) + ui.open() + +/obj/machinery/computer/atmos_alert/ui_data(mob/user) + var/list/data = list() + + data["priority"] = list() + for(var/zone in priority_alarms) + data["priority"] += zone + data["minor"] = list() + for(var/zone in minor_alarms) + data["minor"] += zone + + return data + +/obj/machinery/computer/atmos_alert/ui_act(action, params) + if(..()) + return + switch(action) + if("clear") + var/zone = params["zone"] + if(zone in priority_alarms) + to_chat(usr, "Priority alarm for [zone] cleared.") + priority_alarms -= zone + . = TRUE + if(zone in minor_alarms) + to_chat(usr, "Minor alarm for [zone] cleared.") + minor_alarms -= zone + . = TRUE + update_icon() + +/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency) + SSradio.remove_object(src, receive_frequency) + receive_frequency = new_frequency + radio_connection = SSradio.add_object(src, receive_frequency, GLOB.RADIO_ATMOSIA) + +/obj/machinery/computer/atmos_alert/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/zone = signal.data["zone"] + var/severity = signal.data["alert"] + + if(!zone || !severity) return + + minor_alarms -= zone + priority_alarms -= zone + if(severity == "severe") + priority_alarms += zone + else if (severity == "minor") + minor_alarms += zone + update_icon() + return + +/obj/machinery/computer/atmos_alert/update_icon() + ..() + if(stat & (NOPOWER|BROKEN)) + return + if(priority_alarms.len) + add_overlay("alert:2") + else if(minor_alarms.len) + add_overlay("alert:1") diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 9bdcef6c65..8131dbd4a6 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -1,230 +1,230 @@ -///////////////////////////////////////////////////////////// -// AIR SENSOR (found in gas tanks) -///////////////////////////////////////////////////////////// - -/obj/machinery/air_sensor - name = "gas sensor" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "gsensor1" +///////////////////////////////////////////////////////////// +// AIR SENSOR (found in gas tanks) +///////////////////////////////////////////////////////////// + +/obj/machinery/air_sensor + name = "gas sensor" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "gsensor1" anchored = TRUE - - var/on = TRUE - - var/id_tag - var/frequency = 1441 - var/datum/radio_frequency/radio_connection - -/obj/machinery/air_sensor/update_icon() - icon_state = "gsensor[on]" - -/obj/machinery/air_sensor/process_atmos() - if(on) - var/datum/signal/signal = new - var/datum/gas_mixture/air_sample = return_air() - - signal.transmission_method = 1 //radio signal - signal.data = list( - "sigtype" = "status", - "id_tag" = id_tag, - "timestamp" = world.time, - "pressure" = air_sample.return_pressure(), - "temperature" = air_sample.temperature, - "gases" = list() - ) - var/total_moles = air_sample.total_moles() - if(total_moles) - for(var/gas_id in air_sample.gases) - var/gas_name = air_sample.gases[gas_id][GAS_META][META_GAS_NAME] - signal.data["gases"][gas_name] = air_sample.gases[gas_id][MOLES] / total_moles * 100 - - radio_connection.post_signal(src, signal, filter = GLOB.RADIO_ATMOSIA) - - -/obj/machinery/air_sensor/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, GLOB.RADIO_ATMOSIA) - -/obj/machinery/air_sensor/Initialize() - ..() - SSair.atmos_machinery += src - set_frequency(frequency) - -/obj/machinery/air_sensor/Destroy() - SSair.atmos_machinery -= src + + var/on = TRUE + + var/id_tag + var/frequency = 1441 + var/datum/radio_frequency/radio_connection + +/obj/machinery/air_sensor/update_icon() + icon_state = "gsensor[on]" + +/obj/machinery/air_sensor/process_atmos() + if(on) + var/datum/signal/signal = new + var/datum/gas_mixture/air_sample = return_air() + + signal.transmission_method = 1 //radio signal + signal.data = list( + "sigtype" = "status", + "id_tag" = id_tag, + "timestamp" = world.time, + "pressure" = air_sample.return_pressure(), + "temperature" = air_sample.temperature, + "gases" = list() + ) + var/total_moles = air_sample.total_moles() + if(total_moles) + for(var/gas_id in air_sample.gases) + var/gas_name = air_sample.gases[gas_id][GAS_META][META_GAS_NAME] + signal.data["gases"][gas_name] = air_sample.gases[gas_id][MOLES] / total_moles * 100 + + radio_connection.post_signal(src, signal, filter = GLOB.RADIO_ATMOSIA) + + +/obj/machinery/air_sensor/proc/set_frequency(new_frequency) SSradio.remove_object(src, frequency) - return ..() - -///////////////////////////////////////////////////////////// -// GENERAL AIR CONTROL (a.k.a atmos computer) -///////////////////////////////////////////////////////////// - -/obj/machinery/computer/atmos_control + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, GLOB.RADIO_ATMOSIA) + +/obj/machinery/air_sensor/Initialize() + . = ..() + SSair.atmos_machinery += src + set_frequency(frequency) + +/obj/machinery/air_sensor/Destroy() + SSair.atmos_machinery -= src + SSradio.remove_object(src, frequency) + return ..() + +///////////////////////////////////////////////////////////// +// GENERAL AIR CONTROL (a.k.a atmos computer) +///////////////////////////////////////////////////////////// + +/obj/machinery/computer/atmos_control name = "atmospherics monitoring" - desc = "Used to monitor the station's atmospherics sensors." - icon_screen = "tank" - icon_keyboard = "atmos_key" - circuit = /obj/item/circuitboard/computer/atmos_control - - var/frequency = 1441 - var/list/sensors = list( - "n2_sensor" = "Nitrogen Tank", - "o2_sensor" = "Oxygen Tank", - "co2_sensor" = "Carbon Dioxide Tank", - "tox_sensor" = "Plasma Tank", - "n2o_sensor" = "Nitrous Oxide Tank", - "air_sensor" = "Mixed Air Tank", - "mix_sensor" = "Mix Tank", - "distro_meter" = "Distribution Loop", - "waste_meter" = "Waste Loop", - ) - var/list/sensor_information = list() - var/datum/radio_frequency/radio_connection - - light_color = LIGHT_COLOR_CYAN - -/obj/machinery/computer/atmos_control/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/computer/atmos_control/Destroy() + desc = "Used to monitor the station's atmospherics sensors." + icon_screen = "tank" + icon_keyboard = "atmos_key" + circuit = /obj/item/circuitboard/computer/atmos_control + + var/frequency = 1441 + var/list/sensors = list( + "n2_sensor" = "Nitrogen Tank", + "o2_sensor" = "Oxygen Tank", + "co2_sensor" = "Carbon Dioxide Tank", + "tox_sensor" = "Plasma Tank", + "n2o_sensor" = "Nitrous Oxide Tank", + "air_sensor" = "Mixed Air Tank", + "mix_sensor" = "Mix Tank", + "distro_meter" = "Distribution Loop", + "waste_meter" = "Waste Loop", + ) + var/list/sensor_information = list() + var/datum/radio_frequency/radio_connection + + light_color = LIGHT_COLOR_CYAN + +/obj/machinery/computer/atmos_control/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/computer/atmos_control/Destroy() SSradio.remove_object(src, frequency) - return ..() - + return ..() + /obj/machinery/computer/atmos_control/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, "atmos_control", name, 400, 925, master_ui, state) - ui.open() - -/obj/machinery/computer/atmos_control/ui_data(mob/user) - var/data = list() - - data["sensors"] = list() - for(var/id_tag in sensors) - var/long_name = sensors[id_tag] - var/list/info = sensor_information[id_tag] - if(!info) - continue - data["sensors"] += list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "pressure" = info["pressure"], - "temperature" = info["temperature"], - "gases" = info["gases"] - )) - return data - -/obj/machinery/computer/atmos_control/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) - return - - var/id_tag = signal.data["id_tag"] - if(!id_tag || !sensors.Find(id_tag)) - return - - sensor_information[id_tag] = signal.data - -/obj/machinery/computer/atmos_control/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, GLOB.RADIO_ATMOSIA) - -///////////////////////////////////////////////////////////// -// LARGE TANK CONTROL -///////////////////////////////////////////////////////////// - -/obj/machinery/computer/atmos_control/tank - var/input_tag - var/output_tag - frequency = 1441 - circuit = /obj/item/circuitboard/computer/atmos_control/tank - - var/list/input_info - var/list/output_info - -// This hacky madness is the evidence of the fact that a lot of machines were never meant to be constructable, im so sorry you had to see this -/obj/machinery/computer/atmos_control/tank/proc/reconnect(mob/user) - var/list/IO = list() - var/datum/radio_frequency/freq = SSradio.return_frequency(1441) - var/list/devices = freq.devices["_default"] - for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in devices) - var/list/text = splittext(U.id_tag, "_") - IO |= text[1] - for(var/obj/machinery/atmospherics/components/unary/outlet_injector/U in devices) - var/list/text = splittext(U.id, "_") - IO |= text[1] - if(!IO.len) - to_chat(user, "No machinery detected.") - var/S = input("Select the device set: ", "Selection", IO[1]) as anything in IO - if(src) - src.input_tag = "[S]_in" - src.output_tag = "[S]_out" - name = "[uppertext(S)] Supply Control" - var/list/new_devices = freq.devices["4"] - for(var/obj/machinery/air_sensor/U in new_devices) - var/list/text = splittext(U.id_tag, "_") - if(text[1] == S) - sensors = list("[S]_sensor" = "Tank") - break - - for(var/obj/machinery/atmospherics/components/unary/outlet_injector/U in devices) - U.broadcast_status() - for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in devices) - U.broadcast_status() - + 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, "atmos_control", name, 400, 925, master_ui, state) + ui.open() + +/obj/machinery/computer/atmos_control/ui_data(mob/user) + var/data = list() + + data["sensors"] = list() + for(var/id_tag in sensors) + var/long_name = sensors[id_tag] + var/list/info = sensor_information[id_tag] + if(!info) + continue + data["sensors"] += list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "pressure" = info["pressure"], + "temperature" = info["temperature"], + "gases" = info["gases"] + )) + return data + +/obj/machinery/computer/atmos_control/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) + return + + var/id_tag = signal.data["id_tag"] + if(!id_tag || !sensors.Find(id_tag)) + return + + sensor_information[id_tag] = signal.data + +/obj/machinery/computer/atmos_control/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, GLOB.RADIO_ATMOSIA) + +///////////////////////////////////////////////////////////// +// LARGE TANK CONTROL +///////////////////////////////////////////////////////////// + +/obj/machinery/computer/atmos_control/tank + var/input_tag + var/output_tag + frequency = 1441 + circuit = /obj/item/circuitboard/computer/atmos_control/tank + + var/list/input_info + var/list/output_info + +// This hacky madness is the evidence of the fact that a lot of machines were never meant to be constructable, im so sorry you had to see this +/obj/machinery/computer/atmos_control/tank/proc/reconnect(mob/user) + var/list/IO = list() + var/datum/radio_frequency/freq = SSradio.return_frequency(1441) + var/list/devices = freq.devices["_default"] + for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in devices) + var/list/text = splittext(U.id_tag, "_") + IO |= text[1] + for(var/obj/machinery/atmospherics/components/unary/outlet_injector/U in devices) + var/list/text = splittext(U.id, "_") + IO |= text[1] + if(!IO.len) + to_chat(user, "No machinery detected.") + var/S = input("Select the device set: ", "Selection", IO[1]) as anything in IO + if(src) + src.input_tag = "[S]_in" + src.output_tag = "[S]_out" + name = "[uppertext(S)] Supply Control" + var/list/new_devices = freq.devices["4"] + for(var/obj/machinery/air_sensor/U in new_devices) + var/list/text = splittext(U.id_tag, "_") + if(text[1] == S) + sensors = list("[S]_sensor" = "Tank") + break + + for(var/obj/machinery/atmospherics/components/unary/outlet_injector/U in devices) + U.broadcast_status() + for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in devices) + U.broadcast_status() + /obj/machinery/computer/atmos_control/tank/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, "atmos_control", name, 500, 305, master_ui, state) - ui.open() - -/obj/machinery/computer/atmos_control/tank/ui_data(mob/user) - var/list/data = ..() - data["tank"] = TRUE - data["inputting"] = input_info ? input_info["power"] : FALSE - data["inputRate"] = input_info ? input_info["volume_rate"] : 0 - data["outputting"] = output_info ? output_info["power"] : FALSE - data["outputPressure"] = output_info ? output_info["internal"] : 0 - - return data - -/obj/machinery/computer/atmos_control/tank/ui_act(action, params) - if(..() || !radio_connection) - return - var/datum/signal/signal = new - signal.transmission_method = 1 - signal.source = src - signal.data = list("sigtype" = "command") - switch(action) - if("reconnect") - reconnect(usr) - . = TRUE - if("input") - signal.data += list("tag" = input_tag, "power_toggle" = TRUE) - . = TRUE - if("output") - signal.data += list("tag" = output_tag, "power_toggle" = TRUE) - . = TRUE - if("pressure") - var/target = input("New target pressure:", name, output_info ? output_info["internal"] : 0) as num|null - if(!isnull(target) && !..()) - target = Clamp(target, 0, 50 * ONE_ATMOSPHERE) - signal.data += list("tag" = output_tag, "set_internal_pressure" = target) - . = TRUE - radio_connection.post_signal(src, signal, filter = GLOB.RADIO_ATMOSIA) - -/obj/machinery/computer/atmos_control/tank/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) - return - - var/id_tag = signal.data["tag"] - - if(input_tag == id_tag) - input_info = signal.data - else if(output_tag == id_tag) - output_info = signal.data - else - ..(signal) + 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, "atmos_control", name, 500, 305, master_ui, state) + ui.open() + +/obj/machinery/computer/atmos_control/tank/ui_data(mob/user) + var/list/data = ..() + data["tank"] = TRUE + data["inputting"] = input_info ? input_info["power"] : FALSE + data["inputRate"] = input_info ? input_info["volume_rate"] : 0 + data["outputting"] = output_info ? output_info["power"] : FALSE + data["outputPressure"] = output_info ? output_info["internal"] : 0 + + return data + +/obj/machinery/computer/atmos_control/tank/ui_act(action, params) + if(..() || !radio_connection) + return + var/datum/signal/signal = new + signal.transmission_method = 1 + signal.source = src + signal.data = list("sigtype" = "command") + switch(action) + if("reconnect") + reconnect(usr) + . = TRUE + if("input") + signal.data += list("tag" = input_tag, "power_toggle" = TRUE) + . = TRUE + if("output") + signal.data += list("tag" = output_tag, "power_toggle" = TRUE) + . = TRUE + if("pressure") + var/target = input("New target pressure:", name, output_info ? output_info["internal"] : 0) as num|null + if(!isnull(target) && !..()) + target = Clamp(target, 0, 50 * ONE_ATMOSPHERE) + signal.data += list("tag" = output_tag, "set_internal_pressure" = target) + . = TRUE + radio_connection.post_signal(src, signal, filter = GLOB.RADIO_ATMOSIA) + +/obj/machinery/computer/atmos_control/tank/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) + return + + var/id_tag = signal.data["tag"] + + if(input_tag == id_tag) + input_info = signal.data + else if(output_tag == id_tag) + output_info = signal.data + else + ..(signal) 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/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index ce82cd0e85..5bedf044c3 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1157,7 +1157,7 @@ charge = C else if(istype(C, /obj/item/paper) || istype(C, /obj/item/photo)) if(note) - to_chat(user, "There's already something pinned to this airlock! Use wirecutters to remove it.") + to_chat(user, "There's already something pinned to this airlock! Use wirecutters to remove it.") return if(!user.transferItemToLoc(C, src)) to_chat(user, "For some reason, you can't attach [C]!") diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 6a1bb1ec3a..f76412199c 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -213,7 +213,7 @@ sleep(6) operating = FALSE desc += "
    Its access panel is smoking slightly." - open() + open(2) /obj/machinery/door/window/attackby(obj/item/I, mob/living/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/launch_pad.dm b/code/game/machinery/launch_pad.dm index bc61bdc3ed..65f5b946ef 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -221,7 +221,7 @@ if(!isturf(user.loc)) //no setting up in a locker return add_fingerprint(user) - user.visible_message("[user] starts setting down [src]...", "You start setting up [pad]...") + user.visible_message("[user] starts setting down [src]...", "You start setting up [pad]...") if(do_after(user, 30, target = user)) pad.forceMove(get_turf(src)) pad.closed = FALSE @@ -356,4 +356,4 @@ if("pull") sending = FALSE teleport(usr, pad) - . = TRUE \ No newline at end of file + . = TRUE diff --git a/code/game/machinery/quantum_pad.dm b/code/game/machinery/quantum_pad.dm index 4292f5b08a..e86f78722b 100644 --- a/code/game/machinery/quantum_pad.dm +++ b/code/game/machinery/quantum_pad.dm @@ -16,6 +16,20 @@ var/power_efficiency = 1 var/obj/machinery/quantumpad/linked_pad + //mapping + var/static/list/mapped_quantum_pads = list() + var/map_pad_id = "" as text //what's my name + var/map_pad_link_id = "" as text //who's my friend + +/obj/machinery/quantumpad/Initialize() + . = ..() + if(map_pad_id) + mapped_quantum_pads[map_pad_id] = src + +/obj/machinery/quantumpad/Destroy() + mapped_quantum_pads -= map_pad_id + return ..() + /obj/machinery/quantumpad/RefreshParts() var/E = 0 for(var/obj/item/stock_parts/capacitor/C in component_parts) @@ -60,8 +74,9 @@ return if(!linked_pad || QDELETED(linked_pad)) - to_chat(user, "There is no linked pad!") - return + if(!map_pad_link_id || !initMappedLink()) + to_chat(user, "There is no linked pad!") + return if(world.time < last_teleport + teleport_cooldown) to_chat(user, "[src] is recharging power. Please wait [round((last_teleport + teleport_cooldown - world.time) / 10)] seconds.") @@ -80,7 +95,6 @@ return src.add_fingerprint(user) doteleport(user) - return /obj/machinery/quantumpad/proc/sparks() var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread @@ -88,6 +102,8 @@ s.start() /obj/machinery/quantumpad/attack_ghost(mob/dead/observer/ghost) + if(!linked_pad && map_pad_link_id) + initMappedLink() if(linked_pad) ghost.forceMove(get_turf(linked_pad)) @@ -136,6 +152,12 @@ continue do_teleport(ROI, get_turf(linked_pad)) +/obj/machinery/quantumpad/proc/initMappedLink() + . = FALSE + var/obj/machinery/quantumpad/link = mapped_quantum_pads[map_pad_link_id] + if(link) + linked_pad = link + . = TRUE /obj/item/paper/guides/quantumpad name = "Quantum Pad For Dummies" 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..30ad2627ca 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -1,947 +1,947 @@ -#define STANDARD_CHARGE 1 -#define CONTRABAND_CHARGE 2 -#define COIN_CHARGE 3 - -/datum/data/vending_product - var/product_name = "generic" - var/product_path = null - var/amount = 0 - var/max_amount = 0 - var/display_color = "blue" - - -/obj/machinery/vending - name = "\improper Vendomat" - desc = "A generic vending machine." - icon = 'icons/obj/vending.dmi' - icon_state = "generic" - layer = BELOW_OBJ_LAYER +#define STANDARD_CHARGE 1 +#define CONTRABAND_CHARGE 2 +#define COIN_CHARGE 3 + +/datum/data/vending_product + var/product_name = "generic" + var/product_path = null + var/amount = 0 + var/max_amount = 0 + var/display_color = "blue" + + +/obj/machinery/vending + name = "\improper Vendomat" + desc = "A generic vending machine." + icon = 'icons/obj/vending.dmi' + icon_state = "generic" + layer = BELOW_OBJ_LAYER anchored = TRUE density = TRUE - verb_say = "beeps" - verb_ask = "beeps" - verb_exclaim = "beeps" - max_integrity = 300 - integrity_failure = 100 - armor = list(melee = 20, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) + verb_say = "beeps" + verb_ask = "beeps" + verb_exclaim = "beeps" + max_integrity = 300 + integrity_failure = 100 + armor = list(melee = 20, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) circuit = /obj/item/circuitboard/machine/vendor - var/active = 1 //No sales pitches if off! - var/vend_ready = 1 //Are we ready to vend?? Is it time?? - - // To be filled out at compile time - var/list/products = list() //For each, use the following pattern: + var/active = 1 //No sales pitches if off! + var/vend_ready = 1 //Are we ready to vend?? Is it time?? + + // To be filled out at compile time + var/list/products = list() //For each, use the following pattern: var/list/contraband = list() //list(/type/path = amount, /type/path2 = amount2) - var/list/premium = list() //No specified amount = only one in stock - - var/product_slogans = "" //String of slogans separated by semicolons, optional - var/product_ads = "" //String of small ad messages in the vending screen - random chance - var/list/product_records = list() - var/list/hidden_records = list() - var/list/coin_records = list() - var/list/slogan_list = list() - var/list/small_ads = list() //Small ad messages in the vending screen - random chance of popping up whenever you open it - var/vend_reply //Thank you for shopping! - var/last_reply = 0 - var/last_slogan = 0 //When did we last pitch? - var/slogan_delay = 6000 //How long until we can pitch again? - var/icon_vend //Icon_state when vending! - var/icon_deny //Icon_state when vending! - var/seconds_electrified = 0 //Shock customers like an airlock. - var/shoot_inventory = 0 //Fire items at customers! We're broken! - var/shoot_inventory_chance = 2 - var/shut_up = 0 //Stop spouting those godawful pitches! - var/extended_inventory = 0 //can we access the hidden inventory? - var/scan_id = 1 - var/obj/item/coin/coin - var/obj/item/stack/spacecash/bill - - var/dish_quants = list() //used by the snack machine's custom compartment to count dishes. - - var/obj/item/vending_refill/refill_canister = null //The type of refill canisters used by this machine. - var/refill_count = 3 //The number of canisters the vending machine uses - -/obj/machinery/vending/Initialize() - var/build_inv = FALSE - if(!refill_canister) - circuit = null - build_inv = TRUE - . = ..() - wires = new /datum/wires/vending(src) - if(build_inv) //non-constructable vending machine - build_inventory(products) - build_inventory(contraband, 1) - build_inventory(premium, 0, 1) - - slogan_list = splittext(product_slogans, ";") - // So not all machines speak at the exact same time. - // The first time this machine says something will be at slogantime + this random value, - // so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated. - last_slogan = world.time + rand(0, slogan_delay) - power_change() - -/obj/machinery/vending/Destroy() - QDEL_NULL(wires) - QDEL_NULL(coin) - QDEL_NULL(bill) - return ..() - -/obj/machinery/vending/snack/Destroy() - for(var/obj/item/reagent_containers/food/snacks/S in contents) - S.forceMove(get_turf(src)) - return ..() - -/obj/machinery/vending/RefreshParts() //Better would be to make constructable child - if(component_parts) - product_records = list() - hidden_records = list() - coin_records = list() - build_inventory(products, start_empty = 1) - build_inventory(contraband, 1, start_empty = 1) - build_inventory(premium, 0, 1, start_empty = 1) - for(var/obj/item/vending_refill/VR in component_parts) - refill_inventory(VR, product_records, STANDARD_CHARGE) - refill_inventory(VR, coin_records, COIN_CHARGE) - refill_inventory(VR, hidden_records, CONTRABAND_CHARGE) - - -/obj/machinery/vending/deconstruct(disassembled = TRUE) - if(!refill_canister) //the non constructable vendors drop metal instead of a machine frame. + var/list/premium = list() //No specified amount = only one in stock + + var/product_slogans = "" //String of slogans separated by semicolons, optional + var/product_ads = "" //String of small ad messages in the vending screen - random chance + var/list/product_records = list() + var/list/hidden_records = list() + var/list/coin_records = list() + var/list/slogan_list = list() + var/list/small_ads = list() //Small ad messages in the vending screen - random chance of popping up whenever you open it + var/vend_reply //Thank you for shopping! + var/last_reply = 0 + var/last_slogan = 0 //When did we last pitch? + var/slogan_delay = 6000 //How long until we can pitch again? + var/icon_vend //Icon_state when vending! + var/icon_deny //Icon_state when vending! + var/seconds_electrified = 0 //Shock customers like an airlock. + var/shoot_inventory = 0 //Fire items at customers! We're broken! + var/shoot_inventory_chance = 2 + var/shut_up = 0 //Stop spouting those godawful pitches! + var/extended_inventory = 0 //can we access the hidden inventory? + var/scan_id = 1 + var/obj/item/coin/coin + var/obj/item/stack/spacecash/bill + + var/dish_quants = list() //used by the snack machine's custom compartment to count dishes. + + var/obj/item/vending_refill/refill_canister = null //The type of refill canisters used by this machine. + var/refill_count = 3 //The number of canisters the vending machine uses + +/obj/machinery/vending/Initialize() + var/build_inv = FALSE + if(!refill_canister) + circuit = null + build_inv = TRUE + . = ..() + wires = new /datum/wires/vending(src) + if(build_inv) //non-constructable vending machine + build_inventory(products) + build_inventory(contraband, 1) + build_inventory(premium, 0, 1) + + slogan_list = splittext(product_slogans, ";") + // So not all machines speak at the exact same time. + // The first time this machine says something will be at slogantime + this random value, + // so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated. + last_slogan = world.time + rand(0, slogan_delay) + power_change() + +/obj/machinery/vending/Destroy() + QDEL_NULL(wires) + QDEL_NULL(coin) + QDEL_NULL(bill) + return ..() + +/obj/machinery/vending/snack/Destroy() + for(var/obj/item/reagent_containers/food/snacks/S in contents) + S.forceMove(get_turf(src)) + return ..() + +/obj/machinery/vending/RefreshParts() //Better would be to make constructable child + if(component_parts) + product_records = list() + hidden_records = list() + coin_records = list() + build_inventory(products, start_empty = 1) + build_inventory(contraband, 1, start_empty = 1) + build_inventory(premium, 0, 1, start_empty = 1) + for(var/obj/item/vending_refill/VR in component_parts) + refill_inventory(VR, product_records, STANDARD_CHARGE) + refill_inventory(VR, coin_records, COIN_CHARGE) + refill_inventory(VR, hidden_records, CONTRABAND_CHARGE) + + +/obj/machinery/vending/deconstruct(disassembled = TRUE) + if(!refill_canister) //the non constructable vendors drop metal instead of a machine frame. if(!(flags_1 & NODECONSTRUCT_1)) - new /obj/item/stack/sheet/metal(loc, 3) - qdel(src) - else - ..() - -/obj/machinery/vending/obj_break(damage_flag) + new /obj/item/stack/sheet/metal(loc, 3) + qdel(src) + else + ..() + +/obj/machinery/vending/obj_break(damage_flag) if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1)) - var/dump_amount = 0 - for(var/datum/data/vending_product/R in product_records) - if(R.amount <= 0) //Try to use a record that actually has something to dump. - continue - var/dump_path = R.product_path - if(!dump_path) - continue - - while(R.amount>0) - var/obj/O = new dump_path(loc) - step(O, pick(GLOB.alldirs)) //we only drop 20% of the total of each products and spread it - R.amount -= 5 //around to not fill the turf with too many objects. - dump_amount++ - if(dump_amount > 15) //so we don't drop too many items (e.g. ClothesMate) - break - stat |= BROKEN - icon_state = "[initial(icon_state)]-broken" - -/obj/machinery/vending/proc/build_inventory(list/productlist, hidden=0, req_coin=0, start_empty = null) - for(var/typepath in productlist) - var/amount = productlist[typepath] - if(isnull(amount)) - amount = 0 - - var/atom/temp = new typepath(null) - var/datum/data/vending_product/R = new /datum/data/vending_product() - R.product_name = initial(temp.name) - R.product_path = typepath - if(!start_empty) - R.amount = amount - R.max_amount = amount - R.display_color = pick("red","blue","green") - - if(hidden) - hidden_records += R - else if(req_coin) - coin_records += R - else - product_records += R - -/obj/machinery/vending/proc/refill_inventory(obj/item/vending_refill/refill, datum/data/vending_product/machine, var/charge_type = STANDARD_CHARGE) - var/total = 0 - var/to_restock = 0 - - for(var/datum/data/vending_product/machine_content in machine) - if(machine_content.amount == 0 && refill.charges[charge_type] > 0) - machine_content.amount++ - refill.charges[charge_type]-- - total++ - to_restock += machine_content.max_amount - machine_content.amount - if(to_restock <= refill.charges[charge_type]) - for(var/datum/data/vending_product/machine_content in machine) - machine_content.amount = machine_content.max_amount - refill.charges[charge_type] -= to_restock - total += to_restock - else - var/tmp_charges = refill.charges[charge_type] - for(var/datum/data/vending_product/machine_content in machine) - if(refill.charges[charge_type] == 0) - break - var/restock = Ceiling(((machine_content.max_amount - machine_content.amount)/to_restock)*tmp_charges) - if(restock > refill.charges[charge_type]) - restock = refill.charges[charge_type] - machine_content.amount += restock - refill.charges[charge_type] -= restock - total += restock - return total - -/obj/machinery/vending/snack/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/reagent_containers/food/snacks)) - if(!compartment_access_check(user)) - return - var/obj/item/reagent_containers/food/snacks/S = W - if(!S.junkiness) - if(!iscompartmentfull(user)) - if(!user.drop_item()) - return - W.loc = src - food_load(W) - to_chat(user, "You insert [W] into [src]'s chef compartment.") - else - to_chat(user, "[src]'s chef compartment does not accept junk food.") - - else if(istype(W, /obj/item/storage/bag/tray)) - if(!compartment_access_check(user)) - return - var/obj/item/storage/T = W - var/loaded = 0 - var/denied_items = 0 - for(var/obj/item/reagent_containers/food/snacks/S in T.contents) - if(iscompartmentfull(user)) - break - if(!S.junkiness) - T.remove_from_storage(S, src) - food_load(S) - loaded++ - else - denied_items++ - if(denied_items) - to_chat(user, "[src] refuses some items.") - if(loaded) - to_chat(user, "You insert [loaded] dishes into [src]'s chef compartment.") - updateUsrDialog() - return - - else - return ..() - -/obj/machinery/vending/snack/proc/compartment_access_check(user) - req_access_txt = chef_compartment_access - if(!allowed(user) && !emagged && scan_id) - to_chat(user, "[src]'s chef compartment blinks red: Access denied.") - req_access_txt = "0" - return 0 - req_access_txt = "0" - return 1 - -/obj/machinery/vending/snack/proc/iscompartmentfull(mob/user) - if(contents.len >= 30) // no more than 30 dishes can fit inside - to_chat(user, "[src]'s chef compartment is full.") - return 1 - return 0 - -/obj/machinery/vending/snack/proc/food_load(obj/item/reagent_containers/food/snacks/S) - if(dish_quants[S.name]) - dish_quants[S.name]++ - else - dish_quants[S.name] = 1 - sortList(dish_quants) - -/obj/machinery/vending/attackby(obj/item/W, mob/user, params) - if(panel_open) - if(default_unfasten_wrench(user, W, time = 60)) - return - - if(component_parts) - if(default_deconstruction_crowbar(W)) - return - - if(istype(W, /obj/item/screwdriver)) - if(anchored) - panel_open = !panel_open - to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.") - cut_overlays() - if(panel_open) - add_overlay("[initial(icon_state)]-panel") - playsound(src.loc, W.usesound, 50, 1) - updateUsrDialog() - else - to_chat(user, "You must first secure [src].") - return - else if(istype(W, /obj/item/device/multitool)||istype(W, /obj/item/wirecutters)) - if(panel_open) - attack_hand(user) - return - else if(istype(W, /obj/item/coin)) - if(coin) - to_chat(user, "[src] already has [coin] inserted") - return - if(bill) - to_chat(user, "[src] already has [bill] inserted") - return - if(!premium.len) - to_chat(user, "[src] doesn't have a coin slot.") - return - if(!user.drop_item()) - return - W.loc = src - coin = W - to_chat(user, "You insert [W] into [src].") - return - else if(istype(W, /obj/item/stack/spacecash)) - if(coin) - to_chat(user, "[src] already has [coin] inserted") - return - if(bill) - to_chat(user, "[src] already has [bill] inserted") - return - var/obj/item/stack/S = W - if(!premium.len) - to_chat(user, "[src] doesn't have a bill slot.") - return - S.use(1) - bill = new S.type(src,1) - to_chat(user, "You insert [W] into [src].") - return - else if(istype(W, refill_canister) && refill_canister != null) - if(stat & (BROKEN|NOPOWER)) - to_chat(user, "It does nothing.") - else if(panel_open) - //if the panel is open we attempt to refill the machine - var/obj/item/vending_refill/canister = W - if(canister.charges[STANDARD_CHARGE] == 0) - to_chat(user, "This [canister.name] is empty!") - else - var/transfered = refill_inventory(canister,product_records,STANDARD_CHARGE) - transfered += refill_inventory(canister,coin_records,COIN_CHARGE) - transfered += refill_inventory(canister,hidden_records,CONTRABAND_CHARGE) - if(transfered) - to_chat(user, "You loaded [transfered] items in \the [name].") - else - to_chat(user, "The [name] is fully stocked.") - return - else - to_chat(user, "You should probably unscrew the service panel first.") - else - return ..() - - -/obj/machinery/vending/on_deconstruction() - var/product_list = list(product_records, hidden_records, coin_records) - for(var/i=1, i<=3, i++) - for(var/datum/data/vending_product/machine_content in product_list[i]) - while(machine_content.amount !=0) - var/safety = 0 //to avoid infinite loop - for(var/obj/item/vending_refill/VR in component_parts) - safety++ - if(VR.charges[i] < VR.init_charges[i]) - VR.charges[i]++ - machine_content.amount-- - if(!machine_content.amount) - break - else - safety-- - if(safety <= 0) // all refill canisters are full - break - ..() - -/obj/machinery/vending/emag_act(mob/user) + var/dump_amount = 0 + for(var/datum/data/vending_product/R in product_records) + if(R.amount <= 0) //Try to use a record that actually has something to dump. + continue + var/dump_path = R.product_path + if(!dump_path) + continue + + while(R.amount>0) + var/obj/O = new dump_path(loc) + step(O, pick(GLOB.alldirs)) //we only drop 20% of the total of each products and spread it + R.amount -= 5 //around to not fill the turf with too many objects. + dump_amount++ + if(dump_amount > 15) //so we don't drop too many items (e.g. ClothesMate) + break + stat |= BROKEN + icon_state = "[initial(icon_state)]-broken" + +/obj/machinery/vending/proc/build_inventory(list/productlist, hidden=0, req_coin=0, start_empty = null) + for(var/typepath in productlist) + var/amount = productlist[typepath] + if(isnull(amount)) + amount = 0 + + var/atom/temp = new typepath(null) + var/datum/data/vending_product/R = new /datum/data/vending_product() + R.product_name = initial(temp.name) + R.product_path = typepath + if(!start_empty) + R.amount = amount + R.max_amount = amount + R.display_color = pick("red","blue","green") + + if(hidden) + hidden_records += R + else if(req_coin) + coin_records += R + else + product_records += R + +/obj/machinery/vending/proc/refill_inventory(obj/item/vending_refill/refill, datum/data/vending_product/machine, var/charge_type = STANDARD_CHARGE) + var/total = 0 + var/to_restock = 0 + + for(var/datum/data/vending_product/machine_content in machine) + if(machine_content.amount == 0 && refill.charges[charge_type] > 0) + machine_content.amount++ + refill.charges[charge_type]-- + total++ + to_restock += machine_content.max_amount - machine_content.amount + if(to_restock <= refill.charges[charge_type]) + for(var/datum/data/vending_product/machine_content in machine) + machine_content.amount = machine_content.max_amount + refill.charges[charge_type] -= to_restock + total += to_restock + else + var/tmp_charges = refill.charges[charge_type] + for(var/datum/data/vending_product/machine_content in machine) + if(refill.charges[charge_type] == 0) + break + var/restock = Ceiling(((machine_content.max_amount - machine_content.amount)/to_restock)*tmp_charges) + if(restock > refill.charges[charge_type]) + restock = refill.charges[charge_type] + machine_content.amount += restock + refill.charges[charge_type] -= restock + total += restock + return total + +/obj/machinery/vending/snack/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/reagent_containers/food/snacks)) + if(!compartment_access_check(user)) + return + var/obj/item/reagent_containers/food/snacks/S = W + if(!S.junkiness) + if(!iscompartmentfull(user)) + if(!user.drop_item()) + return + W.loc = src + food_load(W) + to_chat(user, "You insert [W] into [src]'s chef compartment.") + else + to_chat(user, "[src]'s chef compartment does not accept junk food.") + + else if(istype(W, /obj/item/storage/bag/tray)) + if(!compartment_access_check(user)) + return + var/obj/item/storage/T = W + var/loaded = 0 + var/denied_items = 0 + for(var/obj/item/reagent_containers/food/snacks/S in T.contents) + if(iscompartmentfull(user)) + break + if(!S.junkiness) + T.remove_from_storage(S, src) + food_load(S) + loaded++ + else + denied_items++ + if(denied_items) + to_chat(user, "[src] refuses some items.") + if(loaded) + to_chat(user, "You insert [loaded] dishes into [src]'s chef compartment.") + updateUsrDialog() + return + + else + return ..() + +/obj/machinery/vending/snack/proc/compartment_access_check(user) + req_access_txt = chef_compartment_access + if(!allowed(user) && !emagged && scan_id) + to_chat(user, "[src]'s chef compartment blinks red: Access denied.") + req_access_txt = "0" + return 0 + req_access_txt = "0" + return 1 + +/obj/machinery/vending/snack/proc/iscompartmentfull(mob/user) + if(contents.len >= 30) // no more than 30 dishes can fit inside + to_chat(user, "[src]'s chef compartment is full.") + return 1 + return 0 + +/obj/machinery/vending/snack/proc/food_load(obj/item/reagent_containers/food/snacks/S) + if(dish_quants[S.name]) + dish_quants[S.name]++ + else + dish_quants[S.name] = 1 + sortList(dish_quants) + +/obj/machinery/vending/attackby(obj/item/W, mob/user, params) + if(panel_open) + if(default_unfasten_wrench(user, W, time = 60)) + return + + if(component_parts) + if(default_deconstruction_crowbar(W)) + return + + if(istype(W, /obj/item/screwdriver)) + if(anchored) + panel_open = !panel_open + to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.") + cut_overlays() + if(panel_open) + add_overlay("[initial(icon_state)]-panel") + playsound(src.loc, W.usesound, 50, 1) + updateUsrDialog() + else + to_chat(user, "You must first secure [src].") + return + else if(istype(W, /obj/item/device/multitool)||istype(W, /obj/item/wirecutters)) + if(panel_open) + attack_hand(user) + return + else if(istype(W, /obj/item/coin)) + if(coin) + to_chat(user, "[src] already has [coin] inserted") + return + if(bill) + to_chat(user, "[src] already has [bill] inserted") + return + if(!premium.len) + to_chat(user, "[src] doesn't have a coin slot.") + return + if(!user.drop_item()) + return + W.loc = src + coin = W + to_chat(user, "You insert [W] into [src].") + return + else if(istype(W, /obj/item/stack/spacecash)) + if(coin) + to_chat(user, "[src] already has [coin] inserted") + return + if(bill) + to_chat(user, "[src] already has [bill] inserted") + return + var/obj/item/stack/S = W + if(!premium.len) + to_chat(user, "[src] doesn't have a bill slot.") + return + S.use(1) + bill = new S.type(src,1) + to_chat(user, "You insert [W] into [src].") + return + else if(istype(W, refill_canister) && refill_canister != null) + if(stat & (BROKEN|NOPOWER)) + to_chat(user, "It does nothing.") + else if(panel_open) + //if the panel is open we attempt to refill the machine + var/obj/item/vending_refill/canister = W + if(canister.charges[STANDARD_CHARGE] == 0) + to_chat(user, "This [canister.name] is empty!") + else + var/transfered = refill_inventory(canister,product_records,STANDARD_CHARGE) + transfered += refill_inventory(canister,coin_records,COIN_CHARGE) + transfered += refill_inventory(canister,hidden_records,CONTRABAND_CHARGE) + if(transfered) + to_chat(user, "You loaded [transfered] items in \the [name].") + else + to_chat(user, "The [name] is fully stocked.") + return + else + to_chat(user, "You should probably unscrew the service panel first.") + else + return ..() + + +/obj/machinery/vending/on_deconstruction() + var/product_list = list(product_records, hidden_records, coin_records) + for(var/i=1, i<=3, i++) + for(var/datum/data/vending_product/machine_content in product_list[i]) + while(machine_content.amount !=0) + var/safety = 0 //to avoid infinite loop + for(var/obj/item/vending_refill/VR in component_parts) + safety++ + if(VR.charges[i] < VR.init_charges[i]) + VR.charges[i]++ + machine_content.amount-- + if(!machine_content.amount) + break + else + safety-- + if(safety <= 0) // all refill canisters are full + break + ..() + +/obj/machinery/vending/emag_act(mob/user) if(emagged) return emagged = TRUE to_chat(user, "You short out the product lock on [src].") - -/obj/machinery/vending/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/vending/attack_hand(mob/user) - var/dat = "" - if(panel_open && !isAI(user)) - return wires.interact(user) - else - if(stat & (BROKEN|NOPOWER)) - return - - dat += "

    Select an item

    " - dat += "
    " - if(product_records.len == 0) - dat += "No product loaded!" - else - var/list/display_records = product_records - if(extended_inventory) - display_records = product_records + hidden_records - if(coin || bill) - display_records = product_records + coin_records - if((coin || bill) && extended_inventory) - display_records = product_records + hidden_records + coin_records - dat += "
      " - for (var/datum/data/vending_product/R in display_records) - dat += "
    • " - if(R.amount > 0) - dat += "Vend " - else - dat += "Sold out " - dat += "[sanitize(R.product_name)]:" - dat += " [R.amount]" - dat += "
    • " - dat += "
    " - dat += "
    " - if(premium.len > 0) - dat += "Change Return: " - if (coin || bill) - dat += "[(coin ? coin : "")][(bill ? bill : "")]  Remove" - else - dat += "No money  Remove" - if(istype(src, /obj/machinery/vending/snack)) - dat += "

    Chef's Food Selection

    " - dat += "
    " - for (var/O in dish_quants) - if(dish_quants[O] > 0) - var/N = dish_quants[O] - dat += "Dispense " - dat += "[capitalize(O)]: [N]
    " - dat += "
    " - user.set_machine(src) - if(seconds_electrified && !(stat & NOPOWER)) - if(shock(user, 100)) - return - - var/datum/browser/popup = new(user, "vending", (name)) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - - -/obj/machinery/vending/Topic(href, href_list) - if(..()) - return - - if(issilicon(usr)) - if(iscyborg(usr)) - var/mob/living/silicon/robot/R = usr + +/obj/machinery/vending/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/vending/attack_hand(mob/user) + var/dat = "" + if(panel_open && !isAI(user)) + return wires.interact(user) + else + if(stat & (BROKEN|NOPOWER)) + return + + dat += "

    Select an item

    " + dat += "
    " + if(product_records.len == 0) + dat += "No product loaded!" + else + var/list/display_records = product_records + if(extended_inventory) + display_records = product_records + hidden_records + if(coin || bill) + display_records = product_records + coin_records + if((coin || bill) && extended_inventory) + display_records = product_records + hidden_records + coin_records + dat += "
      " + for (var/datum/data/vending_product/R in display_records) + dat += "
    • " + if(R.amount > 0) + dat += "Vend " + else + dat += "Sold out " + dat += "[sanitize(R.product_name)]:" + dat += " [R.amount]" + dat += "
    • " + dat += "
    " + dat += "
    " + if(premium.len > 0) + dat += "Change Return: " + if (coin || bill) + dat += "[(coin ? coin : "")][(bill ? bill : "")]  Remove" + else + dat += "No money  Remove" + if(istype(src, /obj/machinery/vending/snack)) + dat += "

    Chef's Food Selection

    " + dat += "
    " + for (var/O in dish_quants) + if(dish_quants[O] > 0) + var/N = dish_quants[O] + dat += "Dispense " + dat += "[capitalize(O)]: [N]
    " + dat += "
    " + user.set_machine(src) + if(seconds_electrified && !(stat & NOPOWER)) + if(shock(user, 100)) + return + + var/datum/browser/popup = new(user, "vending", (name)) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + + +/obj/machinery/vending/Topic(href, href_list) + if(..()) + return + + if(issilicon(usr)) + if(iscyborg(usr)) + var/mob/living/silicon/robot/R = usr if(!(R.module && istype(R.module, /obj/item/robot_module/butler) )) - to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") - return - else - to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") - return - - if(href_list["remove_coin"]) - if(!(coin || bill)) - to_chat(usr, "There is no money in this machine.") - return - if(coin) - if(!usr.get_active_held_item()) - usr.put_in_hands(coin) - else - coin.forceMove(get_turf(src)) - to_chat(usr, "You remove [coin] from [src].") - coin = null - if(bill) - if(!usr.get_active_held_item()) - usr.put_in_hands(bill) - else - bill.forceMove(get_turf(src)) - to_chat(usr, "You remove [bill] from [src].") - bill = null - - - usr.set_machine(src) - - if((href_list["dispense"]) && (vend_ready)) - var/N = href_list["dispense"] - if(dish_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. - return - vend_ready = 0 - use_power(5) - - dish_quants[N] = max(dish_quants[N] - 1, 0) - for(var/obj/O in contents) - if(O.name == N) - O.loc = src.loc - break - vend_ready = 1 - updateUsrDialog() - return - - if((href_list["vend"]) && (vend_ready)) - if(panel_open) - to_chat(usr, "The vending machine cannot dispense products while its service panel is open!") - return - - if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH - to_chat(usr, "Access denied." ) - flick(icon_deny,src) - return - - vend_ready = 0 //One thing at a time!! - - var/datum/data/vending_product/R = locate(href_list["vend"]) - if(!R || !istype(R) || !R.product_path) - vend_ready = 1 - return - - if(R in hidden_records) - if(!extended_inventory) - vend_ready = 1 - return - else if(R in coin_records) - if(!(coin || bill)) - to_chat(usr, "You need to insert money to get this item!") - vend_ready = 1 - return - if(coin && coin.string_attached) - if(prob(50)) - if(usr.put_in_hands(coin)) - to_chat(usr, "You successfully pull [coin] out before [src] could swallow it.") - coin = null - else - to_chat(usr, "You couldn't pull [coin] out because your hands are full!") - QDEL_NULL(coin) - else - to_chat(usr, "You weren't able to pull [coin] out fast enough, the machine ate it, string and all!") - QDEL_NULL(coin) - else - QDEL_NULL(coin) - QDEL_NULL(bill) - - else if (!(R in product_records)) - vend_ready = 1 - message_admins("Vending machine exploit attempted by [key_name(usr, usr.client)]!") - return - - if (R.amount <= 0) - to_chat(usr, "Sold out.") - vend_ready = 1 - return - else - R.amount-- - - if(((last_reply + 200) <= world.time) && vend_reply) - speak(vend_reply) - last_reply = world.time - - use_power(5) - if(icon_vend) //Show the vending animation if needed - flick(icon_vend,src) - new R.product_path(get_turf(src)) - SSblackbox.add_details("vending_machine_usage","[src.type]|[R.product_path]") - vend_ready = 1 - return - - updateUsrDialog() - return - - else if(href_list["togglevoice"] && panel_open) - shut_up = !shut_up - - updateUsrDialog() - - -/obj/machinery/vending/process() - if(stat & (BROKEN|NOPOWER)) - return - if(!active) - return - - if(seconds_electrified > 0) - seconds_electrified-- - - //Pitch to the people! Really sell it! - if(last_slogan + slogan_delay <= world.time && slogan_list.len > 0 && !shut_up && prob(5)) - var/slogan = pick(slogan_list) - speak(slogan) - last_slogan = world.time - - if(shoot_inventory && prob(shoot_inventory_chance)) - throw_item() - - -/obj/machinery/vending/proc/speak(message) - if(stat & (BROKEN|NOPOWER)) - return - if(!message) - return - - say(message) - -/obj/machinery/vending/power_change() - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - else - if(powered()) - icon_state = initial(icon_state) - stat &= ~NOPOWER - else - icon_state = "[initial(icon_state)]-off" - stat |= NOPOWER - - -//Somebody cut an important wire and now we're following a new definition of "pitch." -/obj/machinery/vending/proc/throw_item() - var/obj/throw_item = null - var/mob/living/target = locate() in view(7,src) - if(!target) - return 0 - - for(var/datum/data/vending_product/R in shuffle(product_records)) - if(R.amount <= 0) //Try to use a record that actually has something to dump. - continue - var/dump_path = R.product_path - if(!dump_path) - continue - - R.amount-- - throw_item = new dump_path(loc) - break - if(!throw_item) - return 0 - - pre_throw(throw_item) - - throw_item.throw_at(target, 16, 3) - visible_message("[src] launches [throw_item] at [target]!") - return 1 - -/obj/machinery/vending/proc/pre_throw(obj/item/I) - return - - -/obj/machinery/vending/proc/shock(mob/user, prb) - if(stat & (BROKEN|NOPOWER)) // unpowered, no shock - return FALSE - if(!prob(prb)) - return FALSE - do_sparks(5, TRUE, src) - var/tmp/check_range = TRUE - if(electrocute_mob(user, get_area(src), src, 0.7, check_range)) - return TRUE - else - return FALSE - -/* - * Vending machine types - */ - -/* - -/obj/machinery/vending/[vendors name here] // --vending machine template :) - name = "" - desc = "" - icon = '' - icon_state = "" - products = list() - contraband = list() - premium = list() - -IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY CANISTER CHARGES in vending_items.dm -*/ - -/* -/obj/machinery/vending/atmospherics //Commenting this out until someone ponies up some actual working, broken, and unpowered sprites - Quarxink - name = "Tank Vendor" - desc = "A vendor with a wide variety of masks and gas tanks." - icon = 'icons/obj/objects.dmi' - icon_state = "dispenser" - product_paths = "/obj/item/tank/internals/oxygen;/obj/item/tank/internals/plasma;/obj/item/tank/internals/emergency_oxygen;/obj/item/tank/internals/emergency_oxygen/engi;/obj/item/clothing/mask/breath" - product_amounts = "10;10;10;5;25" -*/ - -/obj/machinery/vending/boozeomat - name = "\improper Booze-O-Mat" - desc = "A technological marvel, supposedly able to mix just the mixture you'd like to drink the moment you ask for one." - icon_state = "boozeomat" //////////////18 drink entities below, plus the glasses, in case someone wants to edit the number of bottles - icon_deny = "boozeomat-deny" + to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") + return + else + to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") + return + + if(href_list["remove_coin"]) + if(!(coin || bill)) + to_chat(usr, "There is no money in this machine.") + return + if(coin) + if(!usr.get_active_held_item()) + usr.put_in_hands(coin) + else + coin.forceMove(get_turf(src)) + to_chat(usr, "You remove [coin] from [src].") + coin = null + if(bill) + if(!usr.get_active_held_item()) + usr.put_in_hands(bill) + else + bill.forceMove(get_turf(src)) + to_chat(usr, "You remove [bill] from [src].") + bill = null + + + usr.set_machine(src) + + if((href_list["dispense"]) && (vend_ready)) + var/N = href_list["dispense"] + if(dish_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. + return + vend_ready = 0 + use_power(5) + + dish_quants[N] = max(dish_quants[N] - 1, 0) + for(var/obj/O in contents) + if(O.name == N) + O.loc = src.loc + break + vend_ready = 1 + updateUsrDialog() + return + + if((href_list["vend"]) && (vend_ready)) + if(panel_open) + to_chat(usr, "The vending machine cannot dispense products while its service panel is open!") + return + + if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH + to_chat(usr, "Access denied." ) + flick(icon_deny,src) + return + + vend_ready = 0 //One thing at a time!! + + var/datum/data/vending_product/R = locate(href_list["vend"]) + if(!R || !istype(R) || !R.product_path) + vend_ready = 1 + return + + if(R in hidden_records) + if(!extended_inventory) + vend_ready = 1 + return + else if(R in coin_records) + if(!(coin || bill)) + to_chat(usr, "You need to insert money to get this item!") + vend_ready = 1 + return + if(coin && coin.string_attached) + if(prob(50)) + if(usr.put_in_hands(coin)) + to_chat(usr, "You successfully pull [coin] out before [src] could swallow it.") + coin = null + else + to_chat(usr, "You couldn't pull [coin] out because your hands are full!") + QDEL_NULL(coin) + else + to_chat(usr, "You weren't able to pull [coin] out fast enough, the machine ate it, string and all!") + QDEL_NULL(coin) + else + QDEL_NULL(coin) + QDEL_NULL(bill) + + else if (!(R in product_records)) + vend_ready = 1 + message_admins("Vending machine exploit attempted by [key_name(usr, usr.client)]!") + return + + if (R.amount <= 0) + to_chat(usr, "Sold out.") + vend_ready = 1 + return + else + R.amount-- + + if(((last_reply + 200) <= world.time) && vend_reply) + speak(vend_reply) + last_reply = world.time + + use_power(5) + if(icon_vend) //Show the vending animation if needed + flick(icon_vend,src) + new R.product_path(get_turf(src)) + SSblackbox.add_details("vending_machine_usage","[src.type]|[R.product_path]") + vend_ready = 1 + return + + updateUsrDialog() + return + + else if(href_list["togglevoice"] && panel_open) + shut_up = !shut_up + + updateUsrDialog() + + +/obj/machinery/vending/process() + if(stat & (BROKEN|NOPOWER)) + return + if(!active) + return + + if(seconds_electrified > 0) + seconds_electrified-- + + //Pitch to the people! Really sell it! + if(last_slogan + slogan_delay <= world.time && slogan_list.len > 0 && !shut_up && prob(5)) + var/slogan = pick(slogan_list) + speak(slogan) + last_slogan = world.time + + if(shoot_inventory && prob(shoot_inventory_chance)) + throw_item() + + +/obj/machinery/vending/proc/speak(message) + if(stat & (BROKEN|NOPOWER)) + return + if(!message) + return + + say(message) + +/obj/machinery/vending/power_change() + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" + else + if(powered()) + icon_state = initial(icon_state) + stat &= ~NOPOWER + else + icon_state = "[initial(icon_state)]-off" + stat |= NOPOWER + + +//Somebody cut an important wire and now we're following a new definition of "pitch." +/obj/machinery/vending/proc/throw_item() + var/obj/throw_item = null + var/mob/living/target = locate() in view(7,src) + if(!target) + return 0 + + for(var/datum/data/vending_product/R in shuffle(product_records)) + if(R.amount <= 0) //Try to use a record that actually has something to dump. + continue + var/dump_path = R.product_path + if(!dump_path) + continue + + R.amount-- + throw_item = new dump_path(loc) + break + if(!throw_item) + return 0 + + pre_throw(throw_item) + + throw_item.throw_at(target, 16, 3) + visible_message("[src] launches [throw_item] at [target]!") + return 1 + +/obj/machinery/vending/proc/pre_throw(obj/item/I) + return + + +/obj/machinery/vending/proc/shock(mob/user, prb) + if(stat & (BROKEN|NOPOWER)) // unpowered, no shock + return FALSE + if(!prob(prb)) + return FALSE + do_sparks(5, TRUE, src) + var/tmp/check_range = TRUE + if(electrocute_mob(user, get_area(src), src, 0.7, check_range)) + return TRUE + else + return FALSE + +/* + * Vending machine types + */ + +/* + +/obj/machinery/vending/[vendors name here] // --vending machine template :) + name = "" + desc = "" + icon = '' + icon_state = "" + products = list() + contraband = list() + premium = list() + +IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY CANISTER CHARGES in vending_items.dm +*/ + +/* +/obj/machinery/vending/atmospherics //Commenting this out until someone ponies up some actual working, broken, and unpowered sprites - Quarxink + name = "Tank Vendor" + desc = "A vendor with a wide variety of masks and gas tanks." + icon = 'icons/obj/objects.dmi' + icon_state = "dispenser" + product_paths = "/obj/item/tank/internals/oxygen;/obj/item/tank/internals/plasma;/obj/item/tank/internals/emergency_oxygen;/obj/item/tank/internals/emergency_oxygen/engi;/obj/item/clothing/mask/breath" + product_amounts = "10;10;10;5;25" +*/ + +/obj/machinery/vending/boozeomat + name = "\improper Booze-O-Mat" + desc = "A technological marvel, supposedly able to mix just the mixture you'd like to drink the moment you ask for one." + icon_state = "boozeomat" //////////////18 drink entities below, plus the glasses, in case someone wants to edit the number of bottles + icon_deny = "boozeomat-deny" products = list(/obj/item/reagent_containers/food/drinks/bottle/gin = 5, /obj/item/reagent_containers/food/drinks/bottle/whiskey = 5, /obj/item/reagent_containers/food/drinks/bottle/tequila = 5, /obj/item/reagent_containers/food/drinks/bottle/vodka = 5, /obj/item/reagent_containers/food/drinks/bottle/vermouth = 5, /obj/item/reagent_containers/food/drinks/bottle/rum = 5, /obj/item/reagent_containers/food/drinks/bottle/wine = 5, /obj/item/reagent_containers/food/drinks/bottle/cognac = 5, /obj/item/reagent_containers/food/drinks/bottle/kahlua = 5, /obj/item/reagent_containers/food/drinks/bottle/hcider = 5, - /obj/item/reagent_containers/food/drinks/bottle/absinthe = 5, /obj/item/reagent_containers/food/drinks/bottle/grappa = 5, + /obj/item/reagent_containers/food/drinks/bottle/absinthe = 5, /obj/item/reagent_containers/food/drinks/bottle/grappa = 5, /obj/item/reagent_containers/food/drinks/ale = 6, /obj/item/reagent_containers/food/drinks/bottle/orangejuice = 4, /obj/item/reagent_containers/food/drinks/bottle/tomatojuice = 4, /obj/item/reagent_containers/food/drinks/bottle/limejuice = 4, /obj/item/reagent_containers/food/drinks/bottle/cream = 4, /obj/item/reagent_containers/food/drinks/soda_cans/tonic = 8, - /obj/item/reagent_containers/food/drinks/soda_cans/cola = 8, /obj/item/reagent_containers/food/drinks/soda_cans/sodawater = 15, + /obj/item/reagent_containers/food/drinks/soda_cans/cola = 8, /obj/item/reagent_containers/food/drinks/soda_cans/sodawater = 15, /obj/item/reagent_containers/food/drinks/drinkingglass = 30, /obj/item/reagent_containers/food/drinks/ice = 10, /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass = 12, /obj/item/reagent_containers/food/drinks/flask = 3) - contraband = list(/obj/item/reagent_containers/food/drinks/mug/tea = 12) - product_slogans = "I hope nobody asks me for a bloody cup o' tea...;Alcohol is humanity's friend. Would you abandon a friend?;Quite delighted to serve you!;Is nobody thirsty on this station?" - product_ads = "Drink up!;Booze is good for you!;Alcohol is humanity's best friend.;Quite delighted to serve you!;Care for a nice, cold beer?;Nothing cures you like booze!;Have a sip!;Have a drink!;Have a beer!;Beer is good for you!;Only the finest alcohol!;Best quality booze since 2053!;Award-winning wine!;Maximum alcohol!;Man loves beer.;A toast for progress!" - req_access_txt = "25" - refill_canister = /obj/item/vending_refill/boozeomat - -/obj/machinery/vending/assist + contraband = list(/obj/item/reagent_containers/food/drinks/mug/tea = 12) + product_slogans = "I hope nobody asks me for a bloody cup o' tea...;Alcohol is humanity's friend. Would you abandon a friend?;Quite delighted to serve you!;Is nobody thirsty on this station?" + product_ads = "Drink up!;Booze is good for you!;Alcohol is humanity's best friend.;Quite delighted to serve you!;Care for a nice, cold beer?;Nothing cures you like booze!;Have a sip!;Have a drink!;Have a beer!;Beer is good for you!;Only the finest alcohol!;Best quality booze since 2053!;Award-winning wine!;Maximum alcohol!;Man loves beer.;A toast for progress!" + req_access_txt = "25" + refill_canister = /obj/item/vending_refill/boozeomat + +/obj/machinery/vending/assist products = list( /obj/item/device/assembly/prox_sensor = 5, /obj/item/device/assembly/igniter = 3, /obj/item/device/assembly/signaler = 4, - /obj/item/wirecutters = 1, /obj/item/cartridge/signal = 4) - contraband = list(/obj/item/device/assembly/timer = 2, /obj/item/device/assembly/voice = 2, /obj/item/device/assembly/health = 2) - product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!" - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/coffee - name = "\improper Solar's Best Hot Drinks" - desc = "A vending machine which dispenses hot drinks." - product_ads = "Have a drink!;Drink up!;It's good for you!;Would you like a hot joe?;I'd kill for some coffee!;The best beans in the galaxy.;Only the finest brew for you.;Mmmm. Nothing like a coffee.;I like coffee, don't you?;Coffee helps you work!;Try some tea.;We hope you like the best!;Try our new chocolate!;Admin conspiracies" - icon_state = "coffee" - icon_vend = "coffee-vend" + /obj/item/wirecutters = 1, /obj/item/cartridge/signal = 4) + contraband = list(/obj/item/device/assembly/timer = 2, /obj/item/device/assembly/voice = 2, /obj/item/device/assembly/health = 2) + product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/coffee + name = "\improper Solar's Best Hot Drinks" + desc = "A vending machine which dispenses hot drinks." + product_ads = "Have a drink!;Drink up!;It's good for you!;Would you like a hot joe?;I'd kill for some coffee!;The best beans in the galaxy.;Only the finest brew for you.;Mmmm. Nothing like a coffee.;I like coffee, don't you?;Coffee helps you work!;Try some tea.;We hope you like the best!;Try our new chocolate!;Admin conspiracies" + icon_state = "coffee" + icon_vend = "coffee-vend" products = list(/obj/item/reagent_containers/food/drinks/coffee = 25, /obj/item/reagent_containers/food/drinks/mug/tea = 25, /obj/item/reagent_containers/food/drinks/mug/coco = 25) - contraband = list(/obj/item/reagent_containers/food/drinks/ice = 12) - refill_canister = /obj/item/vending_refill/coffee - -/obj/machinery/vending/snack - name = "\improper Getmore Chocolate Corp" - desc = "A snack machine courtesy of the Getmore Chocolate Corporation, based out of Mars." - product_slogans = "Try our new nougat bar!;Twice the calories for half the price!" - product_ads = "The healthiest!;Award-winning chocolate bars!;Mmm! So good!;Oh my god it's so juicy!;Have a snack.;Snacks are good for you!;Have some more Getmore!;Best quality snacks straight from mars.;We love chocolate!;Try our new jerky!" - icon_state = "snack" + contraband = list(/obj/item/reagent_containers/food/drinks/ice = 12) + refill_canister = /obj/item/vending_refill/coffee + +/obj/machinery/vending/snack + name = "\improper Getmore Chocolate Corp" + desc = "A snack machine courtesy of the Getmore Chocolate Corporation, based out of Mars." + product_slogans = "Try our new nougat bar!;Twice the calories for half the price!" + product_ads = "The healthiest!;Award-winning chocolate bars!;Mmm! So good!;Oh my god it's so juicy!;Have a snack.;Snacks are good for you!;Have some more Getmore!;Best quality snacks straight from mars.;We love chocolate!;Try our new jerky!" + icon_state = "snack" products = list(/obj/item/reagent_containers/food/snacks/candy = 6, /obj/item/reagent_containers/food/drinks/dry_ramen = 6, /obj/item/reagent_containers/food/snacks/chips =6, /obj/item/reagent_containers/food/snacks/sosjerky = 6, /obj/item/reagent_containers/food/snacks/no_raisin = 6, /obj/item/reagent_containers/food/snacks/spacetwinkie = 6, - /obj/item/reagent_containers/food/snacks/cheesiehonkers = 6) - contraband = list(/obj/item/reagent_containers/food/snacks/syndicake = 6) - refill_canister = /obj/item/vending_refill/snack - var/chef_compartment_access = "28" - -/obj/machinery/vending/snack/random - name = "\improper Random Snackies" - desc = "Uh oh!" - -/obj/machinery/vending/snack/random/Initialize() - ..() - var/T = pick(subtypesof(/obj/machinery/vending/snack) - /obj/machinery/vending/snack/random) - new T(get_turf(src)) - qdel(src) - -/obj/machinery/vending/snack/blue - icon_state = "snackblue" - -/obj/machinery/vending/snack/orange - icon_state = "snackorange" - -/obj/machinery/vending/snack/green - icon_state = "snackgreen" - -/obj/machinery/vending/snack/teal - icon_state = "snackteal" - -/obj/machinery/vending/sustenance - name = "\improper Sustenance Vendor" - desc = "A vending machine which vends food, as required by section 47-C of the NT's Prisoner Ethical Treatment Agreement." - product_slogans = "Enjoy your meal.;Enough calories to support strenuous labor." - product_ads = "Sufficiently healthy.;Efficiently produced tofu!;Mmm! So good!;Have a meal.;You need food to live!;Have some more candy corn!;Try our new ice cups!" - icon_state = "sustenance" - products = list(/obj/item/reagent_containers/food/snacks/tofu = 24, - /obj/item/reagent_containers/food/drinks/ice = 12, + /obj/item/reagent_containers/food/snacks/cheesiehonkers = 6) + contraband = list(/obj/item/reagent_containers/food/snacks/syndicake = 6) + refill_canister = /obj/item/vending_refill/snack + var/chef_compartment_access = "28" + +/obj/machinery/vending/snack/random + name = "\improper Random Snackies" + desc = "Uh oh!" + +/obj/machinery/vending/snack/random/Initialize() + ..() + var/T = pick(subtypesof(/obj/machinery/vending/snack) - /obj/machinery/vending/snack/random) + new T(get_turf(src)) + qdel(src) + +/obj/machinery/vending/snack/blue + icon_state = "snackblue" + +/obj/machinery/vending/snack/orange + icon_state = "snackorange" + +/obj/machinery/vending/snack/green + icon_state = "snackgreen" + +/obj/machinery/vending/snack/teal + icon_state = "snackteal" + +/obj/machinery/vending/sustenance + name = "\improper Sustenance Vendor" + desc = "A vending machine which vends food, as required by section 47-C of the NT's Prisoner Ethical Treatment Agreement." + product_slogans = "Enjoy your meal.;Enough calories to support strenuous labor." + product_ads = "Sufficiently healthy.;Efficiently produced tofu!;Mmm! So good!;Have a meal.;You need food to live!;Have some more candy corn!;Try our new ice cups!" + icon_state = "sustenance" + products = list(/obj/item/reagent_containers/food/snacks/tofu = 24, + /obj/item/reagent_containers/food/drinks/ice = 12, /obj/item/reagent_containers/food/snacks/candy_corn = 6) - contraband = list(/obj/item/kitchen/knife = 6, - /obj/item/reagent_containers/food/drinks/coffee = 12, - /obj/item/tank/internals/emergency_oxygen = 6, - /obj/item/clothing/mask/breath = 6) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/cola - name = "\improper Robust Softdrinks" - desc = "A softdrink vendor provided by Robust Industries, LLC." - icon_state = "Cola_Machine" - product_slogans = "Robust Softdrinks: More robust than a toolbox to the head!" - product_ads = "Refreshing!;Hope you're thirsty!;Over 1 million drinks sold!;Thirsty? Why not cola?;Please, have a drink!;Drink up!;The best drinks in space." + contraband = list(/obj/item/kitchen/knife = 6, + /obj/item/reagent_containers/food/drinks/coffee = 12, + /obj/item/tank/internals/emergency_oxygen = 6, + /obj/item/clothing/mask/breath = 6) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/cola + name = "\improper Robust Softdrinks" + desc = "A softdrink vendor provided by Robust Industries, LLC." + icon_state = "Cola_Machine" + product_slogans = "Robust Softdrinks: More robust than a toolbox to the head!" + product_ads = "Refreshing!;Hope you're thirsty!;Over 1 million drinks sold!;Thirsty? Why not cola?;Please, have a drink!;Drink up!;The best drinks in space." products = list(/obj/item/reagent_containers/food/drinks/soda_cans/cola = 10, /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind = 10, /obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb = 10, /obj/item/reagent_containers/food/drinks/soda_cans/starkist = 10, /obj/item/reagent_containers/food/drinks/soda_cans/space_up = 10, /obj/item/reagent_containers/food/drinks/soda_cans/pwr_game = 10, /obj/item/reagent_containers/food/drinks/soda_cans/lemon_lime = 10, /obj/item/reagent_containers/glass/beaker/waterbottle = 10) contraband = list(/obj/item/reagent_containers/food/drinks/soda_cans/thirteenloko = 6, /obj/item/reagent_containers/food/drinks/soda_cans/shamblers = 6) premium = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/nuka_cola = 1, /obj/item/reagent_containers/food/drinks/soda_cans/air = 1) - refill_canister = /obj/item/vending_refill/cola - -/obj/machinery/vending/cola/random - name = "\improper Random Drinkies" - desc = "Uh oh!" - -/obj/machinery/vending/cola/random/Initialize() + refill_canister = /obj/item/vending_refill/cola + +/obj/machinery/vending/cola/random + name = "\improper Random Drinkies" + desc = "Uh oh!" + +/obj/machinery/vending/cola/random/Initialize() . = ..() - var/T = pick(subtypesof(/obj/machinery/vending/cola) - /obj/machinery/vending/cola/random) - new T(get_turf(src)) - qdel(src) - -/obj/machinery/vending/cola/blue - icon_state = "Cola_Machine" - -/obj/machinery/vending/cola/black - icon_state = "cola_black" - -/obj/machinery/vending/cola/red - icon_state = "red_cola" - name = "\improper Space Cola Vendor" - desc = "It vends cola, in space." - product_slogans = "Cola in space!" - -/obj/machinery/vending/cola/space_up - icon_state = "space_up" - name = "\improper Space-up! Vendor" - desc = "Indulge in an explosion of flavor." - product_slogans = "Space-up! Like a hull breach in your mouth." - -/obj/machinery/vending/cola/starkist - icon_state = "starkist" - name = "\improper Star-kist Vendor" - desc = "The taste of a star in liquid form." - product_slogans = "Drink the stars! Star-kist!" - -/obj/machinery/vending/cola/sodie - icon_state = "soda" - -/obj/machinery/vending/cola/pwr_game - icon_state = "pwr_game" - name = "\improper Pwr Game Vendor" - desc = "You want it, we got it. Brought to you in partnership with Vlad's Salads." - product_slogans = "The POWER that gamers crave! PWR GAME!" - -/obj/machinery/vending/cola/shamblers - name = "\improper Shambler's Vendor" - desc = "~Shake me up some of that Shambler's Juice!~" - icon_state = "shamblers_juice" + var/T = pick(subtypesof(/obj/machinery/vending/cola) - /obj/machinery/vending/cola/random) + new T(get_turf(src)) + qdel(src) + +/obj/machinery/vending/cola/blue + icon_state = "Cola_Machine" + +/obj/machinery/vending/cola/black + icon_state = "cola_black" + +/obj/machinery/vending/cola/red + icon_state = "red_cola" + name = "\improper Space Cola Vendor" + desc = "It vends cola, in space." + product_slogans = "Cola in space!" + +/obj/machinery/vending/cola/space_up + icon_state = "space_up" + name = "\improper Space-up! Vendor" + desc = "Indulge in an explosion of flavor." + product_slogans = "Space-up! Like a hull breach in your mouth." + +/obj/machinery/vending/cola/starkist + icon_state = "starkist" + name = "\improper Star-kist Vendor" + desc = "The taste of a star in liquid form." + product_slogans = "Drink the stars! Star-kist!" + +/obj/machinery/vending/cola/sodie + icon_state = "soda" + +/obj/machinery/vending/cola/pwr_game + icon_state = "pwr_game" + name = "\improper Pwr Game Vendor" + desc = "You want it, we got it. Brought to you in partnership with Vlad's Salads." + product_slogans = "The POWER that gamers crave! PWR GAME!" + +/obj/machinery/vending/cola/shamblers + name = "\improper Shambler's Vendor" + desc = "~Shake me up some of that Shambler's Juice!~" + icon_state = "shamblers_juice" products = list(/obj/item/reagent_containers/food/drinks/soda_cans/cola = 10, /obj/item/reagent_containers/food/drinks/soda_cans/space_mountain_wind = 10, /obj/item/reagent_containers/food/drinks/soda_cans/dr_gibb = 10, /obj/item/reagent_containers/food/drinks/soda_cans/starkist = 10, /obj/item/reagent_containers/food/drinks/soda_cans/space_up = 10, /obj/item/reagent_containers/food/drinks/soda_cans/pwr_game = 10, /obj/item/reagent_containers/food/drinks/soda_cans/lemon_lime = 10, /obj/item/reagent_containers/food/drinks/soda_cans/shamblers = 10) - product_slogans = "~Shake me up some of that Shambler's Juice!~" - product_ads = "Refreshing!;Jyrbv dv lg jfdv fw kyrk Jyrdscvi'j Alztv!;Over 1 trillion souls drank!;Thirsty? Nyp efk uizeb kyv uribevjj?;Kyv Jyrdscvi uizebj kyv ezxyk!;Drink up!;Krjkp." - - -//This one's from bay12 -/obj/machinery/vending/cart - name = "\improper PTech" - desc = "Cartridges for PDAs" - product_slogans = "Carts to go!" - icon_state = "cart" - icon_deny = "cart-deny" + product_slogans = "~Shake me up some of that Shambler's Juice!~" + product_ads = "Refreshing!;Jyrbv dv lg jfdv fw kyrk Jyrdscvi'j Alztv!;Over 1 trillion souls drank!;Thirsty? Nyp efk uizeb kyv uribevjj?;Kyv Jyrdscvi uizebj kyv ezxyk!;Drink up!;Krjkp." + + +//This one's from bay12 +/obj/machinery/vending/cart + name = "\improper PTech" + desc = "Cartridges for PDAs" + product_slogans = "Carts to go!" + icon_state = "cart" + icon_deny = "cart-deny" products = list(/obj/item/cartridge/medical = 10, /obj/item/cartridge/engineering = 10, /obj/item/cartridge/security = 10, /obj/item/cartridge/janitor = 10, /obj/item/cartridge/signal/toxins = 10, /obj/item/device/pda/heads = 10, /obj/item/cartridge/captain = 3, /obj/item/cartridge/quartermaster = 10) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/liberationstation - name = "\improper Liberation Station" - desc = "An overwhelming amount of ancient patriotism washes over you just by looking at the machine." - icon_state = "liberationstation" - req_access_txt = "1" - product_slogans = "Liberation Station: Your one-stop shop for all things second ammendment!;Be a patriot today, pick up a gun!;Quality weapons for cheap prices!;Better dead than red!" - product_ads = "Float like an astronaut, sting like a bullet!;Express your second ammendment today!;Guns don't kill people, but you can!;Who needs responsibilities when you have guns?" - vend_reply = "Remember the name: Liberation Station!" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/liberationstation + name = "\improper Liberation Station" + desc = "An overwhelming amount of ancient patriotism washes over you just by looking at the machine." + icon_state = "liberationstation" + req_access_txt = "1" + product_slogans = "Liberation Station: Your one-stop shop for all things second ammendment!;Be a patriot today, pick up a gun!;Quality weapons for cheap prices!;Better dead than red!" + product_ads = "Float like an astronaut, sting like a bullet!;Express your second ammendment today!;Guns don't kill people, but you can!;Who needs responsibilities when you have guns?" + vend_reply = "Remember the name: Liberation Station!" products = list(/obj/item/gun/ballistic/automatic/pistol/deagle/gold = 2, /obj/item/gun/ballistic/automatic/pistol/deagle/camo = 2, /obj/item/gun/ballistic/automatic/pistol/m1911 = 2, /obj/item/gun/ballistic/automatic/proto/unrestricted = 2, /obj/item/gun/ballistic/shotgun/automatic/combat = 2, /obj/item/gun/ballistic/automatic/gyropistol = 1, /obj/item/gun/ballistic/shotgun = 2, /obj/item/gun/ballistic/automatic/ar = 2) premium = list(/obj/item/ammo_box/magazine/smgm9mm = 2, /obj/item/ammo_box/magazine/m50 = 4, /obj/item/ammo_box/magazine/m45 = 2, /obj/item/ammo_box/magazine/m75 = 2) contraband = list(/obj/item/clothing/under/patriotsuit = 1, /obj/item/bedsheet/patriot = 3) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/cigarette - name = "\improper ShadyCigs Deluxe" - desc = "If you want to get cancer, might as well do it in style." - product_slogans = "Space cigs taste good like a cigarette should.;I'd rather toolbox than switch.;Smoke!;Don't believe the reports - smoke today!" - product_ads = "Probably not bad for you!;Don't believe the scientists!;It's good for you!;Don't quit, buy more!;Smoke!;Nicotine heaven.;Best cigarettes since 2150.;Award-winning cigs." - icon_state = "cigs" - products = list(/obj/item/storage/fancy/cigarettes = 5, - /obj/item/storage/fancy/cigarettes/cigpack_uplift = 3, - /obj/item/storage/fancy/cigarettes/cigpack_robust = 3, - /obj/item/storage/fancy/cigarettes/cigpack_carp = 3, - /obj/item/storage/fancy/cigarettes/cigpack_midori = 3, - /obj/item/storage/box/matches = 10, - /obj/item/lighter/greyscale = 4, - /obj/item/storage/fancy/rollingpapers = 5) - contraband = list(/obj/item/lighter = 3, /obj/item/clothing/mask/vape = 5) - premium = list(/obj/item/storage/fancy/cigarettes/cigpack_robustgold = 3, \ - /obj/item/storage/fancy/cigarettes/cigars = 1, /obj/item/storage/fancy/cigarettes/cigars/havana = 1, /obj/item/storage/fancy/cigarettes/cigars/cohiba = 1) - refill_canister = /obj/item/vending_refill/cigarette - -/obj/machinery/vending/cigarette/pre_throw(obj/item/I) - if(istype(I, /obj/item/lighter)) - var/obj/item/lighter/L = I - L.set_lit(TRUE) - -/obj/machinery/vending/medical - name = "\improper NanoMed Plus" - desc = "Medical drug dispenser." - icon_state = "med" - icon_deny = "med-deny" - product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!" - req_access_txt = "5" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/cigarette + name = "\improper ShadyCigs Deluxe" + desc = "If you want to get cancer, might as well do it in style." + product_slogans = "Space cigs taste good like a cigarette should.;I'd rather toolbox than switch.;Smoke!;Don't believe the reports - smoke today!" + product_ads = "Probably not bad for you!;Don't believe the scientists!;It's good for you!;Don't quit, buy more!;Smoke!;Nicotine heaven.;Best cigarettes since 2150.;Award-winning cigs." + icon_state = "cigs" + products = list(/obj/item/storage/fancy/cigarettes = 5, + /obj/item/storage/fancy/cigarettes/cigpack_uplift = 3, + /obj/item/storage/fancy/cigarettes/cigpack_robust = 3, + /obj/item/storage/fancy/cigarettes/cigpack_carp = 3, + /obj/item/storage/fancy/cigarettes/cigpack_midori = 3, + /obj/item/storage/box/matches = 10, + /obj/item/lighter/greyscale = 4, + /obj/item/storage/fancy/rollingpapers = 5) + contraband = list(/obj/item/lighter = 3, /obj/item/clothing/mask/vape = 5) + premium = list(/obj/item/storage/fancy/cigarettes/cigpack_robustgold = 3, \ + /obj/item/storage/fancy/cigarettes/cigars = 1, /obj/item/storage/fancy/cigarettes/cigars/havana = 1, /obj/item/storage/fancy/cigarettes/cigars/cohiba = 1) + refill_canister = /obj/item/vending_refill/cigarette + +/obj/machinery/vending/cigarette/pre_throw(obj/item/I) + if(istype(I, /obj/item/lighter)) + var/obj/item/lighter/L = I + L.set_lit(TRUE) + +/obj/machinery/vending/medical + name = "\improper NanoMed Plus" + desc = "Medical drug dispenser." + icon_state = "med" + icon_deny = "med-deny" + product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!" + req_access_txt = "5" 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) - resistance_flags = FIRE_PROOF - refill_canister = /obj/item/vending_refill/medical - -//This one's from bay12 -/obj/machinery/vending/plasmaresearch - name = "\improper Toximate 3000" - desc = "All the fine parts you need in one vending machine!" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + refill_canister = /obj/item/vending_refill/medical + +//This one's from bay12 +/obj/machinery/vending/plasmaresearch + name = "\improper Toximate 3000" + desc = "All the fine parts you need in one vending machine!" products = list(/obj/item/clothing/under/rank/scientist = 6, /obj/item/clothing/suit/bio_suit = 6, /obj/item/clothing/head/bio_hood = 6, /obj/item/device/transfer_valve = 6, /obj/item/device/assembly/timer = 6, /obj/item/device/assembly/signaler = 6, /obj/item/device/assembly/prox_sensor = 6, /obj/item/device/assembly/igniter = 6) - contraband = list(/obj/item/device/assembly/health = 3) - -/obj/machinery/vending/wallmed - name = "\improper NanoMed" - desc = "Wall-mounted Medical Equipment dispenser." - icon_state = "wallmed" - icon_deny = "wallmed-deny" + contraband = list(/obj/item/device/assembly/health = 3) + +/obj/machinery/vending/wallmed + name = "\improper NanoMed" + desc = "Wall-mounted Medical Equipment dispenser." + icon_state = "wallmed" + icon_deny = "wallmed-deny" density = FALSE products = list(/obj/item/reagent_containers/syringe = 3, /obj/item/reagent_containers/pill/patch/styptic = 5, /obj/item/reagent_containers/pill/patch/silver_sulf = 5, /obj/item/reagent_containers/pill/charcoal = 2, - /obj/item/reagent_containers/spray/medical/sterilizer = 1) + /obj/item/reagent_containers/spray/medical/sterilizer = 1) contraband = list(/obj/item/reagent_containers/pill/tox = 2, /obj/item/reagent_containers/pill/morphine = 2) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - refill_canister = /obj/item/vending_refill/medical - refill_count = 1 - -/obj/machinery/vending/security - name = "\improper SecTech" - desc = "A security equipment vendor" - product_ads = "Crack capitalist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?" - icon_state = "sec" - icon_deny = "sec-deny" - req_access_txt = "1" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + refill_canister = /obj/item/vending_refill/medical + refill_count = 1 + +/obj/machinery/vending/security + name = "\improper SecTech" + desc = "A security equipment vendor" + product_ads = "Crack capitalist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?" + icon_state = "sec" + icon_deny = "sec-deny" + req_access_txt = "1" products = list(/obj/item/restraints/handcuffs = 8, /obj/item/restraints/handcuffs/cable/zipties = 10, /obj/item/grenade/flashbang = 4, /obj/item/device/assembly/flash/handheld = 5, /obj/item/reagent_containers/food/snacks/donut = 12, /obj/item/storage/box/evidence = 6, /obj/item/device/flashlight/seclite = 4, /obj/item/restraints/legcuffs/bola/energy = 7) contraband = list(/obj/item/clothing/glasses/sunglasses = 2, /obj/item/storage/fancy/donut_box = 2) - premium = list(/obj/item/coin/antagtoken = 1) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/security/pre_throw(obj/item/I) - if(istype(I, /obj/item/grenade)) - var/obj/item/grenade/G = I - G.preprime() - else if(istype(I, /obj/item/device/flashlight)) - var/obj/item/device/flashlight/F = I - F.on = TRUE - F.update_brightness() - -/obj/machinery/vending/hydronutrients - name = "\improper NutriMax" - desc = "A plant nutrients vendor." - product_slogans = "Aren't you glad you don't have to fertilize the natural way?;Now with 50% less stink!;Plants are people too!" - product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..." - icon_state = "nutri" - icon_deny = "nutri-deny" + premium = list(/obj/item/coin/antagtoken = 1) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/security/pre_throw(obj/item/I) + if(istype(I, /obj/item/grenade)) + var/obj/item/grenade/G = I + G.preprime() + else if(istype(I, /obj/item/device/flashlight)) + var/obj/item/device/flashlight/F = I + F.on = TRUE + F.update_brightness() + +/obj/machinery/vending/hydronutrients + name = "\improper NutriMax" + desc = "A plant nutrients vendor." + product_slogans = "Aren't you glad you don't have to fertilize the natural way?;Now with 50% less stink!;Plants are people too!" + product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..." + icon_state = "nutri" + icon_deny = "nutri-deny" products = list(/obj/item/reagent_containers/glass/bottle/nutrient/ez = 30, /obj/item/reagent_containers/glass/bottle/nutrient/l4z = 20, /obj/item/reagent_containers/glass/bottle/nutrient/rh = 10, /obj/item/reagent_containers/spray/pestspray = 20, /obj/item/reagent_containers/syringe = 5, /obj/item/storage/bag/plants = 5, /obj/item/cultivator = 3, /obj/item/shovel/spade = 3, /obj/item/device/plant_analyzer = 4) contraband = list(/obj/item/reagent_containers/glass/bottle/ammonia = 10, /obj/item/reagent_containers/glass/bottle/diethylamine = 5) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/hydroseeds - name = "\improper MegaSeed Servitor" - desc = "When you need seeds fast!" - product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!" - product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!" - icon_state = "seeds" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/hydroseeds + name = "\improper MegaSeed Servitor" + desc = "When you need seeds fast!" + product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!" + product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!" + icon_state = "seeds" products = list(/obj/item/seeds/ambrosia = 3, /obj/item/seeds/apple = 3, /obj/item/seeds/banana = 3, /obj/item/seeds/berry = 3, /obj/item/seeds/cabbage = 3, /obj/item/seeds/carrot = 3, /obj/item/seeds/cherry = 3, /obj/item/seeds/chanter = 3, /obj/item/seeds/chili = 3, /obj/item/seeds/cocoapod = 3, /obj/item/seeds/coffee = 3, /obj/item/seeds/corn = 3, @@ -952,31 +952,31 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/item/seeds/tower = 3, /obj/item/seeds/watermelon = 3, /obj/item/seeds/wheat = 3, /obj/item/seeds/whitebeet = 3) contraband = list(/obj/item/seeds/amanita = 2, /obj/item/seeds/glowshroom = 2, /obj/item/seeds/liberty = 2, /obj/item/seeds/nettle = 2, /obj/item/seeds/plump = 2, /obj/item/seeds/reishi = 2, /obj/item/seeds/cannabis = 3, /obj/item/seeds/starthistle = 2, - /obj/item/seeds/random = 2) - premium = list(/obj/item/reagent_containers/spray/waterflower = 1) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/magivend - name = "\improper MagiVend" - desc = "A magic vending machine." - icon_state = "MagiVend" - product_slogans = "Sling spells the proper way with MagiVend!;Be your own Houdini! Use MagiVend!" - vend_reply = "Have an enchanted evening!" - product_ads = "FJKLFJSD;AJKFLBJAKL;1234 LOONIES LOL!;>MFW;Kill them fuckers!;GET DAT FUKKEN DISK;HONK!;EI NATH;Destroy the station!;Admin conspiracies since forever!;Space-time bending hardware!" + /obj/item/seeds/random = 2) + premium = list(/obj/item/reagent_containers/spray/waterflower = 1) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/magivend + name = "\improper MagiVend" + desc = "A magic vending machine." + icon_state = "MagiVend" + product_slogans = "Sling spells the proper way with MagiVend!;Be your own Houdini! Use MagiVend!" + vend_reply = "Have an enchanted evening!" + product_ads = "FJKLFJSD;AJKFLBJAKL;1234 LOONIES LOL!;>MFW;Kill them fuckers!;GET DAT FUKKEN DISK;HONK!;EI NATH;Destroy the station!;Admin conspiracies since forever!;Space-time bending hardware!" products = list(/obj/item/clothing/head/wizard = 1, /obj/item/clothing/suit/wizrobe = 1, /obj/item/clothing/head/wizard/red = 1, /obj/item/clothing/suit/wizrobe/red = 1, /obj/item/clothing/head/wizard/yellow = 1, /obj/item/clothing/suit/wizrobe/yellow = 1, /obj/item/clothing/shoes/sandal/magic = 1, /obj/item/staff = 2) - contraband = list(/obj/item/reagent_containers/glass/bottle/wizarditis = 1) //No one can get to the machine to hack it anyways; for the lulz - Microwave - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/autodrobe - name = "\improper AutoDrobe" - desc = "A vending machine for costumes." - icon_state = "theater" - icon_deny = "theater-deny" - req_access_txt = "46" //Theatre access needed, unless hacked. - product_slogans = "Dress for success!;Suited and booted!;It's show time!;Why leave style up to fate? Use AutoDrobe!" - vend_reply = "Thank you for using AutoDrobe!" + contraband = list(/obj/item/reagent_containers/glass/bottle/wizarditis = 1) //No one can get to the machine to hack it anyways; for the lulz - Microwave + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/autodrobe + name = "\improper AutoDrobe" + desc = "A vending machine for costumes." + icon_state = "theater" + icon_deny = "theater-deny" + req_access_txt = "46" //Theatre access needed, unless hacked. + product_slogans = "Dress for success!;Suited and booted!;It's show time!;Why leave style up to fate? Use AutoDrobe!" + vend_reply = "Thank you for using AutoDrobe!" products = list(/obj/item/clothing/suit/chickensuit = 1, /obj/item/clothing/head/chicken = 1, /obj/item/clothing/under/gladiator = 1, /obj/item/clothing/head/helmet/gladiator = 1, /obj/item/clothing/under/gimmick/rank/captain/suit = 1, /obj/item/clothing/head/flatcap = 1, /obj/item/clothing/suit/toggle/labcoat/mad = 1, /obj/item/clothing/shoes/jackboots = 1, @@ -985,7 +985,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/item/clothing/glasses/monocle =1, /obj/item/clothing/head/bowler = 1, /obj/item/cane = 1, /obj/item/clothing/under/sl_suit = 1, /obj/item/clothing/mask/fakemoustache = 1, /obj/item/clothing/suit/bio_suit/plaguedoctorsuit = 1, /obj/item/clothing/head/plaguedoctorhat = 1, /obj/item/clothing/mask/gas/plaguedoctor = 1, /obj/item/clothing/suit/toggle/owlwings = 1, /obj/item/clothing/under/owl = 1, /obj/item/clothing/mask/gas/owl_mask = 1, - /obj/item/clothing/suit/toggle/owlwings/griffinwings = 1, /obj/item/clothing/under/griffin = 1, /obj/item/clothing/shoes/griffin = 1, /obj/item/clothing/head/griffin = 1, + /obj/item/clothing/suit/toggle/owlwings/griffinwings = 1, /obj/item/clothing/under/griffin = 1, /obj/item/clothing/shoes/griffin = 1, /obj/item/clothing/head/griffin = 1, /obj/item/clothing/suit/apron = 1, /obj/item/clothing/under/waiter = 1, /obj/item/clothing/suit/jacket/miljacket = 1, /obj/item/clothing/under/pirate = 1, /obj/item/clothing/suit/pirate = 1, /obj/item/clothing/head/pirate = 1, /obj/item/clothing/head/bandana = 1, /obj/item/clothing/head/bandana = 1, /obj/item/clothing/under/soviet = 1, /obj/item/clothing/head/ushanka = 1, /obj/item/clothing/suit/imperium_monk = 1, @@ -993,132 +993,132 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/item/clothing/suit/wizrobe/marisa/fake = 1, /obj/item/clothing/under/sundress = 1, /obj/item/clothing/head/witchwig = 1, /obj/item/staff/broom = 1, /obj/item/clothing/suit/wizrobe/fake = 1, /obj/item/clothing/head/wizard/fake = 1, /obj/item/staff = 3, /obj/item/clothing/mask/gas/sexyclown = 1, /obj/item/clothing/under/rank/clown/sexy = 1, /obj/item/clothing/mask/gas/sexymime = 1, /obj/item/clothing/under/sexymime = 1, /obj/item/clothing/mask/rat/bat = 1, /obj/item/clothing/mask/rat/bee = 1, /obj/item/clothing/mask/rat/bear = 1, /obj/item/clothing/mask/rat/raven = 1, /obj/item/clothing/mask/rat/jackal = 1, /obj/item/clothing/mask/rat/fox = 1, /obj/item/clothing/mask/rat/tribal = 1, /obj/item/clothing/mask/rat = 1, /obj/item/clothing/suit/apron/overalls = 1, - /obj/item/clothing/head/rabbitears =1, /obj/item/clothing/head/sombrero = 1, /obj/item/clothing/head/sombrero/green = 1, /obj/item/clothing/suit/poncho = 1, - /obj/item/clothing/suit/poncho/green = 1, /obj/item/clothing/suit/poncho/red = 1, + /obj/item/clothing/head/rabbitears =1, /obj/item/clothing/head/sombrero = 1, /obj/item/clothing/head/sombrero/green = 1, /obj/item/clothing/suit/poncho = 1, + /obj/item/clothing/suit/poncho/green = 1, /obj/item/clothing/suit/poncho/red = 1, /obj/item/clothing/under/maid = 1, /obj/item/clothing/under/janimaid = 1, /obj/item/clothing/glasses/cold=1, /obj/item/clothing/glasses/heat=1, - /obj/item/clothing/suit/whitedress = 1, - /obj/item/clothing/under/jester = 1, /obj/item/clothing/head/jester = 1, - /obj/item/clothing/under/villain = 1, + /obj/item/clothing/suit/whitedress = 1, + /obj/item/clothing/under/jester = 1, /obj/item/clothing/head/jester = 1, + /obj/item/clothing/under/villain = 1, /obj/item/clothing/shoes/singery = 1, /obj/item/clothing/under/singery = 1, /obj/item/clothing/shoes/singerb = 1, /obj/item/clothing/under/singerb = 1, - /obj/item/clothing/suit/hooded/carp_costume = 1, - /obj/item/clothing/suit/hooded/ian_costume = 1, - /obj/item/clothing/suit/hooded/bee_costume = 1, - /obj/item/clothing/suit/snowman = 1, - /obj/item/clothing/head/snowman = 1, - /obj/item/clothing/mask/joy = 1, - /obj/item/clothing/head/cueball = 1, - /obj/item/clothing/under/scratch = 1, + /obj/item/clothing/suit/hooded/carp_costume = 1, + /obj/item/clothing/suit/hooded/ian_costume = 1, + /obj/item/clothing/suit/hooded/bee_costume = 1, + /obj/item/clothing/suit/snowman = 1, + /obj/item/clothing/head/snowman = 1, + /obj/item/clothing/mask/joy = 1, + /obj/item/clothing/head/cueball = 1, + /obj/item/clothing/under/scratch = 1, /obj/item/clothing/under/sailor = 1, /obj/item/clothing/ears/headphones = 2) contraband = list(/obj/item/clothing/suit/judgerobe = 1, /obj/item/clothing/head/powdered_wig = 1, /obj/item/gun/magic/wand = 2, /obj/item/clothing/glasses/sunglasses/garb = 2, /obj/item/clothing/glasses/sunglasses/blindfold = 1, /obj/item/clothing/mask/muzzle = 2) - premium = list(/obj/item/clothing/suit/pirate/captain = 2, /obj/item/clothing/head/pirate/captain = 2, /obj/item/clothing/head/helmet/roman = 1, /obj/item/clothing/head/helmet/roman/legionaire = 1, /obj/item/clothing/under/roman = 1, /obj/item/clothing/shoes/roman = 1, /obj/item/shield/riot/roman = 1, /obj/item/skub = 1) - refill_canister = /obj/item/vending_refill/autodrobe - -/obj/machinery/vending/dinnerware - name = "\improper Plasteel Chef's Dinnerware Vendor" - desc = "A kitchen and restaurant equipment vendor" - product_ads = "Mm, food stuffs!;Food and food accessories.;Get your plates!;You like forks?;I like forks.;Woo, utensils.;You don't really need these..." - icon_state = "dinnerware" + premium = list(/obj/item/clothing/suit/pirate/captain = 2, /obj/item/clothing/head/pirate/captain = 2, /obj/item/clothing/head/helmet/roman = 1, /obj/item/clothing/head/helmet/roman/legionaire = 1, /obj/item/clothing/under/roman = 1, /obj/item/clothing/shoes/roman = 1, /obj/item/shield/riot/roman = 1, /obj/item/skub = 1) + refill_canister = /obj/item/vending_refill/autodrobe + +/obj/machinery/vending/dinnerware + name = "\improper Plasteel Chef's Dinnerware Vendor" + desc = "A kitchen and restaurant equipment vendor" + product_ads = "Mm, food stuffs!;Food and food accessories.;Get your plates!;You like forks?;I like forks.;Woo, utensils.;You don't really need these..." + icon_state = "dinnerware" products = list(/obj/item/storage/bag/tray = 8, /obj/item/kitchen/fork = 6, /obj/item/kitchen/knife = 6, /obj/item/kitchen/rollingpin = 2, /obj/item/reagent_containers/food/drinks/drinkingglass = 8, /obj/item/clothing/suit/apron/chef = 2, /obj/item/reagent_containers/food/condiment/pack/ketchup = 5, /obj/item/reagent_containers/food/condiment/pack/hotsauce = 5, /obj/item/reagent_containers/food/condiment/saltshaker = 5, /obj/item/reagent_containers/food/condiment/peppermill = 5, /obj/item/reagent_containers/glass/bowl = 20) - contraband = list(/obj/item/kitchen/rollingpin = 2, /obj/item/kitchen/knife/butcher = 2) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/sovietsoda - name = "\improper BODA" - desc = "Old sweet water vending machine" - icon_state = "sovietsoda" - product_ads = "For Tsar and Country.;Have you fulfilled your nutrition quota today?;Very nice!;We are simple people, for this is all we eat.;If there is a person, there is a problem. If there is no person, then there is no problem." - products = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/soda = 30) - contraband = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/cola = 20) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/tool - name = "\improper YouTool" - desc = "Tools for tools." - icon_state = "tool" - icon_deny = "tool-deny" - //req_access_txt = "12" //Maintenance access - products = list( - /obj/item/stack/cable_coil/random = 10, - /obj/item/crowbar = 5, - /obj/item/weldingtool = 3, - /obj/item/wirecutters = 5, - /obj/item/wrench = 5, - /obj/item/device/analyzer = 5, - /obj/item/device/t_scanner = 5, - /obj/item/screwdriver = 5, - /obj/item/device/flashlight/glowstick = 3, - /obj/item/device/flashlight/glowstick/red = 3, - /obj/item/device/flashlight = 5) - contraband = list( - /obj/item/weldingtool/hugetank = 2, - /obj/item/clothing/gloves/color/fyellow = 2) - premium = list( - /obj/item/clothing/gloves/color/yellow = 1) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 70) - resistance_flags = FIRE_PROOF - -/obj/machinery/vending/engivend - name = "\improper Engi-Vend" - desc = "Spare tool vending. What? Did you expect some witty description?" - icon_state = "engivend" - icon_deny = "engivend-deny" - req_access_txt = "11" //Engineering Equipment access + contraband = list(/obj/item/kitchen/rollingpin = 2, /obj/item/kitchen/knife/butcher = 2) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/sovietsoda + name = "\improper BODA" + desc = "Old sweet water vending machine" + icon_state = "sovietsoda" + product_ads = "For Tsar and Country.;Have you fulfilled your nutrition quota today?;Very nice!;We are simple people, for this is all we eat.;If there is a person, there is a problem. If there is no person, then there is no problem." + products = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/soda = 30) + contraband = list(/obj/item/reagent_containers/food/drinks/drinkingglass/filled/cola = 20) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/tool + name = "\improper YouTool" + desc = "Tools for tools." + icon_state = "tool" + icon_deny = "tool-deny" + //req_access_txt = "12" //Maintenance access + products = list( + /obj/item/stack/cable_coil/random = 10, + /obj/item/crowbar = 5, + /obj/item/weldingtool = 3, + /obj/item/wirecutters = 5, + /obj/item/wrench = 5, + /obj/item/device/analyzer = 5, + /obj/item/device/t_scanner = 5, + /obj/item/screwdriver = 5, + /obj/item/device/flashlight/glowstick = 3, + /obj/item/device/flashlight/glowstick/red = 3, + /obj/item/device/flashlight = 5) + contraband = list( + /obj/item/weldingtool/hugetank = 2, + /obj/item/clothing/gloves/color/fyellow = 2) + premium = list( + /obj/item/clothing/gloves/color/yellow = 1) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 70) + resistance_flags = FIRE_PROOF + +/obj/machinery/vending/engivend + name = "\improper Engi-Vend" + desc = "Spare tool vending. What? Did you expect some witty description?" + icon_state = "engivend" + icon_deny = "engivend-deny" + req_access_txt = "11" //Engineering Equipment access products = list(/obj/item/clothing/glasses/meson/engine = 2, /obj/item/device/multitool = 4, /obj/item/electronics/airlock = 10, /obj/item/electronics/apc = 10, /obj/item/electronics/airalarm = 10, /obj/item/stock_parts/cell/high = 10, /obj/item/construction/rcd/loaded = 3, /obj/item/device/geiger_counter = 5, /obj/item/grenade/chem_grenade/smart_metal_foam = 10) - contraband = list(/obj/item/stock_parts/cell/potato = 3) - premium = list(/obj/item/storage/belt/utility = 3, /obj/item/storage/box/smart_metal_foam = 1) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -//This one's from bay12 -/obj/machinery/vending/engineering - name = "\improper Robco Tool Maker" - desc = "Everything you need for do-it-yourself station repair." - icon_state = "engi" - icon_deny = "engi-deny" - req_access_txt = "11" + contraband = list(/obj/item/stock_parts/cell/potato = 3) + premium = list(/obj/item/storage/belt/utility = 3, /obj/item/storage/box/smart_metal_foam = 1) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +//This one's from bay12 +/obj/machinery/vending/engineering + name = "\improper Robco Tool Maker" + desc = "Everything you need for do-it-yourself station repair." + icon_state = "engi" + icon_deny = "engi-deny" + req_access_txt = "11" products = list(/obj/item/clothing/under/rank/chief_engineer = 4, /obj/item/clothing/under/rank/engineer = 4, /obj/item/clothing/shoes/sneakers/orange = 4, /obj/item/clothing/head/hardhat = 4, /obj/item/storage/belt/utility = 4, /obj/item/clothing/glasses/meson/engine = 4, /obj/item/clothing/gloves/color/yellow = 4, /obj/item/screwdriver = 12, /obj/item/crowbar = 12, /obj/item/wirecutters = 12, /obj/item/device/multitool = 12, /obj/item/wrench = 12, /obj/item/device/t_scanner = 12, /obj/item/stock_parts/cell = 8, /obj/item/weldingtool = 8, /obj/item/clothing/head/welding = 8, /obj/item/light/tube = 10, /obj/item/clothing/suit/fire = 4, /obj/item/stock_parts/scanning_module = 5, /obj/item/stock_parts/micro_laser = 5, /obj/item/stock_parts/matter_bin = 5, /obj/item/stock_parts/manipulator = 5, /obj/item/stock_parts/console_screen = 5) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -//This one's from bay12 -/obj/machinery/vending/robotics - name = "\improper Robotech Deluxe" - desc = "All the tools you need to create your own robot army." - icon_state = "robotics" - icon_deny = "robotics-deny" - req_access_txt = "29" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +//This one's from bay12 +/obj/machinery/vending/robotics + name = "\improper Robotech Deluxe" + desc = "All the tools you need to create your own robot army." + icon_state = "robotics" + icon_deny = "robotics-deny" + req_access_txt = "29" products = list(/obj/item/clothing/suit/toggle/labcoat = 4, /obj/item/clothing/under/rank/roboticist = 4, /obj/item/stack/cable_coil = 4, /obj/item/device/assembly/flash/handheld = 4, /obj/item/stock_parts/cell/high = 12, /obj/item/device/assembly/prox_sensor = 3, /obj/item/device/assembly/signaler = 3, /obj/item/device/healthanalyzer = 3, /obj/item/scalpel = 2, /obj/item/circular_saw = 2, /obj/item/tank/internals/anesthetic = 2, /obj/item/clothing/mask/breath/medical = 5, /obj/item/screwdriver = 5, /obj/item/crowbar = 5) - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -//DON'T FORGET TO CHANGE THE REFILL SIZE IF YOU CHANGE THE MACHINE'S CONTENTS! -/obj/machinery/vending/clothing - name = "ClothesMate" //renamed to make the slogan rhyme - desc = "A vending machine for clothing." - icon_state = "clothes" - product_slogans = "Dress for success!;Prepare to look swagalicious!;Look at all this free swag!;Why leave style up to fate? Use the ClothesMate!" - vend_reply = "Thank you for using the ClothesMate!" + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + +//DON'T FORGET TO CHANGE THE REFILL SIZE IF YOU CHANGE THE MACHINE'S CONTENTS! +/obj/machinery/vending/clothing + name = "ClothesMate" //renamed to make the slogan rhyme + desc = "A vending machine for clothing." + icon_state = "clothes" + product_slogans = "Dress for success!;Prepare to look swagalicious!;Look at all this free swag!;Why leave style up to fate? Use the ClothesMate!" + vend_reply = "Thank you for using the ClothesMate!" products = list(/obj/item/clothing/head/that=2, /obj/item/clothing/head/fedora=1, /obj/item/clothing/glasses/monocle=1, - /obj/item/clothing/suit/jacket=2, /obj/item/clothing/suit/jacket/puffer/vest=2, /obj/item/clothing/suit/jacket/puffer=2, + /obj/item/clothing/suit/jacket=2, /obj/item/clothing/suit/jacket/puffer/vest=2, /obj/item/clothing/suit/jacket/puffer=2, /obj/item/clothing/under/suit_jacket/navy=1, /obj/item/clothing/under/suit_jacket/really_black=1, /obj/item/clothing/under/suit_jacket/burgundy=1, /obj/item/clothing/under/suit_jacket/charcoal=1, /obj/item/clothing/under/suit_jacket/white=1, /obj/item/clothing/under/kilt=1, /obj/item/clothing/under/overalls=1, /obj/item/clothing/under/sl_suit=1, /obj/item/clothing/under/pants/jeans=3, /obj/item/clothing/under/pants/classicjeans=2, /obj/item/clothing/under/pants/camo = 1, /obj/item/clothing/under/pants/blackjeans=2, /obj/item/clothing/under/pants/khaki=2, /obj/item/clothing/under/pants/white=2, /obj/item/clothing/under/pants/red=1, /obj/item/clothing/under/pants/black=2, /obj/item/clothing/under/pants/tan=2, /obj/item/clothing/under/pants/track=1, /obj/item/clothing/suit/jacket/miljacket = 1, - /obj/item/clothing/neck/tie/blue=1, /obj/item/clothing/neck/tie/red=1, /obj/item/clothing/neck/tie/black=1, /obj/item/clothing/neck/tie/horrible=1, + /obj/item/clothing/neck/tie/blue=1, /obj/item/clothing/neck/tie/red=1, /obj/item/clothing/neck/tie/black=1, /obj/item/clothing/neck/tie/horrible=1, /obj/item/clothing/neck/scarf/red=1, /obj/item/clothing/neck/scarf/green=1, /obj/item/clothing/neck/scarf/darkblue=1, /obj/item/clothing/neck/scarf/purple=1, /obj/item/clothing/neck/scarf/yellow=1, /obj/item/clothing/neck/scarf/orange=1, /obj/item/clothing/neck/scarf/cyan=1, /obj/item/clothing/neck/scarf=1, /obj/item/clothing/neck/scarf/black=1, @@ -1130,42 +1130,69 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/item/clothing/glasses/regular=1, /obj/item/clothing/glasses/regular/jamjar=1, /obj/item/clothing/head/sombrero=1, /obj/item/clothing/suit/poncho=1, /obj/item/clothing/suit/ianshirt=1, /obj/item/clothing/shoes/laceup=2, /obj/item/clothing/shoes/sneakers/black=4, /obj/item/clothing/shoes/sandal=1, /obj/item/clothing/gloves/fingerless=2, /obj/item/clothing/glasses/orange=1, /obj/item/clothing/glasses/red=1, - /obj/item/storage/belt/fannypack=1, /obj/item/storage/belt/fannypack/blue=1, /obj/item/storage/belt/fannypack/red=1, /obj/item/clothing/suit/jacket/letterman=2, - /obj/item/clothing/head/beanie=1, /obj/item/clothing/head/beanie/black=1, /obj/item/clothing/head/beanie/red=1, /obj/item/clothing/head/beanie/green=1, /obj/item/clothing/head/beanie/darkblue=1, - /obj/item/clothing/head/beanie/purple=1, /obj/item/clothing/head/beanie/yellow=1, /obj/item/clothing/head/beanie/orange=1, /obj/item/clothing/head/beanie/cyan=1, /obj/item/clothing/head/beanie/christmas=1, - /obj/item/clothing/head/beanie/striped=1, /obj/item/clothing/head/beanie/stripedred=1, /obj/item/clothing/head/beanie/stripedblue=1, /obj/item/clothing/head/beanie/stripedgreen=1, + /obj/item/storage/belt/fannypack=1, /obj/item/storage/belt/fannypack/blue=1, /obj/item/storage/belt/fannypack/red=1, /obj/item/clothing/suit/jacket/letterman=2, + /obj/item/clothing/head/beanie=1, /obj/item/clothing/head/beanie/black=1, /obj/item/clothing/head/beanie/red=1, /obj/item/clothing/head/beanie/green=1, /obj/item/clothing/head/beanie/darkblue=1, + /obj/item/clothing/head/beanie/purple=1, /obj/item/clothing/head/beanie/yellow=1, /obj/item/clothing/head/beanie/orange=1, /obj/item/clothing/head/beanie/cyan=1, /obj/item/clothing/head/beanie/christmas=1, + /obj/item/clothing/head/beanie/striped=1, /obj/item/clothing/head/beanie/stripedred=1, /obj/item/clothing/head/beanie/stripedblue=1, /obj/item/clothing/head/beanie/stripedgreen=1, /obj/item/clothing/suit/jacket/letterman_red=1, /obj/item/clothing/ears/headphones = 10) contraband = list(/obj/item/clothing/under/syndicate/tacticool=1, /obj/item/clothing/mask/balaclava=1, /obj/item/clothing/head/ushanka=1, /obj/item/clothing/under/soviet=1, /obj/item/storage/belt/fannypack/black=2, /obj/item/clothing/suit/jacket/letterman_syndie=1, /obj/item/clothing/under/jabroni=1, /obj/item/clothing/suit/vapeshirt=1, /obj/item/clothing/under/geisha=1) premium = list(/obj/item/clothing/under/suit_jacket/checkered=1, /obj/item/clothing/head/mailman=1, /obj/item/clothing/under/rank/mailman=1, /obj/item/clothing/suit/jacket/leather=1, /obj/item/clothing/suit/jacket/leather/overcoat=1, /obj/item/clothing/under/pants/mustangjeans=1, /obj/item/clothing/neck/necklace/dope=3, /obj/item/clothing/suit/jacket/letterman_nanotrasen=1) - refill_canister = /obj/item/vending_refill/clothing - -/obj/machinery/vending/toyliberationstation - name = "\improper Syndicate Donksoft Toy Vendor" - desc = "A ages 8 and up approved vendor that dispenses toys. If you were to find the right wires, you can unlock the adult mode setting!" - icon_state = "syndi" - req_access_txt = "1" - product_slogans = "Get your cool toys today!;Trigger a valid hunter today!;Quality toy weapons for cheap prices!;Give them to HoPs for all access!;Give them to HoS to get perma briged!" - product_ads = "Feel robust with your toys!;Express your inner child today!;Toy weapons don't kill people, but valid hunters do!;Who needs responsibilities when you have toy weapons?;Make your next murder FUN!" - vend_reply = "Come back for more!" - products = list(/obj/item/gun/ballistic/automatic/toy/unrestricted = 10, - /obj/item/gun/ballistic/automatic/toy/pistol/unrestricted = 10, - /obj/item/gun/ballistic/shotgun/toy/unrestricted = 10, - /obj/item/toy/sword = 10, /obj/item/ammo_box/foambox = 20, - /obj/item/toy/foamblade = 10, - /obj/item/toy/syndicateballoon = 10, - /obj/item/clothing/suit/syndicatefake = 5, - /obj/item/clothing/head/syndicatefake = 5) //OPS IN DORMS oh wait it's just a assistant - contraband = list(/obj/item/gun/ballistic/shotgun/toy/crossbow = 10, //Congrats, you unlocked the +18 setting! - /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted = 10, - /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted = 10, - /obj/item/ammo_box/foambox/riot = 20, - /obj/item/toy/katana = 10, - /obj/item/twohanded/dualsaber/toy = 5, - /obj/item/toy/cards/deck/syndicate = 10) //Gambling and it hurts, making it a +18 item - armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) - resistance_flags = FIRE_PROOF - -#undef STANDARD_CHARGE -#undef CONTRABAND_CHARGE -#undef COIN_CHARGE + refill_canister = /obj/item/vending_refill/clothing + +/obj/machinery/vending/toyliberationstation + name = "\improper Syndicate Donksoft Toy Vendor" + desc = "A ages 8 and up approved vendor that dispenses toys. If you were to find the right wires, you can unlock the adult mode setting!" + icon_state = "syndi" + req_access_txt = "1" + product_slogans = "Get your cool toys today!;Trigger a valid hunter today!;Quality toy weapons for cheap prices!;Give them to HoPs for all access!;Give them to HoS to get perma briged!" + product_ads = "Feel robust with your toys!;Express your inner child today!;Toy weapons don't kill people, but valid hunters do!;Who needs responsibilities when you have toy weapons?;Make your next murder FUN!" + vend_reply = "Come back for more!" + products = list(/obj/item/gun/ballistic/automatic/toy/unrestricted = 10, + /obj/item/gun/ballistic/automatic/toy/pistol/unrestricted = 10, + /obj/item/gun/ballistic/shotgun/toy/unrestricted = 10, + /obj/item/toy/sword = 10, + /obj/item/ammo_box/foambox = 20, + /obj/item/toy/foamblade = 10, + /obj/item/toy/syndicateballoon = 10, + /obj/item/clothing/suit/syndicatefake = 5, + /obj/item/clothing/head/syndicatefake = 5) //OPS IN DORMS oh wait it's just a assistant + contraband = list(/obj/item/gun/ballistic/shotgun/toy/crossbow = 10, //Congrats, you unlocked the +18 setting! + /obj/item/gun/ballistic/automatic/c20r/toy/riot/unrestricted = 10, + /obj/item/gun/ballistic/automatic/l6_saw/toy/riot/unrestricted = 10, + /obj/item/ammo_box/foambox/riot = 20, + /obj/item/toy/katana = 10, + /obj/item/twohanded/dualsaber/toy = 5, + /obj/item/toy/cards/deck/syndicate = 10) //Gambling and it hurts, making it a +18 item + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + refill_canister = /obj/item/vending_refill/donksoft + +/obj/machinery/vending/donksofttoyvendor + name = "\improper Donksoft Toy Vendor" + desc = "Ages 8 and up approved vendor that dispenses toys." + icon_state = "syndi" + product_slogans = "Get your cool toys today!;Trigger a valid hunter today!;Quality toy weapons for cheap prices!;Give them to HoPs for all access!;Give them to HoS to get perma briged!" + product_ads = "Feel robust with your toys!;Express your inner child today!;Toy weapons don't kill people, but valid hunters do!;Who needs responsibilities when you have toy weapons?;Make your next murder FUN!" + vend_reply = "Come back for more!" + products = list(/obj/item/gun/ballistic/automatic/toy/unrestricted = 10, + /obj/item/gun/ballistic/automatic/toy/pistol/unrestricted = 10, + /obj/item/gun/ballistic/shotgun/toy/unrestricted = 10, + /obj/item/toy/sword = 10, + /obj/item/ammo_box/foambox = 20, + /obj/item/toy/foamblade = 10, + /obj/item/toy/syndicateballoon = 10, + /obj/item/clothing/suit/syndicatefake = 5, + /obj/item/clothing/head/syndicatefake = 5) + contraband = list(/obj/item/gun/ballistic/shotgun/toy/crossbow = 10, + /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted = 10, + /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted = 10, + /obj/item/toy/katana = 10, + /obj/item/twohanded/dualsaber/toy = 5) + armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) + resistance_flags = FIRE_PROOF + refill_canister = /obj/item/vending_refill/donksoft + +#undef STANDARD_CHARGE +#undef CONTRABAND_CHARGE +#undef COIN_CHARGE 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/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm index 089cfdf625..e22db80bb7 100644 --- a/code/game/mecha/equipment/tools/other_tools.dm +++ b/code/game/mecha/equipment/tools/other_tools.dm @@ -94,10 +94,13 @@ send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) else if(target!=locked) if(locked in view(chassis)) + var/turf/targ = get_turf(target) + var/turf/orig = get_turf(locked) locked.throw_at(target, 14, 1.5) locked = null send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - return 1 + log_game("[key_name(chassis.occupant)] used a Gravitational Catapult to throw [locked]([COORD(orig)]) at [target]([COORD(targ)]).") + return TRUE else locked = null occupant_message("Lock on [locked] disengaged.") @@ -116,8 +119,8 @@ step_away(A,target) sleep(2) var/turf/T = get_turf(target) - log_game("[chassis.occupant.ckey]([chassis.occupant]) used a Gravitational Catapult in ([T.x],[T.y],[T.z])") - return 1 + log_game("[key_name(chassis.occupant)] used a Gravitational Catapult repulse wave on ([COORD(T)])") + return TRUE /obj/item/mecha_parts/mecha_equipment/gravcatapult/get_equip_info() diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index fe0865ca24..b6b4241402 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -127,6 +127,8 @@ desc = "A device that shoots resonant plasma bursts at extreme velocity. The blasts are capable of crushing rock and demolishing solid obstacles." icon_state = "mecha_plasmacutter" item_state = "plasmacutter" + lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi' energy_drain = 30 origin_tech = "materials=3;plasmatech=4;engineering=3" projectile = /obj/item/projectile/plasma/adv/mech diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 14efd0a610..79c4971665 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -76,7 +76,7 @@ if(emagged) return emagged = TRUE - req_access = null + req_access = list() say("DB error \[Code 0x00F1\]") sleep(10) say("Attempting auto-repair...") diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 9142549d0a..fd52a75c18 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -421,7 +421,7 @@ target = safepick(view(3,target)) if(!target) return - if(!target.Adjacent(src)) + if(!Adjacent(target)) if(selected && selected.is_ranged()) if(selected.action(target,params)) selected.start_cooldown() @@ -451,6 +451,9 @@ . = ..() if(.) events.fireEvent("onMove",get_turf(src)) + if (internal_tank.disconnect()) // Something moved us and broke connection + occupant_message("Air port connection teared off!") + log_message("Lost connection to gas port.") /obj/mecha/Process_Spacemove(var/movement_dir = 0) . = ..() @@ -474,7 +477,7 @@ user.forceMove(get_turf(src)) to_chat(user, "You climb out from [src].") return 0 - if(connected_port) + if(internal_tank.connected_port) if(world.time - last_message > 20) occupant_message("Unable to move while connected to the air system port!") last_message = world.time @@ -623,7 +626,7 @@ if(user.can_dominate_mechs) examine(user) //Get diagnostic information! for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) - to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") + to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") to_chat(user, "[B.get_mecha_info()]") break //Nothing like a big, red link to make the player feel powerful! @@ -675,7 +678,7 @@ AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) if(AI.can_dominate_mechs) if(occupant) //Oh, I am sorry, were you using that? - to_chat(AI, "Pilot detected! Forced ejection initiated!") + to_chat(AI, "Pilot detected! Forced ejection initiated!") to_chat(occupant, "You have been forcibly ejected!") go_out(1) //IT IS MINE, NOW. SUCK IT, RD! ai_enter_mech(AI, interaction) @@ -691,7 +694,7 @@ to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") return if(occupant || dna_lock) //Normal AIs cannot steal mechs! - to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") + to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") return AI.control_disabled = 0 AI.radio_enabled = 1 @@ -763,40 +766,12 @@ . = t_air.return_pressure() return - /obj/mecha/proc/return_temperature() var/datum/gas_mixture/t_air = return_air() if(t_air) . = t_air.return_temperature() return -/obj/mecha/proc/connect(obj/machinery/atmospherics/components/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - var/datum/pipeline/connected_port_parent = connected_port.PARENT1 - connected_port_parent.reconcile_air() - - log_message("Connected to gas port.") - return 1 - -/obj/mecha/proc/disconnect() - if(!connected_port) - return 0 - - connected_port.connected_device = null - connected_port = null - log_message("Disconnected from gas port.") - return 1 - /obj/mecha/portableConnectorReturnAir() return internal_tank.return_air() diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm index 4df6578c50..87009ac292 100644 --- a/code/game/mecha/mecha_topic.dm +++ b/code/game/mecha/mecha_topic.dm @@ -115,6 +115,7 @@ + \ No newline at end of file diff --git a/code/modules/goonchat/browserassets/js/browserOutput.js b/code/modules/goonchat/browserassets/js/browserOutput.js index c7eff15913..bb555982cc 100644 --- a/code/modules/goonchat/browserassets/js/browserOutput.js +++ b/code/modules/goonchat/browserassets/js/browserOutput.js @@ -61,8 +61,17 @@ var opts = { 'clientDataLimit': 5, 'clientData': [], + //Admin music volume update + 'volumeUpdateDelay': 5000, //Time from when the volume updates to data being sent to the server + 'volumeUpdating': false, //True if volume update function set to fire + 'updatedVolume': 0, //The volume level that is sent to the server + }; +function clamp(val, min, max) { + return Math.max(min, Math.min(val, max)) +} + function outerHTML(el) { var wrap = document.createElement('div'); wrap.appendChild(el.cloneNode(true)); @@ -95,6 +104,15 @@ function linkify(text) { }); } +function byondDecode(message) { + // Basically we url_encode twice server side so we can manually read the encoded version and actually do UTF-8. + // The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b. + // Marvelous. + message = message.replace(/\+/g, "%20"); + message = decoder(message); + return message; +} + //Actually turns the highlight term match into appropriate html function addHighlightMarkup(match) { var extra = ''; @@ -176,11 +194,7 @@ function output(message, flag) { if (flag !== 'internal') opts.lastPang = Date.now(); - // Basically we url_encode twice server side so we can manually read the encoded version and actually do UTF-8. - // The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b. - // Marvelous. - message = message.replace(/\+/g, "%20") - message = decoder(message) + message = byondDecode(message) //The behemoth of filter-code (for Admin message filters) //Note: This is proooobably hella inefficient @@ -414,6 +428,7 @@ function ehjaxCallback(data) { } else { handleClientData(data.clientData.ckey, data.clientData.ip, data.clientData.compid); } + sendVolumeUpdate(); } else if (data.firebug) { if (data.trigger) { internalOutput('Loading firebug console, triggered by '+data.trigger+'...', 'internal'); @@ -423,7 +438,22 @@ function ehjaxCallback(data) { var firebugEl = document.createElement('script'); firebugEl.src = 'https://getfirebug.com/firebug-lite-debug.js'; document.body.appendChild(firebugEl); - } + } else if (data.adminMusic) { + if (typeof data.adminMusic === 'string') { + var adminMusic = byondDecode(data.adminMusic); + adminMusic = adminMusic.match(/https?:\/\/\S+/) || ''; + if (data.musicRate) { + var newRate = Number(data.musicRate); + if(newRate) { + $('#adminMusic').prop('defaultPlaybackRate', newRate); + } + } else { + $('#adminMusic').prop('defaultPlaybackRate', 1.0); + } + $('#adminMusic').prop('src', adminMusic); + $('#adminMusic').trigger("play"); + } + } } } @@ -446,6 +476,13 @@ function toggleWasd(state) { opts.wasd = (state == 'on' ? true : false); } +function sendVolumeUpdate() { + opts.volumeUpdating = false; + if(opts.updatedVolume) { + runByond('?_src_=chat&proc=setMusicVolume¶m[volume]='+opts.updatedVolume); + } +} + /***************************************** * * DOM READY @@ -486,6 +523,7 @@ $(function() { 'spingDisabled': getCookie('pingdisabled'), 'shighlightTerms': getCookie('highlightterms'), 'shighlightColor': getCookie('highlightcolor'), + 'smusicVolume': getCookie('musicVolume'), }; if (savedConfig.sfontSize) { @@ -517,6 +555,14 @@ $(function() { opts.highlightColor = savedConfig.shighlightColor; internalOutput('Loaded highlight color of: '+savedConfig.shighlightColor+'', 'internal'); } + if (savedConfig.smusicVolume) { + var newVolume = clamp(savedConfig.smusicVolume, 0, 100); + $('#adminMusic').prop('volume', newVolume / 100); + $('#musicVolume').val(newVolume); + opts.updatedVolume = newVolume; + sendVolumeUpdate(); + internalOutput('Loaded music volume of: '+savedConfig.smusicVolume+'', 'internal'); + } (function() { var dataCookie = getCookie('connData'); @@ -835,6 +881,26 @@ $(function() { opts.messageCount = 0; }); + $('#musicVolumeSpan').hover(function() { + $('#musicVolumeText').addClass('hidden'); + $('#musicVolume').removeClass('hidden'); + }, function() { + $('#musicVolume').addClass('hidden'); + $('#musicVolumeText').removeClass('hidden'); + }); + + $('#musicVolume').change(function() { + var newVolume = $('#musicVolume').val(); + newVolume = clamp(newVolume, 0, 100); + $('#adminMusic').prop('volume', newVolume / 100); + setCookie('musicVolume', newVolume, 365); + opts.updatedVolume = newVolume; + if(!opts.volumeUpdating) { + setTimeout(sendVolumeUpdate, opts.volumeUpdateDelay); + opts.volumeUpdating = true; + } + }); + $('img.icon').error(iconError); 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/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm index b01892e636..316b2298ca 100644 --- a/code/modules/hydroponics/beekeeping/beebox.dm +++ b/code/modules/hydroponics/beekeeping/beebox.dm @@ -25,7 +25,7 @@ /obj/structure/beebox name = "apiary" - desc = "Dr Miles Manners is just your average wasp-themed super hero by day, but by night he becomes DR BEES!" + desc = "Dr. Miles Manners is just your average wasp-themed super hero by day, but by night he becomes DR. BEES!" icon = 'icons/obj/hydroponics/equipment.dmi' icon_state = "beebox" anchored = TRUE @@ -45,9 +45,7 @@ /obj/structure/beebox/Destroy() STOP_PROCESSING(SSobj, src) bees.Cut() - bees = null honeycombs.Cut() - honeycombs = null queen_bee = null return ..() @@ -151,7 +149,7 @@ honey_frames += HF else to_chat(user, "There's no room for any more frames in the apiary!") - + return if(istype(I, /obj/item/wrench)) if(default_unfasten_wrench(user, I, time = 20)) return @@ -187,6 +185,9 @@ to_chat(user, "The queen bee disappeared! Disappearing bees have been in the news lately...") qdel(qb) + return + + ..() /obj/structure/beebox/attack_hand(mob/user) @@ -203,8 +204,10 @@ bees = TRUE if(bees) visible_message("[user] disturbs the bees!") + else + visible_message("[user] disturbs the [name] to no effect!") else - var/option = alert(user, "What action do you wish to perform?","Apiary","Remove a Honey Frame","Remove the Queen Bee") + var/option = alert(user, "What action do you wish to perform?","Apiary","Remove a Honey Frame","Remove the Queen Bee", "Cancel") if(!Adjacent(user)) return switch(option) @@ -244,3 +247,13 @@ QB.loc = get_turf(src) visible_message("[user] removes the queen from the apiary.") queen_bee = null + +/obj/structure/beebox/deconstruct(disassembled = TRUE) + new /obj/item/stack/sheet/mineral/wood (loc, 20) + for(var/mob/living/simple_animal/hostile/poison/bees/B in bees) + if(B.loc == src) + B.loc = get_turf(src) + for(var/obj/item/honey_frame/HF in honey_frames) + if(HF.loc == src) + HF.loc = get_turf(src) + qdel(src) \ No newline at end of file diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index 5218c29c44..3c332b950c 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -72,7 +72,7 @@ break else //If the player has ghosted from his corpse before blood was drawn, his ckey is no longer attached to the mob, so we need to match up the cloned player through the mind key for(var/mob/M in GLOB.player_list) - if(mind && M.mind && ckey(M.mind.key) == ckey(mind.key) && M.ckey && M.client && M.stat == 2 && !M.suiciding) + if(mind && M.mind && ckey(M.mind.key) == ckey(mind.key) && M.ckey && M.client && M.stat == DEAD && !M.suiciding) if(isobserver(M)) var/mob/dead/observer/O = M if(!O.can_reenter_corpse) diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm index a88a095aef..3a9e75fdaa 100644 --- a/code/modules/hydroponics/grown/towercap.dm +++ b/code/modules/hydroponics/grown/towercap.dm @@ -163,7 +163,7 @@ /obj/structure/bonfire/attack_hand(mob/user) if(burning) - to_chat(user, "You need to extinguish [src] before removing the logs!") + to_chat(user, "You need to extinguish [src] before removing the logs!") return if(!has_buckled_mobs() && do_after(user, 50, target = src)) for(var/I in 1 to 5) diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 35e1df3bab..3561ccd2c9 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -21,7 +21,9 @@ icon = 'icons/obj/hydroponics/equipment.dmi' name = "weed spray" icon_state = "weedspray" - item_state = "spray" + item_state = "spraycan" + lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi' volume = 100 container_type = OPENCONTAINER_1 slot_flags = SLOT_BELT diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 4c7af3250b..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() @@ -915,9 +915,11 @@ name = "soil" icon = 'icons/obj/hydroponics/equipment.dmi' icon_state = "soil" + circuit = null density = FALSE use_power = NO_POWER_USE - unwrenchable = 0 + flags_1 = NODECONSTRUCT_1 + unwrenchable = FALSE /obj/machinery/hydroponics/soil/update_icon_hoses() return // Has no hoses diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index 7e7d499fac..f7679898d4 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -262,4 +262,4 @@ GLOBAL_PROTECT(exp_to_update) prefs.db_flags = text2num(flags_read.item[1]) else if(isnull(prefs.db_flags)) prefs.db_flags = 0 //This PROBABLY won't happen, but better safe than sorry. - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/jobs/job_exp.dm.rej b/code/modules/jobs/job_exp.dm.rej new file mode 100644 index 0000000000..128a479acd --- /dev/null +++ b/code/modules/jobs/job_exp.dm.rej @@ -0,0 +1,108 @@ +diff a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm (rejected hunks) +@@ -140,15 +140,12 @@ GLOBAL_PROTECT(exp_to_update) + //resets a client's exp to what was in the db. + /client/proc/set_exp_from_db() + if(!config.use_exp_tracking) +- return ++ return -1 + if(!SSdbcore.Connect()) + return -1 + var/datum/DBQuery/exp_read = SSdbcore.NewQuery("SELECT job, minutes FROM [format_table_name("role_time")] WHERE ckey = '[sanitizeSQL(ckey)]'") + if(!exp_read.Execute()) +- var/err = exp_read.ErrorMsg() +- log_sql("SQL ERROR during exp_update_client read. Error : \[[err]\]\n") +- message_admins("SQL ERROR during exp_update_client read. Error : \[[err]\]\n") +- return ++ return -1 + var/list/play_records = list() + while(exp_read.NextRow()) + play_records[exp_read.item[1]] = text2num(exp_read.item[2]) +@@ -172,42 +169,24 @@ GLOBAL_PROTECT(exp_to_update) + if(!set_db_player_flags()) + return -1 + +- var/datum/DBQuery/flag_read = SSdbcore.NewQuery("SELECT flags FROM [format_table_name("player")] WHERE ckey='[sanitizeSQL(ckey)]'") +- +- if(!flag_read.Execute()) +- var/err = flag_read.ErrorMsg() +- log_sql("SQL ERROR during player flags read. Error : \[[err]\]\n") +- message_admins("SQL ERROR during player flags read. Error : \[[err]\]\n") +- return +- +- var/playerflags = null +- if(flag_read.NextRow()) +- playerflags = text2num(flag_read.item[1]) +- +- if((playerflags & newflag) && !state) ++ if((prefs.db_flags & newflag) && !state) + prefs.db_flags &= ~newflag + else + prefs.db_flags |= newflag + + var/datum/DBQuery/flag_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET flags = '[prefs.db_flags]' WHERE ckey='[sanitizeSQL(ckey)]'") + +- + if(!flag_update.Execute()) +- var/err = flag_update.ErrorMsg() +- log_sql("SQL ERROR during exp_exempt update. Error : \[[err]\]\n") +- message_admins("SQL ERROR during exp_exempt update. Error : \[[err]\]\n") +- return ++ return -1 ++ + + /client/proc/update_exp_list(minutes, announce_changes = FALSE) + if(!config.use_exp_tracking) +- return ++ return -1 + if(!SSdbcore.Connect()) + return -1 + var/datum/DBQuery/exp_read = SSdbcore.NewQuery("SELECT job, minutes FROM [format_table_name("role_time")] WHERE ckey = '[sanitizeSQL(ckey)]'") + if(!exp_read.Execute()) +- var/err = exp_read.ErrorMsg() +- log_sql("SQL ERROR during exp_update_client read. Error : \[[err]\]\n") +- message_admins("SQL ERROR during exp_update_client read. Error : \[[err]\]\n") + return -1 + var/list/play_records = list() + while(exp_read.NextRow()) +@@ -241,9 +220,13 @@ GLOBAL_PROTECT(exp_to_update) + if(announce_changes) + to_chat(mob,"You got: [minutes] [role] EXP!") + if(mob.mind.special_role && !mob.mind.var_edited) +- play_records[mob.mind.special_role] += minutes ++ var/trackedrole = mob.mind.special_role ++ var/gangrole = lookforgangrole(mob.mind.special_role) ++ if(gangrole) ++ trackedrole = gangrole ++ play_records[trackedrole] += minutes + if(announce_changes) +- to_chat(src,"You got: [minutes] [mob.mind.special_role] EXP!") ++ to_chat(src,"You got: [minutes] [trackedrole] EXP!") + if(!rolefound) + play_records["Unknown"] += minutes + else +@@ -276,11 +259,21 @@ GLOBAL_PROTECT(exp_to_update) + var/datum/DBQuery/flags_read = SSdbcore.NewQuery("SELECT flags FROM [format_table_name("player")] WHERE ckey='[ckey]'") + + if(!flags_read.Execute()) +- var/err = flags_read.ErrorMsg() +- log_sql("SQL ERROR during player flags read. Error : \[[err]\]\n") +- message_admins("SQL ERROR during player flags read. Error : \[[err]\]\n") + return FALSE + + if(flags_read.NextRow()) + prefs.db_flags = text2num(flags_read.item[1]) ++ else if(isnull(prefs.db_flags)) ++ prefs.db_flags = 0 //This PROBABLY won't happen, but better safe than sorry. + return TRUE ++ ++//Since each gang is tracked as a different antag type, records need to be generalized or you get up to 57 different possible records ++/proc/lookforgangrole(rolecheck) ++ if(findtext(rolecheck,"Gangster")) ++ return "Gangster" ++ else if(findtext(rolecheck,"Gang Boss")) ++ return "Gang Boss" ++ else if(findtext(rolecheck,"Gang Lieutenant")) ++ return "Gang Lieutenant" ++ else ++ return FALSE +\ No newline at end of file diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index 5a7a0a76e5..03498f1e7b 100755 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -13,6 +13,8 @@ Captain selection_color = "#ccccff" req_admin_notify = 1 minimal_player_age = 14 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/captain @@ -64,6 +66,9 @@ Head of Personnel selection_color = "#ddddff" req_admin_notify = 1 minimal_player_age = 10 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_SUPPLY outfit = /datum/outfit/job/hop diff --git a/code/modules/jobs/job_types/engineering.dm b/code/modules/jobs/job_types/engineering.dm index daad870ec9..b61c61f6c8 100644 --- a/code/modules/jobs/job_types/engineering.dm +++ b/code/modules/jobs/job_types/engineering.dm @@ -14,6 +14,9 @@ Chief Engineer selection_color = "#ffeeaa" req_admin_notify = 1 minimal_player_age = 7 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_ENGINEERING outfit = /datum/outfit/job/ce @@ -72,6 +75,8 @@ Station Engineer spawn_positions = 5 supervisors = "the chief engineer" selection_color = "#fff5cc" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/engineer @@ -127,6 +132,8 @@ Atmospheric Technician spawn_positions = 2 supervisors = "the chief engineer" selection_color = "#fff5cc" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/atmos diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/job.dm index 04a384357f..6b736db7fe 100644 --- a/code/modules/jobs/job_types/job.dm +++ b/code/modules/jobs/job_types/job.dm @@ -43,6 +43,11 @@ var/outfit = null + var/exp_requirements = 0 + + var/exp_type = "" + var/exp_type_department = "" + //Only override this proc //H is usually a human unless an /equip override transformed it /datum/job/proc/after_spawn(mob/living/H, mob/M) diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm index 5f22e1886a..9a33e1e759 100644 --- a/code/modules/jobs/job_types/medical.dm +++ b/code/modules/jobs/job_types/medical.dm @@ -14,6 +14,9 @@ Chief Medical Officer selection_color = "#ffddf0" req_admin_notify = 1 minimal_player_age = 7 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_MEDICAL outfit = /datum/outfit/job/cmo @@ -30,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 @@ -90,6 +94,8 @@ Chemist spawn_positions = 2 supervisors = "the chief medical officer" selection_color = "#ffeef0" + exp_type = EXP_TYPE_CREW + exp_requirements = 60 outfit = /datum/outfit/job/chemist @@ -124,6 +130,8 @@ Geneticist spawn_positions = 2 supervisors = "the chief medical officer and research director" selection_color = "#ffeef0" + exp_type = EXP_TYPE_CREW + exp_requirements = 60 outfit = /datum/outfit/job/geneticist @@ -158,6 +166,8 @@ Virologist spawn_positions = 1 supervisors = "the chief medical officer" selection_color = "#ffeef0" + exp_type = EXP_TYPE_CREW + exp_requirements = 60 outfit = /datum/outfit/job/virologist diff --git a/code/modules/jobs/job_types/science.dm b/code/modules/jobs/job_types/science.dm index 6ce166c48e..4eac15d9f1 100644 --- a/code/modules/jobs/job_types/science.dm +++ b/code/modules/jobs/job_types/science.dm @@ -14,6 +14,9 @@ Research Director selection_color = "#ffddff" req_admin_notify = 1 minimal_player_age = 7 + exp_type_department = EXP_TYPE_SCIENCE + exp_requirements = 180 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/rd @@ -68,6 +71,8 @@ Scientist spawn_positions = 3 supervisors = "the research director" selection_color = "#ffeeff" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/scientist @@ -101,6 +106,8 @@ Roboticist spawn_positions = 2 supervisors = "research director" selection_color = "#ffeeff" + exp_requirements = 60 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/roboticist diff --git a/code/modules/jobs/job_types/security.dm b/code/modules/jobs/job_types/security.dm index 3ccbb7faef..84623873e8 100644 --- a/code/modules/jobs/job_types/security.dm +++ b/code/modules/jobs/job_types/security.dm @@ -20,6 +20,9 @@ Head of Security selection_color = "#ffdddd" req_admin_notify = 1 minimal_player_age = 14 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + exp_type_department = EXP_TYPE_SECURITY outfit = /datum/outfit/job/hos @@ -71,6 +74,8 @@ Warden supervisors = "the head of security" selection_color = "#ffeeee" minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/warden @@ -121,6 +126,8 @@ Detective supervisors = "the head of security" selection_color = "#ffeeee" minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/detective @@ -169,6 +176,8 @@ Security Officer supervisors = "the head of security, and the head of your assigned department (if applicable)" selection_color = "#ffeeee" minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/security diff --git a/code/modules/jobs/job_types/silicon.dm b/code/modules/jobs/job_types/silicon.dm index 0b6380bfaf..2e0a710d41 100644 --- a/code/modules/jobs/job_types/silicon.dm +++ b/code/modules/jobs/job_types/silicon.dm @@ -12,6 +12,8 @@ AI supervisors = "your laws" req_admin_notify = 1 minimal_player_age = 30 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW /datum/job/ai/equip(mob/living/carbon/human/H) return H.AIize(FALSE) @@ -44,6 +46,8 @@ Cyborg supervisors = "your laws and the AI" //Nodrak selection_color = "#ddffdd" minimal_player_age = 21 + exp_requirements = 120 + exp_type = EXP_TYPE_CREW /datum/job/cyborg/equip(mob/living/carbon/human/H) return H.Robotize(FALSE, FALSE) diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm index 2d7caa5fc4..b5ec8fa27a 100644 --- a/code/modules/jobs/jobs.dm +++ b/code/modules/jobs/jobs.dm @@ -59,6 +59,26 @@ GLOBAL_LIST_INIT(nonhuman_positions, list( "Cyborg", "pAI")) +GLOBAL_LIST_INIT(exp_jobsmap, list( + EXP_TYPE_CREW = list("titles" = command_positions | engineering_positions | medical_positions | science_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg")), // crew positions + EXP_TYPE_COMMAND = list("titles" = command_positions), + EXP_TYPE_ENGINEERING = list("titles" = engineering_positions), + EXP_TYPE_MEDICAL = list("titles" = medical_positions), + EXP_TYPE_SCIENCE = list("titles" = science_positions), + EXP_TYPE_SUPPLY = list("titles" = supply_positions), + EXP_TYPE_SECURITY = list("titles" = security_positions), + EXP_TYPE_SILICON = list("titles" = list("AI","Cyborg")), + EXP_TYPE_SERVICE = list("titles" = civilian_positions), +)) + +GLOBAL_LIST_INIT(exp_specialmap, list( + EXP_TYPE_LIVING = list(), // all living mobs + EXP_TYPE_ANTAG = list(), + EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Space Bartender","Beach Bum","Skeleton","Zombie","Space Bar Patron","Lavaland Syndicate","Ghost Role"), // Ghost roles + EXP_TYPE_GHOST = list() // dead people, observers +)) +GLOBAL_PROTECT(exp_jobsmap) +GLOBAL_PROTECT(exp_specialmap) /proc/guest_jobbans(job) return ((job in GLOB.command_positions) || (job in GLOB.nonhuman_positions) || (job in GLOB.security_positions)) diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm index 481b3a6a12..d3d95db974 100644 --- a/code/modules/library/lib_codex_gigas.dm +++ b/code/modules/library/lib_codex_gigas.dm @@ -8,6 +8,8 @@ name = "\improper Codex Gigas" desc = "A book documenting the nature of devils." icon_state ="demonomicon" + lefthand_file = 'icons/mob/inhands/misc/books_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/books_righthand.dmi' throw_speed = 1 throw_range = 10 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 5e7c385416..99127cd595 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -419,7 +419,7 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums var/sqlauthor = sanitizeSQL(scanner.cache.author) var/sqlcontent = sanitizeSQL(scanner.cache.dat) var/sqlcategory = sanitizeSQL(upload_category) - var/datum/DBQuery/query_library_upload = SSdbcore.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[usr.ckey]', Now())") + var/datum/DBQuery/query_library_upload = SSdbcore.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime, round_id_created) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[usr.ckey]', Now(), '[GLOB.round_id]')") if(!query_library_upload.Execute()) alert("Database error encountered uploading to Archive") return diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index d0fbf029b8..e4e0609599 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -1,5 +1,3 @@ -GLOBAL_LIST_EMPTY(all_lighting_objects) // Global list of lighting objects. - /atom/movable/lighting_object name = "" @@ -20,7 +18,6 @@ GLOBAL_LIST_EMPTY(all_lighting_objects) // Global list of lighting objects. /atom/movable/lighting_object/Initialize(mapload) . = ..() verbs.Cut() - GLOB.all_lighting_objects += src var/turf/T = loc // If this runtimes atleast we'll know what's creating overlays in things that aren't turfs. T.lighting_object = src @@ -34,7 +31,6 @@ GLOBAL_LIST_EMPTY(all_lighting_objects) // Global list of lighting objects. /atom/movable/lighting_object/Destroy(var/force) if (force) - GLOB.all_lighting_objects -= src GLOB.lighting_update_objects -= src var/turf/T = loc @@ -142,4 +138,4 @@ GLOBAL_LIST_EMPTY(all_lighting_objects) // Global list of lighting objects. // Override here to prevent things accidentally moving around overlays. /atom/movable/lighting_object/forceMove(atom/destination, var/no_tp=FALSE, var/harderforce = FALSE) if(harderforce) - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index fc03cfb8c0..8372d21a5f 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -103,12 +103,12 @@ reconsider_lights() /turf/proc/change_area(var/area/old_area, var/area/new_area) - if (new_area.dynamic_lighting != old_area.dynamic_lighting) - if (new_area.dynamic_lighting) - lighting_build_overlay() - - else - lighting_clear_overlay() + if(SSlighting.initialized) + if (new_area.dynamic_lighting != old_area.dynamic_lighting) + if (new_area.dynamic_lighting) + lighting_build_overlay() + else + lighting_clear_overlay() /turf/proc/get_corners() if (!IS_DYNAMIC_LIGHTING(src) && !light_sources) diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 372956ce2f..89e9d6fd10 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -231,6 +231,31 @@ else H.ranged_cooldown_time = bonus_value + world.time +//magmawing watcher +/obj/item/crusher_trophy/blaster_tubes/magma_wing + name = "magmawing watcher wing" + desc = "A still-searing wing from a magmawing watcher. Suitable as a trophy for a kinetic crusher." + icon_state = "magma_wing" + gender = NEUTER + bonus_value = 5 + +/obj/item/crusher_trophy/blaster_tubes/magma_wing/effect_desc() + return "mark detonation to make the next destabilizer shot deal [bonus_value] damage" + +/obj/item/crusher_trophy/blaster_tubes/magma_wing/on_projectile_fire(obj/item/projectile/destabilizer/marker, mob/living/user) + if(deadly_shot) + marker.name = "heated [marker.name]" + marker.icon_state = "lava" + marker.damage = bonus_value + marker.nodamage = FALSE + deadly_shot = FALSE + +//icewing watcher +/obj/item/crusher_trophy/watcher_wing/ice_wing + name = "icewing watcher wing" + desc = "A carefully preserved frozen wing from an icewing watcher. Suitable as a trophy for a kinetic crusher." + icon_state = "ice_wing" + //legion /obj/item/crusher_trophy/legion_skull name = "legion skull" @@ -281,7 +306,7 @@ playsound(L, 'sound/magic/fireball.ogg', 20, 1) new /obj/effect/temp_visual/fire(L.loc) addtimer(CALLBACK(src, .proc/pushback, L, user), 1) //no free backstabs, we push AFTER module stuff is done - L.adjustBruteLoss(bonus_value) + L.adjustFireLoss(bonus_value, forced = TRUE) /obj/item/crusher_trophy/tail_spike/proc/pushback(mob/living/target, mob/living/user) if(!QDELETED(target) && !QDELETED(user) && (!target.anchored || ismegafauna(target))) //megafauna will always be pushed diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 827df09480..b9fd781629 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -480,6 +480,8 @@ icon = 'icons/obj/vehicles.dmi' icon_state = "oar" item_state = "oar" + lefthand_file = 'icons/mob/inhands/misc/lavaland_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/lavaland_righthand.dmi' desc = "Not to be confused with the kind Research hassles you for." force = 12 w_class = WEIGHT_CLASS_NORMAL @@ -800,7 +802,7 @@ to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.") H.set_species(/datum/species/skeleton) if(3) - to_chat(user, "Power courses through you! You can now shift your form at will.") + to_chat(user, "Power courses through you! You can now shift your form at will.") if(user.mind) var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new user.mind.AddSpell(D) @@ -835,6 +837,8 @@ desc = "The ability to fill the emergency shuttle with lava. What more could you want out of life?" icon_state = "staffofstorms" item_state = "staffofstorms" + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' icon = 'icons/obj/guns/magic.dmi' slot_flags = SLOT_BACK w_class = WEIGHT_CLASS_BULKY @@ -1008,6 +1012,7 @@ slot_flags = SLOT_BACK w_class = WEIGHT_CLASS_BULKY force = 15 + attack_verb = list("clubbed", "beat", "pummeled") hitsound = 'sound/weapons/sonic_jackhammer.ogg' actions_types = list(/datum/action/item_action/vortex_recall, /datum/action/item_action/toggle_unfriendly_fire) var/cooldown_time = 20 //how long the cooldown between non-melee ranged attacks is @@ -1024,6 +1029,21 @@ ..() to_chat(user, "The[beacon ? " beacon is not currently":"re is a beacon"] attached.") +/obj/item/hierophant_club/suicide_act(mob/living/user) + say("Xverwpsgexmrk...") + user.visible_message("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide!") + new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user)) + playsound(user,'sound/machines/airlockopen.ogg', 75, TRUE) + user.visible_message("[user] fades out, leaving their belongings behind!") + for(var/obj/item/I in user) + if(I != src) + user.dropItemToGround(I) + for(var/turf/T in RANGE_TURFS(1, user)) + var/obj/effect/temp_visual/hierophant/blast/B = new(T, user, TRUE) + B.damage = 0 + user.dropItemToGround(src) //Drop us last, so it goes on top of their stuff + qdel(user) + /obj/item/hierophant_club/afterattack(atom/target, mob/user, proximity_flag, click_parameters) ..() var/turf/T = get_turf(target) diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm index 9a49c23cfc..4de3b1d4dd 100644 --- a/code/modules/mob/dead/new_player/login.dm +++ b/code/modules/mob/dead/new_player/login.dm @@ -1,4 +1,7 @@ /mob/dead/new_player/Login() + if(config.use_exp_tracking) + client.set_exp_from_db() + client.set_db_player_flags() if(!mind) mind = new /datum/mind(key) mind.active = 1 diff --git a/code/modules/mob/dead/new_player/login.dm.rej b/code/modules/mob/dead/new_player/login.dm.rej new file mode 100644 index 0000000000..b7361e13ff --- /dev/null +++ b/code/modules/mob/dead/new_player/login.dm.rej @@ -0,0 +1,9 @@ +diff a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm (rejected hunks) +@@ -1,5 +1,6 @@ + /mob/dead/new_player/Login() +- client.update_exp_client(0, 0) ++ if(config.use_exp_tracking) ++ client.update_exp_client(0, 0) + if(!mind) + mind = new /datum/mind(key) + mind.active = 1 diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index c65cd1a5fb..b11c6bc336 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -307,6 +307,8 @@ return 0 if(!job.player_old_enough(src.client)) return 0 + if(job.required_playtime_remaining(client)) + return 0 if(config.enforce_human_authority && !client.prefs.pref_species.qualifies_for_rank(rank, client.prefs.features)) return 0 return 1 diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm index 9fba6eb2e9..9ca76c6356 100644 --- a/code/modules/mob/dead/new_player/poll.dm +++ b/code/modules/mob/dead/new_player/poll.dm @@ -411,7 +411,7 @@ to_chat(src, "Error: Invalid (non-numeric) votes in the vote data.") return 0 if (!(vote in optionlist)) - to_chat(src, "Votes for choices that do not appear to be in the poll detected") + to_chat(src, "Votes for choices that do not appear to be in the poll detected.") return 0 if (!numberedvotelist.len) to_chat(src, "Invalid vote data") 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 18a8b38c5b..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,25 +102,51 @@ 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 = TRUE + extra_color_src = MUTCOLORS2 + icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/tails_animated/human/kitsune + name = "Kitsune" + icon_state = "kitsune" + extra = TRUE + extra_color_src = MUTCOLORS2 + icon = 'icons/mob/mam_bodyparts.dmi' /datum/sprite_accessory/ears/lab name = "Dog, Floppy" @@ -119,6 +171,51 @@ color_src = 0 icon = 'icons/mob/mam_bodyparts.dmi' +/datum/sprite_accessory/ears/human/otie + name = "Otusian" + icon_state = "otie" + hasinner= 1 + +/datum/sprite_accessory/tails/human/otie + name = "Otusian" + icon_state = "otie" + +/datum/sprite_accessory/tails_animated/human/otie + 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" + +/datum/sprite_accessory/tails/human/skunk + name = "skunk" + icon_state = "skunk" + color_src = 0 + +/datum/sprite_accessory/tails_animated/human/skunk + name = "skunk" + icon_state = "skunk" + color_src = 0 + /datum/sprite_accessory/tails/human/shark name = "Shark" icon_state = "shark" @@ -150,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" @@ -162,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 **************** *******************************************/ @@ -198,35 +283,46 @@ **************** Snouts ******************* *******************************************/ +/datum/sprite_accessory/snouts/none + name = "None" + icon_state = "none" + icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/snouts/bird + 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 *************** @@ -235,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" @@ -272,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" @@ -280,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" @@ -290,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" @@ -309,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" @@ -337,15 +430,54 @@ 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 = TRUE + +/datum/sprite_accessory/mam_tails/kangaroo + name = "kangaroo" + icon_state = "kangaroo" + +/datum/sprite_accessory/mam_tails_animated/kangaroo + name = "kangaroo" + icon_state = "kangaroo" + +/datum/sprite_accessory/mam_tails/kitsune + name = "Kitsune" + icon_state = "kitsune" + extra = TRUE + +/datum/sprite_accessory/mam_tails_animated/kitsune + name = "Kitsune" + icon_state = "kitsune" + extra = TRUE /datum/sprite_accessory/mam_ears/lab name = "Dog, Long" @@ -386,6 +518,48 @@ 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" + +/datum/sprite_accessory/mam_tails/skunk + name = "skunk" + icon_state = "skunk" + color_src = 0 + extra = TRUE + +/datum/sprite_accessory/mam_tails_animated/skunk + name = "skunk" + icon_state = "skunk" + color_src = 0 + extra = TRUE + /datum/sprite_accessory/mam_tails/shark name = "Shark" icon_state = "shark" @@ -394,30 +568,19 @@ /datum/sprite_accessory/mam_tails_animated/shark name = "Shark" icon_state = "shark" - color_src = 0 - -/datum/sprite_accessory/mam_tails/shark/datashark - name = "DataShark" - icon_state = "datashark" - color_src = 0 -// ckeys_allowed = list("rubyflamewing") - -/datum/sprite_accessory/mam_tails_animated/shark/datashark - name = "DataShark" - icon_state = "datashark" - 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" @@ -435,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" @@ -445,39 +608,13 @@ name = "Wolf" icon_state = "wolf" -/datum/sprite_accessory/mam_tails/guilmon - name = "Guilmon" - icon_state = "guilmon" - extra = 1 - -/datum/sprite_accessory/mam_tails_animated/guilmon - name = "Guilmon" - icon_state = "guilmon" - extra = 1 - -/datum/sprite_accessory/mam_ears/guilmon - name = "Guilmon" - icon_state = "guilmon" - icon = 'icons/mob/mam_bodyparts.dmi' - -/datum/sprite_accessory/mam_tails/rabbit - name = "Rabbit" - icon_state = "rabbit" - icon = 'icons/mob/mam_bodyparts.dmi' - -/datum/sprite_accessory/mam_ears/rabbit - name = "Rabbit" - icon_state = "rabbit" - hasinner= 1 - icon = 'icons/mob/mam_bodyparts.dmi' - /****************************************** ************ 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 @@ -487,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 @@ -530,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 @@ -586,30 +719,23 @@ color_src = MUTCOLORS2 gender_specific = 1 -/datum/sprite_accessory/mam_body_markings/guilmon - name = "Guilmon" - icon_state = "guilmon" - extra = 1 - extra2 = 1 - icon = 'icons/mob/mam_body_markings.dmi' - gender_specific = 1 - - /datum/sprite_accessory/mam_body_markings/xeno +/datum/sprite_accessory/mam_body_markings/xeno name = "Xeno" icon_state = "xeno" color_src = MUTCOLORS2 extra_color_src = MUTCOLORS3 gender_specific = 1 + /****************************************** ************ Taur Bodies ****************** *******************************************/ /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 @@ -712,7 +838,6 @@ icon = 'icons/mob/xeno_parts_greyscale.dmi' //Xeno Caste Heads -//unused as of October 3, 2016 /datum/sprite_accessory/xeno_head icon = 'icons/mob/xeno_parts_greyscale.dmi' @@ -740,17 +865,65 @@ icon_state = "warrior" icon = 'icons/mob/xeno_parts_greyscale.dmi' +// *** Snooooow flaaaaake *** + +/datum/sprite_accessory/mam_body_markings/guilmon + name = "Guilmon" + icon_state = "guilmon" + gender_specific = 1 + +/datum/sprite_accessory/mam_tails/guilmon + name = "Guilmon" + icon_state = "guilmon" + extra = TRUE + +/datum/sprite_accessory/mam_tails_animated/guilmon + name = "Guilmon" + icon_state = "guilmon" + extra = TRUE + +/datum/sprite_accessory/mam_ears/guilmon + name = "Guilmon" + icon_state = "guilmon" + icon = 'icons/mob/mam_bodyparts.dmi' + +/datum/sprite_accessory/mam_tails/shark/datashark + name = "DataShark" + icon_state = "datashark" + color_src = 0 +// ckeys_allowed = list("rubyflamewing") + +/datum/sprite_accessory/mam_tails_animated/shark/datashark + name = "DataShark" + icon_state = "datashark" + color_src = 0 + /* -//Slimecoon Parts -/datum/sprite_accessory/slimecoon_ears - icon = 'icons/mob/exotic_bodyparts.dmi' - name = "Slimecoon Ears" - icon_state = "slimecoon" -/datum/sprite_accessory/slimecoon_tail - icon = 'icons/mob/exotic_bodyparts.dmi' - name = "Slimecoon Tail" - icon_state = "slimecoon" -/datum/sprite_accessory/slimecoon_snout - icon = 'icons/mob/exotic_bodyparts.dmi' - name = "Hunter" - icon_state = "slimecoon" */ +//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 = TRUE + extra_color_src = MUTCOLORS3 + locked = TRUE + +/datum/sprite_accessory/mam_tails/sabresune + name = "sabresune" + icon_state = "sabresune" + extra = TRUE + locked = TRUE + +/datum/sprite_accessory/mam_tails_animated/sabresune + name = "sabresune" + icon_state = "sabresune" + extra = TRUE + +/datum/sprite_accessory/mam_body_markings/sabresune + name = "Sabresune" + icon_state = "sabresune" + color_src = MUTCOLORS2 + extra = FALSE + extra2 = FALSE + locked = TRUE +*/ diff --git a/code/modules/mob/living/bloodcrawl.dm b/code/modules/mob/living/bloodcrawl.dm index 32ee4d9e3d..b749bcda91 100644 --- a/code/modules/mob/living/bloodcrawl.dm +++ b/code/modules/mob/living/bloodcrawl.dm @@ -162,7 +162,7 @@ return src.loc = B.loc src.client.eye = src - src.visible_message("[src] rises out of the pool of blood!") + src.visible_message("[src] rises out of the pool of blood!") exit_blood_effect(B) if(iscarbon(src)) var/mob/living/carbon/C = src diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index cb1535f724..48ce6e5e7b 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -17,7 +17,6 @@ initial_language_holder = /datum/language_holder/alien bubble_icon = "alien" type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab/xeno - var/nightvision = 1 var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie var/has_fine_manipulation = 0 @@ -32,7 +31,7 @@ var/static/regex/alien_name_regex = new("alien (larva|sentinel|drone|hunter|praetorian|queen)( \\(\\d+\\))?") devourable = TRUE - + /mob/living/carbon/alien/Initialize() verbs += /mob/living/proc/mob_sleep verbs += /mob/living/proc/lay_down diff --git a/code/modules/mob/living/carbon/alien/larva/powers.dm b/code/modules/mob/living/carbon/alien/larva/powers.dm index 64ee746b06..36191203ad 100644 --- a/code/modules/mob/living/carbon/alien/larva/powers.dm +++ b/code/modules/mob/living/carbon/alien/larva/powers.dm @@ -1,62 +1,62 @@ -/obj/effect/proc_holder/alien/hide - name = "Hide" - desc = "Allows aliens to hide beneath tables or certain items. Toggled on or off." - plasma_cost = 0 - - action_icon_state = "alien_hide" - -/obj/effect/proc_holder/alien/hide/fire(mob/living/carbon/alien/user) - if(user.stat != CONSCIOUS) - return - - if (user.layer != ABOVE_NORMAL_TURF_LAYER) - user.layer = ABOVE_NORMAL_TURF_LAYER - user.visible_message("[user] scurries to the ground!", \ - "You are now hiding.") - else - user.layer = MOB_LAYER - user.visible_message("[user.] slowly peeks up from the ground...", \ - "You stop hiding.") - return 1 - - -/obj/effect/proc_holder/alien/larva_evolve - name = "Evolve" - desc = "Evolve into a higher alien caste." - plasma_cost = 0 - - action_icon_state = "alien_evolve_larva" - -/obj/effect/proc_holder/alien/larva_evolve/fire(mob/living/carbon/alien/user) - if(!islarva(user)) - return - var/mob/living/carbon/alien/larva/L = user - - if(L.handcuffed || L.legcuffed) // Cuffing larvas ? Eh ? - to_chat(user, "You cannot evolve when you are cuffed.") - - if(L.amount_grown >= L.max_grown) //TODO ~Carn - to_chat(L, "You are growing into a beautiful alien! It is time to choose a caste.") - to_chat(L, "There are three to choose from:") - to_chat(L, "Hunters are the most agile caste, tasked with hunting for hosts. They are faster than a human and can even pounce, but are not much tougher than a drone.") - to_chat(L, "Sentinels are tasked with protecting the hive. With their ranged spit, invisibility, and high health, they make formidable guardians and acceptable secondhand hunters.") - to_chat(L, "Drones are the weakest and slowest of the castes, but can grow into a praetorian and then queen if no queen exists, and are vital to maintaining a hive with their resin secretion abilities.") - var/alien_caste = alert(L, "Please choose which alien caste you shall belong to.",,"Hunter","Sentinel","Drone") - - if(user.incapacitated()) //something happened to us while we were choosing. - return - - var/mob/living/carbon/alien/humanoid/new_xeno - switch(alien_caste) - if("Hunter") - new_xeno = new /mob/living/carbon/alien/humanoid/hunter(L.loc) - if("Sentinel") - new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(L.loc) - if("Drone") - new_xeno = new /mob/living/carbon/alien/humanoid/drone(L.loc) - - L.alien_evolve(new_xeno) - return 0 - else - to_chat(user, "You are not fully grown.") - return 0 \ No newline at end of file +/obj/effect/proc_holder/alien/hide + name = "Hide" + desc = "Allows aliens to hide beneath tables or certain items. Toggled on or off." + plasma_cost = 0 + + action_icon_state = "alien_hide" + +/obj/effect/proc_holder/alien/hide/fire(mob/living/carbon/alien/user) + if(user.stat != CONSCIOUS) + return + + if (user.layer != ABOVE_NORMAL_TURF_LAYER) + user.layer = ABOVE_NORMAL_TURF_LAYER + user.visible_message("[user] scurries to the ground!", \ + "You are now hiding.") + else + user.layer = MOB_LAYER + user.visible_message("[user.] slowly peeks up from the ground...", \ + "You stop hiding.") + return 1 + + +/obj/effect/proc_holder/alien/larva_evolve + name = "Evolve" + desc = "Evolve into a higher alien caste." + plasma_cost = 0 + + action_icon_state = "alien_evolve_larva" + +/obj/effect/proc_holder/alien/larva_evolve/fire(mob/living/carbon/alien/user) + if(!islarva(user)) + return + var/mob/living/carbon/alien/larva/L = user + + if(L.handcuffed || L.legcuffed) // Cuffing larvas ? Eh ? + to_chat(user, "You cannot evolve when you are cuffed.") + + if(L.amount_grown >= L.max_grown) //TODO ~Carn + to_chat(L, "You are growing into a beautiful alien! It is time to choose a caste.") + to_chat(L, "There are three to choose from:") + to_chat(L, "Hunters are the most agile caste, tasked with hunting for hosts. They are faster than a human and can even pounce, but are not much tougher than a drone.") + to_chat(L, "Sentinels are tasked with protecting the hive. With their ranged spit, invisibility, and high health, they make formidable guardians and acceptable secondhand hunters.") + to_chat(L, "Drones are the weakest and slowest of the castes, but can grow into a praetorian and then queen if no queen exists, and are vital to maintaining a hive with their resin secretion abilities.") + var/alien_caste = alert(L, "Please choose which alien caste you shall belong to.",,"Hunter","Sentinel","Drone") + + if(user.incapacitated()) //something happened to us while we were choosing. + return + + var/mob/living/carbon/alien/humanoid/new_xeno + switch(alien_caste) + if("Hunter") + new_xeno = new /mob/living/carbon/alien/humanoid/hunter(L.loc) + if("Sentinel") + new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(L.loc) + if("Drone") + new_xeno = new /mob/living/carbon/alien/humanoid/drone(L.loc) + + L.alien_evolve(new_xeno) + return 0 + else + to_chat(user, "You are not fully grown.") + return 0 diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index e538d32fb7..ae8eec5529 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -128,12 +128,12 @@ return if(isalien(owner)) //Different effects for aliens than humans to_chat(owner, "Your Queen has been struck down!") - to_chat(owner, "You are struck with overwhelming agony! You feel confused, and your connection to the hivemind is severed.") + to_chat(owner, "You are struck with overwhelming agony! You feel confused, and your connection to the hivemind is severed.") owner.emote("roar") owner.Stun(200) //Actually just slows them down a bit. else if(ishuman(owner)) //Humans, being more fragile, are more overwhelmed by the mental backlash. - to_chat(owner, "You feel a splitting pain in your head, and are struck with a wave of nausea. You cannot hear the hivemind anymore!") + to_chat(owner, "You feel a splitting pain in your head, and are struck with a wave of nausea. You cannot hear the hivemind anymore!") owner.emote("scream") owner.Knockdown(100) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 34d788e5ff..717c5478a5 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -610,50 +610,94 @@ if(!client) return - if(stat == UNCONSCIOUS && health <= HEALTH_THRESHOLD_CRIT) + if(health <= HEALTH_THRESHOLD_CRIT) var/severity = 0 switch(health) - if(-20 to -10) severity = 1 - if(-30 to -20) severity = 2 - if(-40 to -30) severity = 3 - if(-50 to -40) severity = 4 - if(-60 to -50) severity = 5 - if(-70 to -60) severity = 6 - if(-80 to -70) severity = 7 - if(-90 to -80) severity = 8 - if(-95 to -90) severity = 9 - if(-INFINITY to -95) severity = 10 + if(-20 to -10) + severity = 1 + if(-30 to -20) + severity = 2 + if(-40 to -30) + severity = 3 + if(-50 to -40) + severity = 4 + if(-50 to -40) + severity = 5 + if(-60 to -50) + severity = 6 + if(-70 to -60) + severity = 7 + if(-90 to -70) + severity = 8 + if(-95 to -90) + severity = 9 + if(-INFINITY to -95) + severity = 10 + if(!InFullCritical()) + var/visionseverity = 4 + switch(health) + if(-8 to -4) + visionseverity = 5 + if(-12 to -8) + visionseverity = 6 + if(-16 to -12) + visionseverity = 7 + if(-20 to -16) + visionseverity = 8 + if(-24 to -20) + visionseverity = 9 + if(-INFINITY to -24) + visionseverity = 10 + overlay_fullscreen("critvision", /obj/screen/fullscreen/crit/vision, visionseverity) + else + clear_fullscreen("critvision") overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity) else clear_fullscreen("crit") - if(oxyloss) - var/severity = 0 - switch(oxyloss) - if(10 to 20) severity = 1 - if(20 to 25) severity = 2 - if(25 to 30) severity = 3 - if(30 to 35) severity = 4 - if(35 to 40) severity = 5 - if(40 to 45) severity = 6 - if(45 to INFINITY) severity = 7 - overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity) - else - clear_fullscreen("oxy") + clear_fullscreen("critvision") - //Fire and Brute damage overlay (BSSR) - var/hurtdamage = getBruteLoss() + getFireLoss() + damageoverlaytemp - if(hurtdamage) - var/severity = 0 - switch(hurtdamage) - if(5 to 15) severity = 1 - if(15 to 30) severity = 2 - if(30 to 45) severity = 3 - if(45 to 70) severity = 4 - if(70 to 85) severity = 5 - if(85 to INFINITY) severity = 6 - overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) - else - clear_fullscreen("brute") + //Oxygen damage overlay + if(oxyloss) + var/severity = 0 + switch(oxyloss) + if(10 to 20) + severity = 1 + if(20 to 25) + severity = 2 + if(25 to 30) + severity = 3 + if(30 to 35) + severity = 4 + if(35 to 40) + severity = 5 + if(40 to 45) + severity = 6 + if(45 to INFINITY) + severity = 7 + overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity) + else + clear_fullscreen("oxy") + + //Fire and Brute damage overlay (BSSR) + var/hurtdamage = getBruteLoss() + getFireLoss() + damageoverlaytemp + if(hurtdamage) + var/severity = 0 + switch(hurtdamage) + if(5 to 15) + severity = 1 + if(15 to 30) + severity = 2 + if(30 to 45) + severity = 3 + if(45 to 70) + severity = 4 + if(70 to 85) + severity = 5 + if(85 to INFINITY) + severity = 6 + overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) + else + clear_fullscreen("brute") /mob/living/carbon/update_health_hud(shown_health_amount) if(!client || !hud_used) @@ -688,20 +732,19 @@ if(status_flags & GODMODE) return if(stat != DEAD) - if(health<= HEALTH_THRESHOLD_DEAD) + if(health <= HEALTH_THRESHOLD_DEAD) death() return - if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (status_flags & FAKEDEATH) || health <= HEALTH_THRESHOLD_CRIT) - if(stat == CONSCIOUS) - stat = UNCONSCIOUS - blind_eyes(1) - update_canmove() + if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (status_flags & FAKEDEATH) || health <= HEALTH_THRESHOLD_FULLCRIT) + stat = UNCONSCIOUS + blind_eyes(1) else - if(stat == UNCONSCIOUS) + if(health <= HEALTH_THRESHOLD_CRIT) + stat = SOFT_CRIT + else stat = CONSCIOUS - resting = 0 - adjust_blindness(-1) - update_canmove() + adjust_blindness(-1) + update_canmove() update_damage_hud() update_health_hud() med_hud_set_status() @@ -817,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/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index fab12538fc..6068ef2d66 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -244,7 +244,7 @@ /mob/living/carbon/proc/help_shake_act(mob/living/carbon/M) if(on_fire) - to_chat(M, "You can't put them out with just your bare hands!") + to_chat(M, "You can't put them out with just your bare hands!") return if(health >= 0 && !(status_flags & FAKEDEATH)) @@ -255,6 +255,8 @@ else if(check_zone(M.zone_selected) == "head") M.visible_message("[M] gives [src] a pat on the head to make [p_them()] feel better!", \ "You give [src] a pat on the head to make [p_them()] feel better!") + if(dna && dna.species && (("tail_lizard" in dna.species.mutant_bodyparts) || (dna.features["tail_human"] != "None") || ("mam_tail" in dna.species.mutant_bodyparts))) + emote("wag") //lewd else M.visible_message("[M] hugs [src] to make [p_them()] feel better!", \ "You hug [src] to make [p_them()] feel better!") diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index 70ec78092d..2bacece28d 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -1,10 +1,8 @@ /mob/living/carbon/movement_delay() - var/FP - if(iscarbon(src)) - var/mob/living/carbon/C = src - var/obj/item/device/flightpack/F = C.get_flightpack() - if(istype(F) && F.flight) - FP = 1 + var/FP = FALSE + var/obj/item/device/flightpack/F = get_flightpack() + if(istype(F) && F.flight) + FP = TRUE . = ..(FP) if(!FP) . += grab_state * 1 //Flightpacks are too powerful to be slowed too much by the weight of a corpse. diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 600a50e595..e7fe862b1e 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -79,6 +79,8 @@ if(!appears_dead) if(stat == UNCONSCIOUS) msg += "[t_He] [t_is]n't responding to anything around [t_him] and seems to be asleep.\n" + else if(InCritical()) + msg += "[t_His] breathing is shallow and labored.\n" if(digitalcamo) msg += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly unsimian manner.\n" diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 165019160e..dc7542fbab 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -613,7 +613,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) //Check for dresscode violations if(istype(head, /obj/item/clothing/head/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/syndi) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi)) - threatcount += 3 + threatcount += 6 //Check for nonhuman scum if(dna && dna.species.id && !(dna.species.id in list("human" , "lizard", "mammal", "avian", "aquatic", "insect"))) @@ -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_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 1c44c96d64..b5f8ab7c81 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -105,9 +105,6 @@ /mob/living/carbon/human/IsAdvancedToolUser() return 1//Humans can use guns and such -/mob/living/carbon/human/InCritical() - return (health <= HEALTH_THRESHOLD_CRIT && stat == UNCONSCIOUS) - /mob/living/carbon/human/reagent_check(datum/reagent/R) return dna.species.handle_chemicals(R,src) // if it returns 0, it will run the usual on_mob_life for that reagent. otherwise, it will stop after running handle_chemicals for the species. 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/human/interactive.dm b/code/modules/mob/living/carbon/human/interactive.dm index 18a4b3a7fb..7a80758eae 100644 --- a/code/modules/mob/living/carbon/human/interactive.dm +++ b/code/modules/mob/living/carbon/human/interactive.dm @@ -87,17 +87,23 @@ /// SNPC voice handling /mob/living/carbon/human/interactive/proc/loadVoice() - var/savefile/S = new /savefile("data/npc_saves/snpc.sav") - S["knownStrings"] >> knownStrings - + var/json_file = file("data/npc_saves/snpc.json") + if(!fexists(json_file)) + return + var/list/json = list() + json = json_decode(file2text(json_file)) + knownStrings = json["knownStrings"] if(isnull(knownStrings)) knownStrings = list() /mob/living/carbon/human/interactive/proc/saveVoice() if(voice_saved) return - var/savefile/S = new /savefile("data/npc_saves/snpc.sav") - WRITE_FILE(S["knownStrings"], knownStrings) + var/json_file = file("data/npc_saves/snpc.json") + var/list/file_data = list() + file_data["knownStrings"] = knownStrings + fdel(json_file) + WRITE_FILE(json_file, json_encode(file_data)) //botPool funcs /mob/living/carbon/human/interactive/proc/takeDelegate(mob/living/carbon/human/interactive/from,doReset=TRUE) @@ -284,7 +290,7 @@ //job specific favours switch(myjob.title) if("Assistant") - favoured_types = list(/obj/item/clothing, /obj/item/weapon) + favoured_types = list(/obj/item/clothing, /obj/item) if("Captain","Head of Personnel") favoured_types = list(/obj/item/clothing, /obj/item/stamp/captain, /obj/item/disk/nuclear) if("Cook") @@ -296,14 +302,14 @@ functions += "bartend" restrictedJob = 1 if("Station Engineer","Chief Engineer","Atmospheric Technician") - favoured_types = list(/obj/item/stack, /obj/item/weapon, /obj/item/clothing) + favoured_types = list(/obj/item/stack, /obj/item, /obj/item/clothing) if("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Geneticist") favoured_types = list(/obj/item/reagent_containers/glass/beaker, /obj/item/storage/firstaid, /obj/item/stack/medical, /obj/item/reagent_containers/syringe) functions += "healpeople" if("Research Director","Scientist","Roboticist") favoured_types = list(/obj/item/reagent_containers/glass/beaker, /obj/item/stack, /obj/item/reagent_containers) if("Head of Security","Warden","Security Officer","Detective") - favoured_types = list(/obj/item/clothing, /obj/item/weapon, /obj/item/restraints) + favoured_types = list(/obj/item/clothing, /obj/item, /obj/item/restraints) if("Janitor") favoured_types = list(/obj/item/mop, /obj/item/reagent_containers/glass/bucket, /obj/item/reagent_containers/spray/cleaner, /obj/effect/decal/cleanable) functions += "dojanitor" @@ -313,7 +319,7 @@ if("Mime") functions -= "chatter" if("Botanist") - favoured_types = list(/obj/machinery/hydroponics, /obj/item/reagent_containers, /obj/item/weapon) + favoured_types = list(/obj/machinery/hydroponics, /obj/item/reagent_containers, /obj/item) functions += "botany" restrictedJob = 1 else @@ -633,7 +639,7 @@ insert_into_backpack() // dump random item into backpack to make space //---------ITEMS if(isitem(TARGET)) - if(istype(TARGET, /obj/item/weapon)) + if(istype(TARGET, /obj/item)) var/obj/item/W = TARGET if(W.force >= best_force || prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/2)) if(!get_item_for_held_index(1) || !get_item_for_held_index(2)) @@ -1473,7 +1479,7 @@ foundFav = 1 return if(!foundFav) - if(istype(test, /obj/item/weapon)) + if(istype(test, /obj/item)) var/obj/item/R = test if(R.force > 2) // make sure we don't equip any non-weaponlike items, ie bags and stuff if(!best) @@ -1498,7 +1504,7 @@ if(istype(A, /obj/item/gun)) // guns are for shooting, not throwing. continue if(prob(robustness)) - if(istype(A, /obj/item/weapon)) + if(istype(A, /obj/item)) var/obj/item/W = A if(W.throwforce > 19) // Only throw worthwile stuff, no more lobbing wrenches at wenches npcDrop(W,1) @@ -1612,4 +1618,4 @@ TRAITS |= TRAIT_ROBUST TRAITS |= TRAIT_SMART faction += "bot_power" - ..() + ..() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index da99954f29..55203bf2e5 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -147,7 +147,7 @@ . = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should. if(!. || !I) return - if(index && dna.species.mutanthands) + if(index && !QDELETED(src) && dna.species.mutanthands) //hand freed, fill with claws, skip if we're getting deleted. put_in_hand(new dna.species.mutanthands(), index) if(I == wear_suit) if(s_store && invdrop) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 1fcf412fc8..9d8d2b704b 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -57,6 +57,8 @@ /mob/living/carbon/human/calculate_affecting_pressure(pressure) if((wear_suit && (wear_suit.flags_1 & STOPSPRESSUREDMAGE_1)) && (head && (head.flags_1 & STOPSPRESSUREDMAGE_1))) return ONE_ATMOSPHERE + if(ismob(loc)) + return ONE_ATMOSPHERE else return pressure @@ -242,6 +244,9 @@ if(dna && (RESISTCOLD in dna.species.species_traits)) return 1 + + if(ismob(loc)) + return 1 //because lazy and being inside somemone insulates you from space temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. var/thermal_protection_flags = get_cold_protection_flags(temperature) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 730a398caa..7b83347953 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1108,7 +1108,7 @@ if(prob(15)) if(!( H.hair_style == "Shaved") || !(H.hair_style == "Bald") || (HAIR in species_traits)) - to_chat(H, "Your hair starts to fall out in clumps...") + to_chat(H, "Your hair starts to fall out in clumps...") addtimer(CALLBACK(src, .proc/go_bald, H), 50) if(75 to 100) @@ -1198,6 +1198,8 @@ . += (1.5 - flight) if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) . += (BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR + if(H.stat == SOFT_CRIT) + . = max(SOFTCRIT_MIN_SLOWDOWN, . + SOFTCRIT_ADD_SLOWDOWN) //regardless of how fast you are, you move at a maximum of SOFTCRIT_MIN_SLOWDOWN while in softcrit return . ////////////////// diff --git a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm index 99206f04de..d15280790e 100644 --- a/code/modules/mob/living/carbon/human/species_types/furrypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/furrypeople.dm @@ -9,6 +9,8 @@ attack_sound = 'sound/weapons/slash.ogg' miss_sound = 'sound/weapons/slashmiss.ogg' roundstart = 1 + liked_food = MEAT | FRIED + disliked_food = TOXIC /datum/species/mammal/spec_death(gibbed, mob/living/carbon/human/H) if(H) @@ -24,12 +26,14 @@ say_mod = "chirps" default_color = "BCAC9B" species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR) - mutant_bodyparts = list("snout", "wings", "taur", "mam_tail", "mam_body_markings") + mutant_bodyparts = list("snout", "wings", "taur", "mam_tail", "mam_body_markings", "taur") default_features = list("snout" = "Sharp", "wings" = "None", "taur" = "None", "mam_body_markings" = "Hawk") attack_verb = "peck" attack_sound = 'sound/weapons/slash.ogg' miss_sound = 'sound/weapons/slashmiss.ogg' roundstart = 1 + liked_food = MEAT | FRUIT + disliked_food = TOXIC /datum/species/avian/spec_death(gibbed, mob/living/carbon/human/H) if(H) @@ -44,12 +48,14 @@ id = "aquatic" default_color = "BCAC9B" species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR) - mutant_bodyparts = list("mam_tail", "mam_body_markings", "mam_ears") + mutant_bodyparts = list("mam_tail", "mam_body_markings", "mam_ears", "taur") default_features = list("mcolor" = "FFF","mcolor2" = "FFF","mcolor3" = "FFF","mam_tail" = "shark", "mam_body_markings" = "None", "mam_ears" = "None") attack_verb = "bite" attack_sound = 'sound/weapons/bite.ogg' miss_sound = 'sound/weapons/slashmiss.ogg' roundstart = 1 + liked_food = MEAT + disliked_food = TOXIC /datum/species/aquatic/spec_death(gibbed, mob/living/carbon/human/H) if(H) @@ -64,12 +70,14 @@ id = "insect" default_color = "BCAC9B" species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAIR) - mutant_bodyparts = list("mam_body_markings", "mam_ears", "mam_tail") + mutant_bodyparts = list("mam_body_markings", "mam_ears", "mam_tail", "taur") default_features = list("mcolor" = "FFF","mcolor2" = "FFF","mcolor3" = "FFF", "mam_body_markings" = "moth", "mam_tail" = "None", "mam_ears" = "None") attack_verb = "flutter" //wat? attack_sound = 'sound/weapons/slash.ogg' miss_sound = 'sound/weapons/slashmiss.ogg' roundstart = 1 + liked_food = MEAT | FRUIT + disliked_food = TOXIC /datum/species/insect/spec_death(gibbed, mob/living/carbon/human/H) if(H) @@ -98,6 +106,7 @@ exotic_bloodtype = "L" damage_overlay_type = "xeno" roundstart = 1 + liked_food = MEAT //Praise the Omnissiah, A challange worthy of my skills - HS diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 14726df76a..49659f0128 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -80,11 +80,11 @@ /datum/species/golem/plasma/spec_life(mob/living/carbon/human/H) if(H.bodytemperature > 750) if(!boom_warning && H.on_fire) - to_chat(H, "You feel like you could blow up at any moment!") + to_chat(H, "You feel like you could blow up at any moment!") boom_warning = TRUE else if(boom_warning) - to_chat(H, "You feel more stable.") + to_chat(H, "You feel more stable.") boom_warning = FALSE if(H.bodytemperature > 850 && H.on_fire && prob(25)) diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 1a37d1b0ed..3319895fe0 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -10,7 +10,7 @@ damage_overlay_type = "" var/datum/action/innate/regenerate_limbs/regenerate_limbs toxic_food = NONE - liked_food = NONE + liked_food = MEAT /datum/species/jelly/on_species_loss(mob/living/carbon/C) if(regenerate_limbs) diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index 3d30a3e1c1..636c2b7890 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -1,23 +1,115 @@ -/datum/species/shadow - // Humans cursed to stay in the darkness, lest their life forces drain. They regain health in shadow and die in light. - name = "???" - id = "shadow" - sexes = 0 - blacklisted = 1 - ignored_by = list(/mob/living/simple_animal/hostile/faithless) - meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/shadow - species_traits = list(NOBREATH,NOBLOOD,RADIMMUNE,VIRUSIMMUNE) - dangerous_existence = 1 - mutanteyes = /obj/item/organ/eyes/night_vision - - -/datum/species/shadow/spec_life(mob/living/carbon/human/H) - var/light_amount = 0 - if(isturf(H.loc)) - var/turf/T = H.loc - light_amount = T.get_lumcount() - - if(light_amount > 0.2) //if there's enough light, start dying - H.take_overall_damage(1,1) - else if (light_amount < 0.2) //heal in the dark - H.heal_overall_damage(1,1) +/datum/species/shadow + // Humans cursed to stay in the darkness, lest their life forces drain. They regain health in shadow and die in light. + name = "???" + id = "shadow" + sexes = 0 + blacklisted = 1 + ignored_by = list(/mob/living/simple_animal/hostile/faithless) + meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/shadow + species_traits = list(NOBREATH,NOBLOOD,RADIMMUNE,VIRUSIMMUNE) + + dangerous_existence = 1 + mutanteyes = /obj/item/organ/eyes/night_vision + + +/datum/species/shadow/spec_life(mob/living/carbon/human/H) + var/turf/T = H.loc + if(istype(T)) + var/light_amount = T.get_lumcount() + + if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD) //if there's enough light, start dying + H.take_overall_damage(1,1) + else if (light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) //heal in the dark + H.heal_overall_damage(1,1) + + +/datum/species/shadow/nightmare + name = "Nightmare" + id = "nightmare" + limbs_id = "shadow" + burnmod = 1.5 + blacklisted = TRUE + no_equip = list(slot_wear_mask, slot_wear_suit, slot_gloves, slot_shoes, slot_w_uniform, slot_s_store) + species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,NO_UNDERWEAR,NOHUNGER) + mutanteyes = /obj/item/organ/eyes/night_vision/nightmare + var/obj/effect/proc_holder/spell/targeted/shadowwalk/shadowwalk + + var/info_text = "You are a Nightmare. The ability shadow walk allows unlimited, unrestricted movement in the dark using. \ + Your light eater will destroy any light producing objects you attack, as well as destroy any lights a living creature may be holding. You will automatically dodge gunfire and melee attacks when on a dark tile." + +/datum/species/shadow/nightmare/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + var/obj/effect/proc_holder/spell/targeted/shadowwalk/SW = new + C.AddSpell(SW) + shadowwalk = SW + var/obj/item/light_eater/blade = new + C.put_in_hands(blade) + + to_chat(C, "[info_text]") + + C.real_name = "Nightmare" + C.name = "Nightmare" + if(C.mind) + C.mind.name = "Nightmare" + C.dna.real_name = "Nightmare" + +/datum/species/shadow/nightmare/on_species_loss(mob/living/carbon/C) + . = ..() + if(shadowwalk) + C.RemoveSpell(shadowwalk) + +/datum/species/shadow/nightmare/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H) + var/turf/T = H.loc + if(istype(T)) + var/light_amount = T.get_lumcount() + if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) + H.visible_message("[H] dances in the shadows, evading [P]!") + playsound(T, "bullet_miss", 75, 1) + return -1 + return 0 + +/obj/item/light_eater + name = "light eater" + icon_state = "arm_blade" + item_state = "arm_blade" + force = 25 + armour_penetration = 35 + lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi' + righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' + flags_1 = ABSTRACT_1 | NODROP_1 | DROPDEL_1 + w_class = WEIGHT_CLASS_HUGE + sharpness = IS_SHARP + +/obj/item/light_eater/afterattack(atom/movable/AM, mob/user, proximity) + if(!proximity) + return + if(isopenturf(AM)) //So you can actually melee with it + return + if(isliving(AM)) + var/mob/living/L = AM + if(iscyborg(AM)) + var/mob/living/silicon/robot/borg = AM + borg.update_headlamp(TRUE, 100) + else + for(var/obj/item/O in AM) + if(O.light_range && O.light_power) + disintegrate(O) + if(L.pulling && L.pulling.light_range && isitem(L.pulling)) + disintegrate(L.pulling) + else if(isitem(AM)) + var/obj/item/I = AM + if(I.light_range && I.light_power) + disintegrate(I) + +/obj/item/light_eater/proc/disintegrate(obj/item/O) + if(istype(O, /obj/item/device/pda)) + var/obj/item/device/pda/PDA = O + PDA.set_light(0) + PDA.fon = 0 + PDA.f_lum = 0 + PDA.update_icon() + visible_message("The light in [PDA] shorts out!") + else + visible_message("[O] is disintegrated by [src]!") + O.burn() + playsound(src, 'sound/items/welder.ogg', 50, 1) diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index afd4d832da..4592d76138 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -95,24 +95,30 @@ if(I == head) head = null - head_update(I) + if(!QDELETED(src)) + head_update(I) else if(I == back) back = null - update_inv_back() + if(!QDELETED(src)) + update_inv_back() else if(I == wear_mask) wear_mask = null - wear_mask_update(I, toggle_off = 1) + if(!QDELETED(src)) + wear_mask_update(I, toggle_off = 1) if(I == wear_neck) wear_neck = null - update_inv_neck(I) + if(!QDELETED(src)) + update_inv_neck(I) else if(I == handcuffed) handcuffed = null if(buckled && buckled.buckle_requires_restraints) buckled.unbuckle_mob(src) - update_handcuffed() + if(!QDELETED(src)) + update_handcuffed() else if(I == legcuffed) legcuffed = null - update_inv_legcuffed() + if(!QDELETED(src)) + update_inv_legcuffed() //handle stuff to update when a mob equips/unequips a mask. /mob/living/proc/wear_mask_update(obj/item/clothing/C, toggle_off = 1) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index f1a6998523..b2e88e278a 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -48,6 +48,8 @@ return if(istype(loc, /obj/machinery/atmospherics/components/unary/cryo_cell)) return + if(istype(loc, /obj/item/device/dogborg/sleeper)) + return if(ismob(loc)) return var/datum/gas_mixture/environment @@ -56,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") @@ -148,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 b56028634f..52ca8c8226 100644 --- a/code/modules/mob/living/carbon/monkey/punpun.dm +++ b/code/modules/mob/living/carbon/monkey/punpun.dm @@ -24,7 +24,7 @@ ..() //These have to be after the parent new to ensure that the monkey - //bodyparts are actually created before we try to equip things to + //bodyparts are actually created before we try to equip things to //those slots if(relic_hat) equip_to_slot_or_del(new relic_hat, slot_head) @@ -42,32 +42,32 @@ ..() /mob/living/carbon/monkey/punpun/proc/Read_Memory() - var/savefile/S = new /savefile("data/npc_saves/Punpun.sav") - S["ancestor_name"] >> ancestor_name - S["ancestor_chain"] >> ancestor_chain - S["relic_hat"] >> relic_hat - S["relic_mask"] >> relic_mask + var/json_file = file("data/npc_saves/Punpun.json") + if(!fexists(json_file)) + return + var/list/json = list() + json = json_decode(file2text(json_file)) + ancestor_name = json["ancestor_name"] + ancestor_chain = json["ancestor_chain"] + relic_hat = json["relic_hat"] + relic_mask = json["relic_hat"] /mob/living/carbon/monkey/punpun/proc/Write_Memory(dead, gibbed) - var/savefile/S = new /savefile("data/npc_saves/Punpun.sav") + var/json_file = file("data/npc_saves/Punpun.json") + var/list/file_data = list() if(gibbed) - WRITE_FILE(S["ancestor_name"], null) - WRITE_FILE(S["ancestor_chain"], 1) - WRITE_FILE(S["relic_hat"], null) - WRITE_FILE(S["relic_mask"], null) - return + file_data["ancestor_name"] = null + file_data["ancestor_chain"] = null + file_data["relic_hat"] = null + file_data["relic_mask"] = null if(dead) - WRITE_FILE(S["ancestor_name"], ancestor_name) - WRITE_FILE(S["ancestor_chain"], ancestor_chain + 1) - if(!ancestor_name) //new monkey name this round - WRITE_FILE(S["ancestor_name"], name) - if(head) - WRITE_FILE(S["relic_hat"], head.type) - else - WRITE_FILE(S["relic_hat"], null) - if(wear_mask) - WRITE_FILE(S["relic_mask"], wear_mask.type) - else - WRITE_FILE(S["relic_mask"], null) + file_data["ancestor_name"] = ancestor_name + file_data["ancestor_chain"] = ancestor_chain + 1 + file_data["relic_hat"] = head ? head.type : null + file_data["relic_mask"] = wear_mask ? wear_mask.type : null + if(!ancestor_name) + file_data["ancestor_name"] = name + fdel(json_file) + WRITE_FILE(json_file, json_encode(file_data)) if(!dead) - memory_saved = 1 + 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 3a3a051baa..acea72e911 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -229,8 +229,8 @@ /mob/living/verb/succumb(whispered as null) set hidden = 1 if (InCritical()) - src.log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] with [round(health, 0.1)] points of health!", INDIVIDUAL_ATTACK_LOG) - src.adjustOxyLoss(src.health - HEALTH_THRESHOLD_DEAD) + log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", INDIVIDUAL_ATTACK_LOG) + adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD) updatehealth() if(!whispered) to_chat(src, "You have given up life and succumbed to death.") @@ -241,7 +241,10 @@ return 1 /mob/living/proc/InCritical() - return (health < HEALTH_THRESHOLD_CRIT && health > HEALTH_THRESHOLD_DEAD && stat == UNCONSCIOUS) + return (health <= HEALTH_THRESHOLD_CRIT && (stat == SOFT_CRIT || stat == UNCONSCIOUS)) + +/mob/living/proc/InFullCritical() + return (health <= HEALTH_THRESHOLD_FULLCRIT && stat == UNCONSCIOUS) //This proc is used for mobs which are affected by pressure to calculate the amount of pressure that actually //affects them once clothing is factored in. ~Errorage @@ -426,6 +429,7 @@ else return 0 + var/old_direction = dir var/atom/movable/pullee = pulling if(pullee && get_dist(src, pullee) > 1) stop_pulling() @@ -441,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() @@ -455,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()) @@ -471,31 +475,32 @@ if(MOVE_INTENT_WALK) . += config.walk_speed -/mob/living/proc/makeTrail(turf/T) +/mob/living/proc/makeTrail(turf/target_turf, turf/start, direction) if(!has_gravity()) return - var/blood_exists = 0 + var/blood_exists = FALSE - for(var/obj/effect/decal/cleanable/trail_holder/C in src.loc) //checks for blood splatter already on the floor - blood_exists = 1 - if (isturf(src.loc)) + for(var/obj/effect/decal/cleanable/trail_holder/C in start) //checks for blood splatter already on the floor + blood_exists = TRUE + if(isturf(start)) var/trail_type = getTrail() if(trail_type) - var/brute_ratio = round(getBruteLoss()/maxHealth, 0.1) + 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(T, src.loc) - if(newdir != src.dir) - newdir = newdir | src.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(T, src.loc), 180) + newdir = turn(get_dir(target_turf, start), 180) if(!blood_exists) - new /obj/effect/decal/cleanable/trail_holder(src.loc) - for(var/obj/effect/decal/cleanable/trail_holder/TH in src.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)) @@ -566,10 +571,10 @@ var/obj/C = loc C.container_resist(src) - else if(has_status_effect(/datum/status_effect/freon)) + else if(IsFrozen()) to_chat(src, "You start breaking out of the ice cube!") if(do_mob(src, src, 40)) - if(has_status_effect(/datum/status_effect/freon)) + if(IsFrozen()) to_chat(src, "You break out of the ice cube!") remove_status_effect(/datum/status_effect/freon) update_canmove() @@ -687,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 @@ -943,13 +949,14 @@ //Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it. //Robots, animals and brains have their own version so don't worry about them /mob/living/proc/update_canmove() - var/ko = IsKnockdown() || IsUnconscious() || stat || (status_flags & FAKEDEATH) + var/ko = IsKnockdown() || IsUnconscious() || (stat && (stat != SOFT_CRIT || pulledby)) || (status_flags & FAKEDEATH) + var/move_and_fall = stat == SOFT_CRIT && !pulledby var/chokehold = pulledby && pulledby.grab_state >= GRAB_NECK var/buckle_lying = !(buckled && !buckled.buckle_lying) var/has_legs = get_num_legs() var/has_arms = get_num_arms() var/ignore_legs = get_leg_ignore() - if(ko || resting || has_status_effect(STATUS_EFFECT_STUN) || chokehold) + if(ko || resting || move_and_fall || IsStun() || chokehold) drop_all_held_items() unset_machine() if(pulling) @@ -962,9 +969,9 @@ else if(!lying) if(resting) fall() - else if(ko || (!has_legs && !ignore_legs) || chokehold) + else if(ko || move_and_fall || (!has_legs && !ignore_legs) || chokehold) fall(forced = 1) - canmove = !(ko || resting || has_status_effect(STATUS_EFFECT_STUN) || has_status_effect(/datum/status_effect/freon) || chokehold || buckled || (!has_legs && !ignore_legs && !has_arms)) + canmove = !(ko || resting || IsStun() || IsFrozen() || chokehold || buckled || (!has_legs && !ignore_legs && !has_arms)) density = !lying if(lying) if(layer == initial(layer)) //to avoid special cases like hiding larvas. diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 6cb3a8daa9..f32ca97b09 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -75,3 +75,5 @@ var/datum/riding/riding_datum var/datum/language/selected_default_language + + var/last_words //used for database logging diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 68e2b150f3..9c34ca4dbd 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -145,16 +145,18 @@ GLOBAL_LIST_INIT(department_radio_keys, list( var/succumbed = FALSE - if(message_mode == MODE_WHISPER) + var/fullcrit = InFullCritical() + if((InCritical() && !fullcrit) || message_mode == MODE_WHISPER) message_range = 1 - spans |= SPAN_ITALICS + message_mode = MODE_WHISPER log_talk(src,"[key_name(src)] : [message]",LOGWHISPER) - if(in_critical) + if(fullcrit) var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health) // If we cut our message short, abruptly end it with a-.. var/message_len = length(message) message = copytext(message, 1, health_diff) + "[message_len > health_diff ? "-.." : "..."]" message = Ellipsis(message, 10, 1) + last_words = message message_mode = MODE_WHISPER_CRIT succumbed = TRUE else @@ -164,7 +166,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list( if(!message) return - spans += get_spans() + spans |= get_spans() if(language) var/datum/language/L = GLOB.language_datum_instances[language] @@ -178,6 +180,8 @@ GLOBAL_LIST_INIT(department_radio_keys, list( spans |= SPAN_ITALICS if(radio_return & REDUCE_RANGE) message_range = 1 + if(radio_return & NOPASS) + return 1 //No screams in space, unless you're next to someone. var/turf/T = get_turf(src) @@ -295,10 +299,10 @@ GLOBAL_LIST_INIT(department_radio_keys, list( /mob/living/proc/get_message_mode(message) var/key = copytext(message, 1, 2) - if(key == ";") - return MODE_HEADSET - else if(key == "#") + if(key == "#") return MODE_WHISPER + else if(key == ";") + return MODE_HEADSET else if(length(message) > 2 && (key in GLOB.department_radio_prefixes)) var/key_symbol = lowertext(copytext(message, 2, 3)) return GLOB.department_radio_keys[key_symbol] @@ -384,6 +388,8 @@ GLOBAL_LIST_INIT(department_radio_keys, list( /mob/living/proc/radio(message, message_mode, list/spans, language) switch(message_mode) + if(MODE_WHISPER) + return ITALICS if(MODE_R_HAND) for(var/obj/item/r_hand in get_held_items_for_side("r", all = TRUE)) if (r_hand) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 35c107b532..61bc0d5d5b 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -465,7 +465,7 @@ set category = "AI Commands" set name = "Access Robot Control" set desc = "Wirelessly control various automatic robots." - if(stat == 2) + if(stat == DEAD) return //won't work if dead if(control_disabled) @@ -585,7 +585,7 @@ cameraFollow = null var/cameralist[0] - if(stat == 2) + if(stat == DEAD) return //won't work if dead var/mob/living/silicon/ai/U = usr @@ -629,7 +629,7 @@ set category = "AI Commands" set name = "AI Status" - if(stat == 2) + if(stat == DEAD) return //won't work if dead var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer", "Dorfy", "Blue Glow", "Red Glow") var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions @@ -652,7 +652,7 @@ set desc = "Change the default hologram available to AI to something else." set category = "AI Commands" - if(stat == 2) + if(stat == DEAD) return //won't work if dead var/input switch(alert("Would you like to select a hologram based on a crew member, an animal, or switch to a unique avatar?",,"Crew Member","Unique","Animal")) @@ -774,7 +774,7 @@ set desc = "Allows you to change settings of your radio." set category = "AI Commands" - if(stat == 2) + if(stat == DEAD) return //won't work if dead to_chat(src, "Accessing Subspace Transceiver control...") @@ -790,7 +790,7 @@ set desc = "Modify the default radio setting for your automatic announcements." set category = "AI Commands" - if(stat == 2) + if(stat == DEAD) return //won't work if dead set_autosay() 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/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index 0613cacb47..2c7d81179c 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -106,7 +106,7 @@ set category = "AI Commands" set name = "Toggle Camera Acceleration" - if(usr.stat == 2) + if(usr.stat == DEAD) return //won't work if dead acceleration = !acceleration to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm index 4a01e1dca8..0f1c078ef8 100644 --- a/code/modules/mob/living/silicon/ai/laws.dm +++ b/code/modules/mob/living/silicon/ai/laws.dm @@ -2,7 +2,7 @@ /mob/living/silicon/ai/proc/show_laws_verb() set category = "AI Commands" set name = "Show Laws" - if(usr.stat == 2) + if(usr.stat == DEAD) return //won't work if dead src.show_laws() diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index f0e079b594..02053681ae 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,5 +1,5 @@ /mob/living/silicon/ai/say(message, language) - if(parent && istype(parent) && parent.stat != 2) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. + if(parent && istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. parent.say(message, language) return ..(message) @@ -71,7 +71,7 @@ set desc = "Display a list of vocal words to announce to the crew." set category = "AI Commands" - if(usr.stat == 2) + if(usr.stat == DEAD) return //won't work if dead var/dat = "Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.
    \ diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 8ca558cd81..3a143c7910 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -104,7 +104,7 @@ pda.owner = text("[]", src) pda.name = pda.owner + " (" + pda.ownjob + ")" - ..() + . = ..() var/datum/action/innate/pai/shell/AS = new /datum/action/innate/pai/shell var/datum/action/innate/pai/chassis/AC = new /datum/action/innate/pai/chassis diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 97ac300f8e..acb74b6372 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -32,7 +32,7 @@ if(temp) left_part = temp - else if(src.stat == 2) // Show some flavor text if the pAI is dead + else if(src.stat == DEAD) // Show some flavor text if the pAI is dead left_part = "�Rr�R �a�� ��Rr����o�" right_part = "
    Program index hash not found
    " diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 644f845901..09d2570b72 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -72,7 +72,7 @@ var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. - var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. + var/lamp_cooldown = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. var/sight_mode = 0 hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD) @@ -742,7 +742,7 @@ set_autosay() /mob/living/silicon/robot/proc/control_headlamp() - if(stat || lamp_recharging || low_power_mode) + if(stat || lamp_cooldown > world.time || low_power_mode) to_chat(src, "This function is currently offline.") return @@ -757,8 +757,7 @@ if(lamp_intensity && (turn_off || stat || low_power_mode)) to_chat(src, "Your headlamp has been deactivated.") lamp_intensity = 0 - lamp_recharging = TRUE - addtimer(CALLBACK(src, .proc/reset_headlamp), cooldown) + lamp_cooldown = world.time + cooldown else set_light(lamp_intensity) @@ -767,9 +766,6 @@ update_icons() -/mob/living/silicon/robot/proc/reset_headlamp() - lamp_recharging = FALSE - /mob/living/silicon/robot/proc/deconstruct() var/turf/T = get_turf(src) if (robot_suit) 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/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 4d3c468191..562b51945b 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -36,11 +36,11 @@ var/d_hud = DATA_HUD_DIAGNOSTIC //There is only one kind of diag hud var/law_change_counter = 0 - var/obj/machinery/camera/builtInCamera = null - var/updating = FALSE //portable camera camerachunk update + var/obj/machinery/camera/builtInCamera = null + var/updating = FALSE //portable camera camerachunk update /mob/living/silicon/Initialize() - . = ..() + . = ..() GLOB.silicon_mobs += src var/datum/atom_hud/data/diagnostic/diag_hud = GLOB.huds[DATA_HUD_DIAGNOSTIC] diag_hud.add_to_hud(src) @@ -56,7 +56,7 @@ /mob/living/silicon/Destroy() radio = null aicamera = null - QDEL_NULL(builtInCamera) + QDEL_NULL(builtInCamera) GLOB.silicon_mobs -= src return ..() @@ -310,7 +310,7 @@ else //For department channels, if any, given by the internal radio. for(var/key in GLOB.department_radio_keys) if(GLOB.department_radio_keys[key] == Autochan) - radiomod = key + radiomod = ":" + key break to_chat(src, "Automatic announcements [Autochan == "None" ? "will not use the radio." : "set to [Autochan]."]") diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 584464e655..3cd66724e9 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -1,567 +1,567 @@ -/mob/living/simple_animal/bot/ed209 - name = "\improper ED-209 Security Robot" - desc = "A security robot. He looks less than thrilled." - icon = 'icons/mob/aibots.dmi' - icon_state = "ed2090" +/mob/living/simple_animal/bot/ed209 + name = "\improper ED-209 Security Robot" + desc = "A security robot. He looks less than thrilled." + icon = 'icons/mob/aibots.dmi' + icon_state = "ed2090" density = TRUE anchored = FALSE - health = 100 - maxHealth = 100 - damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) - obj_damage = 60 - environment_smash = ENVIRONMENT_SMASH_WALLS //Walls can't stop THE LAW - mob_size = MOB_SIZE_LARGE - - radio_key = /obj/item/device/encryptionkey/headset_sec - radio_channel = "Security" - bot_type = SEC_BOT - model = "ED-209" - bot_core = /obj/machinery/bot_core/secbot - window_id = "autoed209" - window_name = "Automatic Security Unit v2.6" - allow_pai = 0 - data_hud_type = DATA_HUD_SECURITY_ADVANCED - - var/lastfired = 0 - var/shot_delay = 15 - var/lasercolor = "" - var/disabled = 0//A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag - - - var/mob/living/carbon/target - var/oldtarget_name - var/threatlevel = 0 - var/target_lastloc //Loc of target when arrested. - var/last_found //There's a delay - var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds? - var/idcheck = 1 //If true, arrest people with no IDs - var/weaponscheck = 1 //If true, arrest people for weapons if they don't have access - var/check_records = 1 //Does it check security records? - var/arrest_type = 0 //If true, don't handcuff - var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type - var/shoot_sound = 'sound/weapons/taser.ogg' - - -/mob/living/simple_animal/bot/ed209/Initialize(mapload,created_name,created_lasercolor) - ..() - if(created_name) - name = created_name - if(created_lasercolor) - lasercolor = created_lasercolor - icon_state = "[lasercolor]ed209[on]" - set_weapon() //giving it the right projectile and firing sound. - spawn(3) - var/datum/job/detective/J = new/datum/job/detective - access_card.access += J.get_access() - prev_access = access_card.access - - if(lasercolor) - shot_delay = 6//Longer shot delay because JESUS CHRIST - check_records = 0//Don't actively target people set to arrest - arrest_type = 1//Don't even try to cuff + health = 100 + maxHealth = 100 + damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + obj_damage = 60 + environment_smash = ENVIRONMENT_SMASH_WALLS //Walls can't stop THE LAW + mob_size = MOB_SIZE_LARGE + + radio_key = /obj/item/device/encryptionkey/headset_sec + radio_channel = "Security" + bot_type = SEC_BOT + model = "ED-209" + bot_core = /obj/machinery/bot_core/secbot + window_id = "autoed209" + window_name = "Automatic Security Unit v2.6" + allow_pai = 0 + data_hud_type = DATA_HUD_SECURITY_ADVANCED + + var/lastfired = 0 + var/shot_delay = 15 + var/lasercolor = "" + var/disabled = 0//A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag + + + var/mob/living/carbon/target + var/oldtarget_name + var/threatlevel = 0 + var/target_lastloc //Loc of target when arrested. + var/last_found //There's a delay + var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds? + var/idcheck = 1 //If true, arrest people with no IDs + var/weaponscheck = 1 //If true, arrest people for weapons if they don't have access + var/check_records = 1 //Does it check security records? + var/arrest_type = 0 //If true, don't handcuff + var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type + var/shoot_sound = 'sound/weapons/taser.ogg' + + +/mob/living/simple_animal/bot/ed209/Initialize(mapload,created_name,created_lasercolor) + . = ..() + if(created_name) + name = created_name + if(created_lasercolor) + lasercolor = created_lasercolor + icon_state = "[lasercolor]ed209[on]" + set_weapon() //giving it the right projectile and firing sound. + spawn(3) + var/datum/job/detective/J = new/datum/job/detective + access_card.access += J.get_access() + prev_access = access_card.access + + if(lasercolor) + shot_delay = 6//Longer shot delay because JESUS CHRIST + check_records = 0//Don't actively target people set to arrest + arrest_type = 1//Don't even try to cuff bot_core.req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) - arrest_type = 1 - if((lasercolor == "b") && (name == "\improper ED-209 Security Robot"))//Picks a name if there isn't already a custome one - name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT") - if((lasercolor == "r") && (name == "\improper ED-209 Security Robot")) - name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT") - - //SECHUD - var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] - secsensor.add_hud_to(src) - -/mob/living/simple_animal/bot/ed209/turn_on() - . = ..() - icon_state = "[lasercolor]ed209[on]" - mode = BOT_IDLE - -/mob/living/simple_animal/bot/ed209/turn_off() - ..() - icon_state = "[lasercolor]ed209[on]" - -/mob/living/simple_animal/bot/ed209/bot_reset() - ..() - target = null - oldtarget_name = null + arrest_type = 1 + if((lasercolor == "b") && (name == "\improper ED-209 Security Robot"))//Picks a name if there isn't already a custome one + name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT") + if((lasercolor == "r") && (name == "\improper ED-209 Security Robot")) + name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT") + + //SECHUD + var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] + secsensor.add_hud_to(src) + +/mob/living/simple_animal/bot/ed209/turn_on() + . = ..() + icon_state = "[lasercolor]ed209[on]" + mode = BOT_IDLE + +/mob/living/simple_animal/bot/ed209/turn_off() + ..() + icon_state = "[lasercolor]ed209[on]" + +/mob/living/simple_animal/bot/ed209/bot_reset() + ..() + target = null + oldtarget_name = null anchored = FALSE - walk_to(src,0) - last_found = world.time - set_weapon() - -/mob/living/simple_animal/bot/ed209/set_custom_texts() - text_hack = "You disable [name]'s combat inhibitor." - text_dehack = "You restore [name]'s combat inhibitor." - text_dehack_fail = "[name] ignores your attempts to restrict him!" - -/mob/living/simple_animal/bot/ed209/get_controls(mob/user) - var/dat - dat += hack(user) - dat += showpai(user) - dat += text({" -Security Unit v2.6 controls

    -Status: []
    -Behaviour controls are [locked ? "locked" : "unlocked"]
    -Maintenance panel panel is [open ? "opened" : "closed"]
    "}, - -"[on ? "On" : "Off"]" ) - - if(!locked || issilicon(user)|| IsAdminGhost(user)) - if(!lasercolor) - dat += text({"
    -Arrest Unidentifiable Persons: []
    -Arrest for Unauthorized Weapons: []
    -Arrest for Warrant: []
    -
    -Operating Mode: []
    -Report Arrests[]
    -Auto Patrol[]"}, - -"[idcheck ? "Yes" : "No"]", -"[weaponscheck ? "Yes" : "No"]", -"[check_records ? "Yes" : "No"]", -"[arrest_type ? "Detain" : "Arrest"]", -"[declare_arrests ? "Yes" : "No"]", -"[auto_patrol ? "On" : "Off"]" ) - - return dat - -/mob/living/simple_animal/bot/ed209/Topic(href, href_list) - if(lasercolor && ishuman(usr)) - var/mob/living/carbon/human/H = usr - if((lasercolor == "b") && (istype(H.wear_suit, /obj/item/clothing/suit/redtag)))//Opposing team cannot operate it - return - else if((lasercolor == "r") && (istype(H.wear_suit, /obj/item/clothing/suit/bluetag))) - return - if(..()) - return 1 - - switch(href_list["operation"]) - if("idcheck") - idcheck = !idcheck - update_controls() - if("weaponscheck") - weaponscheck = !weaponscheck - update_controls() - if("ignorerec") - check_records = !check_records - update_controls() - if("switchmode") - arrest_type = !arrest_type - update_controls() - if("declarearrests") - declare_arrests = !declare_arrests - update_controls() - -/mob/living/simple_animal/bot/ed209/proc/judgement_criteria() + walk_to(src,0) + last_found = world.time + set_weapon() + +/mob/living/simple_animal/bot/ed209/set_custom_texts() + text_hack = "You disable [name]'s combat inhibitor." + text_dehack = "You restore [name]'s combat inhibitor." + text_dehack_fail = "[name] ignores your attempts to restrict him!" + +/mob/living/simple_animal/bot/ed209/get_controls(mob/user) + var/dat + dat += hack(user) + dat += showpai(user) + dat += text({" +Security Unit v2.6 controls

    +Status: []
    +Behaviour controls are [locked ? "locked" : "unlocked"]
    +Maintenance panel panel is [open ? "opened" : "closed"]
    "}, + +"[on ? "On" : "Off"]" ) + + if(!locked || issilicon(user)|| IsAdminGhost(user)) + if(!lasercolor) + dat += text({"
    +Arrest Unidentifiable Persons: []
    +Arrest for Unauthorized Weapons: []
    +Arrest for Warrant: []
    +
    +Operating Mode: []
    +Report Arrests[]
    +Auto Patrol[]"}, + +"[idcheck ? "Yes" : "No"]", +"[weaponscheck ? "Yes" : "No"]", +"[check_records ? "Yes" : "No"]", +"[arrest_type ? "Detain" : "Arrest"]", +"[declare_arrests ? "Yes" : "No"]", +"[auto_patrol ? "On" : "Off"]" ) + + return dat + +/mob/living/simple_animal/bot/ed209/Topic(href, href_list) + if(lasercolor && ishuman(usr)) + var/mob/living/carbon/human/H = usr + if((lasercolor == "b") && (istype(H.wear_suit, /obj/item/clothing/suit/redtag)))//Opposing team cannot operate it + return + else if((lasercolor == "r") && (istype(H.wear_suit, /obj/item/clothing/suit/bluetag))) + return + if(..()) + return 1 + + switch(href_list["operation"]) + if("idcheck") + idcheck = !idcheck + update_controls() + if("weaponscheck") + weaponscheck = !weaponscheck + update_controls() + if("ignorerec") + check_records = !check_records + update_controls() + if("switchmode") + arrest_type = !arrest_type + update_controls() + if("declarearrests") + declare_arrests = !declare_arrests + update_controls() + +/mob/living/simple_animal/bot/ed209/proc/judgement_criteria() var/final = FALSE - if(idcheck) - final = final|JUDGE_IDCHECK - if(check_records) - final = final|JUDGE_RECORDCHECK - if(weaponscheck) - final = final|JUDGE_WEAPONCHECK - if(emagged) - final = final|JUDGE_EMAGGED - //ED209's ignore monkeys - final = final|JUDGE_IGNOREMONKEYS - return final - -/mob/living/simple_animal/bot/ed209/proc/retaliate(mob/living/carbon/human/H) - var/judgement_criteria = judgement_criteria() - threatlevel = H.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) - threatlevel += 6 - if(threatlevel >= 4) - target = H - mode = BOT_HUNT - -/mob/living/simple_animal/bot/ed209/attack_hand(mob/living/carbon/human/H) - if(H.a_intent == INTENT_HARM) - retaliate(H) - return ..() - -/mob/living/simple_animal/bot/ed209/attackby(obj/item/W, mob/user, params) - ..() - if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM) // Any intent but harm will heal, so we shouldn't get angry. - return - if(!istype(W, /obj/item/screwdriver) && (!target)) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. - if(W.force && W.damtype != STAMINA)//If force is non-zero and damage type isn't stamina. - retaliate(user) - if(lasercolor)//To make up for the fact that lasertag bots don't hunt - shootAt(user) - -/mob/living/simple_animal/bot/ed209/emag_act(mob/user) - ..() - if(emagged == 2) - if(user) - to_chat(user, "You short out [src]'s target assessment circuits.") - oldtarget_name = user.name - audible_message("[src] buzzes oddly!") - declare_arrests = 0 - icon_state = "[lasercolor]ed209[on]" - set_weapon() - -/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj) + if(idcheck) + final = final|JUDGE_IDCHECK + if(check_records) + final = final|JUDGE_RECORDCHECK + if(weaponscheck) + final = final|JUDGE_WEAPONCHECK + if(emagged) + final = final|JUDGE_EMAGGED + //ED209's ignore monkeys + final = final|JUDGE_IGNOREMONKEYS + return final + +/mob/living/simple_animal/bot/ed209/proc/retaliate(mob/living/carbon/human/H) + var/judgement_criteria = judgement_criteria() + threatlevel = H.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) + threatlevel += 6 + if(threatlevel >= 4) + target = H + mode = BOT_HUNT + +/mob/living/simple_animal/bot/ed209/attack_hand(mob/living/carbon/human/H) + if(H.a_intent == INTENT_HARM) + retaliate(H) + return ..() + +/mob/living/simple_animal/bot/ed209/attackby(obj/item/W, mob/user, params) + ..() + if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM) // Any intent but harm will heal, so we shouldn't get angry. + return + if(!istype(W, /obj/item/screwdriver) && (!target)) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. + if(W.force && W.damtype != STAMINA)//If force is non-zero and damage type isn't stamina. + retaliate(user) + if(lasercolor)//To make up for the fact that lasertag bots don't hunt + shootAt(user) + +/mob/living/simple_animal/bot/ed209/emag_act(mob/user) + ..() + if(emagged == 2) + if(user) + to_chat(user, "You short out [src]'s target assessment circuits.") + oldtarget_name = user.name + audible_message("[src] buzzes oddly!") + declare_arrests = 0 + icon_state = "[lasercolor]ed209[on]" + set_weapon() + +/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj) if(istype(Proj , /obj/item/projectile/beam/laser)||istype(Proj, /obj/item/projectile/bullet)) - if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) - if(!Proj.nodamage && Proj.damage < src.health) - retaliate(Proj.firer) - ..() - -/mob/living/simple_animal/bot/ed209/handle_automated_action() - if(!..()) - return - - if(disabled) - return - - var/judgement_criteria = judgement_criteria() - var/list/targets = list() - for(var/mob/living/carbon/C in view(7,src)) //Let's find us a target - var/threatlevel = 0 - if((C.stat) || (C.lying)) - continue - threatlevel = C.assess_threat(judgement_criteria, lasercolor, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) - //speak(C.real_name + text(": threat: []", threatlevel)) - if(threatlevel < 4 ) - continue - - var/dst = get_dist(src, C) - if(dst <= 1 || dst > 7) - continue - - targets += C - if(targets.len>0) - var/mob/living/carbon/t = pick(targets) - if((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down. - shootAt(t) - switch(mode) - - if(BOT_IDLE) // idle - walk_to(src,0) - if(!lasercolor) //lasertag bots don't want to arrest anyone - look_for_perp() // see if any criminals are in range - if(!mode && auto_patrol) // still idle, and set to patrol - mode = BOT_START_PATROL // switch to patrol mode - - if(BOT_HUNT) // hunting for perp - // if can't reach perp for long enough, go idle - if(frustration >= 8) - walk_to(src,0) - back_to_idle() - - if(target) // make sure target exists - if(Adjacent(target) && isturf(target.loc)) // if right next to perp - stun_attack(target) - - mode = BOT_PREP_ARREST + if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) + if(!Proj.nodamage && Proj.damage < src.health) + retaliate(Proj.firer) + ..() + +/mob/living/simple_animal/bot/ed209/handle_automated_action() + if(!..()) + return + + if(disabled) + return + + var/judgement_criteria = judgement_criteria() + var/list/targets = list() + for(var/mob/living/carbon/C in view(7,src)) //Let's find us a target + var/threatlevel = 0 + if((C.stat) || (C.lying)) + continue + threatlevel = C.assess_threat(judgement_criteria, lasercolor, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) + //speak(C.real_name + text(": threat: []", threatlevel)) + if(threatlevel < 4 ) + continue + + var/dst = get_dist(src, C) + if(dst <= 1 || dst > 7) + continue + + targets += C + if(targets.len>0) + var/mob/living/carbon/t = pick(targets) + if((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down. + shootAt(t) + switch(mode) + + if(BOT_IDLE) // idle + walk_to(src,0) + if(!lasercolor) //lasertag bots don't want to arrest anyone + look_for_perp() // see if any criminals are in range + if(!mode && auto_patrol) // still idle, and set to patrol + mode = BOT_START_PATROL // switch to patrol mode + + if(BOT_HUNT) // hunting for perp + // if can't reach perp for long enough, go idle + if(frustration >= 8) + walk_to(src,0) + back_to_idle() + + if(target) // make sure target exists + if(Adjacent(target) && isturf(target.loc)) // if right next to perp + stun_attack(target) + + mode = BOT_PREP_ARREST anchored = TRUE - target_lastloc = target.loc - return - - else // not next to perp - var/turf/olddist = get_dist(src, target) - walk_to(src, target,1,4) - if((get_dist(src, target)) >= (olddist)) - frustration++ - else - frustration = 0 - else - back_to_idle() - - if(BOT_PREP_ARREST) // preparing to arrest target - - // see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again. - if(!Adjacent(target) || !isturf(target.loc) || target.AmountKnockdown() < 40) - back_to_hunt() - return - - if(iscarbon(target) && target.canBeHandcuffed()) - if(!arrest_type) - if(!target.handcuffed) //he's not cuffed? Try to cuff him! - cuff(target) - else - back_to_idle() - return - else - back_to_idle() - return - - if(BOT_ARREST) - if(!target) + target_lastloc = target.loc + return + + else // not next to perp + var/turf/olddist = get_dist(src, target) + walk_to(src, target,1,4) + if((get_dist(src, target)) >= (olddist)) + frustration++ + else + frustration = 0 + else + back_to_idle() + + if(BOT_PREP_ARREST) // preparing to arrest target + + // see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again. + if(!Adjacent(target) || !isturf(target.loc) || target.AmountKnockdown() < 40) + back_to_hunt() + return + + if(iscarbon(target) && target.canBeHandcuffed()) + if(!arrest_type) + if(!target.handcuffed) //he's not cuffed? Try to cuff him! + cuff(target) + else + back_to_idle() + return + else + back_to_idle() + return + + if(BOT_ARREST) + if(!target) anchored = FALSE - mode = BOT_IDLE - last_found = world.time - frustration = 0 - return - - if(target.handcuffed) //no target or target cuffed? back to idle. - back_to_idle() - return - - if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.AmountKnockdown() < 40)) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again. - back_to_hunt() - return - else - mode = BOT_PREP_ARREST + mode = BOT_IDLE + last_found = world.time + frustration = 0 + return + + if(target.handcuffed) //no target or target cuffed? back to idle. + back_to_idle() + return + + if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.AmountKnockdown() < 40)) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again. + back_to_hunt() + return + else + mode = BOT_PREP_ARREST anchored = FALSE - - if(BOT_START_PATROL) - look_for_perp() - start_patrol() - - if(BOT_PATROL) - look_for_perp() - bot_patrol() - - - return - -/mob/living/simple_animal/bot/ed209/proc/back_to_idle() + + if(BOT_START_PATROL) + look_for_perp() + start_patrol() + + if(BOT_PATROL) + look_for_perp() + bot_patrol() + + + return + +/mob/living/simple_animal/bot/ed209/proc/back_to_idle() anchored = FALSE - mode = BOT_IDLE - target = null - last_found = world.time - frustration = 0 - INVOKE_ASYNC(src, .proc/handle_automated_action) //ensure bot quickly responds - -/mob/living/simple_animal/bot/ed209/proc/back_to_hunt() + mode = BOT_IDLE + target = null + last_found = world.time + frustration = 0 + INVOKE_ASYNC(src, .proc/handle_automated_action) //ensure bot quickly responds + +/mob/living/simple_animal/bot/ed209/proc/back_to_hunt() anchored = FALSE - frustration = 0 - mode = BOT_HUNT - INVOKE_ASYNC(src, .proc/handle_automated_action) //ensure bot quickly responds - -// look for a criminal in view of the bot - -/mob/living/simple_animal/bot/ed209/proc/look_for_perp() - if(disabled) - return + frustration = 0 + mode = BOT_HUNT + INVOKE_ASYNC(src, .proc/handle_automated_action) //ensure bot quickly responds + +// look for a criminal in view of the bot + +/mob/living/simple_animal/bot/ed209/proc/look_for_perp() + if(disabled) + return anchored = FALSE - threatlevel = 0 - var/judgement_criteria = judgement_criteria() - for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal - if((C.stat) || (C.handcuffed)) - continue - - if((C.name == oldtarget_name) && (world.time < last_found + 100)) - continue - - threatlevel = C.assess_threat(judgement_criteria, lasercolor, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) - - if(!threatlevel) - continue - - else if(threatlevel >= 4) - target = C - oldtarget_name = C.name - speak("Level [threatlevel] infraction alert!") - playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, 0) - visible_message("[src] points at [C.name]!") - mode = BOT_HUNT - spawn(0) - handle_automated_action() // ensure bot quickly responds to a perp - break - else - continue - -/mob/living/simple_animal/bot/ed209/proc/check_for_weapons(var/obj/item/slot_item) - if(slot_item && slot_item.needs_permit) - return 1 - return 0 - -/mob/living/simple_animal/bot/ed209/explode() - walk_to(src,0) - visible_message("[src] blows apart!") - var/turf/Tsec = get_turf(src) - - var/obj/item/ed209_assembly/Sa = new /obj/item/ed209_assembly(Tsec) - Sa.build_step = 1 - Sa.add_overlay("hs_hole") - Sa.created_name = name - new /obj/item/device/assembly/prox_sensor(Tsec) - - if(!lasercolor) - var/obj/item/gun/energy/e_gun/advtaser/G = new /obj/item/gun/energy/e_gun/advtaser(Tsec) - G.cell.charge = 0 - G.update_icon() - else if(lasercolor == "b") - var/obj/item/gun/energy/laser/bluetag/G = new /obj/item/gun/energy/laser/bluetag(Tsec) - G.cell.charge = 0 - G.update_icon() - else if(lasercolor == "r") - var/obj/item/gun/energy/laser/redtag/G = new /obj/item/gun/energy/laser/redtag(Tsec) - G.cell.charge = 0 - G.update_icon() - - if(prob(50)) - new /obj/item/bodypart/l_leg/robot(Tsec) - if(prob(25)) - new /obj/item/bodypart/r_leg/robot(Tsec) - if(prob(25))//50% chance for a helmet OR vest - if(prob(50)) - new /obj/item/clothing/head/helmet(Tsec) - else - if(!lasercolor) - new /obj/item/clothing/suit/armor/vest(Tsec) - if(lasercolor == "b") - new /obj/item/clothing/suit/bluetag(Tsec) - if(lasercolor == "r") - new /obj/item/clothing/suit/redtag(Tsec) - - do_sparks(3, TRUE, src) - - new /obj/effect/decal/cleanable/oil(loc) - ..() - -/mob/living/simple_animal/bot/ed209/proc/set_weapon() //used to update the projectile type and firing sound - shoot_sound = 'sound/weapons/laser.ogg' - if(emagged == 2) - if(lasercolor) - projectile = /obj/item/projectile/beam/lasertag - else - projectile = /obj/item/projectile/beam - else - if(!lasercolor) - shoot_sound = 'sound/weapons/taser.ogg' - projectile = /obj/item/projectile/energy/electrode - else if(lasercolor == "b") - projectile = /obj/item/projectile/beam/lasertag/bluetag - else if(lasercolor == "r") - projectile = /obj/item/projectile/beam/lasertag/redtag - -/mob/living/simple_animal/bot/ed209/proc/shootAt(mob/target) - if(lastfired && world.time - lastfired < shot_delay) - return - lastfired = world.time - var/turf/T = loc - var/turf/U = get_turf(target) - if(!U) - return - if(!isturf(T)) - return - - if(!projectile) - return - - var/obj/item/projectile/A = new projectile (loc) - playsound(loc, shoot_sound, 50, 1) - A.current = U - A.yo = U.y - T.y - A.xo = U.x - T.x - A.fire() - -/mob/living/simple_animal/bot/ed209/attack_alien(mob/living/carbon/alien/user) - ..() - if(!isalien(target)) - target = user - mode = BOT_HUNT - - -/mob/living/simple_animal/bot/ed209/emp_act(severity) - - if(severity==2 && prob(70)) - ..(severity-1) - else - new /obj/effect/temp_visual/emp(loc) - var/list/mob/living/carbon/targets = new - for(var/mob/living/carbon/C in view(12,src)) - if(C.stat==2) - continue - targets += C - if(targets.len) - if(prob(50)) - var/mob/toshoot = pick(targets) - if(toshoot) - targets-=toshoot - if(prob(50) && emagged < 2) - emagged = 2 - set_weapon() - shootAt(toshoot) + threatlevel = 0 + var/judgement_criteria = judgement_criteria() + for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal + if((C.stat) || (C.handcuffed)) + continue + + if((C.name == oldtarget_name) && (world.time < last_found + 100)) + continue + + threatlevel = C.assess_threat(judgement_criteria, lasercolor, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) + + if(!threatlevel) + continue + + else if(threatlevel >= 4) + target = C + oldtarget_name = C.name + speak("Level [threatlevel] infraction alert!") + playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, 0) + visible_message("[src] points at [C.name]!") + mode = BOT_HUNT + spawn(0) + handle_automated_action() // ensure bot quickly responds to a perp + break + else + continue + +/mob/living/simple_animal/bot/ed209/proc/check_for_weapons(var/obj/item/slot_item) + if(slot_item && slot_item.needs_permit) + return 1 + return 0 + +/mob/living/simple_animal/bot/ed209/explode() + walk_to(src,0) + visible_message("[src] blows apart!") + var/turf/Tsec = get_turf(src) + + var/obj/item/ed209_assembly/Sa = new /obj/item/ed209_assembly(Tsec) + Sa.build_step = 1 + Sa.add_overlay("hs_hole") + Sa.created_name = name + new /obj/item/device/assembly/prox_sensor(Tsec) + + if(!lasercolor) + var/obj/item/gun/energy/e_gun/advtaser/G = new /obj/item/gun/energy/e_gun/advtaser(Tsec) + G.cell.charge = 0 + G.update_icon() + else if(lasercolor == "b") + var/obj/item/gun/energy/laser/bluetag/G = new /obj/item/gun/energy/laser/bluetag(Tsec) + G.cell.charge = 0 + G.update_icon() + else if(lasercolor == "r") + var/obj/item/gun/energy/laser/redtag/G = new /obj/item/gun/energy/laser/redtag(Tsec) + G.cell.charge = 0 + G.update_icon() + + if(prob(50)) + new /obj/item/bodypart/l_leg/robot(Tsec) + if(prob(25)) + new /obj/item/bodypart/r_leg/robot(Tsec) + if(prob(25))//50% chance for a helmet OR vest + if(prob(50)) + new /obj/item/clothing/head/helmet(Tsec) + else + if(!lasercolor) + new /obj/item/clothing/suit/armor/vest(Tsec) + if(lasercolor == "b") + new /obj/item/clothing/suit/bluetag(Tsec) + if(lasercolor == "r") + new /obj/item/clothing/suit/redtag(Tsec) + + do_sparks(3, TRUE, src) + + new /obj/effect/decal/cleanable/oil(loc) + ..() + +/mob/living/simple_animal/bot/ed209/proc/set_weapon() //used to update the projectile type and firing sound + shoot_sound = 'sound/weapons/laser.ogg' + if(emagged == 2) + if(lasercolor) + projectile = /obj/item/projectile/beam/lasertag + else + projectile = /obj/item/projectile/beam + else + if(!lasercolor) + shoot_sound = 'sound/weapons/taser.ogg' + projectile = /obj/item/projectile/energy/electrode + else if(lasercolor == "b") + projectile = /obj/item/projectile/beam/lasertag/bluetag + else if(lasercolor == "r") + projectile = /obj/item/projectile/beam/lasertag/redtag + +/mob/living/simple_animal/bot/ed209/proc/shootAt(mob/target) + if(lastfired && world.time - lastfired < shot_delay) + return + lastfired = world.time + var/turf/T = loc + var/turf/U = get_turf(target) + if(!U) + return + if(!isturf(T)) + return + + if(!projectile) + return + + var/obj/item/projectile/A = new projectile (loc) + playsound(loc, shoot_sound, 50, 1) + A.current = U + A.yo = U.y - T.y + A.xo = U.x - T.x + A.fire() + +/mob/living/simple_animal/bot/ed209/attack_alien(mob/living/carbon/alien/user) + ..() + if(!isalien(target)) + target = user + mode = BOT_HUNT + + +/mob/living/simple_animal/bot/ed209/emp_act(severity) + + if(severity==2 && prob(70)) + ..(severity-1) + else + new /obj/effect/temp_visual/emp(loc) + var/list/mob/living/carbon/targets = new + for(var/mob/living/carbon/C in view(12,src)) + if(C.stat==DEAD) + continue + targets += C + if(targets.len) + if(prob(50)) + var/mob/toshoot = pick(targets) + if(toshoot) + targets-=toshoot + if(prob(50) && emagged < 2) + emagged = 2 + set_weapon() + shootAt(toshoot) emagged = FALSE - set_weapon() - else - shootAt(toshoot) - else if(prob(50)) - if(targets.len) - var/mob/toarrest = pick(targets) - if(toarrest) - target = toarrest - mode = BOT_HUNT - - -/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj) - if(!disabled) - var/lasertag_check = 0 - if((lasercolor == "b")) - if(istype(Proj, /obj/item/projectile/beam/lasertag/redtag)) - lasertag_check++ - else if((lasercolor == "r")) - if(istype(Proj, /obj/item/projectile/beam/lasertag/bluetag)) - lasertag_check++ - if(lasertag_check) - icon_state = "[lasercolor]ed2090" - disabled = 1 - target = null - spawn(100) - disabled = 0 - icon_state = "[lasercolor]ed2091" - return 1 - else - ..(Proj) - else - ..(Proj) - -/mob/living/simple_animal/bot/ed209/bluetag - lasercolor = "b" - -/mob/living/simple_animal/bot/ed209/redtag - lasercolor = "r" - -/mob/living/simple_animal/bot/ed209/UnarmedAttack(atom/A) - if(!on) - return - if(iscarbon(A)) - var/mob/living/carbon/C = A - if(!C.IsStun() || arrest_type) - stun_attack(A) - else if(C.canBeHandcuffed() && !C.handcuffed) - cuff(A) - else - ..() - -/mob/living/simple_animal/bot/ed209/RangedAttack(atom/A) - if(!on) - return - shootAt(A) - -/mob/living/simple_animal/bot/ed209/proc/stun_attack(mob/living/carbon/C) - playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) - icon_state = "[lasercolor]ed209-c" - spawn(2) - icon_state = "[lasercolor]ed209[on]" - var/threat = 5 - C.Knockdown(100) - C.stuttering = 5 - if(ishuman(C)) - var/mob/living/carbon/human/H = C - var/judgement_criteria = judgement_criteria() - threat = H.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) - add_logs(src,C,"stunned") - if(declare_arrests) - var/area/location = get_area(src) - speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel) - C.visible_message("[src] has stunned [C]!",\ - "[src] has stunned you!") - -/mob/living/simple_animal/bot/ed209/proc/cuff(mob/living/carbon/C) - mode = BOT_ARREST - playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) - C.visible_message("[src] is trying to put zipties on [C]!",\ - "[src] is trying to put zipties on you!") - - spawn(60) - if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. - return - if(!C.handcuffed) - C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C) - C.update_handcuffed() - back_to_idle() + set_weapon() + else + shootAt(toshoot) + else if(prob(50)) + if(targets.len) + var/mob/toarrest = pick(targets) + if(toarrest) + target = toarrest + mode = BOT_HUNT + + +/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj) + if(!disabled) + var/lasertag_check = 0 + if((lasercolor == "b")) + if(istype(Proj, /obj/item/projectile/beam/lasertag/redtag)) + lasertag_check++ + else if((lasercolor == "r")) + if(istype(Proj, /obj/item/projectile/beam/lasertag/bluetag)) + lasertag_check++ + if(lasertag_check) + icon_state = "[lasercolor]ed2090" + disabled = 1 + target = null + spawn(100) + disabled = 0 + icon_state = "[lasercolor]ed2091" + return 1 + else + ..(Proj) + else + ..(Proj) + +/mob/living/simple_animal/bot/ed209/bluetag + lasercolor = "b" + +/mob/living/simple_animal/bot/ed209/redtag + lasercolor = "r" + +/mob/living/simple_animal/bot/ed209/UnarmedAttack(atom/A) + if(!on) + return + if(iscarbon(A)) + var/mob/living/carbon/C = A + if(!C.IsStun() || arrest_type) + stun_attack(A) + else if(C.canBeHandcuffed() && !C.handcuffed) + cuff(A) + else + ..() + +/mob/living/simple_animal/bot/ed209/RangedAttack(atom/A) + if(!on) + return + shootAt(A) + +/mob/living/simple_animal/bot/ed209/proc/stun_attack(mob/living/carbon/C) + playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) + icon_state = "[lasercolor]ed209-c" + spawn(2) + icon_state = "[lasercolor]ed209[on]" + var/threat = 5 + C.Knockdown(100) + C.stuttering = 5 + if(ishuman(C)) + var/mob/living/carbon/human/H = C + var/judgement_criteria = judgement_criteria() + threat = H.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) + add_logs(src,C,"stunned") + if(declare_arrests) + var/area/location = get_area(src) + speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel) + C.visible_message("[src] has stunned [C]!",\ + "[src] has stunned you!") + +/mob/living/simple_animal/bot/ed209/proc/cuff(mob/living/carbon/C) + mode = BOT_ARREST + playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + C.visible_message("[src] is trying to put zipties on [C]!",\ + "[src] is trying to put zipties on you!") + + spawn(60) + if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. + return + if(!C.handcuffed) + C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C) + C.update_handcuffed() + back_to_idle() diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 1b7647d412..9f86737717 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -39,7 +39,7 @@ #define TILE_EMAG 7 /mob/living/simple_animal/bot/floorbot/Initialize() - ..() + . = ..() update_icon() var/datum/job/engineer/J = new/datum/job/engineer access_card.access += J.get_access() diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 7bbb36e11a..7b7334befc 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -250,7 +250,7 @@ oldpatient = user /mob/living/simple_animal/bot/medbot/process_scan(mob/living/carbon/human/H) - if(H.stat == 2) + if(H.stat == DEAD) return if((H == oldpatient) && (world.time < last_found + 200)) diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 0d4edd53f5..4007822ebb 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -47,7 +47,7 @@ var/bloodiness = 0 /mob/living/simple_animal/bot/mulebot/Initialize() - ..() + . = ..() wires = new /datum/wires/mulebot(src) var/datum/job/cargo_tech/J = new/datum/job/cargo_tech access_card.access = J.get_access() @@ -650,7 +650,7 @@ /mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H) add_logs(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])") H.visible_message("[src] drives over [H]!", \ - "[src] drives over you!") + "[src] drives over you!") playsound(loc, 'sound/effects/splat.ogg', 50, 1) var/damage = rand(5,15) diff --git a/code/modules/mob/living/simple_animal/friendly/butterfly.dm b/code/modules/mob/living/simple_animal/friendly/butterfly.dm index 07222ade05..9c16c2a924 100644 --- a/code/modules/mob/living/simple_animal/friendly/butterfly.dm +++ b/code/modules/mob/living/simple_animal/friendly/butterfly.dm @@ -28,3 +28,6 @@ . = ..() var/newcolor = rgb(rand(0, 255), rand(0, 255), rand(0, 255)) add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY) + +/mob/living/simple_animal/butterfly/bee_friendly() + return TRUE //treaty signed at the Beeneeva convention diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index c762bca6a0..49ef79a4d9 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -32,7 +32,7 @@ devourable = TRUE /mob/living/simple_animal/pet/cat/Initialize() - ..() + . = ..() verbs += /mob/living/proc/lay_down /mob/living/simple_animal/pet/cat/update_canmove() @@ -92,7 +92,7 @@ icon_living = "original" icon_dead = "original_dead" Read_Memory() - ..() + . = ..() /mob/living/simple_animal/pet/cat/Runtime/Life() if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP) @@ -113,14 +113,17 @@ ..() /mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory() - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") - S["family"] >> family - + var/json_file = file("data/npc_saves/Runtime.json") + if(!fexists(json_file)) + return + var/list/json = list() + json = json_decode(file2text(json_file)) + family = json["family"] if(isnull(family)) family = list() /mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead) - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") + var/json_file = file("data/npc_saves/Runtime.json") family = list() if(!dead) for(var/mob/living/simple_animal/pet/cat/kitten/C in children) @@ -130,7 +133,8 @@ family[C.type] += 1 else family[C.type] = 1 - WRITE_FILE(S["family"], family) + fdel(json_file) + WRITE_FILE(json_file, json_encode(family)) memory_saved = 1 /mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats() @@ -271,4 +275,4 @@ ..() if(L.a_intent == INTENT_HARM && L.reagents && !stat) L.reagents.add_reagent("nutriment", 0.4) - L.reagents.add_reagent("vitamin", 0.4) + L.reagents.add_reagent("vitamin", 0.4) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 5a6b00489e..a6db2dc0b5 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -328,33 +328,37 @@ ..() /mob/living/simple_animal/pet/dog/corgi/Ian/proc/Read_Memory() - var/savefile/S = new /savefile("data/npc_saves/Ian.sav") - S["age"] >> age - S["record_age"] >> record_age - S["saved_head"] >> saved_head - + var/json_file = file("data/npc_saves/Ian.json") + if(!fexists(json_file)) + return + var/list/json = list() + json = json_decode(file2text(json_file)) + age = json["age"] + record_age = json["record_age"] + saved_head = json["saved_head"] if(isnull(age)) age = 0 if(isnull(record_age)) record_age = 1 - if(saved_head) place_on_head(new saved_head) /mob/living/simple_animal/pet/dog/corgi/Ian/proc/Write_Memory(dead) - var/savefile/S = new /savefile("data/npc_saves/Ian.sav") + var/json_file = file("data/npc_saves/Ian.json") + var/list/file_data = list() if(!dead) - WRITE_FILE(S["age"], age + 1) + file_data["age"] = age + 1 if((age + 1) > record_age) - WRITE_FILE(S["record_age"], record_age + 1) + file_data["record_age"] = record_age + 1 if(inventory_head) - WRITE_FILE(S["saved_head"], inventory_head.type) + file_data["saved_head"] = inventory_head.type else - WRITE_FILE(S["age"], 0) - WRITE_FILE(S["saved_head"], null) + file_data["age"] = 0 + file_data["saved_head"] = null + fdel(json_file) + WRITE_FILE(json_file, json_encode(file_data)) memory_saved = 1 - /mob/living/simple_animal/pet/dog/corgi/Ian/Life() ..() @@ -565,4 +569,4 @@ emote("me", 1, "yaps happily!") else if(M && stat != DEAD) // Same check here, even though emote checks it as well (poor form to check it only in the help case) - emote("me", 1, "growls!") + emote("me", 1, "growls!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm index 7d7ec3db82..d920bf63b3 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm @@ -33,7 +33,7 @@ flavortext = null /mob/living/simple_animal/drone/syndrone/Initialize() - ..() + . = ..() internal_storage.hidden_uplink.telecrystals = 10 /mob/living/simple_animal/drone/syndrone/Login() @@ -46,7 +46,7 @@ default_storage = /obj/item/device/radio/uplink/nuclear /mob/living/simple_animal/drone/syndrone/badass/Initialize() - ..() + . = ..() internal_storage.hidden_uplink.telecrystals = 30 var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(src) W.implant(src) @@ -55,7 +55,7 @@ default_hatmask = /obj/item/clothing/head/chameleon/drone /mob/living/simple_animal/drone/snowflake/Initialize() - ..() + . = ..() desc += " This drone appears to have a complex holoprojector built on its 'head'." /obj/item/drone_shell/syndrone 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/living/simple_animal/friendly/gondola.dm b/code/modules/mob/living/simple_animal/friendly/gondola.dm new file mode 100644 index 0000000000..e5dfd71e09 --- /dev/null +++ b/code/modules/mob/living/simple_animal/friendly/gondola.dm @@ -0,0 +1,29 @@ +//Gondolas + +/mob/living/simple_animal/pet/gondola + name = "gondola" + real_name = "gondola" + desc = "Gondola is the silent walker. Having no hands he embodies the Taoist principle of wu-wei (non-action) while his smiling facial expression shows his utter and complete acceptance of the world as it is. Its hide is extremely valuable." + response_help = "pets" + response_disarm = "bops" + response_harm = "kicks" + emote_see = list("watches.", "stares off into the distance.","contemplates.") + faction = list("gondola") + turns_per_move = 10 + icon = 'icons/mob/gondolas.dmi' + icon_state = "gondola" + icon_living = "gondola" + icon_dead = "gondola_dead" + butcher_results = list(/obj/item/stack/sheet/animalhide/gondola = 1) + //Gondolas aren't affected by cold. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxbodytemp = 1500 + maxHealth = 200 + health = 200 + +/mob/living/simple_animal/pet/gondola/IsVocal() //Gondolas are the silent walker. + return FALSE + +/mob/living/simple_animal/pet/gondola/emote() + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index c95cbb116a..6983bed378 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -27,7 +27,7 @@ devourable = TRUE /mob/living/simple_animal/mouse/Initialize() - ..() + . = ..() if(!body_color) body_color = pick( list("brown","gray","white") ) icon_state = "mouse_[body_color]" diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm index fdec7e7b47..1fa0fd53b8 100644 --- a/code/modules/mob/living/simple_animal/friendly/penguin.dm +++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm @@ -24,11 +24,17 @@ butcher_results = list() gold_core_spawnable = 2 +/mob/living/simple_animal/pet/penguin/emperor/shamebrero + name = "Shamebrero penguin." + desc = "Shameful of all he surveys." + icon_state = "penguin_shamebrero" + icon_living = "penguin_shamebrero" + /mob/living/simple_animal/pet/penguin/baby speak = list("gah", "noot noot", "noot!", "noot", "squeee!", "noo!") name = "Penguin chick" real_name = "penguin" - desc = "Can't fly and can barely waddles, but the prince of all chicks." + desc = "Can't fly and barely waddles, yet the prince of all chicks." icon_state = "penguin_baby" icon_living = "penguin_baby" icon_dead = "penguin_baby_dead" diff --git a/code/modules/mob/living/simple_animal/friendly/pet.dm b/code/modules/mob/living/simple_animal/friendly/pet.dm index b2c14faaa2..3200c8b7d7 100644 --- a/code/modules/mob/living/simple_animal/friendly/pet.dm +++ b/code/modules/mob/living/simple_animal/friendly/pet.dm @@ -31,7 +31,7 @@ ..() /mob/living/simple_animal/pet/Initialize() - ..() + . = ..() if(pcollar) pcollar = new(src) regenerate_icons() diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index 1f1f937d9a..91fe96ca86 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -54,10 +54,10 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians GLOB.parasites += src setthemename(theme) - ..() + . = ..() /mob/living/simple_animal/hostile/guardian/med_hud_set_health() - if(summoner) + if(!QDELETED(summoner)) var/image/holder = hud_list[HEALTH_HUD] holder.icon_state = "hud[RoundHealth(summoner)]" @@ -659,7 +659,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians name = "holoparasite injector kit" /obj/item/storage/box/syndie_kit/guardian/Initialize() - ..() + . = ..() new /obj/item/guardiancreator/tech/choose/traitor(src) new /obj/item/paper/guides/antag/guardian(src) return diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm index 154afc47bc..9ae70745cf 100644 --- a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm +++ b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm @@ -2,7 +2,7 @@ name = "A Perfectly Generic Boss Placeholder" desc = "" robust_searching = 1 - stat_attack = 1 + stat_attack = UNCONSCIOUS status_flags = 0 a_intent = INTENT_HARM gender = NEUTER @@ -12,7 +12,7 @@ /mob/living/simple_animal/hostile/boss/Initialize() - ..() + . = ..() atb = new() atb.point_regen_delay = point_regen_delay diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index 4be957d771..68e6745c8d 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -15,7 +15,7 @@ speed = 0 maxHealth = 80 health = 80 - stat_attack = 1 + stat_attack = UNCONSCIOUS robust_searching = 1 harm_intent_damage = 10 diff --git a/code/modules/mob/living/simple_animal/hostile/headcrab.dm b/code/modules/mob/living/simple_animal/hostile/headcrab.dm index 5f2c011665..2839ac5ea0 100644 --- a/code/modules/mob/living/simple_animal/hostile/headcrab.dm +++ b/code/modules/mob/living/simple_animal/hostile/headcrab.dm @@ -15,7 +15,7 @@ attack_sound = 'sound/weapons/bite.ogg' faction = list("creature") robust_searching = 1 - stat_attack = 2 + stat_attack = DEAD obj_damage = 0 environment_smash = ENVIRONMENT_SMASH_NONE speak_emote = list("squeaks") diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 7b4fe44d37..866f9c995a 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -28,6 +28,9 @@ var/ranged_message = "fires" //Fluff text for ranged mobs var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is + var/ranged_telegraph = "prepares to fire at *TARGET*!" //A message shown when the mob prepares to fire; use *TARGET* if you want to show the target's name + var/ranged_telegraph_sound //A sound played when the mob prepares to fire + var/ranged_telegraph_time = 0 //In deciseconds, how long between the telegraph and ranged shot var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. @@ -43,8 +46,8 @@ var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled - var/stat_attack = 0 //Mobs with stat_attack to 1 will attempt to attack things that are unconscious, Mobs with stat_attack set to 2 will attempt to attack the dead. - var/stat_exclusive = 0 //Mobs with this set to 1 will exclusively attack things defined by stat_attack, stat_attack 2 means they will only attack corpses + var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to 2 will attempt to attack the dead. + var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack 2 means they will only attack corpses var/attack_same = 0 //Set us to 1 to allow us to attack our own faction, or 2, to only ever attack our own faction var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such @@ -171,7 +174,7 @@ var/mob/living/L = the_target var/faction_check = faction_check_mob(L) if(robust_searching) - if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive == 1) + if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive) return 0 if(faction_check && !attack_same || !faction_check && attack_same == 2) return 0 @@ -232,7 +235,14 @@ var/target_distance = get_dist(targets_from,target) if(ranged) //We ranged? Shoot at em if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown - OpenFire(target) + if(!ranged_telegraph_time || client) + OpenFire(target) + else + if(ranged_telegraph) + visible_message("[src] [replacetext(ranged_telegraph, "*TARGET*", "[target]")]") + if(ranged_telegraph_sound) + playsound(src, ranged_telegraph_sound, 75, FALSE) + addtimer(CALLBACK(src, .proc/OpenFire, target), ranged_telegraph_time) if(!Process_Spacemove()) //Drifting walk(src,0) return 1 diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm index 491aca3233..de8545d3f0 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm @@ -19,7 +19,7 @@ pixel_x = -16 layer = LARGE_MOB_LAYER speed = 10 - stat_attack = 1 + stat_attack = UNCONSCIOUS robust_searching = 1 var/hopping = FALSE var/hop_cooldown = 0 //Strictly for player controlled leapers diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index fd92cc17ff..ab39c35413 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -277,19 +277,24 @@ Difficulty: Very Hard WriteMemory() /obj/machinery/smartfridge/black_box/proc/WriteMemory() - var/savefile/S = new /savefile("data/npc_saves/Blackbox.sav") + var/json_file = file("data/npc_saves/Blackbox.json") stored_items = list() for(var/obj/O in (contents-component_parts)) stored_items += O.type - - WRITE_FILE(S["stored_items"], stored_items) + var/list/file_data = list() + file_data["data"] = stored_items + fdel(json_file) + WRITE_FILE(json_file, json_encode(file_data)) memory_saved = TRUE /obj/machinery/smartfridge/black_box/proc/ReadMemory() - var/savefile/S = new /savefile("data/npc_saves/Blackbox.sav") - S["stored_items"] >> stored_items - + var/json_file = file("data/npc_saves/Blackbox.json") + if(!fexists(json_file)) + return + var/list/json = list() + json = json_decode(file2text(json_file)) + stored_items = json["data"] if(isnull(stored_items)) stored_items = list() @@ -782,4 +787,4 @@ Difficulty: Very Hard #undef ACTIVATE_WEAPON #undef ACTIVATE_MAGIC -#undef MEDAL_PREFIX +#undef MEDAL_PREFIX \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index c214ee0490..02ca4a4e26 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -16,7 +16,7 @@ movement_type = FLYING robust_searching = 1 ranged_ignores_vision = TRUE - stat_attack = 2 + stat_attack = DEAD atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) minbodytemp = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm index 28bc25fd0d..b883720ace 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm @@ -14,6 +14,9 @@ ranged = 1 ranged_message = "stares" ranged_cooldown_time = 30 + ranged_telegraph = "gathers energy and stares at *TARGET*!" + ranged_telegraph_sound = 'sound/magic/magic_missile.ogg' + ranged_telegraph_time = 7 throw_message = "does nothing against the hard shell of" vision_range = 2 speed = 3 @@ -70,9 +73,11 @@ melee_damage_lower = 15 melee_damage_upper = 15 attacktext = "impales" + ranged_telegraph = "fixates on *TARGET* as its eye shines blue!" + ranged_telegraph_sound = 'sound/magic/tail_swing.ogg' + ranged_telegraph_time = 5 a_intent = INTENT_HARM speak_emote = list("telepathically cries") - attack_sound = 'sound/weapons/bladeslice.ogg' stat_attack = UNCONSCIOUS movement_type = FLYING robust_searching = 1 @@ -80,5 +85,70 @@ loot = list() butcher_results = list(/obj/item/ore/diamond = 2, /obj/item/stack/sheet/sinew = 2, /obj/item/stack/sheet/bone = 1) +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random/Initialize() + . = ..() + if(prob(1)) + if(prob(75)) + new /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/magmawing(loc) + else + new /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/icewing(loc) + return INITIALIZE_HINT_QDEL + +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/magmawing + name = "magmawing watcher" + desc = "When raised very close to lava, some watchers adapt to the extreme heat and use lava as both a weapon and wings." + icon_state = "watcher_magmawing" + icon_living = "watcher_magmawing" + icon_aggro = "watcher_magmawing" + icon_dead = "watcher_magmawing_dead" + maxHealth = 215 //Compensate for the lack of slowdown on projectiles with a bit of extra health + health = 215 + light_range = 3 + light_power = 2.5 + light_color = LIGHT_COLOR_LAVA + projectiletype = /obj/item/projectile/temp/basilisk/magmawing + crusher_loot = /obj/item/crusher_trophy/blaster_tubes/magma_wing + crusher_drop_mod = 60 + +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/icewing + name = "icewing watcher" + desc = "Very rarely, some watchers will eke out an existence far from heat sources. In the absence of warmth, they become icy and fragile but fire much stronger freezing blasts." + icon_state = "watcher_icewing" + icon_living = "watcher_icewing" + icon_aggro = "watcher_icewing" + icon_dead = "watcher_icewing_dead" + maxHealth = 170 + health = 170 + projectiletype = /obj/item/projectile/temp/basilisk/icewing + butcher_results = list(/obj/item/ore/diamond = 5, /obj/item/stack/sheet/bone = 1) //No sinew; the wings are too fragile to be usable + crusher_loot = /obj/item/crusher_trophy/watcher_wing/ice_wing + crusher_drop_mod = 30 + +/obj/item/projectile/temp/basilisk/magmawing + name = "scorching blast" + icon_state = "lava" + damage = 5 + damage_type = BURN + nodamage = FALSE + temperature = 500 //Heats you up! + +/obj/item/projectile/temp/basilisk/magmawing/on_hit(atom/target, blocked = FALSE) + . = ..() + if(.) + var/mob/living/L = target + L.adjust_fire_stacks(0.1) + L.IgniteMob() + +/obj/item/projectile/temp/basilisk/icewing + damage = 5 + damage_type = BURN + nodamage = FALSE + +/obj/item/projectile/temp/basilisk/icewing/on_hit(atom/target, blocked = FALSE) + . = ..() + if(.) + var/mob/living/L = target + L.apply_status_effect(/datum/status_effect/freon/watcher) + /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril fromtendril = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm index 1d2ab6b6f2..12388807ba 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm @@ -33,7 +33,7 @@ loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) /mob/living/simple_animal/hostile/asteroid/goliath/Life() - ..() + . = ..() handle_preattack() /mob/living/simple_animal/hostile/asteroid/goliath/proc/handle_preattack() @@ -57,8 +57,8 @@ if(!isturf(tturf)) return if(get_dist(src, target) <= 7)//Screen range check, so you can't get tentacle'd offscreen - visible_message("The [src.name] digs its tentacles under [target.name]!") - new /obj/effect/goliath_tentacle/original(tturf) + visible_message("[src] digs its tentacles under [target]!") + new /obj/effect/temp_visual/goliath_tentacle/original(tturf, src) ranged_cooldown = world.time + ranged_cooldown_time icon_state = icon_aggro pre_attack = 0 @@ -91,43 +91,101 @@ stat_attack = UNCONSCIOUS robust_searching = 1 +/mob/living/simple_animal/hostile/asteroid/goliath/beast/random/Initialize() + . = ..() + if(prob(1)) + new /mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient(loc) + return INITIALIZE_HINT_QDEL + +/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient + name = "ancient goliath" + desc = "Goliaths are biologically immortal, and rare specimens have survived for centuries. This one is clearly ancient, and its tentacles constantly churn the earth around it." + icon_state = "Goliath" + icon_living = "Goliath" + icon_aggro = "Goliath_alert" + icon_dead = "Goliath_dead" + maxHealth = 400 + health = 400 + speed = 4 + pre_attack_icon = "Goliath_preattack" + throw_message = "does nothing to the rocky hide of the" + loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) //A throwback to the asteroid days + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/goliath = 2, /obj/item/stack/sheet/bone = 2) + crusher_drop_mod = 30 + wander = FALSE + var/list/cached_tentacle_turfs + var/turf/last_location + var/tentacle_recheck_cooldown = 100 + +/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient/Life() + . = ..() + if(isturf(loc)) + if(!LAZYLEN(cached_tentacle_turfs) || loc != last_location || tentacle_recheck_cooldown <= world.time) + LAZYCLEARLIST(cached_tentacle_turfs) + last_location = loc + tentacle_recheck_cooldown = world.time + initial(tentacle_recheck_cooldown) + for(var/turf/open/T in orange(4, loc)) + LAZYADD(cached_tentacle_turfs, T) + for(var/t in cached_tentacle_turfs) + if(isopenturf(t)) + if(prob(10)) + new /obj/effect/temp_visual/goliath_tentacle(t, src) + else + cached_tentacle_turfs -= t + /mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril fromtendril = TRUE //tentacles -/obj/effect/goliath_tentacle - name = "Goliath tentacle" +/obj/effect/temp_visual/goliath_tentacle + name = "goliath tentacle" icon = 'icons/mob/lavaland/lavaland_monsters.dmi' - icon_state = "Goliath_tentacle" - var/latched = FALSE - anchored = TRUE + icon_state = "Goliath_tentacle_spawn" + layer = BELOW_MOB_LAYER + var/mob/living/spawner -/obj/effect/goliath_tentacle/Initialize() +/obj/effect/temp_visual/goliath_tentacle/Initialize(mapload, mob/living/new_spawner) . = ..() + if(locate(/obj/effect/temp_visual/goliath_tentacle) in loc) + return INITIALIZE_HINT_QDEL + if(!QDELETED(new_spawner)) + spawner = new_spawner if(ismineralturf(loc)) var/turf/closed/mineral/M = loc M.gets_drilled() - addtimer(CALLBACK(src, .proc/Trip), 10) + deltimer(timerid) + timerid = addtimer(CALLBACK(src, .proc/tripanim), 7, TIMER_STOPPABLE) -/obj/effect/goliath_tentacle/original/Initialize() +/obj/effect/temp_visual/goliath_tentacle/original/Initialize(mapload, new_spawner) . = ..() - for(var/obj/effect/goliath_tentacle/original/O in loc)//No more GG NO RE from 2+ goliaths simultaneously tentacling you - if(O != src) - qdel(src) var/list/directions = GLOB.cardinals.Copy() for(var/i in 1 to 3) var/spawndir = pick_n_take(directions) - var/turf/T = get_step(src,spawndir) + var/turf/T = get_step(src, spawndir) if(T) - new /obj/effect/goliath_tentacle(T) + new /obj/effect/temp_visual/goliath_tentacle(T, spawner) -/obj/effect/goliath_tentacle/proc/Trip() - for(var/mob/living/M in src.loc) - visible_message("The [src.name] grabs hold of [M.name]!") - M.Stun(100) - M.adjustBruteLoss(rand(10,15)) +/obj/effect/temp_visual/goliath_tentacle/proc/tripanim() + icon_state = "Goliath_tentacle_wiggle" + deltimer(timerid) + timerid = addtimer(CALLBACK(src, .proc/trip), 3, TIMER_STOPPABLE) + +/obj/effect/temp_visual/goliath_tentacle/proc/trip() + var/latched = FALSE + for(var/mob/living/L in loc) + if((!QDELETED(spawner) && spawner.faction_check_mob(L)) || L.stat == DEAD) + continue + visible_message("[src] grabs hold of [L]!") + L.Stun(100) + L.adjustBruteLoss(rand(10,15)) latched = TRUE if(!latched) - qdel(src) + retract() else - QDEL_IN(src, 50) + deltimer(timerid) + timerid = addtimer(CALLBACK(src, .proc/retract), 10, TIMER_STOPPABLE) + +/obj/effect/temp_visual/goliath_tentacle/proc/retract() + icon_state = "Goliath_tentacle_retract" + deltimer(timerid) + timerid = QDEL_IN(src, 7) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm index c13b63115d..100327ea72 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm @@ -110,8 +110,28 @@ del_on_death = 1 stat_attack = UNCONSCIOUS robust_searching = 1 + var/dwarf_mob = FALSE var/mob/living/carbon/human/stored_mob +/mob/living/simple_animal/hostile/asteroid/hivelord/legion/random/Initialize() + . = ..() + if(prob(5)) + new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf(loc) + return INITIALIZE_HINT_QDEL + +/mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf + name = "dwarf legion" + desc = "You can still see what was once a rather small human under the shifting mass of corruption." + icon_state = "dwarf_legion" + icon_living = "dwarf_legion" + icon_aggro = "dwarf_legion" + icon_dead = "dwarf_legion" + maxHealth = 60 + health = 60 + speed = 2 //faster! + crusher_drop_mod = 20 + dwarf_mob = TRUE + /mob/living/simple_animal/hostile/asteroid/hivelord/legion/death(gibbed) visible_message("The skulls on [src] wail in anger as they flee from their dying host!") var/turf/T = get_turf(src) @@ -121,6 +141,8 @@ stored_mob = null else if(fromtendril) new /obj/effect/mob_spawn/human/corpse/charredskeleton(T) + else if(dwarf_mob) + new /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf(T) else new /obj/effect/mob_spawn/human/corpse/damaged/legioninfested(T) ..(gibbed) @@ -164,7 +186,11 @@ /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/proc/infest(mob/living/carbon/human/H) visible_message("[name] burrows into the flesh of [H]!") - var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L = new(H.loc) + var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L + if(H.dna.check_mutation(DWARFISM)) //dwarf legions aren't just fluff! + L = new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf(H.loc) + else + L = new(H.loc) visible_message("[L] staggers to their feet!") H.death() H.adjustBruteLoss(1000) @@ -174,7 +200,7 @@ //Advanced Legion is slightly tougher to kill and can raise corpses (revive other legions) /mob/living/simple_animal/hostile/asteroid/hivelord/legion/advanced - stat_attack = 2 + stat_attack = DEAD maxHealth = 120 health = 120 brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/advanced @@ -184,7 +210,7 @@ icon_dead = "dwarf_legion" /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/advanced - stat_attack = 2 + stat_attack = DEAD can_infest_dead = TRUE //Legion that spawns Legions @@ -236,6 +262,10 @@ //Legion infested mobs +/obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf/equip(mob/living/carbon/human/H) + . = ..() + H.dna.add_mutation(DWARFISM) + /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/Initialize() var/type = pickweight(list("Miner" = 66, "Ashwalker" = 10, "Golem" = 10,"Clown" = 10, pick(list("Shadow", "YeOlde","Operative", "Cultist")) = 4)) switch(type) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm index 5788a6fdc6..0ea88aedcf 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm @@ -15,11 +15,12 @@ a_intent = INTENT_HARM var/crusher_loot var/throw_message = "bounces off of" - var/icon_aggro = null // for swapping to when we get aggressive var/fromtendril = FALSE see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE mob_size = MOB_SIZE_LARGE + var/icon_aggro = null + var/crusher_drop_mod = 5 /mob/living/simple_animal/hostile/asteroid/Initialize(mapload) . = ..() @@ -57,7 +58,7 @@ /mob/living/simple_animal/hostile/asteroid/death(gibbed) SSblackbox.add_details("mobs_killed_mining","[src.type]") var/datum/status_effect/crusher_damage/C = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING) - if(C && crusher_loot && prob((C.total_damage/maxHealth)) * 5) //on average, you'll need to kill 20 creatures before getting the item + if(C && crusher_loot && prob((C.total_damage/maxHealth) * crusher_drop_mod)) //on average, you'll need to kill 20 creatures before getting the item spawn_crusher_loot() ..(gibbed) diff --git a/code/modules/mob/living/simple_animal/hostile/mushroom.dm b/code/modules/mob/living/simple_animal/hostile/mushroom.dm index e51c4d3f29..ba2a0f3702 100644 --- a/code/modules/mob/living/simple_animal/hostile/mushroom.dm +++ b/code/modules/mob/living/simple_animal/hostile/mushroom.dm @@ -21,7 +21,7 @@ attack_sound = 'sound/weapons/bite.ogg' faction = list("mushroom") environment_smash = ENVIRONMENT_SMASH_NONE - stat_attack = 2 + stat_attack = DEAD mouse_opacity = MOUSE_OPACITY_ICON speed = 1 ventcrawler = VENTCRAWLER_ALWAYS diff --git a/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm b/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm index de69f623bd..0d4021400c 100644 --- a/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm +++ b/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm @@ -12,7 +12,7 @@ response_disarm = "shoves" response_harm = "hits" speed = 0 - stat_attack = 1 + stat_attack = UNCONSCIOUS robust_searching = 1 maxHealth = 100 health = 100 diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm index 96d9087459..d6d46b1a46 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm @@ -36,7 +36,7 @@ response_disarm = "shoves" response_harm = "hits" speed = 0 - stat_attack = 1 + stat_attack = UNCONSCIOUS robust_searching = 1 vision_range = 3 maxHealth = 100 diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm index f2f4cd7d0b..3a3cfbb27d 100644 --- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm +++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm @@ -24,7 +24,7 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 10 robust_searching = 1 - stat_attack = 1 + stat_attack = UNCONSCIOUS gold_core_spawnable = 1 faction = list("skeleton") see_in_dark = 8 diff --git a/code/modules/mob/living/simple_animal/hostile/stickman.dm b/code/modules/mob/living/simple_animal/hostile/stickman.dm index 23921ed1f5..fd66b8d0ae 100644 --- a/code/modules/mob/living/simple_animal/hostile/stickman.dm +++ b/code/modules/mob/living/simple_animal/hostile/stickman.dm @@ -12,7 +12,7 @@ response_disarm = "shoves" response_harm = "hits" speed = 0 - stat_attack = 1 + stat_attack = UNCONSCIOUS robust_searching = 1 environment_smash = ENVIRONMENT_SMASH_NONE maxHealth = 100 diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index ef109758e8..7cadba01d9 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -23,7 +23,7 @@ response_disarm = "shoves" response_harm = "hits" speed = 0 - stat_attack = 1 + stat_attack = UNCONSCIOUS robust_searching = 1 maxHealth = 100 health = 100 diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 66fbc8af8c..467bd13c65 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -734,7 +734,7 @@ visible_message("[src] grabs [held_item] out of [C]'s hand!", "You snag [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") return held_item - to_chat(src, "There is nothing of interest to take!") + to_chat(src, "There is nothing of interest to take!") return 0 /mob/living/simple_animal/parrot/verb/drop_held_item_player() @@ -896,7 +896,7 @@ else speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") - ..() + . = ..() /mob/living/simple_animal/parrot/Poly/Life() if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) @@ -924,22 +924,28 @@ ..(gibbed) /mob/living/simple_animal/parrot/Poly/proc/Read_Memory() - var/savefile/S = new /savefile("data/npc_saves/Poly.sav") - S["phrases"] >> speech_buffer - S["roundssurvived"] >> rounds_survived - S["longestsurvival"] >> longest_survival - S["longestdeathstreak"] >> longest_deathstreak - + var/json_file = file("data/npc_saves/Poly.json") + if(!fexists(json_file)) + return + var/list/json = list() + json = json_decode(file2text(json_file)) + speech_buffer = json["phrases"] + rounds_survived = json["roundssurvived"] + longest_survival = json["longestsurvival"] + longest_deathstreak = json["longestdeathstreak"] if(!islist(speech_buffer)) speech_buffer = list() /mob/living/simple_animal/parrot/Poly/proc/Write_Memory() - var/savefile/S = new /savefile("data/npc_saves/Poly.sav") + var/json_file = file("data/npc_saves/Poly.json") + var/list/file_data = list() if(islist(speech_buffer)) - WRITE_FILE(S["phrases"], speech_buffer) - WRITE_FILE(S["roundssurvived"], rounds_survived) - WRITE_FILE(S["longestsurvival"], longest_survival) - WRITE_FILE(S["longestdeathstreak"], longest_deathstreak) + file_data["phrases"] = speech_buffer + file_data["roundssurvived"] = rounds_survived + file_data["longestsurvival"] = longest_survival + file_data["longestdeathstreak"] = longest_deathstreak + fdel(json_file) + WRITE_FILE(json_file, json_encode(file_data)) memory_saved = 1 /mob/living/simple_animal/parrot/Poly/ghost diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index a7af1daef8..3d50f03e17 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -99,6 +99,11 @@ K = apply_status_effect(STATUS_EFFECT_KNOCKDOWN, amount, updating) return K +///////////////////////////////// FROZEN ///////////////////////////////////// + +/mob/living/proc/IsFrozen() + return has_status_effect(/datum/status_effect/freon) + ///////////////////////////////////// STUN ABSORPTION ///////////////////////////////////// /mob/living/proc/add_stun_absorption(key, duration, priority, message, self_message, examine_message) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 297f788001..f768cbe2d2 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -438,7 +438,7 @@ if (!( GLOB.abandon_allowed )) return - if ((stat != 2 || !( SSticker ))) + if ((stat != DEAD || !( SSticker ))) to_chat(usr, "You must be dead to use this!") return @@ -491,7 +491,7 @@ var/t1 = text("window=[href_list["mach_close"]]") unset_machine() src << browse(null, t1) - + if(href_list["flavor_more"]) usr << browse(text("[][]", name, replacetext(flavor_text, "\n", "
    ")), text("window=[];size=500x200", name)) onclose(usr, "[name]") @@ -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/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index b8350e2a02..457376990e 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -385,7 +385,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp return for(var/mob/dead/observer/O in GLOB.player_list) if(O.client) - to_chat(O, "[message][(enter_link) ? " [enter_link]" : ""]") + to_chat(O, "[message][(enter_link) ? " [enter_link]" : ""]") if(ghost_sound) SEND_SOUND(O, sound(ghost_sound)) if(flashwindow) diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index bd938cf598..80ab6929d5 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -146,7 +146,7 @@ var/old_eye_blind = eye_blind eye_blind = max(eye_blind, amount) if(!old_eye_blind) - if(stat == CONSCIOUS) + if(stat == CONSCIOUS || stat == SOFT_CRIT) throw_alert("blind", /obj/screen/alert/blind) overlay_fullscreen("blind", /obj/screen/fullscreen/blind) @@ -155,12 +155,12 @@ var/old_eye_blind = eye_blind eye_blind += amount if(!old_eye_blind) - if(stat == CONSCIOUS) + if(stat == CONSCIOUS || stat == SOFT_CRIT) throw_alert("blind", /obj/screen/alert/blind) overlay_fullscreen("blind", /obj/screen/fullscreen/blind) else if(eye_blind) var/blind_minimum = 0 - if(stat != CONSCIOUS || (disabilities & BLIND)) + if((stat != CONSCIOUS && stat != SOFT_CRIT) || (disabilities & BLIND)) blind_minimum = 1 eye_blind = max(eye_blind+amount, blind_minimum) if(!eye_blind) @@ -172,12 +172,12 @@ var/old_eye_blind = eye_blind eye_blind = amount if(client && !old_eye_blind) - if(stat == CONSCIOUS) + if(stat == CONSCIOUS || stat == SOFT_CRIT) throw_alert("blind", /obj/screen/alert/blind) overlay_fullscreen("blind", /obj/screen/fullscreen/blind) else if(eye_blind) var/blind_minimum = 0 - if(stat != CONSCIOUS || (disabilities & BLIND)) + if((stat != CONSCIOUS && stat != SOFT_CRIT) || (disabilities & BLIND)) blind_minimum = 1 eye_blind = blind_minimum if(!eye_blind) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index c0db06f730..95e78391bb 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -13,7 +13,6 @@ var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged var/last_world_time = "00:00" var/list/last_header_icons - var/emagged = FALSE // Whether the computer is emagged. var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too. var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops) diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm index d62907f026..c12c83aaed 100644 --- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm @@ -5,7 +5,7 @@ program_icon_state = "smmon_0" extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." requires_ntnet = TRUE - transfer_access = ACCESS_ENGINE + transfer_access = ACCESS_CONSTRUCTION network_destination = "supermatter monitoring system" size = 5 tgui_id = "ntos_supermatter_monitor" diff --git a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm b/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm index 76cacc00ef..270e1f106f 100644 --- a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm +++ b/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm @@ -12,90 +12,72 @@ It is possible to destroy the net by the occupant or someone else. density = TRUE//Can't pass through. opacity = 0//Can see through. mouse_opacity = MOUSE_OPACITY_ICON//So you can hit it with stuff. - anchored = TRUE//Can't drag/grab the trapped mob. + anchored = TRUE//Can't drag/grab the net. layer = ABOVE_ALL_MOB_LAYER max_integrity = 25 //How much health it has. - var/mob/living/affecting = null//Who it is currently affecting, if anyone. - var/mob/living/master = null//Who shot web. Will let this person know if the net was successful or failed. - + can_buckle = 1 + buckle_lying = 0 + buckle_prevents_pull = TRUE + var/mob/living/carbon/affecting//Who it is currently affecting, if anyone. + var/mob/living/carbon/master//Who shot web. Will let this person know if the net was successful or failed. + var/check = 15//30 seconds before teleportation. Could be extended I guess. + var/success = FALSE /obj/structure/energy_net/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0) switch(damage_type) if(BRUTE) - playsound(src.loc, 'sound/weapons/slash.ogg', 80, 1) + playsound(src, 'sound/weapons/slash.ogg', 80, 1) if(BURN) - playsound(src.loc, 'sound/weapons/slash.ogg', 80, 1) + playsound(src, 'sound/weapons/slash.ogg', 80, 1) /obj/structure/energy_net/Destroy() - if(affecting) - var/mob/living/carbon/M = affecting - M.anchored = FALSE - for(var/mob/O in viewers(src, 3)) - O.show_message("[M.name] was recovered from the energy net!", 1, "You hear a grunt.", 2) - if(master)//As long as they still exist. + if(!success) + if(!QDELETED(affecting)) + affecting.visible_message("[affecting.name] was recovered from the energy net!", "You were recovered from the energy net!", "You hear a grunt.") + if(!QDELETED(master))//As long as they still exist. to_chat(master, "ERROR: unable to initiate transport protocol. Procedure terminated.") return ..() -/obj/structure/energy_net/process(mob/living/carbon/M) - var/check = 30//30 seconds before teleportation. Could be extended I guess. - var/mob_name = affecting.name//Since they will report as null if terminated before teleport. - //The person can still try and attack the net when inside. - - M.notransform = 1 //No moving for you! - - while(!isnull(M)&&!isnull(src)&&check>0)//While M and net exist, and 30 seconds have not passed. - check-- - sleep(10) - - if(isnull(M)||M.loc!=loc)//If mob is gone or not at the location. - if(!isnull(master))//As long as they still exist. - to_chat(master, "ERROR: unable to locate \the [mob_name]. Procedure terminated.") +/obj/structure/energy_net/process() + if(QDELETED(affecting)||affecting.loc!=loc) qdel(src)//Get rid of the net. - M.notransform = 0 return - if(!isnull(src))//As long as both net and person exist. - //No need to check for countdown here since while() broke, it's implicit that it finished. + if(check>0) + check-- + return - density = FALSE//Make the net pass-through. - invisibility = INVISIBILITY_ABSTRACT//Make the net invisible so all the animations can play out. - resistance_flags |= INDESTRUCTIBLE //Make the net invincible so that an explosion/something else won't kill it while, spawn() is running. - for(var/obj/item/W in M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(W == H.w_uniform) - continue//So all they're left with are shoes and uniform. - if(W == H.shoes) - continue - M.dropItemToGround(W) + success = TRUE + qdel(src) + if(ishuman(affecting)) + var/mob/living/carbon/human/H = affecting + for(var/obj/item/W in H) + if(W == H.w_uniform) + continue//So all they're left with are shoes and uniform. + if(W == H.shoes) + continue + H.dropItemToGround(W) - playsound(M.loc, 'sound/effects/sparks4.ogg', 50, 1) - new /obj/effect/temp_visual/dir_setting/ninja/phase/out(get_turf(M), M.dir) - - visible_message("[M] suddenly vanishes!") - M.forceMove(pick(GLOB.holdingfacility)) //Throw mob in to the holding facility. - to_chat(M, "You appear in a strange place!") - - if(!isnull(master))//As long as they still exist. - to_chat(master, "SUCCESS: transport procedure of \the [affecting] complete.") - M.notransform = 0 - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, M.loc) - spark_system.start() - playsound(M.loc, 'sound/effects/phasein.ogg', 25, 1) - playsound(M.loc, 'sound/effects/sparks2.ogg', 50, 1) - new /obj/effect/temp_visual/dir_setting/ninja/phase(get_turf(M), M.dir) - qdel(src) - - else//And they are free. - to_chat(M, "You are free of the net!") - M.notransform = 0 - return + playsound(affecting, 'sound/effects/sparks4.ogg', 50, 1) + new /obj/effect/temp_visual/dir_setting/ninja/phase/out(affecting.drop_location(), affecting.dir) + visible_message("[affecting] suddenly vanishes!") + affecting.forceMove(pick(GLOB.holdingfacility)) //Throw mob in to the holding facility. + to_chat(affecting, "You appear in a strange place!") + if(!QDELETED(master))//As long as they still exist. + to_chat(master, "SUCCESS: transport procedure of [affecting] complete.") + do_sparks(5, FALSE, affecting) + playsound(affecting, 'sound/effects/phasein.ogg', 25, 1) + playsound(affecting, 'sound/effects/sparks2.ogg', 50, 1) + new /obj/effect/temp_visual/dir_setting/ninja/phase(affecting.drop_location(), affecting.dir) /obj/structure/energy_net/attack_paw(mob/user) return attack_hand() +/obj/structure/energy_net/user_buckle_mob(mob/living/M, mob/living/user) + return//We only want our target to be buckled +/obj/structure/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/living/user) + return//The net must be destroyed to free the target diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm index 71f8166339..6c0dddf88b 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm @@ -2,20 +2,19 @@ //Movement impairing would indicate drugs and the like. /obj/item/clothing/suit/space/space_ninja/proc/ninjaboost() - if(!ninjacost(0,N_ADRENALINE))//Have to make sure stat is not counted for this ability. + if(!ninjacost(0,N_ADRENALINE)) var/mob/living/carbon/human/H = affecting H.SetUnconscious(0) H.SetStun(0) H.SetKnockdown(0) - - spawn(30)//Slight delay so the enemy does not immedietly know the ability was used. Due to lag, this often came before waking up. - H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!")) - spawn(70) - if(reagents.total_volume) - var/fraction = min(a_transfer/reagents.total_volume, 1) - reagents.reaction(H, INJECT, fraction) - reagents.trans_id_to(H, "radium", a_transfer) - to_chat(H, "You are beginning to feel the after-effect of the injection.") + H.stuttering = 0 + H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!")) a_boost-- to_chat(H, "There are [a_boost] adrenaline boosts remaining.") s_coold = 3 + addtimer(CALLBACK(src, .proc/ninjaboost_after), 70) + +/obj/item/clothing/suit/space/space_ninja/proc/ninjaboost_after() + var/mob/living/carbon/human/H = affecting + H.reagents.add_reagent("radium", a_transfer) + to_chat(H, "You are beginning to feel the after-effect of the injection.") diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm index 1d728084b3..12fea51815 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm @@ -3,10 +3,6 @@ //Cost function for suit Procs/Verbs/Abilities /obj/item/clothing/suit/space/space_ninja/proc/ninjacost(cost = 0, specificCheck = 0) var/mob/living/carbon/human/H = affecting - if((H.stat || H.incorporeal_move) && (specificCheck != N_ADRENALINE))//Will not return if user is using an adrenaline booster since you can use them when stat==1. - to_chat(H, "You must be conscious and solid to do this.") - return 1 - var/actualCost = cost*10 if(cost && cell.charge < actualCost) to_chat(H, "Not enough energy.") diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm index 18682e87eb..81d731b110 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm @@ -1,24 +1,33 @@ //Allows the ninja to kidnap people -/obj/item/clothing/suit/space/space_ninja/proc/ninjanet(mob/living/carbon/C in oview())//Only living carbon mobs. +/obj/item/clothing/suit/space/space_ninja/proc/ninjanet() + var/mob/living/carbon/human/H = affecting + var/mob/living/carbon/C = input("Select who to capture:","Capture who?",null) as null|mob in oview(H) - if(!ninjacost(200,N_STEALTH_CANCEL) && iscarbon(C)) - var/mob/living/carbon/human/H = affecting - if(C.client)//Monkeys without a client can still step_to() and bypass the net. Also, netting inactive people is lame. - if(!locate(/obj/structure/energy_net) in C.loc)//Check if they are already being affected by an energy net. - for(var/turf/T in getline(H.loc, C.loc)) - if(T.density)//Don't want them shooting nets through walls. It's kind of cheesy. - to_chat(H, "You may not use an energy net through solid obstacles!") - return - H.Beam(C,"n_beam",time=15) - H.say("Get over here!") - var/obj/structure/energy_net/E = new /obj/structure/energy_net(C.loc) - H.visible_message("[H] caught [C] with an energy net!","You caught [C] with an energy net!") - E.affecting = C - E.master = H - spawn(0)//Parallel processing. - E.process(C) - else - to_chat(H, "[C.p_they(TRUE)] are already trapped inside an energy net!") - else - to_chat(H, "[C.p_they(TRUE)] will bring no honor to your Clan!") + if(QDELETED(C)||!(C in oview(H))) + return 0 + + if(!C.client)//Monkeys without a client can still step_to() and bypass the net. Also, netting inactive people is lame. + to_chat(H, "[C.p_they(TRUE)] will bring no honor to your Clan!") + return + if(locate(/obj/structure/energy_net) in get_turf(C))//Check if they are already being affected by an energy net. + to_chat(H, "[C.p_they(TRUE)] are already trapped inside an energy net!") + return + for(var/turf/T in getline(get_turf(H), get_turf(C))) + if(T.density)//Don't want them shooting nets through walls. It's kind of cheesy. + to_chat(H, "You may not use an energy net through solid obstacles!") + return + if(!ninjacost(200,N_STEALTH_CANCEL)) + H.Beam(C,"n_beam",time=15) + H.say("Get over here!") + var/obj/structure/energy_net/E = new /obj/structure/energy_net(C.drop_location()) + E.affecting = C + E.master = H + H.visible_message("[H] caught [C] with an energy net!","You caught [C] with an energy net!") + + if(C.buckled) + C.buckled.unbuckle_mob(affecting,TRUE) + E.buckle_mob(C, TRUE) //No moving for you! + //The person can still try and attack the net when inside. + + START_PROCESSING(SSobj, E) diff --git a/code/modules/ninja/suit/suit.dm b/code/modules/ninja/suit/suit.dm index 7cf355fde6..40d18951e6 100644 --- a/code/modules/ninja/suit/suit.dm +++ b/code/modules/ninja/suit/suit.dm @@ -17,7 +17,7 @@ Contents: icon_state = "s-ninja" item_state = "s-ninja_suit" allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals, /obj/item/stock_parts/cell) - slowdown = 0 + slowdown = 1 resistance_flags = LAVA_PROOF | ACID_PROOF armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30, fire = 100, acid = 100) strip_delay = 12 @@ -28,7 +28,6 @@ Contents: var/mob/living/carbon/human/affecting = null var/obj/item/stock_parts/cell/cell var/datum/effect_system/spark_spread/spark_system - var/list/reagent_list = list("omnizine","salbutamol","spaceacillin","charcoal","nutriment","radium","potass_iodide")//The reagents ids which are added to the suit at New(). var/list/stored_research = list()//For stealing station research. var/obj/item/disk/tech_disk/t_disk//To copy design onto disk. var/obj/item/dash/energy_katana/energyKatana //For teleporting the katana back to the ninja (It's an ability) @@ -44,11 +43,10 @@ Contents: var/s_cost = 5//Base energy cost each ntick. var/s_acost = 25//Additional cost for additional powers active. var/s_delay = 40//How fast the suit does certain things, lower is faster. Can be overridden in specific procs. Also determines adverse probability. - var/a_transfer = 20//How much reagent is transferred when injecting. - var/r_maxamount = 80//How much reagent in total there is. + var/a_transfer = 20//How much radium is used per adrenaline boost. + var/a_maxamount = 7//Maximum number of adrenaline boosts. //Support function variables. - var/spideros = 0//Mode of SpiderOS. This can change so I won't bother listing the modes here (0 is hub). Check ninja_equipment.dm for how it all works. var/s_active = 0//Stealth off. var/s_busy = FALSE//Is the suit busy with a process? Like AI hacking. Used for safety functions. @@ -73,28 +71,12 @@ Contents: for(var/T in subtypesof(/datum/tech))//Store up on research. stored_research += new T(src) - //Reagent Init - var/reagent_amount - for(var/reagent_id in reagent_list) - reagent_amount += reagent_id == "radium" ? r_maxamount+(a_boost*a_transfer) : r_maxamount - reagents = new(reagent_amount) - reagents.my_atom = src - for(var/reagent_id in reagent_list) - reagent_id == "radium" ? reagents.add_reagent(reagent_id, r_maxamount+(a_boost*a_transfer)) : reagents.add_reagent(reagent_id, r_maxamount)//It will take into account radium used for adrenaline boosting. - //Cell Init cell = new/obj/item/stock_parts/cell/high cell.charge = 9000 cell.name = "black power cell" cell.icon_state = "bscell" - -/obj/item/clothing/suit/space/space_ninja/Destroy() - if(affecting) - affecting << browse(null, "window=hack spideros") - return ..() - - //Simply deletes all the attachments and self, killing all related procs. /obj/item/clothing/suit/space/space_ninja/proc/terminate() qdel(n_hood) @@ -117,7 +99,7 @@ Contents: if(!istype(H)) return 0 if(!is_ninja(H)) - to_chat(H, "\red fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAU†HORIZED USÈ DETÈC†††eD\nCoMMÈNCING SUB-R0U†IN3 13...\nTÈRMInATING U-U-USÈR...") + to_chat(H, "fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAU?HORIZED USÈ DETÈC???eD\nCoMMÈNCING SUB-R0U?IN3 13...\nTÈRMInATING U-U-USÈR...") H.gib() return FALSE if(!istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja)) @@ -131,7 +113,7 @@ Contents: return FALSE affecting = H flags_1 |= NODROP_1 //colons make me go all |= - slowdown = FALSE + slowdown = 0 n_hood = H.head n_hood.flags_1 |= NODROP_1 n_shoes = H.shoes @@ -170,7 +152,7 @@ Contents: ..() if(s_initialized) if(user == affecting) - to_chat(user, "All systems operational. Current energy capacity: [cell.charge].") + to_chat(user, "All systems operational. Current energy capacity: [DisplayPower(cell.charge)].") to_chat(user, "The CLOAK-tech device is [s_active?"active":"inactive"].") to_chat(user, "There are [s_bombs] smoke bomb\s remaining.") to_chat(user, "There are [a_boost] adrenaline booster\s remaining.") diff --git a/code/modules/ninja/suit/suit_attackby.dm b/code/modules/ninja/suit/suit_attackby.dm index 23f2ac4b9e..ce75fd6ed3 100644 --- a/code/modules/ninja/suit/suit_attackby.dm +++ b/code/modules/ninja/suit/suit_attackby.dm @@ -1,70 +1,60 @@ /obj/item/clothing/suit/space/space_ninja/attackby(obj/item/I, mob/U, params) - if(U==affecting)//Safety, in case you try doing this without wearing the suit/being the person with the suit. + if(U!=affecting)//Safety, in case you try doing this without wearing the suit/being the person with the suit. + return ..() - if(istype(I, /obj/item/reagent_containers/glass))//If it's a glass beaker. - var/total_reagent_transfer//Keep track of this stuff. - for(var/reagent_id in reagent_list) - var/datum/reagent/R = I.reagents.has_reagent(reagent_id)//Mostly to pull up the name of the reagent after calculating. Also easier to use than writing long proc paths. - if(R&&reagents.get_reagent_amount(reagent_id)=a_transfer)//Radium is always special. - //Here we determine how much reagent will actually transfer if there is enough to transfer or there is a need of transfer. Minimum of max amount available (using a_transfer) or amount needed. - var/amount_to_transfer = min( (r_maxamount+(reagent_id == "radium"?(a_boost*a_transfer):0)-reagents.get_reagent_amount(reagent_id)) ,(round(R.volume/a_transfer))*a_transfer)//In the end here, we round the amount available, then multiply it again. - R.volume -= amount_to_transfer//Remove from reagent volume. Don't want to delete the reagent now since we need to perserve the name. - reagents.add_reagent(reagent_id, amount_to_transfer)//Add to suit. Reactions are not important. - total_reagent_transfer += amount_to_transfer//Add to total reagent trans. - to_chat(U, "Added [amount_to_transfer] units of [R.name].") - I.reagents.update_total()//Now we manually update the total to make sure everything is properly shoved under the rug. - - to_chat(U, "Replenished a total of [total_reagent_transfer ? total_reagent_transfer : "zero"] chemical units.") + if(istype(I, /obj/item/reagent_containers/glass))//If it's a glass beaker. + if(I.reagents.has_reagent("radium", a_transfer) && a_boost < a_maxamount) + I.reagents.remove_reagent("radium", a_transfer) + a_boost++; + to_chat(U, "There are now [a_boost] adrenaline boosts remaining.") return - else if(istype(I, /obj/item/stock_parts/cell)) - var/obj/item/stock_parts/cell/CELL = I - if(CELL.maxcharge > cell.maxcharge && n_gloves && n_gloves.candrain) - to_chat(U, "Higher maximum capacity detected.\nUpgrading...") - if (n_gloves && n_gloves.candrain && do_after(U,s_delay, target = src)) - U.drop_item() - CELL.loc = src - CELL.charge = min(CELL.charge+cell.charge, CELL.maxcharge) - var/obj/item/stock_parts/cell/old_cell = cell - old_cell.charge = 0 - U.put_in_hands(old_cell) - old_cell.add_fingerprint(U) - old_cell.corrupt() - old_cell.update_icon() - cell = CELL - to_chat(U, "Upgrade complete. Maximum capacity: [round(cell.maxcharge/100)]%") - else - to_chat(U, "Procedure interrupted. Protocol terminated.") - return - - else if(istype(I, /obj/item/disk/tech_disk))//If it's a data disk, we want to copy the research on to the suit. - var/obj/item/disk/tech_disk/TD = I - var/has_research = 0 - for(var/V in TD.tech_stored) - if(V) - has_research = 1 - break - if(has_research)//If it has something on it. - to_chat(U, "Research information detected, processing...") - if(do_after(U,s_delay, target = src)) - for(var/V1 in 1 to TD.max_tech_stored) - var/datum/tech/new_data = TD.tech_stored[V1] - TD.tech_stored[V1] = null - if(!new_data) - continue - for(var/V2 in stored_research) - var/datum/tech/current_data = V2 - if(current_data.id == new_data.id) - current_data.level = max(current_data.level, new_data.level) - break - to_chat(U, "Data analyzed and updated. Disk erased.") - else - to_chat(U, "ERROR: Procedure interrupted. Process terminated.") + else if(istype(I, /obj/item/stock_parts/cell)) + var/obj/item/stock_parts/cell/CELL = I + if(CELL.maxcharge > cell.maxcharge && n_gloves && n_gloves.candrain) + to_chat(U, "Higher maximum capacity detected.\nUpgrading...") + if (n_gloves && n_gloves.candrain && do_after(U,s_delay, target = src)) + U.transferItemToLoc(CELL, src) + CELL.charge = min(CELL.charge+cell.charge, CELL.maxcharge) + var/obj/item/stock_parts/cell/old_cell = cell + old_cell.charge = 0 + U.put_in_hands(old_cell) + old_cell.add_fingerprint(U) + old_cell.corrupt() + old_cell.update_icon() + cell = CELL + to_chat(U, "Upgrade complete. Maximum capacity: [round(cell.maxcharge/100)]%") else - I.loc = src - t_disk = I - to_chat(U, "You slot \the [I] into \the [src].") - return - ..() \ No newline at end of file + to_chat(U, "Procedure interrupted. Protocol terminated.") + return + + else if(istype(I, /obj/item/disk/tech_disk))//If it's a data disk, we want to copy the research on to the suit. + var/obj/item/disk/tech_disk/TD = I + var/has_research = FALSE + for(var/V in TD.tech_stored) + if(V) + has_research = TRUE + break + if(has_research)//If it has something on it. + to_chat(U, "Research information detected, processing...") + if(do_after(U,s_delay, target = src)) + for(var/V1 in 1 to TD.max_tech_stored) + var/datum/tech/new_data = TD.tech_stored[V1] + TD.tech_stored[V1] = null + if(!new_data) + continue + for(var/V2 in stored_research) + var/datum/tech/current_data = V2 + if(current_data.id == new_data.id) + current_data.level = max(current_data.level, new_data.level) + break + to_chat(U, "Data analyzed and updated. Disk erased.") + + else + to_chat(U, "ERROR: Procedure interrupted. Process terminated.") + else + to_chat(U, "No research information detected.") + return + return ..() diff --git a/code/modules/ninja/suit/suit_initialisation.dm b/code/modules/ninja/suit/suit_initialisation.dm index 5fb3fe8a62..0084ff1995 100644 --- a/code/modules/ninja/suit/suit_initialisation.dm +++ b/code/modules/ninja/suit/suit_initialisation.dm @@ -11,9 +11,6 @@ /obj/item/clothing/suit/space/space_ninja/proc/ninitialize(delay = s_delay, mob/living/carbon/human/U = loc) if(!U.mind) return //Not sure how this could happen. - if(!is_ninja(U)) - to_chat(U, "You do not understand how this suit functions. Where the heck did it even come from?") - return s_busy = TRUE to_chat(U, "Now initializing...") addtimer(CALLBACK(src, .proc/ninitialize_two, delay, U), delay) @@ -30,9 +27,10 @@ addtimer(CALLBACK(src, .proc/ninitialize_four, delay, U), delay) /obj/item/clothing/suit/space/space_ninja/proc/ninitialize_four(delay, mob/living/carbon/human/U) - if(U.stat==2||U.health<=0) - to_chat(U, "FĆAL �Rr�R: 344--93#�&&21 BR��N |/|/aV� PATT$RN RED\nA-A-aB�rT�NG...") + if(U.stat == DEAD|| U.health <= 0) + to_chat(U, "FÄ?AL �Rr�R: 344--93#�&&21 BR��N |/|/aV� PATT$RN RED\nA-A-aB�rT�NG...") unlock_suit() + s_busy = FALSE return lockIcons(U)//Check for icons. U.regenerate_icons() @@ -44,13 +42,12 @@ addtimer(CALLBACK(src, .proc/ninitialize_six, delay, U), delay) /obj/item/clothing/suit/space/space_ninja/proc/ninitialize_six(delay, mob/living/carbon/human/U) - to_chat(U, "Primary system status: ONLINE.\nBackup system status: ONLINE.\nCurrent energy capacity: [cell.charge].") + to_chat(U, "Primary system status: ONLINE.\nBackup system status: ONLINE.\nCurrent energy capacity: [DisplayPower(cell.charge)].") addtimer(CALLBACK(src, .proc/ninitialize_seven, delay, U), delay) /obj/item/clothing/suit/space/space_ninja/proc/ninitialize_seven(delay, mob/living/carbon/human/U) to_chat(U, "All systems operational. Welcome to SpiderOS, [U.real_name].") - grant_ninja_verbs() - grant_equip_verbs() + s_initialized = TRUE ntick() s_busy = FALSE @@ -66,7 +63,6 @@ /obj/item/clothing/suit/space/space_ninja/proc/deinitialize_two(delay, mob/living/carbon/human/U) to_chat(U, "Now de-initializing...") - spideros = 0//Spideros resets. addtimer(CALLBACK(src, .proc/deinitialize_three, delay, U), delay) /obj/item/clothing/suit/space/space_ninja/proc/deinitialize_three(delay, mob/living/carbon/human/U) @@ -92,6 +88,7 @@ /obj/item/clothing/suit/space/space_ninja/proc/deinitialize_eight(delay, mob/living/carbon/human/U) to_chat(U, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.") - remove_equip_verbs() + unlock_suit() U.regenerate_icons() + s_initialized = FALSE s_busy = FALSE diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index cb16d0838d..e3d6154606 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -97,7 +97,7 @@ H.update_damage_hud() return var/n_name = stripped_input(usr, "What would you like to label the paper?", "Paper Labelling", null, MAX_NAME_LEN) - if((loc == usr && usr.stat == 0)) + if((loc == usr && usr.stat == CONSCIOUS)) name = "paper[(n_name ? text("- '[n_name]'") : null)]" add_fingerprint(usr) diff --git a/code/modules/paperwork/paper_cutter.dm b/code/modules/paperwork/paper_cutter.dm index 541a49706a..2c704b8568 100644 --- a/code/modules/paperwork/paper_cutter.dm +++ b/code/modules/paperwork/paper_cutter.dm @@ -124,3 +124,5 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "cutterblade" item_state = "knife" + lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi' diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index f6b39514b2..1cb8295df3 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -17,6 +17,8 @@ desc = "A camera film cartridge. Insert it into a camera to reload it." icon_state = "film" item_state = "electropack" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' w_class = WEIGHT_CLASS_TINY resistance_flags = FLAMMABLE @@ -45,7 +47,7 @@ if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) txt = copytext(txt, 1, 128) - if(loc == user && user.stat == 0) + if(loc == user && user.stat == CONSCIOUS) scribble = txt ..() @@ -76,7 +78,7 @@ var/n_name = copytext(sanitize(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text), 1, MAX_NAME_LEN) //loc.loc check is for making possible renaming photos in clipboards - if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == 0 && usr.canmove && !usr.restrained()) + if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained()) name = "photo[(n_name ? text("- '[n_name]'") : null)]" add_fingerprint(usr) 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/power/cell.dm b/code/modules/power/cell.dm index 8dd013facc..34635084dd 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -1,11 +1,11 @@ /obj/item/stock_parts/cell name = "power cell" - desc = "A rechargeable electrochemical power cell." + desc = "A rechargeable electrochemical power cell." icon = 'icons/obj/power.dmi' icon_state = "cell" item_state = "cell" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' origin_tech = "powerstorage=1" force = 5 throwforce = 5 @@ -156,7 +156,7 @@ /obj/item/stock_parts/cell/blob_act(obj/structure/blob/B) - ex_act(EXPLODE_DEVASTATE) + ex_act(EXPLODE_DEVASTATE) /obj/item/stock_parts/cell/proc/get_electrocute_damage() if(charge >= 1000) @@ -166,7 +166,7 @@ /* Cell variants*/ /obj/item/stock_parts/cell/crap - name = "\improper Nanotrasen brand rechargeable AA battery" + name = "\improper Nanotrasen brand rechargeable AA battery" desc = "You can't top the plasma top." //TOTALLY TRADEMARK INFRINGEMENT maxcharge = 500 materials = list(MAT_GLASS=40) @@ -176,8 +176,21 @@ ..() charge = 0 +/obj/item/stock_parts/cell/upgraded + name = "high-capacity power cell" + desc = "A power cell with a slightly higher capacity than normal!" + maxcharge = 2500 + materials = list(MAT_GLASS=50) + rating = 2 + chargerate = 1000 + +/obj/item/stock_parts/cell/upgraded/plus + name = "upgraded power cell+" + desc = "A power cell with an even higher capacity than the base model!" + maxcharge = 5000 + /obj/item/stock_parts/cell/secborg - name = "security borg rechargeable D battery" + name = "security borg rechargeable D battery" origin_tech = null maxcharge = 600 //600 max charge / 100 charge per shot = six shots materials = list(MAT_GLASS=40) @@ -249,7 +262,7 @@ /obj/item/stock_parts/cell/bluespace name = "bluespace power cell" - desc = "A rechargeable transdimensional power cell." + desc = "A rechargeable transdimensional power cell." origin_tech = "powerstorage=5;bluespace=4;materials=4;engineering=4" icon_state = "bscell" maxcharge = 40000 @@ -289,7 +302,7 @@ /obj/item/stock_parts/cell/potato name = "potato battery" - desc = "A rechargeable starch based power cell." + desc = "A rechargeable starch based power cell." icon = 'icons/obj/hydroponics/harvest.dmi' icon_state = "potato" origin_tech = "powerstorage=1;biotech=1" diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 8654953994..096c050fcc 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -588,6 +588,8 @@ icon_state = "lbulb" base_state = "lbulb" item_state = "contvapour" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' brightness = 4 /obj/item/light/throw_impact(atom/hit_atom) diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index ca25ce708f..f819b35d75 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -57,8 +57,7 @@ /obj/machinery/field/containment/Crossed(mob/mover) if(isliving(mover)) shock(mover) - -/obj/machinery/field/containment/Crossed(obj/mover) + if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) bump_field(mover) @@ -79,21 +78,25 @@ qdel(src) - // Abstract Field Class // Used for overriding certain procs /obj/machinery/field var/hasShocked = FALSE //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. -/obj/machinery/field/CanPass(atom/movable/mover, turf/target) +/obj/machinery/field/CollidedWith(atom/movable/mover) if(hasShocked) - return FALSE - if(isliving(mover)) // Don't let mobs through + return + if(isliving(mover)) shock(mover) - return FALSE + return if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) bump_field(mover) + return + + +/obj/machinery/field/CanPass(atom/movable/mover, turf/target) + if(hasShocked || isliving(mover) || istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) return FALSE return ..() diff --git a/code/modules/projectiles/boxes_magazines/external_mag.dm b/code/modules/projectiles/boxes_magazines/external_mag.dm index 3bfb90224d..525466be28 100644 --- a/code/modules/projectiles/boxes_magazines/external_mag.dm +++ b/code/modules/projectiles/boxes_magazines/external_mag.dm @@ -324,6 +324,7 @@ /obj/item/ammo_box/magazine/toy/smg name = "foam force SMG magazine" icon_state = "smg9mm-42" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart max_ammo = 20 /obj/item/ammo_box/magazine/toy/smg/update_icon() @@ -348,23 +349,29 @@ /obj/item/ammo_box/magazine/toy/smgm45 name = "donksoft SMG magazine" caliber = "foam_force" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + ammo_type = /obj/item/ammo_casing/caseless/foam_dart max_ammo = 20 /obj/item/ammo_box/magazine/toy/smgm45/update_icon() ..() icon_state = "c20r45-[round(ammo_count(),2)]" +/obj/item/ammo_box/magazine/toy/smgm45/riot + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + /obj/item/ammo_box/magazine/toy/m762 name = "donksoft box magazine" caliber = "foam_force" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + ammo_type = /obj/item/ammo_casing/caseless/foam_dart max_ammo = 50 /obj/item/ammo_box/magazine/toy/m762/update_icon() ..() icon_state = "a762-[round(ammo_count(),10)]" +/obj/item/ammo_box/magazine/toy/m762/riot + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm index 52bff5129b..b73c1b7242 100644 --- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm +++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm @@ -7,6 +7,8 @@ icon = 'icons/obj/guns/minigun.dmi' icon_state = "holstered" item_state = "backpack" + lefthand_file = 'icons/mob/inhands/equipment/backpack_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi' slot_flags = SLOT_BACK w_class = WEIGHT_CLASS_HUGE var/obj/item/gun/ballistic/minigun/gun diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index 16353f4255..c460a24fcf 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -54,7 +54,7 @@ name = "stechkin APS pistol" desc = "The original russian version of a widely used Syndicate sidearm. Uses 9mm ammo." icon_state = "aps" - w_class = WEIGHT_CLASS_NORMAL + w_class = WEIGHT_CLASS_SMALL origin_tech = "combat=3;materials=2;syndicate=3" mag_type = /obj/item/ammo_box/magazine/pistolm9mm can_suppress = 0 diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm index 135540436a..fc1ad963e4 100644 --- a/code/modules/projectiles/guns/ballistic/toy.dm +++ b/code/modules/projectiles/guns/ballistic/toy.dm @@ -89,6 +89,12 @@ /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted pin = /obj/item/device/firing_pin +/obj/item/gun/ballistic/automatic/smgm45/toy/riot + mag_type = /obj/item/ammo_box/magazine/toy/smgm45/riot + +/obj/item/gun/ballistic/automatic/c20r/toy/riot/unrestricted + pin = /obj/item/device/firing_pin + /obj/item/gun/ballistic/automatic/l6_saw/toy name = "donksoft LMG" desc = "A heavily modified toy light machine gun, designated 'L6 SAW'. Ages 8 and up." @@ -99,4 +105,10 @@ casing_ejector = 0 /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted - pin = /obj/item/device/firing_pin \ No newline at end of file + pin = /obj/item/device/firing_pin + +/obj/item/gun/ballistic/automatic/l6_saw/toy/riot + mag_type = /obj/item/ammo_box/magazine/toy/m762/riot + +/obj/item/gun/ballistic/automatic/l6_saw/toy/riot/unrestricted + pin = /obj/item/device/firing_pin diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 74987d2052..0eb6a7137d 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -130,7 +130,7 @@ if(!suppressed) playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) else - to_chat(loc, "[src] silently charges up.") + to_chat(loc, "[src] silently charges up.") update_icon() overheat = FALSE diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index 8e97a3d7a9..21bc12bb1f 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -4,6 +4,8 @@ icon = 'icons/obj/guns/magic.dmi' icon_state = "staffofnothing" item_state = "staff" + lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' fire_sound = 'sound/weapons/emitter.ogg' flags_1 = CONDUCT_1 w_class = WEIGHT_CLASS_HUGE @@ -26,7 +28,7 @@ if(no_den_usage) var/area/A = get_area(user) if(istype(A, /area/wizard_station)) - to_chat(user, "You know better than to violate the security of The Den, best wait until you leave to use [src].") + to_chat(user, "You know better than to violate the security of The Den, best wait until you leave to use [src].") return else no_den_usage = 0 @@ -72,7 +74,7 @@ return /obj/item/gun/magic/shoot_with_empty_chamber(mob/living/user as mob|obj) - to_chat(user, "The [name] whizzles quietly.") + to_chat(user, "The [name] whizzles quietly.") /obj/item/gun/magic/suicide_act(mob/user) user.visible_message("[user] is twisting [src] above [user.p_their()] head, releasing a magical blast! It looks like [user.p_theyre()] trying to commit suicide!") @@ -83,4 +85,4 @@ . = ..() switch (var_name) if ("charges") - recharge_newshot() \ No newline at end of file + recharge_newshot() diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index f19fc7c693..27fb040de4 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -37,7 +37,7 @@ if(no_den_usage) var/area/A = get_area(user) if(istype(A, /area/wizard_station)) - to_chat(user, "You know better than to violate the security of The Den, best wait until you leave to use [src].") + to_chat(user, "You know better than to violate the security of The Den, best wait until you leave to use [src].") return else no_den_usage = 0 @@ -167,4 +167,4 @@ /obj/item/gun/magic/wand/fireball/zap_self(mob/living/user) ..() explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) - charges-- \ No newline at end of file + charges-- diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm index 232cb5987e..75066540cf 100644 --- a/code/modules/projectiles/pins.dm +++ b/code/modules/projectiles/pins.dm @@ -8,7 +8,6 @@ flags_1 = CONDUCT_1 w_class = WEIGHT_CLASS_TINY attack_verb = list("poked") - var/emagged = FALSE var/fail_message = "INVALID USER." var/selfdestruct = 0 // Explode when user check is failed. var/force_replace = 0 // Can forcefully replace other pins. diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index fa31fb8ec1..64f0eae564 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -1,3 +1,6 @@ +#define MAIN_SCREEN 1 +#define SYMPTOM_DETAILS 2 + /obj/machinery/computer/pandemic name = "PanD.E.M.I.C 2200" desc = "Used to work with viruses." @@ -10,6 +13,8 @@ idle_power_usage = 20 resistance_flags = ACID_PROOF var/wait + var/mode = MAIN_SCREEN + var/datum/symptom/selected_symptom var/obj/item/reagent_containers/beaker /obj/machinery/computer/pandemic/Initialize() @@ -34,9 +39,7 @@ /obj/machinery/computer/pandemic/proc/get_viruses_data(datum/reagent/blood/B) . = list() - if(!islist(B.data["viruses"])) - return - var/list/V = B.data["viruses"] + var/list/V = B.get_diseases() var/index = 1 for(var/virus in V) var/datum/disease/D = virus @@ -47,21 +50,24 @@ this["name"] = D.name if(istype(D, /datum/disease/advance)) var/datum/disease/advance/A = D - var/datum/disease/advance/archived = SSdisease.archive_diseases[D.GetDiseaseID()] - if(archived.name == "Unknown") + var/disease_name = SSdisease.get_disease_name(A.GetDiseaseID()) + if(disease_name == "Unknown") this["can_rename"] = TRUE - this["name"] = archived.name + this["name"] = disease_name this["is_adv"] = TRUE - this["resistance"] = A.totalResistance() - this["stealth"] = A.totalStealth() - this["stage_speed"] = A.totalStageSpeed() - this["transmission"] = A.totalTransmittable() this["symptoms"] = list() + var/symptom_index = 1 for(var/symptom in A.symptoms) var/datum/symptom/S = symptom var/list/this_symptom = list() this_symptom["name"] = S.name + this_symptom["sym_index"] = symptom_index + symptom_index++ this["symptoms"] += list(this_symptom) + this["resistance"] = A.totalResistance() + this["stealth"] = A.totalStealth() + this["stage_speed"] = A.totalStageSpeed() + this["transmission"] = A.totalTransmittable() this["index"] = index++ this["agent"] = D.agent this["description"] = D.desc || "none" @@ -70,6 +76,20 @@ . += list(this) +/obj/machinery/computer/pandemic/proc/get_symptom_data(datum/symptom/S) + . = list() + var/list/this = list() + this["name"] = S.name + this["desc"] = S.desc + this["stealth"] = S.stealth + this["resistance"] = S.resistance + this["stage_speed"] = S.stage_speed + this["transmission"] = S.transmittable + this["level"] = S.level + this["neutered"] = S.neutered + this["threshold_desc"] = S.threshold_desc + . += this + /obj/machinery/computer/pandemic/proc/get_resistance_data(datum/reagent/blood/B) . = list() if(!islist(B.data["resistances"])) @@ -81,6 +101,7 @@ if(D) this["id"] = id this["name"] = D.name + . += list(this) /obj/machinery/computer/pandemic/proc/reset_replicator_cooldown() @@ -113,18 +134,23 @@ /obj/machinery/computer/pandemic/ui_data(mob/user) var/list/data = list() data["is_ready"] = !wait - if(beaker) - data["has_beaker"] = TRUE - if(!beaker.reagents.total_volume || !beaker.reagents.reagent_list) - data["beaker_empty"] = TRUE - var/datum/reagent/blood/B = locate() in beaker.reagents.reagent_list - if(B) - data["has_blood"] = TRUE - data["blood"] = list() - data["blood"]["dna"] = B.data["blood_DNA"] || "none" - data["blood"]["type"] = B.data["blood_type"] || "none" - data["viruses"] = get_viruses_data(B) - data["resistances"] = get_resistance_data(B) + data["mode"] = mode + switch(mode) + if(MAIN_SCREEN) + if(beaker) + data["has_beaker"] = TRUE + if(!beaker.reagents.total_volume || !beaker.reagents.reagent_list) + data["beaker_empty"] = TRUE + var/datum/reagent/blood/B = locate() in beaker.reagents.reagent_list + if(B) + data["has_blood"] = TRUE + data["blood"] = list() + data["blood"]["dna"] = B.data["blood_DNA"] || "none" + data["blood"]["type"] = B.data["blood_type"] || "none" + data["viruses"] = get_viruses_data(B) + data["resistances"] = get_resistance_data(B) + if(SYMPTOM_DETAILS) + data["symptom"] = get_symptom_data(selected_symptom) return data @@ -166,8 +192,8 @@ addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 50) . = TRUE if("create_vaccine_bottle") - var/index = params["index"] - var/datum/disease/D = SSdisease.archive_diseases[index] + var/index = text2num(params["index"]) + var/datum/disease/D = SSdisease.archive_diseases[get_virus_id_by_index(index)] var/obj/item/reagent_containers/glass/bottle/B = new(get_turf(src)) B.name = "[D.name] vaccine bottle" B.reagents.add_reagent("vaccine", 15, list(index)) @@ -175,6 +201,19 @@ update_icon() addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 200) . = TRUE + if("symptom_details") + var/picked_symptom_index = text2num(params["picked_symptom"]) + var/index = text2num(params["index"]) + var/datum/disease/advance/A = get_by_index("viruses", index) + var/datum/symptom/S = A.symptoms[picked_symptom_index] + mode = SYMPTOM_DETAILS + selected_symptom = S + . = TRUE + if("back") + mode = MAIN_SCREEN + selected_symptom = null + . = TRUE + /obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/reagent_containers) && (I.container_type & OPENCONTAINER_1)) @@ -182,14 +221,13 @@ if(stat & (NOPOWER|BROKEN)) return if(beaker) - to_chat(user, "A beaker is already loaded into the machine!") + to_chat(user, "A container is already loaded into [src]!") return - if(!user.drop_item()) + if(!user.transferItemToLoc(I, src)) return beaker = I - beaker.forceMove(src) - to_chat(user, "You add the beaker to the machine.") + to_chat(user, "You insert [I] into [src].") update_icon() else return ..() 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/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index ac86057e80..2fd728d546 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -276,11 +276,11 @@ icon_state = "bottle3" spawned_disease = /datum/disease/advance/heal -/obj/item/reagent_containers/glass/bottle/hullucigen_virion - name = "Hullucigen virion culture bottle" - desc = "A small bottle. Contains hullucigen virion culture in synthblood medium." +/obj/item/reagent_containers/glass/bottle/hallucigen_virion + name = "Hallucigen virion culture bottle" + desc = "A small bottle. Contains hallucigen virion culture in synthblood medium." icon_state = "bottle3" - spawned_disease = /datum/disease/advance/hullucigen + spawned_disease = /datum/disease/advance/hallucigen /obj/item/reagent_containers/glass/bottle/pierrot_throat name = "Pierrot's Throat culture bottle" diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 33d2b81017..324a7d7066 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -135,7 +135,7 @@ if(L.reagents.total_volume >= L.reagents.maximum_volume) return L.visible_message("[user] injects [L] with the syringe!", \ - "[user] injects [L] with the syringe!") + "[user] injects [L] with the syringe!") var/list/rinject = list() for(var/datum/reagent/R in reagents.reagent_list) 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/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index cee8167666..bb2887e2a1 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -418,3 +418,11 @@ req_tech = list("programming" = 1) build_path = /obj/item/circuitboard/machine/deep_fryer category = list ("Misc. Machinery") + +/datum/design/board/donksofttoyvendor + name = "Machine Design (Donksoft Toy Vendor Board)" + desc = "The circuit board for a Donksoft Toy Vendor." + id = "donksofttoyvendor" + req_tech = list("programming" = 1, "syndicate" = 2) + build_path = /obj/item/circuitboard/machine/vending/donksofttoyvendor + category = list ("Misc. Machinery") 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 63861b8e35..da965aae9d 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -46,70 +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 /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/ruins/lavalandruin_code/biodome_clown_planet.dm b/code/modules/ruins/lavalandruin_code/biodome_clown_planet.dm new file mode 100644 index 0000000000..5b9a1b86b0 --- /dev/null +++ b/code/modules/ruins/lavalandruin_code/biodome_clown_planet.dm @@ -0,0 +1,7 @@ +//////lavaland clown planet papers + +/obj/item/paper/crumpled/bloody/ruins/lavaland/clown_planet/escape + info = "If you dare not continue down this path of madness, escape can be found through the chute in this room." + +/obj/item/paper/crumpled/bloody/ruins/lavaland/clown_planet/hope + info = "Abandon hope, all ye who enter here." diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm index a061d00a44..a01a144bc3 100644 --- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm +++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm @@ -111,7 +111,9 @@ desc = "Their success will be yours." icon = 'icons/obj/wizard.dmi' icon_state = "render" - item_state = "render" + item_state = "knife" + lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi' force = 18 throwforce = 10 w_class = WEIGHT_CLASS_NORMAL diff --git a/code/modules/ruins/spaceruin_code/TheDerelict.dm b/code/modules/ruins/spaceruin_code/TheDerelict.dm index 27d9e59063..58e257d587 100644 --- a/code/modules/ruins/spaceruin_code/TheDerelict.dm +++ b/code/modules/ruins/spaceruin_code/TheDerelict.dm @@ -10,4 +10,10 @@ /obj/item/paper/fluff/ruins/thederelict/nukie_objectives name = "Objectives of a Nuclear Operative" - info = "Objective #1: Destroy the station with a nuclear device." \ No newline at end of file + info = "Objective #1: Destroy the station with a nuclear device." + +/obj/item/paper/crumpled/bloody/ruins/thederelict/unfinished + name = "unfinished paper scrap" + desc = "Looks like someone started shakily writing a will in space common, but were interrupted by something bloody..." + info = "I, Victor Belyakov, do hereby leave my _- " + diff --git a/code/modules/ruins/spaceruin_code/bigderelict1.dm b/code/modules/ruins/spaceruin_code/bigderelict1.dm new file mode 100644 index 0000000000..a86fb3c146 --- /dev/null +++ b/code/modules/ruins/spaceruin_code/bigderelict1.dm @@ -0,0 +1,8 @@ +/////////// bigderelict1 items + +/obj/item/paper/crumpled/ruins/bigderelict1/manifest + info = "A crumpled piece of manifest paper, out of the barely legible pen writing, you can see something about a warning involving whatever was originally in the crate." + +/obj/item/paper/crumpled/ruins/bigderelict1/coward + icon_state = "scrap_bloodied" + info = "If anyone finds this, please, don't let my kids know I died a coward.." diff --git a/code/modules/ruins/spaceruin_code/originalcontent.dm b/code/modules/ruins/spaceruin_code/originalcontent.dm index 488ff8b295..5da28af26d 100644 --- a/code/modules/ruins/spaceruin_code/originalcontent.dm +++ b/code/modules/ruins/spaceruin_code/originalcontent.dm @@ -1,4 +1,28 @@ /////////// originalcontent items /obj/item/paper/crumpled/ruins/originalcontent - desc = "Various scrawled out drawings and sketches reside on the paper, apparently he didn't much care for these drawings." \ No newline at end of file + desc = "Various scrawled out drawings and sketches reside on the paper, apparently he didn't much care for these drawings." + +/obj/item/paper/pamphlet/ruin/originalcontent + icon = 'icons/obj/fluff.dmi' + +/obj/item/paper/pamphlet/ruin/originalcontent/stickman + name = "Painting - 'BANG'" + info = "This picture depicts a crudely-drawn stickman firing a crudely-drawn gun." + icon_state = "painting4" + +/obj/item/paper/pamphlet/ruin/originalcontent/treeside + name = "Painting - 'Treeside'" + info = "This picture depicts a sunny day on a lush hillside, set under a shaded tree." + icon_state = "painting1" + +/obj/item/paper/pamphlet/ruin/originalcontent/pennywise + name = "Painting - 'Pennywise'" + info = "This picture depicts a smiling clown. Something doesn't feel right about this.." + icon_state = "painting3" + +/obj/item/paper/pamphlet/ruin/originalcontent/yelling + name = "Painting - 'Hands-On-Face'" + info = "This picture depicts a man yelling on a bridge for no apparent reason." + icon_state = "painting2" + 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/assault_pod.dm b/code/modules/shuttle/assault_pod.dm index 7015192722..5b21d22d19 100644 --- a/code/modules/shuttle/assault_pod.dm +++ b/code/modules/shuttle/assault_pod.dm @@ -11,7 +11,7 @@ /obj/docking_port/mobile/assault_pod/dock(obj/docking_port/stationary/S1) - ..() + . = ..() if(!istype(S1, /obj/docking_port/stationary/transit)) playsound(get_turf(src.loc), 'sound/effects/explosion1.ogg',50,1) diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/computer.dm index bdaac1d3cb..a3ebba05a1 100644 --- a/code/modules/shuttle/computer.dm +++ b/code/modules/shuttle/computer.dm @@ -78,7 +78,7 @@ /obj/machinery/computer/shuttle/emag_act(mob/user) if(emagged) return - req_access = null + req_access = list() emagged = TRUE to_chat(user, "You fried the consoles ID checking system.") diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index af03453e45..6999de9555 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -290,7 +290,7 @@ if(SHUTTLE_CALL) if(time_left <= 0) //move emergency shuttle to station - if(dock(SSshuttle.getDock("emergency_home"))) + if(dock(SSshuttle.getDock("emergency_home")) != DOCKING_SUCCESS) setTimer(20) return mode = SHUTTLE_DOCKED diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index b09c38d714..77eec512d8 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -4,15 +4,18 @@ All ShuttleMove procs go here /************************************Base procs************************************/ -// Called on every turf in the shuttle region, return false if it doesn't want to move -/turf/proc/fromShuttleMove(turf/newT, turf_type, baseturf_type) - if(type == turf_type && baseturf == baseturf_type) - return FALSE - return TRUE +// Called on every turf in the shuttle region, returns a bitflag for allowed movements of that turf +// returns the new move_mode (based on the old) +/turf/proc/fromShuttleMove(turf/newT, turf_type, list/baseturf_cache, move_mode) + if(!(move_mode & MOVE_AREA) || (istype(src, turf_type) && baseturf_cache[baseturf])) + return move_mode + return move_mode | MOVE_TURF | MOVE_CONTENTS // Called from the new turf before anything has been moved // Only gets called if fromShuttleMove returns true first -/turf/proc/toShuttleMove(turf/oldT, shuttle_dir) +// returns the new move_mode (based on the old) +/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)) @@ -38,7 +41,7 @@ All ShuttleMove procs go here else qdel(thing) - return TRUE + return move_mode // Called on the old turf to move the turf data /turf/proc/onShuttleMove(turf/newT, turf_type, baseturf_type, rotation, list/movement_force, move_dir) @@ -73,9 +76,9 @@ All ShuttleMove procs go here ///////////////////////////////////////////////////////////////////////////////////// // Called on every atom in shuttle turf contents before anything has been moved -// Return true if it should be moved regardless of turf being moved -/atom/movable/proc/beforeShuttleMove(turf/newT, rotation) - return FALSE +// returns the new move_mode (based on the old) +/atom/movable/proc/beforeShuttleMove(turf/newT, rotation, move_mode) + return move_mode // Called on atoms to move the atom to the new location /atom/movable/proc/onShuttleMove(turf/newT, turf/oldT, rotation, list/movement_force, move_dir, old_dock) @@ -102,13 +105,16 @@ All ShuttleMove procs go here ///////////////////////////////////////////////////////////////////////////////////// // Called on areas before anything has been moved -/area/proc/beforeShuttleMove() - return TRUE +// returns the new move_mode (based on the old) +/area/proc/beforeShuttleMove(list/shuttle_areas) + if(!shuttle_areas[src]) + return NONE + return MOVE_AREA // Called on areas to move their turf between areas /area/proc/onShuttleMove(turf/oldT, turf/newT, area/underlying_old_area) if(newT == oldT) // In case of in place shuttle rotation shenanigans. - return + return TRUE contents -= oldT underlying_old_area.contents += oldT @@ -117,7 +123,7 @@ All ShuttleMove procs go here var/area/old_dest_area = newT.loc parallax_movedir = old_dest_area.parallax_movedir - + old_dest_area.contents -= newT contents += newT newT.change_area(old_dest_area, src) @@ -160,9 +166,11 @@ All ShuttleMove procs go here SSair.add_to_active(src, TRUE) SSair.add_to_active(oldT, TRUE) +/************************************Area move procs************************************/ + /************************************Machinery move procs************************************/ -/obj/machinery/door/airlock/beforeShuttleMove(turf/newT, rotation) +/obj/machinery/door/airlock/beforeShuttleMove(turf/newT, rotation, move_mode) . = ..() shuttledocked = 0 for(var/obj/machinery/door/airlock/A in range(1, src)) @@ -176,11 +184,11 @@ All ShuttleMove procs go here for(var/obj/machinery/door/airlock/A in range(1, src)) A.shuttledocked = 1 -/obj/machinery/camera/beforeShuttleMove(turf/newT, rotation) +/obj/machinery/camera/beforeShuttleMove(turf/newT, rotation, move_mode) . = ..() GLOB.cameranet.removeCamera(src) GLOB.cameranet.updateChunk() - return TRUE + . |= MOVE_CONTENTS /obj/machinery/camera/afterShuttleMove(list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir) . = ..() @@ -207,7 +215,7 @@ All ShuttleMove procs go here if(z == ZLEVEL_MINING) //Avoids double logging and landing on other Z-levels due to badminnery SSblackbox.add_details("colonies_dropped", "[x]|[y]|[z]") //Number of times a base has been dropped! -/obj/machinery/gravity_generator/main/beforeShuttleMove(turf/newT, rotation) +/obj/machinery/gravity_generator/main/beforeShuttleMove(turf/newT, rotation, move_mode) . = ..() on = FALSE update_list() @@ -218,9 +226,9 @@ All ShuttleMove procs go here on = TRUE update_list() -/obj/machinery/thruster/beforeShuttleMove(turf/newT, rotation) +/obj/machinery/thruster/beforeShuttleMove(turf/newT, rotation, move_mode) . = ..() - . = TRUE + . |= MOVE_CONTENTS //Properly updates pipes on shuttle movement /obj/machinery/atmospherics/shuttleRotate(rotation) @@ -271,7 +279,7 @@ All ShuttleMove procs go here var/turf/T = loc hide(T.intact) -/obj/machinery/navbeacon/beforeShuttleMove(turf/newT, rotation) +/obj/machinery/navbeacon/beforeShuttleMove(turf/newT, rotation, move_mode) . = ..() GLOB.navbeacons["[z]"] -= src GLOB.deliverybeacons -= src @@ -333,13 +341,13 @@ All ShuttleMove procs go here /************************************Structure move procs************************************/ -/obj/structure/grille/beforeShuttleMove(turf/newT, rotation) +/obj/structure/grille/beforeShuttleMove(turf/newT, rotation, move_mode) . = ..() - . = TRUE + . |= MOVE_CONTENTS -/obj/structure/lattice/beforeShuttleMove(turf/newT, rotation) +/obj/structure/lattice/beforeShuttleMove(turf/newT, rotation, move_mode) . = ..() - . = TRUE + . |= MOVE_CONTENTS /obj/structure/disposalpipe/afterShuttleMove(list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir) . = ..() @@ -350,6 +358,11 @@ All ShuttleMove procs go here var/turf/T = loc if(level==1) hide(T.intact) + +/obj/structure/shuttle/beforeShuttleMove(turf/newT, rotation, move_mode) + . = ..() + . |= MOVE_CONTENTS + /************************************Misc move procs************************************/ diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index d0d5952a0c..4326e6298e 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -21,6 +21,10 @@ var/dwidth = 0 //position relative to covered area, perpendicular to dir var/dheight = 0 //position relative to covered area, parallel to dir + var/area_type + var/turf_type + var/baseturf_type + //these objects are indestructible /obj/docking_port/Destroy(force) // unless you assert that you know what you're doing. Horrible things @@ -119,6 +123,39 @@ else . += T +/obj/docking_port/proc/return_ordered_assoc_turfs(_x, _y, _z, _dir) + if(!_dir) + _dir = dir + if(!_x) + _x = x + if(!_y) + _y = y + if(!_z) + _z = z + var/cos = 1 + var/sin = 0 + switch(_dir) + if(WEST) + cos = 0 + sin = 1 + if(SOUTH) + cos = -1 + sin = 0 + if(EAST) + cos = 0 + sin = -1 + + . = list() + + var/xi + var/yi + for(var/dx=0, dx 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/shuttle/supply.dm b/code/modules/shuttle/supply.dm index 7c7fd1c68b..6ef04dcaba 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -67,7 +67,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( /obj/docking_port/mobile/supply/dock() if(getDockedId() == "supply_away") // Buy when we leave home. buy() - if(..()) // Fly/enter transit. + . = ..() // Fly/enter transit. + if(. != DOCKING_SUCCESS) return if(getDockedId() == "supply_away") // Sell when we get home sell() diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index ea113ba4fd..3cb56becf8 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -18,7 +18,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th /obj/effect/proc_holder/proc/InterceptClickOn(mob/living/caller, params, atom/A) if(caller.ranged_ability != src || ranged_ability_user != caller) //I'm not actually sure how these would trigger, but, uh, safety, I guess? - to_chat(caller, "[caller.ranged_ability.name] has been disabled.") + to_chat(caller, "[caller.ranged_ability.name] has been disabled.") caller.ranged_ability.remove_ranged_ability() return TRUE //TRUE for failed, FALSE for passed. if(ranged_clickcd_override >= 0) @@ -33,7 +33,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th return if(user.ranged_ability && user.ranged_ability != src) if(forced) - to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") + to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") user.ranged_ability.remove_ranged_ability() else return diff --git a/code/modules/spells/spell_types/devil.dm b/code/modules/spells/spell_types/devil.dm index f54034c5ec..9d1afdcebc 100644 --- a/code/modules/spells/spell_types/devil.dm +++ b/code/modules/spells/spell_types/devil.dm @@ -114,7 +114,7 @@ to_chat(user, "You are no longer near a potential signer.") else - to_chat(user, "You can only re-appear near a potential signer.") + to_chat(user, "You can only re-appear near a potential signer.") revert_cast() return ..() else @@ -166,7 +166,7 @@ fakefire() src.loc = get_turf(src) src.client.eye = src - src.visible_message("[src] appears in a fiery blaze!") + src.visible_message("[src] appears in a fiery blaze!") playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1) addtimer(CALLBACK(src, .proc/fakefireextinguish), 15, TIMER_UNIQUE) @@ -260,4 +260,4 @@ effect_type = /obj/effect/particle_effect/smoke/transparent/dancefloor_devil /obj/effect/particle_effect/smoke/transparent/dancefloor_devil - lifetime = 2 \ No newline at end of file + lifetime = 2 diff --git a/code/modules/spells/spell_types/rightandwrong.dm b/code/modules/spells/spell_types/rightandwrong.dm index 024e1ceae7..1d657117c3 100644 --- a/code/modules/spells/spell_types/rightandwrong.dm +++ b/code/modules/spells/spell_types/rightandwrong.dm @@ -10,7 +10,7 @@ message_admins("[key_name_admin(user, 1)] summoned [summon_type ? "magic" : "guns"]!") log_game("[key_name(user)] summoned [summon_type ? "magic" : "guns"]!") for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.stat == 2 || !(H.client)) continue + if(H.stat == DEAD || !(H.client)) continue if(H.mind) if(H.mind.special_role == "Wizard" || H.mind.special_role == "apprentice" || H.mind.special_role == "survivalist") continue if(prob(survivor_probability) && !(H.mind in SSticker.mode.traitors)) diff --git a/code/modules/spells/spell_types/shadow_walk.dm b/code/modules/spells/spell_types/shadow_walk.dm index 546b960793..45ea521098 100644 --- a/code/modules/spells/spell_types/shadow_walk.dm +++ b/code/modules/spells/spell_types/shadow_walk.dm @@ -13,7 +13,7 @@ action_icon_state = "ninja_cloak" action_background_icon_state = "bg_alien" -/obj/effect/proc_holder/spell/targeted/shadowwalk/cast(list/targets,mob/user = usr) +/obj/effect/proc_holder/spell/targeted/shadowwalk/cast(list/targets,mob/living/user = usr) var/L = user.loc if(istype(user.loc, /obj/effect/dummy/shadow)) var/obj/effect/dummy/shadow/S = L @@ -22,9 +22,11 @@ else var/turf/T = get_turf(user) var/light_amount = T.get_lumcount() - if(light_amount < 0.2) + if(light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) visible_message("[user] melts into the shadows!") + user.AdjustStun(-20, 0) + user.AdjustKnockdown(-20, 0) var/obj/effect/dummy/shadow/S2 = new(get_turf(user.loc)) user.forceMove(S2) S2.jaunter = user diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm index 1b6d11719f..cfa95f2d1a 100644 --- a/code/modules/station_goals/dna_vault.dm +++ b/code/modules/station_goals/dna_vault.dm @@ -90,10 +90,10 @@ to_chat(user, "Plant needs to be ready to harvest to perform full data scan.") //Because space dna is actually magic return if(plants[H.myseed.type]) - to_chat(user, "Plant data already present in local storage.") + to_chat(user, "Plant data already present in local storage.") return plants[H.myseed.type] = 1 - to_chat(user, "Plant data added to local storage.") + to_chat(user, "Plant data added to local storage.") //animals var/static/list/non_simple_animals = typecacheof(list(/mob/living/carbon/monkey, /mob/living/carbon/alien)) @@ -104,19 +104,19 @@ to_chat(user, "No compatible DNA detected") return if(animals[target.type]) - to_chat(user, "Animal data already present in local storage.") + to_chat(user, "Animal data already present in local storage.") return animals[target.type] = 1 - to_chat(user, "Animal data added to local storage.") + to_chat(user, "Animal data added to local storage.") //humans if(ishuman(target)) var/mob/living/carbon/human/H = target if(dna[H.dna.uni_identity]) - to_chat(user, "Humanoid data already present in local storage.") + to_chat(user, "Humanoid data already present in local storage.") return dna[H.dna.uni_identity] = 1 - to_chat(user, "Humanoid data added to local storage.") + to_chat(user, "Humanoid data added to local storage.") /obj/machinery/dna_vault name = "DNA Vault" 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/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index d71cddd3e4..4e478d3420 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -52,7 +52,7 @@ if(active) for(var/obj/item/I in owner.held_items) if(!(I.flags_1 & NODROP_1)) - flags_1 += I + stored_items += I var/list/L = owner.get_empty_held_indexes() if(LAZYLEN(L) == owner.held_items.len) @@ -87,6 +87,7 @@ /obj/item/organ/cyberimp/brain/anti_drop/proc/release_items() for(var/obj/item/I in stored_items) I.flags_1 &= ~NODROP_1 + stored_items = list() /obj/item/organ/cyberimp/brain/anti_drop/Remove(var/mob/living/carbon/M, special = 0) diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index 0b8131d4cc..caa6cc7a97 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -38,6 +38,9 @@ if(!uses) desc = "[initial(desc)] Looks like it's been used up." +/obj/item/device/autosurgeon/attack_self_tk(mob/user) + return //stops TK fuckery + /obj/item/device/autosurgeon/attackby(obj/item/I, mob/user, params) if(istype(I, organ_type)) if(storedorgan) diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index 2ee223b403..26c2239bb6 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -80,9 +80,6 @@ icon = 'icons/obj/clothing/hats.dmi' icon_state = "kitty" -/obj/item/organ/ears/cat/adjustEarDamage(ddmg, ddeaf) - ..(ddmg*2,ddeaf*2) - /obj/item/organ/ears/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE) ..() color = H.hair_color @@ -93,4 +90,4 @@ ..() color = H.hair_color H.dna.features["ears"] = "None" - H.update_body() \ No newline at end of file + H.update_body() diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 554bcfdf60..4b939bf4f1 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -61,14 +61,17 @@ /obj/item/organ/eyes/night_vision/alien name = "alien eyes" desc = "It turned out they had them after all!" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE sight_flags = SEE_MOBS /obj/item/organ/eyes/night_vision/zombie name = "undead eyes" desc = "Somewhat counterintuitively, these half rotten eyes actually have superior vision to those of a living human." +/obj/item/organ/eyes/night_vision/nightmare + name = "burning red eyes" + desc = "Even without their shadowy owner, looking at these eyes gives you a sense of dread." + icon_state = "burning_eyes" + ///Robotic /obj/item/organ/eyes/robotic 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/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 0f5f1de91d..8f32aa89eb 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -569,6 +569,7 @@ message_admins("[key_name_admin(user)] has said '[log_message]' with a Voice of God, affecting [english_list(listeners)], with a power multiplier of [power_multiplier].") log_game("[key_name(user)] has said '[log_message]' with a Voice of God, affecting [english_list(listeners)], with a power multiplier of [power_multiplier].") + SSblackbox.add_details("voice_of_god", log_message) return cooldown diff --git a/code/modules/uplink/uplink_item.dm b/code/modules/uplink/uplink_item.dm index e864603681..1f57ac93ce 100644 --- a/code/modules/uplink/uplink_item.dm +++ b/code/modules/uplink/uplink_item.dm @@ -399,6 +399,12 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. item = /obj/item/ammo_box/magazine/m10mm/hp cost = 3 +/datum/uplink_item/ammo/pistolaps + name = "9mm Handgun Magazine" + desc = "An additional 15-round 9mm magazine, compatible with the Stetchkin APS pistol, found in the Spetsnaz Pyro bundle." + item = /obj/item/ammo_box/magazine/pistolm9mm + cost = 2 + /datum/uplink_item/ammo/bolt_action name = "Surplus Rifle Clip" desc = "A stripper clip used to quickly load bolt action rifles. Contains 5 rounds." @@ -962,7 +968,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. \ Be careful with wording, as artificial intelligences may look for loopholes to exploit." item = /obj/item/aiModule/syndicate - cost = 14 + cost = 9 /datum/uplink_item/device_tools/briefcase_launchpad name = "Briefcase Launchpad" diff --git a/code/modules/vore/eating/belly_vr.dm b/code/modules/vore/eating/belly_vr.dm index d5c484d387..f1e4274ce9 100644 --- a/code/modules/vore/eating/belly_vr.dm +++ b/code/modules/vore/eating/belly_vr.dm @@ -24,7 +24,7 @@ var/escapetime = 200 // Deciseconds, how long to escape this belly var/escapechance = 45 // % Chance of prey beginning to escape if prey struggles. var/tmp/digest_mode = DM_HOLD // Whether or not to digest. Default to not digest. - var/tmp/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL) // Possible digest modes + var/tmp/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL,DM_NOISY) // Possible digest modes var/tmp/mob/living/owner // The mob whose belly this is. var/tmp/list/internal_contents = list() // People/Things you've eaten into this belly! var/tmp/is_full // Flag for if digested remeans are present. (for disposal messages) @@ -106,7 +106,7 @@ return 0 for (var/atom/movable/M in internal_contents) M.forceMove(owner.loc) // Move the belly contents into the same location as belly's owner. - M << sound(null, repeat = 0, wait = 0, volume = 80, channel = 50) + M << sound(null, repeat = 0, wait = 0, volume = 80, channel = CHANNEL_PREYLOOP) internal_contents.Remove(M) // Remove from the belly contents var/datum/belly/B = check_belly(owner) // This makes sure that the mob behaves properly if released into another mob @@ -124,7 +124,7 @@ return FALSE // They weren't in this belly anyway M.forceMove(owner.loc) // Move the belly contents into the same location as belly's owner. - M << sound(null, repeat = 0, wait = 0, volume = 80, channel = 50) + M << sound(null, repeat = 0, wait = 0, volume = 80, channel = CHANNEL_PREYLOOP) src.internal_contents.Add(M) // Remove from the belly contents var/datum/belly/B = check_belly(owner) if(B) @@ -143,7 +143,7 @@ prey.forceMove(owner) internal_contents.Add(prey) - prey << sound('sound/vore/prey/loop.ogg', repeat = 1, wait = 0, volume = 80, channel = 50) + prey << sound('sound/vore/prey/loop.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_PREYLOOP) if(inside_flavor) prey << "[inside_flavor]" @@ -224,7 +224,7 @@ /datum/belly/proc/digestion_death(var/mob/living/M) is_full = TRUE internal_contents.Remove(M) - M << sound(null, repeat = 0, wait = 0, volume = 80, channel = 50) + M << sound(null, repeat = 0, wait = 0, volume = 80, channel = CHANNEL_PREYLOOP) // If digested prey is also a pred... anyone inside their bellies gets moved up. if (is_vore_predator(M)) for (var/bellytype in M.vore_organs) @@ -255,6 +255,7 @@ return // User is not in this belly, or struggle too soon. R.setClickCooldown(50) + var/sound/prey_struggle = sound(get_sfx("prey_struggle")) if(owner.stat) //If owner is stat (dead, KO) we can actually escape to_chat(R, "You attempt to climb out of \the [name]. (This will take around [escapetime/10] seconds.)") @@ -288,9 +289,9 @@ // for(var/mob/M in hearers(4, owner)) // M.visible_message(struggle_outer_message) // hearable R.visible_message( "[struggle_outer_message]", "[struggle_user_message]") - playsound(get_turf(owner),"struggle_sound",75,0,-5,1,channel=51) - R.stop_sound_channel(51) - R.playsound_local("prey_struggle_sound",60) + playsound(get_turf(owner),"struggle_sound",35,0,-6,1,channel=151) + R.stop_sound_channel(151) + R.playsound_local(get_turf(R), null, 45, S = prey_struggle) if(escapable && R.a_intent != "help") //If the stomach has escapable enabled and the person is actually trying to kick out to_chat(R, "You attempt to climb out of \the [name].") diff --git a/code/modules/vore/eating/bellymodes_vr.dm b/code/modules/vore/eating/bellymodes_vr.dm index b58875bd1f..19d07e6fb6 100644 --- a/code/modules/vore/eating/bellymodes_vr.dm +++ b/code/modules/vore/eating/bellymodes_vr.dm @@ -1,6 +1,8 @@ // Process the predator's effects upon the contents of its belly (i.e digestion/transformation etc) // Called from /mob/living/Life() proc. /datum/belly/proc/process_Life() + var/sound/prey_gurgle = sound(get_sfx("digest_prey")) + var/sound/prey_digest = sound(get_sfx("death_prey")) /////////////////////////// Auto-Emotes /////////////////////////// if((digest_mode in emote_lists) && !emotePend) @@ -19,11 +21,11 @@ //////////////////////////// DM_DIGEST //////////////////////////// if(digest_mode == DM_DIGEST) for (var/mob/living/M in internal_contents) - if(prob(50)) + if(prob(15)) M.stop_sound_channel(CHANNEL_PRED) - playsound(get_turf(owner),"digest_pred",75,0,-6,0,channel=CHANNEL_PRED) + playsound(get_turf(owner),"digest_pred",50,0,-6,0,channel=CHANNEL_PRED) M.stop_sound_channel(CHANNEL_PRED) - M.playsound_local("digest_prey",60) + M.playsound_local(get_turf(M), null, 45, S = prey_gurgle) //Pref protection! if (!M.digestable) @@ -50,9 +52,9 @@ owner.nutrition += 400 // so eating dead mobs gives you *something*. M.stop_sound_channel(CHANNEL_PRED) - playsound(get_turf(owner),"death_pred",50,0,-6,0,channel=CHANNEL_PRED) + playsound(get_turf(owner),"death_pred",45,0,-6,0,channel=CHANNEL_PRED) M.stop_sound_channel(CHANNEL_PRED) - M.playsound_local("death_prey",60) + M.playsound_local(get_turf(M), null, 45, S = prey_digest) digestion_death(M) owner.update_icons() continue @@ -67,11 +69,11 @@ ///////////////////////////// DM_HEAL ///////////////////////////// if(digest_mode == DM_HEAL) for (var/mob/living/M in internal_contents) - if(prob(50)) + if(prob(15)) M.stop_sound_channel(CHANNEL_PRED) - playsound(get_turf(owner),"digest_pred",50,0,-6,0,channel=CHANNEL_PRED) + playsound(get_turf(owner),"digest_pred",35,0,-6,0,channel=CHANNEL_PRED) M.stop_sound_channel(CHANNEL_PRED) - M.playsound_local("digest_prey",60) + M.playsound_local(get_turf(M), null, 45, S = prey_gurgle) if(M.stat != DEAD) if(owner.nutrition >= NUTRITION_LEVEL_STARVING && (M.health < M.maxHealth)) @@ -79,3 +81,13 @@ M.adjustFireLoss(-1) owner.nutrition -= 10 return + +////////////////////////// DM_NOISY ///////////////////////////////// +//for when you just want people to squelch around + if(digest_mode == DM_NOISY) + for (var/mob/living/M in internal_contents) + if(prob(35)) + M.stop_sound_channel(CHANNEL_PRED) + playsound(get_turf(owner),"digest_pred",35,0,-6,0,channel=CHANNEL_PRED) + M.stop_sound_channel(CHANNEL_PRED) + M.playsound_local(get_turf(M), null, 45, S = prey_gurgle) diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index 7be607bd2a..98db33a763 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -21,7 +21,7 @@ if(M.client && M.client.prefs_vr) if(!M.copy_from_prefs_vr()) - M << "ERROR: You seem to have saved VOREStation prefs, but they couldn't be loaded." + M << "ERROR: You seem to have saved vore prefs, but they couldn't be loaded." return FALSE if(M.vore_organs && M.vore_organs.len) M.vore_selected = M.vore_organs[1] diff --git a/code/modules/vore/trycatch_vr.dm b/code/modules/vore/trycatch_vr.dm index 1ae5c3bc0c..2adf6e0cf6 100644 --- a/code/modules/vore/trycatch_vr.dm +++ b/code/modules/vore/trycatch_vr.dm @@ -1,6 +1,6 @@ /* This file is for jamming single-line procs into Polaris procs. -It will prevent runtimes and allow their code to run if VOREStation's fails. +It will prevent runtimes and allow their code to run if these fail. It will also log when we mess up our code rather than making it vague. Call it at the top of a stock proc with... diff --git a/config/config.txt b/config/config.txt index b344482679..652651e425 100644 --- a/config/config.txt +++ b/config/config.txt @@ -32,6 +32,19 @@ BAN_LEGACY_SYSTEM ## Uncomment this to have the job system use the player's account creation date, rather than the when they first joined the server for job timers. #USE_ACCOUNT_AGE_FOR_JOBS +## Unhash this to track player playtime in the database. Requires database to be enabled. +#USE_EXP_TRACKING +## Unhash this to enable playtime requirements for head jobs. +#USE_EXP_RESTRICTIONS_HEADS +## Unhash this to override head jobs' playtime requirements with this number of hours. +#USE_EXP_RESTRICTIONS_HEADS_HOURS 15 +## Unhash this to change head jobs' playtime requirements so that they're based on department playtime, rather than crew playtime. +#USE_EXP_RESTRICTIONS_HEADS_DEPARTMENT +## Unhash this to enable playtime requirements for certain non-head jobs, like Engineer and Scientist. +#USE_EXP_RESTRICTIONS_OTHER +## Allows admins to bypass job playtime requirements. +#USE_EXP_RESTRICTIONS_ADMIN_BYPASS + ## log OOC channel LOG_OOC @@ -178,6 +191,14 @@ CHECK_RANDOMIZER ## Ban appeals URL - usually for a forum or wherever people should go to contact your admins. # BANAPPEALS http://justanotherday.example.com +## System command that invokes youtube-dl, used by Play Internet Sound. +## You can install youtube-dl with +## "pip install youtube-dl" if you have pip installed +## from https://github.com/rg3/youtube-dl/releases +## or your package manager +## The default value assumes youtube-dl is in your system PATH +# INVOKE_YOUTUBEDL youtube-dl + ## In-game features ##Toggle for having jobs load up from the .txt # LOAD_JOBS_FROM_TXT @@ -315,4 +336,7 @@ MINUTE_TOPIC_LIMIT 100 #ERROR_MSG_DELAY 50 ## Send a message to IRC when starting a new game -#IRC_ANNOUNCE_NEW_GAME \ No newline at end of file +#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/config/config.txt.rej b/config/config.txt.rej new file mode 100644 index 0000000000..fee79cf699 --- /dev/null +++ b/config/config.txt.rej @@ -0,0 +1,25 @@ +diff a/config/config.txt b/config/config.txt (rejected hunks) +@@ -32,17 +32,17 @@ BAN_LEGACY_SYSTEM + ## Uncomment this to have the job system use the player's account creation date, rather than the when they first joined the server for job timers. + #USE_ACCOUNT_AGE_FOR_JOBS + +-##Unhash this to track player playtime in the database. Requires database to be enabled. ++## Unhash this to track player playtime in the database. Requires database to be enabled. + #USE_EXP_TRACKING +-##Unhash this to enable playtime requirements for head jobs. ++## Unhash this to enable playtime requirements for head jobs. + #USE_EXP_RESTRICTIONS_HEADS +-##Unhash this to override head jobs' playtime requirements with this number of hours. ++## Unhash this to override head jobs' playtime requirements with this number of hours. + #USE_EXP_RESTRICTIONS_HEADS_HOURS 15 +-##Unhash this to change head jobs' playtime requirements so that they're based on department playtime, rather than crew playtime. ++## Unhash this to change head jobs' playtime requirements so that they're based on department playtime, rather than crew playtime. + #USE_EXP_RESTRICTIONS_HEADS_DEPARTMENT +-##Unhash this to enable playtime requirements for certain non-head jobs, like Engineer and Scientist. ++## Unhash this to enable playtime requirements for certain non-head jobs, like Engineer and Scientist. + #USE_EXP_RESTRICTIONS_OTHER +-##Allows admins to bypass job playtime requirements. ++## Allows admins to bypass job playtime requirements. + #USE_EXP_RESTRICTIONS_ADMIN_BYPASS + + diff --git a/config/custom_roundstart_items.txt b/config/custom_roundstart_items.txt new file mode 100644 index 0000000000..8c5a87a676 --- /dev/null +++ b/config/custom_roundstart_items.txt @@ -0,0 +1,10 @@ +//File should be in the format of ckey|exact character name/exact second character name/ALL for all chars|exact job name/exact job name/or put ALL instead of any job names|/path/to/item=amount;/path/to/item=amount +//Each ckey should be in a different line +//if there's multiple entries of a single ckey the later ones will add to the earlier definitions. +//is obviously a comment. +//Recommend defining one job per line, but do what you want. +//test1|Secondary Memenamefortesting|testjob1/test job 2/ALL|/obj/item/gun/energy/laser=3;/obj/item/gun/energy/laser/retro=1;/obj/item/gun/energy=2 +//test1|Memename Lastname|testjob1|/obj/item/device/aicard=3;/obj/item/device/flightpack=1;/obj/item/gun/energy=3 +//kevinz000|Skylar Lineman|ALL|/obj/item/bikehorn/airhorn=1 +//kevinz000|ALL|Clown|/obj/item/bikehorn=1 +JayEhh|ALL|ALL|/obj/item/custom/ceb_soap=1 \ No newline at end of file diff --git a/config/spaceRuinBlacklist.txt b/config/spaceRuinBlacklist.txt index 56f65f6c24..8a1069408e 100644 --- a/config/spaceRuinBlacklist.txt +++ b/config/spaceRuinBlacklist.txt @@ -41,3 +41,4 @@ #_maps/RandomRuins/SpaceRuins/bus.dmm #_maps/RandomRuins/SpaceRuins/miracle.dmm #_maps/RandomRuins/SpaceRuins/dragoontomb.dmm +#_maps/RandomRuins/SpaceRuins/oldstation.dmm diff --git a/html/changelogs/AutoChangeLog-pr-2463.yml b/html/changelogs/AutoChangeLog-pr-2463.yml deleted file mode 100644 index 72c9de6edd..0000000000 --- a/html/changelogs/AutoChangeLog-pr-2463.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Xhuis" -delete-after: True -changes: - - bugfix: "You can now empty storage objects onto floors again." diff --git a/html/changelogs/AutoChangeLog-pr-2509.yml b/html/changelogs/AutoChangeLog-pr-2509.yml deleted file mode 100644 index c54ef55884..0000000000 --- a/html/changelogs/AutoChangeLog-pr-2509.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "MMMiracles" -delete-after: True -changes: - - rscdel: "Cerestation has been decommissioned. Nanotrasen apologizes for any spikes of suicidal tendencies, sporadic outbursts of primitive anger, and other issues that may of been caused during the station's run." diff --git a/html/changelogs/AutoChangeLog-pr-2545.yml b/html/changelogs/AutoChangeLog-pr-2545.yml deleted file mode 100644 index b3f555c1c2..0000000000 --- a/html/changelogs/AutoChangeLog-pr-2545.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Cobby & Cyberboss" -delete-after: True -changes: - - rscadd: "A stealth option for the traitor microlaser has been added. When used, it adds 30 seconds to the cooldown of the device." diff --git a/html/changelogs/AutoChangeLog-pr-2555.yml b/html/changelogs/AutoChangeLog-pr-2555.yml deleted file mode 100644 index 6991c2709b..0000000000 --- a/html/changelogs/AutoChangeLog-pr-2555.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Jay" -delete-after: True -changes: - - bugfix: "Fixes missing Guilmon var that prevented their bodymarkings form showing up in character creation" diff --git a/html/changelogs/AutoChangeLog-pr-2699.yml b/html/changelogs/AutoChangeLog-pr-2699.yml new file mode 100644 index 0000000000..6b0a665b67 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2699.yml @@ -0,0 +1,4 @@ +author: "CitadelStationBot" +delete-after: True +changes: + - rscadd: "Mechs now can be connected to atmos ports" diff --git a/html/changelogs/AutoChangeLog-pr-2701.yml b/html/changelogs/AutoChangeLog-pr-2701.yml new file mode 100644 index 0000000000..8ad7c8ac56 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2701.yml @@ -0,0 +1,5 @@ +author: "BeeSting12" +delete-after: True +changes: + - balance: "The stetchkin APS pistol is smaller." + - rscadd: "The 9mm pistol magazines can be purchased from nuke op uplinks at two telecrystals each." diff --git a/html/changelogs/AutoChangeLog-pr-2709.yml b/html/changelogs/AutoChangeLog-pr-2709.yml new file mode 100644 index 0000000000..15a73203fb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2709.yml @@ -0,0 +1,4 @@ +author: "Kor" +delete-after: True +changes: + - rscadd: "Added Nightmares. They're admin only currently, so as usual, make sure to beg admins to be one." diff --git a/html/changelogs/AutoChangeLog-pr-2710.yml b/html/changelogs/AutoChangeLog-pr-2710.yml new file mode 100644 index 0000000000..3ff0987838 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2710.yml @@ -0,0 +1,6 @@ +author: "VexingRaven" +delete-after: True +changes: + - bugfix: "Changeling Augmented Eyesight ability now grants flash protection when toggled off" + - bugfix: "Changeling Augmented Eyesight ability can now be toggled properly" + - bugfix: "Changeling Augmented Eyesight ability is properly removed when readapting" diff --git a/html/changelogs/AutoChangeLog-pr-2717.yml b/html/changelogs/AutoChangeLog-pr-2717.yml new file mode 100644 index 0000000000..59473e3f39 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2717.yml @@ -0,0 +1,4 @@ +author: "Jay" +delete-after: True +changes: + - rscadd: "SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS" diff --git a/icons/mob/gondolas.dmi b/icons/mob/gondolas.dmi new file mode 100644 index 0000000000..15c83bad93 Binary files /dev/null and b/icons/mob/gondolas.dmi differ diff --git a/icons/mob/hair_extensions.dmi b/icons/mob/hair_extensions.dmi index 5a4ee36157..4ca025ed34 100644 Binary files a/icons/mob/hair_extensions.dmi and b/icons/mob/hair_extensions.dmi differ diff --git a/icons/mob/human_parts_greyscale.dmi b/icons/mob/human_parts_greyscale.dmi index a06f490fa7..b6d51f17ee 100644 Binary files a/icons/mob/human_parts_greyscale.dmi and b/icons/mob/human_parts_greyscale.dmi differ diff --git a/icons/mob/inhands/equipment/instruments_lefthand.dmi b/icons/mob/inhands/equipment/instruments_lefthand.dmi index 3c168f9d3d..11ae1895d1 100644 Binary files a/icons/mob/inhands/equipment/instruments_lefthand.dmi and b/icons/mob/inhands/equipment/instruments_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/instruments_righthand.dmi b/icons/mob/inhands/equipment/instruments_righthand.dmi index bb264c1370..fcf891a581 100644 Binary files a/icons/mob/inhands/equipment/instruments_righthand.dmi and b/icons/mob/inhands/equipment/instruments_righthand.dmi differ diff --git a/icons/mob/inhands/equipment/medical_lefthand.dmi b/icons/mob/inhands/equipment/medical_lefthand.dmi index d55cf81f92..24c79727d0 100644 Binary files a/icons/mob/inhands/equipment/medical_lefthand.dmi and b/icons/mob/inhands/equipment/medical_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/medical_righthand.dmi b/icons/mob/inhands/equipment/medical_righthand.dmi index 764c5c542a..427e29c49c 100644 Binary files a/icons/mob/inhands/equipment/medical_righthand.dmi and b/icons/mob/inhands/equipment/medical_righthand.dmi differ diff --git a/icons/mob/inhands/equipment/security_lefthand.dmi b/icons/mob/inhands/equipment/security_lefthand.dmi index a9f3c36ac1..6ccdfba3fc 100644 Binary files a/icons/mob/inhands/equipment/security_lefthand.dmi and b/icons/mob/inhands/equipment/security_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/security_righthand.dmi b/icons/mob/inhands/equipment/security_righthand.dmi index 201eaa19aa..e3f930a13e 100644 Binary files a/icons/mob/inhands/equipment/security_righthand.dmi and b/icons/mob/inhands/equipment/security_righthand.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 414d12d508..198c2ee663 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index e6c633294e..cef6409ca2 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/inhands/misc/books_lefthand.dmi b/icons/mob/inhands/misc/books_lefthand.dmi index 180e1999a4..71d06d345a 100644 Binary files a/icons/mob/inhands/misc/books_lefthand.dmi and b/icons/mob/inhands/misc/books_lefthand.dmi differ diff --git a/icons/mob/inhands/misc/books_righthand.dmi b/icons/mob/inhands/misc/books_righthand.dmi index ac7ed504d6..906f47bfe9 100644 Binary files a/icons/mob/inhands/misc/books_righthand.dmi and b/icons/mob/inhands/misc/books_righthand.dmi differ diff --git a/icons/mob/inhands/misc/lavaland_lefthand.dmi b/icons/mob/inhands/misc/lavaland_lefthand.dmi new file mode 100644 index 0000000000..74654a89a4 Binary files /dev/null and b/icons/mob/inhands/misc/lavaland_lefthand.dmi differ diff --git a/icons/mob/inhands/misc/lavaland_righthand.dmi b/icons/mob/inhands/misc/lavaland_righthand.dmi new file mode 100644 index 0000000000..c1b2447e22 Binary files /dev/null and b/icons/mob/inhands/misc/lavaland_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/bombs_lefthand.dmi b/icons/mob/inhands/weapons/bombs_lefthand.dmi index a084091414..df168f848d 100644 Binary files a/icons/mob/inhands/weapons/bombs_lefthand.dmi and b/icons/mob/inhands/weapons/bombs_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/bombs_righthand.dmi b/icons/mob/inhands/weapons/bombs_righthand.dmi index 39b50584ce..718441b312 100644 Binary files a/icons/mob/inhands/weapons/bombs_righthand.dmi and b/icons/mob/inhands/weapons/bombs_righthand.dmi differ diff --git a/icons/mob/inhands/weapons/staves_lefthand.dmi b/icons/mob/inhands/weapons/staves_lefthand.dmi index 382e8e4848..1384624a58 100644 Binary files a/icons/mob/inhands/weapons/staves_lefthand.dmi and b/icons/mob/inhands/weapons/staves_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/staves_righthand.dmi b/icons/mob/inhands/weapons/staves_righthand.dmi index 591b5d0a2c..94ee9bd6f1 100644 Binary files a/icons/mob/inhands/weapons/staves_righthand.dmi and b/icons/mob/inhands/weapons/staves_righthand.dmi differ diff --git a/icons/mob/lavaland/lavaland_monsters.dmi b/icons/mob/lavaland/lavaland_monsters.dmi index 3e8ecaf1ce..f072421196 100644 Binary files a/icons/mob/lavaland/lavaland_monsters.dmi and b/icons/mob/lavaland/lavaland_monsters.dmi differ diff --git a/icons/mob/lavaland/watcher.dmi b/icons/mob/lavaland/watcher.dmi index 23960478b3..df4bbb6182 100644 Binary files a/icons/mob/lavaland/watcher.dmi and b/icons/mob/lavaland/watcher.dmi differ diff --git a/icons/mob/mam_body_markings.dmi b/icons/mob/mam_body_markings.dmi index fdb97490c8..ea4ca10627 100644 Binary files a/icons/mob/mam_body_markings.dmi and b/icons/mob/mam_body_markings.dmi differ diff --git a/icons/mob/mam_bodyparts.dmi b/icons/mob/mam_bodyparts.dmi index 7de9b40734..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 59635b8c7d..e7c2d4025d 100644 Binary files a/icons/mob/neck.dmi and b/icons/mob/neck.dmi differ diff --git a/icons/mob/penguins.dmi b/icons/mob/penguins.dmi index fae32b6bcc..c7417f89b4 100644 Binary files a/icons/mob/penguins.dmi and b/icons/mob/penguins.dmi differ diff --git a/icons/mob/screen_full.dmi b/icons/mob/screen_full.dmi index 207f2d3e91..988bf59592 100644 Binary files a/icons/mob/screen_full.dmi and b/icons/mob/screen_full.dmi differ diff --git a/icons/obj/cigarettes.dmi b/icons/obj/cigarettes.dmi index 76ac7bd0a8..ed8f37c4da 100644 Binary files a/icons/obj/cigarettes.dmi and b/icons/obj/cigarettes.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/cloaks.dmi b/icons/obj/clothing/cloaks.dmi index dd1ae7d727..b61ea963da 100644 Binary files a/icons/obj/clothing/cloaks.dmi and b/icons/obj/clothing/cloaks.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/custom.dmi b/icons/obj/custom.dmi new file mode 100644 index 0000000000..3b812e445a Binary files /dev/null and b/icons/obj/custom.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/guns/magic.dmi b/icons/obj/guns/magic.dmi index 45e7b49f4e..b8e77530be 100644 Binary files a/icons/obj/guns/magic.dmi and b/icons/obj/guns/magic.dmi differ diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi index 7667672715..94c0c03835 100644 Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ diff --git a/icons/obj/lavaland/artefacts.dmi b/icons/obj/lavaland/artefacts.dmi index 9b7ed4a35f..ce3030b6c5 100644 Binary files a/icons/obj/lavaland/artefacts.dmi and b/icons/obj/lavaland/artefacts.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index def7a71bd3..74f9df85a7 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/icons/obj/sofa.dmi b/icons/obj/sofa.dmi new file mode 100644 index 0000000000..dafe06a7f1 Binary files /dev/null and b/icons/obj/sofa.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/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index 3753cc6e11..d18a1bf262 100755 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/icons/obj/vending_restock.dmi b/icons/obj/vending_restock.dmi index eac7b8a266..7f8289a087 100644 Binary files a/icons/obj/vending_restock.dmi and b/icons/obj/vending_restock.dmi differ diff --git a/sound/ambience/title1.ogg b/sound/ambience/title1.ogg index 1858f38e33..560fb6342f 100644 Binary files a/sound/ambience/title1.ogg and b/sound/ambience/title1.ogg differ diff --git a/sound/music/flytothemoon_otomatone.ogg b/sound/music/flytothemoon_otomatone.ogg new file mode 100644 index 0000000000..d0267dd12b Binary files /dev/null and b/sound/music/flytothemoon_otomatone.ogg differ diff --git a/strings/greek_letters.txt b/strings/greek_letters.txt new file mode 100644 index 0000000000..b1014aa178 --- /dev/null +++ b/strings/greek_letters.txt @@ -0,0 +1,24 @@ +Alpha +Beta +Gamma +Delta +Epsilon +Zeta +Eta +Theta +Iota +Kappa +Lambda +Mu +Nu +Xi +Omicron +Pi +Rho +Sigma +Tau +Upsilon +Phi +Chi +Psi +Omega \ No newline at end of file diff --git a/strings/numbers_as_words.txt b/strings/numbers_as_words.txt new file mode 100644 index 0000000000..f7c620cd9b --- /dev/null +++ b/strings/numbers_as_words.txt @@ -0,0 +1,20 @@ +One +Two +Three +Four +Five +Six +Seven +Eight +Nine +Ten +Eleven +Twelve +Thirteen +Fourteen +Fifteen +Sixteen +Seventeen +Eighteen +Nineteen +Twenty \ No newline at end of file diff --git a/strings/phonetic_alphabet.txt b/strings/phonetic_alphabet.txt new file mode 100644 index 0000000000..d77c1164ca --- /dev/null +++ b/strings/phonetic_alphabet.txt @@ -0,0 +1,26 @@ +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 \ No newline at end of file diff --git a/strings/round_start_sounds.txt b/strings/round_start_sounds.txt index 620c4af537..01323a6120 100644 --- a/strings/round_start_sounds.txt +++ b/strings/round_start_sounds.txt @@ -18,4 +18,5 @@ sound/music/torvus.ogg sound/music/shootingstars.ogg sound/music/oceanman.ogg sound/music/indeep.ogg -sound/music/goodbyemoonmen.ogg \ No newline at end of file +sound/music/goodbyemoonmen.ogg +sound/music/flytothemoon_otomatone.ogg diff --git a/strings/station_names.txt b/strings/station_names.txt new file mode 100644 index 0000000000..0937a10da6 --- /dev/null +++ b/strings/station_names.txt @@ -0,0 +1,82 @@ +Stanford +Dorf +Alium +Prefix +Clowning +Aegis +Ishimura +Scaredy +Death-World +Mime +Honk +Rogue +MacRagge +Ultrameens +Safety +Paranoia +Explosive +Neckbear +Donk +Muppet +North +West +East +South +Slant-ways +Widdershins +Rimward +Expensive +Procreatory +Imperial +Unidentified +Immoral +Carp +Ork +Pete +Control +Nettle +Aspie +Class +Crab +Fist +Corrogated +Skeleton +Race +Fatguy +Gentleman +Capitalist +Communist +Bear +Beard +Space +Spess +Star +Moon +System +Mining +Neckbeard +Research +Supply +Military +Orbital +Battle +Science +Asteroid +Home +Production +Transport +Delivery +Extraplanetary +Orbital +Correctional +Robot +Hats +Pizza +Taco +Burger +Box +Meta +Pub +Ship +Book +Refactor \ No newline at end of file diff --git a/strings/station_prefixes.txt b/strings/station_prefixes.txt new file mode 100644 index 0000000000..576e68dc1e --- /dev/null +++ b/strings/station_prefixes.txt @@ -0,0 +1,41 @@ +Imperium +Heretical +Cuban +Psychic +Elegant +Common +Uncommon +Rare +Unique +Houseruled +Religious +Atheist +Traditional +Houseruled +Mad +Super +Ultra +Secret +Top Secret +Deep +Death +Zybourne +Central +Main +Government +Uoi +Fat +Automated +Experimental +Augmented +American +Funky +Thin +Legendary +Szechuan +White +Black +Grey +Grim +Electric +Burning \ No newline at end of file diff --git a/strings/station_suffixes.txt b/strings/station_suffixes.txt new file mode 100644 index 0000000000..c30e18c245 --- /dev/null +++ b/strings/station_suffixes.txt @@ -0,0 +1,64 @@ +Station +Frontier +Suffix +Death-trap +Space-hulk +Lab +Hazard +Spess Junk +Fishery +No-Moon +Tomb +Crypt +Hut +Monkey +Bomb +Trade Post +Fortress +Village +Town +City +Edition +Hive +Complex +Base +Facility +Depot +Outpost +Installation +Drydock +Observatory +Array +Relay +Monitor +Platform +Construct +Hangar +Prison +Center +Port +Waystation +Factory +Waypoint +Stopover +Hub +HQ +Office +Object +Fortification +Colony +Planet-Cracker +Roost +Fat Camp +Airstrip +Harbor +Garden +Continent +Environment +Course +Country +Province +Workspace +Metro +Warehouse +Space Junk \ No newline at end of file diff --git a/tgstation.dme b/tgstation.dme index a8d98a120f..105c1f47a0 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -95,6 +95,7 @@ #include "code\__HELPERS\pronouns.dm" #include "code\__HELPERS\radio.dm" #include "code\__HELPERS\sanitize_values.dm" +#include "code\__HELPERS\shell.dm" #include "code\__HELPERS\text.dm" #include "code\__HELPERS\text_vr.dm" #include "code\__HELPERS\time.dm" @@ -173,6 +174,9 @@ #include "code\citadel\cit_uniforms.dm" #include "code\citadel\cit_vendors.dm" #include "code\citadel\dogborgstuff.dm" +#include "code\citadel\custom_loadout\custom_items.dm" +#include "code\citadel\custom_loadout\load_to_mob.dm" +#include "code\citadel\custom_loadout\read_from_file.dm" #include "code\citadel\organs\breasts.dm" #include "code\citadel\organs\eggsack.dm" #include "code\citadel\organs\genitals.dm" @@ -278,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" @@ -375,7 +380,6 @@ #include "code\game\data_huds.dm" #include "code\game\say.dm" #include "code\game\shuttle_engines.dm" -#include "code\game\skincmd.dm" #include "code\game\sound.dm" #include "code\game\world.dm" #include "code\game\area\ai_monitored.dm" @@ -787,6 +791,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" @@ -973,6 +978,7 @@ #include "code\game\objects\structures\beds_chairs\alien_nest.dm" #include "code\game\objects\structures\beds_chairs\bed.dm" #include "code\game\objects\structures\beds_chairs\chair.dm" +#include "code\game\objects\structures\beds_chairs\sofa.dm" #include "code\game\objects\structures\crates_lockers\closets.dm" #include "code\game\objects\structures\crates_lockers\crates.dm" #include "code\game\objects\structures\crates_lockers\closets\bodybag.dm" @@ -1742,6 +1748,7 @@ #include "code\modules\mob\living\simple_animal\friendly\dog.dm" #include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm" #include "code\modules\mob\living\simple_animal\friendly\fox.dm" +#include "code\modules\mob\living\simple_animal\friendly\gondola.dm" #include "code\modules\mob\living\simple_animal\friendly\lizard.dm" #include "code\modules\mob\living\simple_animal\friendly\mouse.dm" #include "code\modules\mob\living\simple_animal\friendly\penguin.dm" @@ -1890,7 +1897,6 @@ #include "code\modules\ninja\suit\suit_attackby.dm" #include "code\modules\ninja\suit\suit_initialisation.dm" #include "code\modules\ninja\suit\suit_process.dm" -#include "code\modules\ninja\suit\suit_verbs_handlers.dm" #include "code\modules\ninja\suit\n_suit_verbs\energy_net_nets.dm" #include "code\modules\ninja\suit\n_suit_verbs\ninja_adrenaline.dm" #include "code\modules\ninja\suit\n_suit_verbs\ninja_cost_check.dm" @@ -2087,12 +2093,14 @@ #include "code\modules\research\xenobiology\xenobio_camera.dm" #include "code\modules\research\xenobiology\xenobiology.dm" #include "code\modules\ruins\lavaland_ruin_code.dm" +#include "code\modules\ruins\lavalandruin_code\biodome_clown_planet.dm" #include "code\modules\ruins\lavalandruin_code\sloth.dm" #include "code\modules\ruins\lavalandruin_code\surface.dm" #include "code\modules\ruins\objects_and_mobs\ash_walker_den.dm" #include "code\modules\ruins\objects_and_mobs\necropolis_gate.dm" #include "code\modules\ruins\objects_and_mobs\sin_ruins.dm" #include "code\modules\ruins\spaceruin_code\asteroid4.dm" +#include "code\modules\ruins\spaceruin_code\bigderelict1.dm" #include "code\modules\ruins\spaceruin_code\crashedclownship.dm" #include "code\modules\ruins\spaceruin_code\crashedship.dm" #include "code\modules\ruins\spaceruin_code\deepstorage.dm" diff --git a/tgstation.dme.rej b/tgstation.dme.rej deleted file mode 100644 index a5ff4dc3cb..0000000000 --- a/tgstation.dme.rej +++ /dev/null @@ -1,13 +0,0 @@ -diff a/tgstation.dme b/tgstation.dme (rejected hunks) -@@ -1746,6 +1745,11 @@ - #include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm" - #include "code\modules\mob\living\simple_animal\hostile\gorilla\gorilla.dm" - #include "code\modules\mob\living\simple_animal\hostile\gorilla\visuals_icons.dm" -+#include "code\modules\mob\living\simple_animal\hostile\jungle\_jungle_mobs.dm" -+#include "code\modules\mob\living\simple_animal\hostile\jungle\leaper.dm" -+#include "code\modules\mob\living\simple_animal\hostile\jungle\mega_arachnid.dm" -+#include "code\modules\mob\living\simple_animal\hostile\jungle\mook.dm" -+#include "code\modules\mob\living\simple_animal\hostile\jungle\seedling.dm" - #include "code\modules\mob\living\simple_animal\hostile\megafauna\blood_drunk_miner.dm" - #include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm" - #include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm" diff --git a/tgui/assets/tgui.css b/tgui/assets/tgui.css index e0cc437429..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:-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+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjAiIHZpZXdCb3g9IjAgMCA0MjUgMjAwIiBvcGFjaXR5PSIuMzMiPgogIDxwYXRoIGQ9Im0gMTc4LjAwMzk5LDAuMDM4NjkgLTcxLjIwMzkzLDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTM0LDYuMDI1NTUgbCAwLDE4Ny44NzE0NyBhIDYuNzYxMzQyMiw2LjAyNTU0OTUgMCAwIDAgNi43NjEzNCw2LjAyNTU0IGwgNTMuMTA3MiwwIGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA2Ljc2MTM1LC02LjAyNTU0IGwgMCwtMTAxLjU0NDAxOCA3Mi4yMTYyOCwxMDQuNjk5Mzk4IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA1Ljc2MDE1LDIuODcwMTYgbCA3My41NTQ4NywwIGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA2Ljc2MTM1LC02LjAyNTU0IGwgMCwtMTg3Ljg3MTQ3IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCAtNi43NjEzNSwtNi4wMjU1NSBsIC01NC43MTY0NCwwIGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCAtNi43NjEzMyw2LjAyNTU1IGwgMCwxMDIuNjE5MzUgTCAxODMuNzY0MTMsMi45MDg4NiBhIDYuNzYxMzQyMiw2LjAyNTU0OTUgMCAwIDAgLTUuNzYwMTQsLTIuODcwMTcgeiIgLz4KICA8cGF0aCBkPSJNIDQuODQ0NjMzMywyMi4xMDg3NSBBIDEzLjQxMjAzOSwxMi41MDE4NDIgMCAwIDEgMTMuNDc3NTg4LDAuMDM5MjQgbCA2Ni4xMTgzMTUsMCBhIDUuMzY0ODE1OCw1LjAwMDczNyAwIDAgMSA1LjM2NDgyMyw1LjAwMDczIGwgMCw3OS44NzkzMSB6IiAvPgogIDxwYXRoIGQ9Im0gNDIwLjE1NTM1LDE3Ny44OTExOSBhIDEzLjQxMjAzOCwxMi41MDE4NDIgMCAwIDEgLTguNjMyOTUsMjIuMDY5NTEgbCAtNjYuMTE4MzIsMCBhIDUuMzY0ODE1Miw1LjAwMDczNyAwIDAgMSAtNS4zNjQ4MiwtNS4wMDA3NCBsIDAsLTc5Ljg3OTMxIHoiIC8+Cjwvc3ZnPgo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4KPCEtLSBodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS80LjAvIC0tPgo=") 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+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjAiIHZpZXdCb3g9IjAgMCAyMDAgMjg5Ljc0MiIgb3BhY2l0eT0iLjMzIj4KICA8cGF0aCBkPSJtIDkzLjUzNzY3NywwIGMgLTE4LjExMzEyNSwwIC0zNC4yMjAxMzMsMy4xMTE2NCAtNDguMzIzNDg0LDkuMzM0MzcgLTEzLjk2NTA5Miw2LjIyMTY3IC0yNC42MTI0NDIsMTUuMDcxMTQgLTMxLjk0MDY1MSwyNi41NDcxIC03LjE4OTkzOTgsMTEuMzM3ODkgLTEwLjMwMTIyNjYsMjQuNzQ5MTEgLTEwLjMwMTIyNjYsNDAuMjM0NzggMCwxMC42NDY2MiAyLjcyNTAwMjYsMjAuNDY0NjUgOC4xNzUxMTE2LDI5LjQ1MjU4IDUuNjE1Mjc3LDguOTg2ODYgMTQuMDM4Mjc3LDE3LjM1MjA0IDI1LjI2ODgyMSwyNS4wOTQzNiAxMS4yMzA1NDQsNy42MDUzMSAyNi41MDc0MjEsMTUuNDE4MzUgNDUuODMwNTE0LDIzLjQzNzgyIDE5Ljk4Mzc0OCw4LjI5NTU3IDM0Ljg0ODg0OCwxNS41NTQ3MSA0NC41OTI5OTgsMjEuNzc2MzggOS43NDQxNCw2LjIyMjczIDE2Ljc2MTcsMTIuODU4NSAyMS4wNTU3MiwxOS45MDk1MSA0LjI5NDA0LDcuMDUyMDggNi40NDE5MywxNS43NjQwOCA2LjQ0MTkzLDI2LjEzNDU5IDAsMTYuMTc3MDIgLTUuMjAxOTYsMjguNDgyMjIgLTE1LjYwNjczLDM2LjkxNjgyIC0xMC4yMzk2LDguNDM0NyAtMjUuMDIyMDMsMTIuNjUyMyAtNDQuMzQ1MTY5LDEyLjY1MjMgLTE0LjAzODE3MSwwIC0yNS41MTUyNDcsLTEuNjU5NCAtMzQuNDMzNjE4LC00Ljk3NzcgLTguOTE4MzcsLTMuNDU2NiAtMTYuMTg1NTcyLC04LjcxMTMgLTIxLjgwMDgzOSwtMTUuNzYzMyAtNS42MTUyNzcsLTcuMDUyMSAtMTAuMDc0Nzk1LC0xNi42NjA4OCAtMTMuMzc3ODk5LC0yOC44MjgxMiBsIC0yNC43NzMxNjI2MjkzOTQ1LDAgMCw1Ni44MjYzMiBDIDMzLjg1Njc2OSwyODYuMDc2MDEgNjMuNzQ5MDQsMjg5Ljc0MjAxIDg5LjY3ODM4MywyODkuNzQyMDEgYyAxNi4wMjAwMjcsMCAzMC43MTk3ODcsLTEuMzgyNyA0NC4wOTczMzcsLTQuMTQ3OSAxMy41NDI3MiwtMi45MDQzIDI1LjEwNDEsLTcuNDY3NiAzNC42ODMwOSwtMTMuNjg5MyA5Ljc0NDEzLC02LjM1OTcgMTcuMzQwNDIsLTE0LjUxOTUgMjIuNzkwNTIsLTI0LjQ3NDggNS40NTAxLC0xMC4wOTMzMiA4LjE3NTExLC0yMi4zOTk1OSA4LjE3NTExLC0zNi45MTY4MiAwLC0xMi45OTc2NCAtMy4zMDIxLC0yNC4zMzUzOSAtOS45MDgyOSwtMzQuMDE0NiAtNi40NDEwNSwtOS44MTcyNSAtMTUuNTI1NDUsLTE4LjUyNzA3IC0yNy4yNTE0NiwtMjYuMTMxMzMgLTExLjU2MDg1LC03LjYwNDI3IC0yNy45MTA4MywtMTUuODMxNDIgLTQ5LjA1MDY2LC0yNC42ODAyMiAtMTcuNTA2NDQsLTcuMTkwMTIgLTMwLjcxOTY2OCwtMTMuNjg5NDggLTM5LjYzODAzOCwtMTkuNDk3MDEgLTguOTE4MzcxLC01LjgwNzUyIC0xOC42MDc0NzQsLTEyLjQzNDA5IC0yNC4wOTY1MjQsLTE4Ljg3NDE3IC01LjQyNjA0MywtNi4zNjYxNiAtOS42NTg4MjYsLTE1LjA3MDAzIC05LjY1ODgyNiwtMjQuODg3MjkgMCwtOS4yNjQwMSAyLjA3NTQxNCwtMTcuMjEzNDUgNi4yMjM0NTQsLTIzLjg1MDMzIDExLjA5ODI5OCwtMTQuMzk3NDggNDEuMjg2NjM4LC0xLjc5NTA3IDQ1LjA3NTYwOSwyNC4zNDc2MiA0LjgzOTM5Miw2Ljc3NDkxIDguODQ5MzUsMTYuMjQ3MjkgMTIuMDI5NTE1LDI4LjQxNTYgbCAyMC41MzIzNCwwIDAsLTU1Ljk5OTY3IGMgLTQuNDc4MjUsLTUuOTI0NDggLTkuOTU0ODgsLTEwLjYzMjIyIC0xNS45MDgzNywtMTQuMzc0MTEgMS42NDA1NSwwLjQ3OTA1IDMuMTkwMzksMS4wMjM3NiA0LjYzODY1LDEuNjQwMjQgNi40OTg2MSwyLjYyNjA3IDEyLjE2NzkzLDcuMzI3NDcgMTcuMDA3MywxNC4xMDM0NSA0LjgzOTM5LDYuNzc0OTEgOC44NDkzNSwxNi4yNDU2NyAxMi4wMjk1MiwyOC40MTM5NyAwLDAgOC40ODEyOCwtMC4xMjg5NCA4LjQ4OTc4LC0wLjAwMiAwLjQxNzc2LDYuNDE0OTQgLTEuNzUzMzksOS40NTI4NiAtNC4xMjM0MiwxMi41NjEwNCAtMi40MTc0LDMuMTY5NzggLTUuMTQ0ODYsNi43ODk3MyAtNC4wMDI3OCwxMy4wMDI5IDEuNTA3ODYsOC4yMDMxOCAxMC4xODM1NCwxMC41OTY0MiAxNC42MjE5NCw5LjMxMTU0IC0zLjMxODQyLC0wLjQ5OTExIC01LjMxODU1LC0xLjc0OTQ4IC01LjMxODU1LC0xLjc0OTQ4IDAsMCAxLjg3NjQ2LDAuOTk4NjggNS42NTExNywtMS4zNTk4MSAtMy4yNzY5NSwwLjk1NTcxIC0xMC43MDUyOSwtMC43OTczOCAtMTEuODAxMjUsLTYuNzYzMTMgLTAuOTU3NTIsLTUuMjA4NjEgMC45NDY1NCwtNy4yOTUxNCAzLjQwMTEzLC0xMC41MTQ4MiAyLjQ1NDYyLC0zLjIxOTY4IDUuMjg0MjYsLTYuOTU4MzEgNC42ODQzLC0xNC40ODgyNCBsIDAuMDAzLDAuMDAyIDguOTI2NzYsMCAwLC01NS45OTk2NyBjIC0xNS4wNzEyNSwtMy44NzE2OCAtMjcuNjUzMTQsLTYuMzYwNDIgLTM3Ljc0NjcxLC03LjQ2NTg2IC05Ljk1NTMxLC0xLjEwNzU1IC0yMC4xODgyMywtMS42NTk4MSAtMzAuNjk2NjEzLC0xLjY1OTgxIHogbSA3MC4zMjE2MDMsMTcuMzA4OTMgMC4yMzgwNSw0MC4zMDQ5IGMgMS4zMTgwOCwxLjIyNjY2IDIuNDM5NjUsMi4yNzgxNSAzLjM0MDgxLDMuMTA2MDIgNC44MzkzOSw2Ljc3NDkxIDguODQ5MzQsMTYuMjQ1NjYgMTIuMDI5NTEsMjguNDEzOTcgbCAyMC41MzIzNCwwIDAsLTU1Ljk5OTY3IGMgLTYuNjc3MzEsLTQuNTkzODEgLTE5LjgzNjQzLC0xMC40NzMwOSAtMzYuMTQwNzEsLTE1LjgyNTIyIHogbSAtMjguMTIwNDksNS42MDU1MSA4LjU2NDc5LDE3LjcxNjU1IGMgLTExLjk3MDM3LC02LjQ2Njk3IC0xMy44NDY3OCwtOS43MTcyNiAtOC41NjQ3OSwtMTcuNzE2NTUgeiBtIDIyLjc5NzA1LDAgYyAyLjc3MTUsNy45OTkyOSAxLjc4NzQxLDExLjI0OTU4IC00LjQ5MzU0LDE3LjcxNjU1IGwgNC40OTM1NCwtMTcuNzE2NTUgeiBtIDE1LjIyMTk1LDI0LjAwODQ4IDguNTY0NzksMTcuNzE2NTUgYyAtMTEuOTcwMzgsLTYuNDY2OTcgLTEzLjg0Njc5LC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk3MDQsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IG0gLTk5LjExMzg0LDIuMjA3NjQgOC41NjQ3OSwxNy43MTY1NSBjIC0xMS45NzAzODIsLTYuNDY2OTcgLTEzLjg0Njc4MiwtOS43MTcyNiAtOC41NjQ3OSwtMTcuNzE2NTUgeiBtIDIyLjc5NTQyLDAgYyAyLjc3MTUsNy45OTkyOSAxLjc4NzQxLDExLjI0OTU4IC00LjQ5MzU0LDE3LjcxNjU1IGwgNC40OTM1NCwtMTcuNzE2NTUgeiIgLz4KPC9zdmc+CjwhLS0gVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIGEgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1TaGFyZUFsaWtlIDQuMCBJbnRlcm5hdGlvbmFsIExpY2Vuc2UuIC0tPgo8IS0tIGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LXNhLzQuMC8gLS0+Cg==") 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 +@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 111e452df1..a41d648fe1 100644 --- a/tgui/assets/tgui.js +++ b/tgui/assets/tgui.js @@ -1,17 +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),d=1;for(s>u&&u+l>s&&(d=-1,u+=l-1,s+=l-1);l-- >0;)u in n?n[s]=n[u]:delete n[s],s+=d,u+=d;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(d,f,h){for(var m,v,g=i(d),b=r(g),y=a(f,h,3),x=o(b.length),_=0,w=e?s(d,x):n?s(d,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),d=t(82)("id"),f=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(!f(t,d)){if(!g(t))return"F";if(!e)return"E";r(t,d,++y)}return"O"+t[d]},_=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"),d=Object.isExtensible||o,f=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 f(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)?d(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)?d(t)?c(t,l)&&c(t[l],this._i):v(this).has(t):!1}}),i},def:function(t,e,n){return d(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),d=t(66);e.exports=function(t,e,n,f,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=f.getConstructor(e,t,h,b),o(g.prototype,n);return d(g,t),x[t]=g,r(r.G+r.W+r.F*(g!=v),x),m||f.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,d,f,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,d=(l?y:n)[c],f=b&&l?s(d,a):g&&"function"==typeof d?s(Function.call,d):d,y&&!l&&o(y,c,d),x[c]!=d&&i(x,c,f),g&&_[c]!=d&&(_[c]=d)};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,d,f=u(t),h=a(n,p,e?2:1),m=0;if("function"!=typeof f)throw TypeError(t+" is not iterable!");if(i(f))for(c=s(t.length);c>m;m++)e?h(o(l=t[m])[0],l[1]):h(t[m]);else for(d=f.call(t);!(l=d.next()).done;)r(d,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,d=t(83)("iterator"),f=!([].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(!f&&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[d]||A[h]||y&&A[y],T=O||E(y);if(O){var j=l(T.call(new t));c(j,S,!0),!a&&s(A,h)&&o(j,d,g),C&&O.name!==v&&(P=!0,T=function(){return O.call(this)})}if(a&&!_||!f&&!P&&A[d]||o(A,d,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*(f||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),d=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(d)};else if(u){var f=1,h=document.createTextNode("");new u(d).observe(h,{characterData:!0}),i=function(){h.data=f=-f}}else i=c&&c.resolve?function(){c.resolve().then(d)}:function(){s.call(o,d)};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 d,f=i(o[u++]),h=c?p(f).concat(c(f)):p(f),m=h.length,v=0;m>v;)l.call(f,d=h[v++])&&(n[d]=f[d]);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,d=r.call(p,Math.ceil(l/p.length));return d.length>l&&(d=d.slice(0,l)),o?d+s:s+d}},{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(d),a(a.P+a.F*i(function(){return!!o[t]()||u[t]()!=u}),"String",n)},d=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,d=c.setImmediate,f=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)};d&&f||(d=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},f=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:d,clear:f}},{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),d=t(33),f=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,j=r.getDesc,M=r.setDescs,R={};o||(a=!f(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 j(t,e)}catch(n){}return c(t,e)?s(!C.propertyIsEnumerable.call(t,e),t[e]):void 0},r.setDescs=M=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:M});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(" - {{data.occupant.name ? data.occupant.name : "No Occupant"}} {{#if data.hasOccupant}} - {{data.occupant.stat == 0 ? "Conscious" : data.occupant.stat == 1 ? "Unconcious" : "Dead"}} + {{data.occupant.stat}} - {{Math.round(adata.occupant.bodyTemperature)}} K + {{data.occupant.bodyTemperature}} K {{Math.round(adata.occupant.health)}} + state='{{data.occupant.health >= 0 ? "good" : "average"}}'>{{data.occupant.health}} {{#each [{label: "Brute", type: "bruteLoss"}, {label: "Respiratory", type: "oxyLoss"}, {label: "Toxin", type: "toxLoss"}, {label: "Burn", type: "fireLoss"}]}} - {{Math.round(adata.occupant[type])}} + {{data.occupant[type]}} {{/each}} {{/if}} @@ -49,7 +28,7 @@ action='power'>{{data.isOperating ? "On" : "Off"}} - {{Math.round(adata.cellTemperature)}} K + {{data.cellTemperature}} K {{data.isOpen ? "Open" : "Closed"}} @@ -63,7 +42,7 @@ {{#if data.isBeakerLoaded}} {{#each adata.beakerContents}} - {{Math.fixed(volume, 2)}} units of {{name}}
    + {{volume}} units of {{name}}
    {{else}} Beaker Empty {{/each}} diff --git a/tgui/src/interfaces/dogborg_sleeper.ract b/tgui/src/interfaces/dogborg_sleeper.ract index 46da372198..e7613c0097 100644 --- a/tgui/src/interfaces/dogborg_sleeper.ract +++ b/tgui/src/interfaces/dogborg_sleeper.ract @@ -1,24 +1,10 @@ - - {{data.occupant.name ? data.occupant.name : "No Occupant"}} {{#if data.occupied}} - {{data.occupant.stat == 0 ? "Conscious" : data.occupant.stat == 1 ? "Unconcious" : "Dead"}} + {{data.occupant.stat}} - - Eject + + {{data.open ? "Open" : "Closed"}} {{#each data.chems}} diff --git a/tgui/src/interfaces/pandemic.ract b/tgui/src/interfaces/pandemic.ract index 4ffccd0f66..f9bfc0566b 100644 --- a/tgui/src/interfaces/pandemic.ract +++ b/tgui/src/interfaces/pandemic.ract @@ -1,84 +1,113 @@ - - {{#partial button}} - - Empty and eject - - - Empty - - - Eject - - {{/partial}} - {{#if data.has_beaker}} - - {{#if data.beaker_empty}} - The beaker is empty! - {{else}} - - {{#if data.has_blood}} - {{data.blood.dna}} - {{data.blood.type}} - {{else}} - - No blood sample detected. - - {{/if}} - - {{/if}} - - {{else}} - - No beaker loaded. - - {{/if}} - -{{#if data.has_blood}} - - {{#each data.viruses}} - - {{#partial button}} - {{#if is_adv}} - - Name advanced disease - - {{/if}} - - Create virus culture bottle - - {{/partial}} - {{agent}} - {{description}} - {{spread}} - {{cure}} - {{#if is_adv}} - {{resistance}} - {{stealth}} - {{stage_speed}} - - {{#each symptoms}} - {{name}}
    - {{/each}} -
    +{{#if data.mode == 1}} + + {{#partial button}} + + Empty and eject + + + Empty + + + Eject + + {{/partial}} + {{#if data.has_beaker}} + + {{#if data.beaker_empty}} + The beaker is empty! + {{else}} + + {{#if data.has_blood}} + {{data.blood.dna}} + {{data.blood.type}} + {{else}} + + No blood sample detected. + + {{/if}} + {{/if}} - - {{else}} - - No detectable virus in the blood sample. - - {{/each}} -
    - - {{#each data.resistances}} - - - Create vaccine bottle - {{else}} - No antibodies detected in the blood sample. + No beaker loaded. - {{/each}} + {{/if}} -{{/if}} \ No newline at end of file + {{#if data.has_blood}} + + {{#each data.viruses}} + + {{#partial button}} + {{#if is_adv}} + + Name advanced disease + + {{/if}} + + Create virus culture bottle + + {{/partial}} + {{agent}} + {{description}} + {{spread}} + {{cure}} + {{#if is_adv}} + + {{#each symptoms}} + + {{name}} +
    + {{/each}} +
    + {{resistance}} + {{stealth}} + {{stage_speed}} + {{transmission}} + {{/if}} +
    + {{else}} + + No detectable virus in the blood sample. + + {{/each}} +
    + + {{#each data.resistances}} + + + Create vaccine bottle + + + {{else}} + + No antibodies detected in the blood sample. + + {{/each}} + + {{/if}} +{{else}} + + Back + + {{#with data.symptom}} + + + {{desc}} + {{#if neutered}} +
    + This symptom has been neutered, and has no effect. It will still affect the virus' statistics. + {{/if}} +
    + + {{level}} + {{resistance}} + {{stealth}} + {{stage_speed}} + {{transmission}} + + + {{{threshold_desc}}} + + {{/with}} +{{/if}} diff --git a/tgui/src/interfaces/smartvend.ract b/tgui/src/interfaces/smartvend.ract new file mode 100644 index 0000000000..ebe02d1235 --- /dev/null +++ b/tgui/src/interfaces/smartvend.ract @@ -0,0 +1,48 @@ + + +{{#partial button}} + {{#if data.isdryer}}{{data.drying ? 'Stop drying' : 'Dry'}}{{/if}} +{{/partial}} +{{#if data.contents.length == 0}} + + Unfortunately, this {{data.name}} is empty. + +{{else}} +
    +
    +
    + Item +
    +
    + Quantity +
    +
    + {{#if data.verb}}{{data.verb}}{{else}}Dispense{{/if}} +
    +
    + {{#each data.contents}} +
    +
    + {{name}} +
    +
    + {{amount}} +
    +
    +
    +
    + = 1) ? null : 'disabled'}} params='{ "name" : {{name}}, "amount" : 1 }' > + One + +
    +
    + 1) ? null : 'disabled'}} params='{ "name" : {{name}} }' > + Many + +
    +
    +
    + {{/each}} +
    +{{/if}} +
    \ No newline at end of file diff --git a/tools/github_webhook_processor.php b/tools/WebhookProcessor/github_webhook_processor.php similarity index 72% rename from tools/github_webhook_processor.php rename to tools/WebhookProcessor/github_webhook_processor.php index 4ccdafa3f3..dde21e4f43 100644 --- a/tools/github_webhook_processor.php +++ b/tools/WebhookProcessor/github_webhook_processor.php @@ -17,27 +17,24 @@ //CONFIG START (all defaults are random examples, do change them) //Use single quotes for config options that are strings. - -//Github lets you have it sign the message with a secret that you can validate. This prevents people from faking events. -//This var should match the secret you configured for this webhook on github. -//This is required as otherwise somebody could trick the script into leaking the api key. + +//These are all default settings that are described in secret.php $hookSecret = '08ajh0qj93209qj90jfq932j32r'; - -//Api key for pushing changelogs. $apiKey = '209ab8d879c0f987d06a09b9d879c0f987d06a09b9d8787d0a089c'; - -//servers to announce PRs to. +$repoOwnerAndName = "tgstation/tgstation"; $servers = array(); -/* -$servers[0] = array(); -$servers[0]['address'] = 'game.tgstation13.org'; -$servers[0]['port'] = '1337'; -$servers[0]['comskey'] = '89aj90cq2fm0amc90832mn9rm90'; -$servers[1] = array(); -$servers[1]['address'] = 'game.tgstation13.org'; -$servers[1]['port'] = '2337'; -$servers[1]['comskey'] = '89aj90cq2fm0amc90832mn9rm90'; -*/ +$enable_live_tracking = true; +$path_to_script = 'tools/WebhookProcessor/github_webhook_processor.php'; +$tracked_branch = "master"; +$trackPRBalance = true; +$prBalanceJson = ''; +$startingPRBalance = 3; +$maintainer_team_id = 133041; +$validation = "org"; +$validation_count = 1; +$tracked_branch = 'master'; + +require_once 'secret.php'; //CONFIG END set_error_handler(function($severity, $message, $file, $line) { @@ -138,7 +135,7 @@ function tag_pr($payload, $opened) { if(strpos($lowertitle, 'refactor') !== FALSE) $tags[] = 'Refactor'; - if(strpos($lowertitle, 'revert') !== FALSE || strpos($lowertitle, 'removes') !== FALSE) + if(strpos(strtolower($title), 'revert') !== FALSE || strpos(strtolower($title), 'removes') !== FALSE) $tags[] = 'Revert/Removal'; } @@ -168,7 +165,7 @@ function tag_pr($payload, $opened) { if(strpos($lowertitle, '[wip]') !== FALSE) $tags[] = 'Work In Progress'; - $url = $payload['pull_request']['base']['repo']['url'] . '/issues/' . $payload['pull_request']['number'] . '/labels'; + $url = $payload['pull_request']['issue_url'] . '/labels'; $existing_labels = file_get_contents($url, false, stream_context_create($scontext)); $existing_labels = json_decode($existing_labels, true); @@ -195,6 +192,12 @@ function handle_pr($payload) { switch ($payload["action"]) { case 'opened': tag_pr($payload, true); + if(get_pr_code_friendliness($payload) < 0){ + $balances = pr_balances(); + $author = $payload['pull_request']['user']['login']; + if(isset($balances[$author]) && $balances[$author] < 0) + create_comment($payload, 'You currently have a negative Fix/Feature pull request delta of ' . $balances[$author] . '. Maintainers may close this PR at will. Fixing issues or improving the codebase will improve this score.'); + } break; case 'edited': case 'synchronize': @@ -209,7 +212,10 @@ function handle_pr($payload) { } else { $action = 'merged'; + auto_update($payload); checkchangelog($payload, true, true); + update_pr_balance($payload); + $validated = TRUE; //pr merged events always get announced. } break; default: @@ -223,16 +229,147 @@ function handle_pr($payload) { $msg = '['.$payload['pull_request']['base']['repo']['full_name'].'] Pull Request '.$action.' by '.htmlSpecialChars($payload['sender']['login']).': '.htmlSpecialChars('#'.$payload['pull_request']['number'].' '.$payload['pull_request']['user']['login'].' - '.$payload['pull_request']['title']).''; sendtoallservers('?announce='.urlencode($msg), $payload); - } +//creates a comment on the payload issue +function create_comment($payload, $comment){ + apisend($payload['pull_request']['comments_url'], 'POST', json_encode(array('body' => $comment))); +} + +//returns the payload issue's labels as a flat array +function get_pr_labels_array($payload){ + $url = $payload['pull_request']['issue_url'] . '/labels'; + $issue = json_decode(apisend($url), true); + $result = array(); + foreach($issue as $l) + $result[] = $l['name']; + return $result; +} + +//helper for getting the path the the balance json file +function pr_balance_json_path(){ + global $prBalanceJson; + return $prBalanceJson != '' ? $prBalanceJson : 'pr_balances.json'; +} + +//return the assoc array of login -> balance for prs +function pr_balances(){ + $path = pr_balance_json_path(); + if(file_exists($path)) + return json_decode(file_get_contents($path), true); + else + return array(); +} + +//returns the difference in PR balance a pull request would cause +function get_pr_code_friendliness($payload, $oldbalance = null){ + global $startingPRBalance; + if($oldbalance == null) + $oldbalance = $startingPRBalance; + $labels = get_pr_labels_array($payload); + //anything not in this list defaults to 0 + $label_values = array( + 'Fix' => 2, + 'Refactor' => 2, + 'Code Improvement' => 1, + 'Priority: High' => 4, + 'Priority: CRITICAL' => 5, + 'Atmospherics' => 4, + 'Logging' => 1, + 'Feedback' => 1, + 'Performance' => 3, + 'Feature' => -1, + 'Balance/Rebalance' => -1, + 'Tweak' => -1, + 'PRB: Reset' => $startingPRBalance - $oldbalance, + ); + + $affecting = 0; + $is_neutral = FALSE; + $found_something_positive = false; + foreach($labels as $l){ + if($l == 'PRB: No Update') { //no effect on balance + $affecting = 0; + break; + } + else if(isset($label_values[$l])) { + $friendliness = $label_values[$l]; + if($friendliness > 0) + $found_something_positive = true; + $affecting = $found_something_positive ? max($affecting, $friendliness) : $friendliness; + } + } + return $affecting; +} + +function is_maintainer($payload, $author){ + global $maintainer_team_id; + $repo_is_org = $payload['pull_request']['base']['repo']['owner']['type'] == 'Organization'; + if($maintainer_team_id == null || !$repo_is_org) { + $collaburl = $payload['pull_request']['base']['repo']['collaborators_url'] . '/' . $author . '/permissions'; + $perms = json_decode(apisend($collaburl), true); + $permlevel = $perms['permission']; + return $permlevel == 'admin' || $permlevel == 'write'; + } + else { + $check_url = 'https://api.github.com/teams/' . $maintainer_team_id . '/memberships/' . $author; + $result = json_decode(apisend($check_url), true); + return isset($result['state']); //this field won't be here if they aren't a member + } +} + +//payload is a merged pull request, updates the pr balances file with the correct positive or negative balance based on comments +function update_pr_balance($payload) { + global $startingPRBalance; + global $trackPRBalance; + if(!$trackPRBalance) + return; + $author = $payload['pull_request']['user']['login']; + if(is_maintainer($payload, $author)) //immune + return; + $balances = pr_balances(); + if(!isset($balances[$author])) + $balances[$author] = $startingPRBalance; + $friendliness = get_pr_code_friendliness($payload, $balances[$author]); + $balances[$author] += $friendliness; + if($balances[$author] < 0 && $friendliness < 0) + create_comment($payload, 'Your Fix/Feature pull request delta is currently below zero (' . $balances[$author] . '). Maintainers may close future Feature/Tweak/Balance PRs. Fixing issues or helping to improve the codebase will raise this score.'); + else if($balances[$author] >= 0 && ($balances[$author] - $friendliness) < 0) + create_comment($payload, 'Your Fix/Feature pull request delta is now above zero (' . $balances[$author] . '). Feel free to make Feature/Tweak/Balance PRs.'); + $balances_file = fopen(pr_balance_json_path(), 'w'); + fwrite($balances_file, json_encode($balances)); + fclose($balances_file); +} + +function auto_update($payload){ + global $enable_live_tracking; + global $path_to_script; + global $repoOwnerAndName; + global $tracked_branch; + if(!$enable_live_tracking || !has_tree_been_edited($payload, $path_to_script) || $payload['pull_request']['base']['ref'] != $tracked_branch) + return; + + $content = file_get_contents('https://raw.githubusercontent.com/' . $repoOwnerAndName . '/' . $tracked_branch . '/'. $path_to_script); + + create_comment($payload, "Edit detected. Self updating... Here is my new code:\n``" . "`HTML+PHP\n" . $content . "\n``" . '`'); + + $code_file = fopen(basename($path_to_script), 'w'); + fwrite($code_file, $content); + fclose($code_file); +} + +$github_diff = null; + function has_tree_been_edited($payload, $tree){ - //go to the diff url - $url = $payload['pull_request']['diff_url']; - $content = file_get_contents($url); + global $github_diff; + if ($github_diff === null) { + //go to the diff url + $url = $payload['pull_request']['diff_url']; + $github_diff = file_get_contents($url); + } //find things in the _maps/map_files tree //e.g. diff --git a/_maps/map_files/Cerestation/cerestation.dmm b/_maps/map_files/Cerestation/cerestation.dmm - return $content !== FALSE && strpos($content, 'diff --git a/' . $tree) !== FALSE; + return $github_diff !== FALSE && strpos($github_diff, 'diff --git a/' . $tree) !== FALSE; } function checkchangelog($payload, $merge = false, $compile = true) { diff --git a/tools/WebhookProcessor/secret.php b/tools/WebhookProcessor/secret.php new file mode 100644 index 0000000000..abea10564f --- /dev/null +++ b/tools/WebhookProcessor/secret.php @@ -0,0 +1,49 @@ +