diff --git a/SQL/database_changelog.txt b/SQL/database_changelog.txt index 5d3c3ccec2..ce5c2c654d 100644 --- a/SQL/database_changelog.txt +++ b/SQL/database_changelog.txt @@ -1,17 +1,114 @@ + +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`; + +UPDATE `schema_revision` SET minor = 1; + +Remember to add a prefix to the table name if you use them. + +---------------------------------------------------- + +Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255. + +The latest database version is 3.0; The query to update the schema revision table is: + +INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 0); +or +INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (3, 0); + +---------------------------------------------------- +28 June 2017, by oranges +Added schema_revision to store the current db revision, why start at 3.0? + +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 +121,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 +164,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 +187,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 +197,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 +207,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 +217,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 +230,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 +240,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 +262,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. diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql index 4c76e1f275..9795b90672 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, @@ -119,7 +117,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 +133,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, @@ -155,7 +152,6 @@ CREATE TABLE `death` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `feedback` -- @@ -257,6 +253,21 @@ CREATE TABLE `messages` ( ) 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` -- @@ -272,6 +283,7 @@ CREATE TABLE `player` ( `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 +301,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,6 +403,17 @@ 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 */; diff --git a/SQL/tgstation_schema.sql.rej b/SQL/tgstation_schema.sql.rej new file mode 100644 index 0000000000..51068bed4e --- /dev/null +++ b/SQL/tgstation_schema.sql.rej @@ -0,0 +1,9 @@ +diff a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql (rejected hunks) +@@ -268,7 +283,6 @@ CREATE TABLE `player` ( + `ip` int(10) unsigned NOT NULL, + `computerid` varchar(32) NOT NULL, + `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', +- `exp` mediumtext, + PRIMARY KEY (`id`), + UNIQUE KEY `ckey` (`ckey`), + KEY `idx_player_cid_ckey` (`computerid`,`ckey`), diff --git a/SQL/tgstation_schema_prefixed.sql b/SQL/tgstation_schema_prefixed.sql index f717bf96ad..b810a82ca3 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 */; @@ -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, @@ -129,10 +127,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 +141,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, @@ -253,6 +253,21 @@ CREATE TABLE `SS13_messages` ( ) 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` -- @@ -268,6 +283,7 @@ CREATE TABLE `SS13_player` ( `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 +301,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,6 +403,17 @@ 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 */; diff --git a/SQL/tgstation_schema_prefixed.sql.rej b/SQL/tgstation_schema_prefixed.sql.rej new file mode 100644 index 0000000000..dd4bc6a7f5 --- /dev/null +++ b/SQL/tgstation_schema_prefixed.sql.rej @@ -0,0 +1,9 @@ +diff a/SQL/tgstation_schema_prefixed.sql b/SQL/tgstation_schema_prefixed.sql (rejected hunks) +@@ -268,7 +297,6 @@ CREATE TABLE `SS13_player` ( + `ip` int(10) unsigned NOT NULL, + `computerid` varchar(32) NOT NULL, + `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', +- `exp` mediumtext, + PRIMARY KEY (`id`), + UNIQUE KEY `ckey` (`ckey`), + KEY `idx_player_cid_ckey` (`computerid`,`ckey`), diff --git a/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/_compile_options.dm b/code/_compile_options.dm index c1f02856a7..54459da0b5 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 1 diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 43b523fcd1..1d8f7add38 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -103,6 +103,13 @@ 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/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 var/hard_popcap = 0 @@ -337,6 +344,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") 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/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index 74536c7f0f..b85e99a0b9 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -1,269 +1,285 @@ -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 - -/datum/feedback_variable/proc/set_details(deets) - details = "\"[deets]\"" +SUBSYSTEM_DEF(blackbox) + name = "Blackbox" + wait = 6000 + flags = SS_NO_TICK_CHECK + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + init_order = INIT_ORDER_BLACKBOX -/datum/feedback_variable/proc/add_details(deets) - if (!details) - set_details(deets) + 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) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')") + 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/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 - details += " | \"[deets]\"" - -/datum/feedback_variable/proc/get_details() - return details - -/datum/feedback_variable/proc/get_parsed() + 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(text) + if (istext(text)) + details = text + +/datum/feedback_variable/proc/add_details(text) + if (istext(text)) + if (!details) + details = "\"[text]\"" + else + details += " | \"[text]\"" + +/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 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/job.dm b/code/controllers/subsystem/job.dm index 012c2e33af..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 @@ -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/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/datums/mind.dm b/code/datums/mind.dm index 66e0a1f922..78a402a2dc 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1481,6 +1481,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) @@ -1728,7 +1729,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/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/modules/admin/admin.dm b/code/modules/admin/admin.dm index 5943e753a0..b88451b724 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -31,6 +31,8 @@ if(M.client) body += " played by [M.client] " body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" + if(config.use_exp_tracking) + body += "\[" + M.client.get_exp_living() + "\]" if(isnewplayer(M)) body += " Hasn't Entered Game " diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 5507d856f6..f8e5c31687 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -61,6 +61,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin()) /client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/ /client/proc/cmd_admin_create_centcom_report, /client/proc/cmd_change_command_name, + /client/proc/cmd_admin_check_player_exp, /* shows players by playtime */ /client/proc/toggle_antag_hud, /*toggle display of the admin antag hud*/ /client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/ /client/proc/customiseSNPC, /* Customise any interactive crewmembers in the world */ diff --git a/code/modules/admin/admin_verbs.dm.rej b/code/modules/admin/admin_verbs.dm.rej new file mode 100644 index 0000000000..c2f884d37c --- /dev/null +++ b/code/modules/admin/admin_verbs.dm.rej @@ -0,0 +1,10 @@ +diff a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm (rejected hunks) +@@ -663,7 +664,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, AVerbsHideable()) + + if(!holder) + return +- ++ + if(has_antag_hud()) + toggle_antag_hud() + diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index f3de43b6c6..f7aa6caaf6 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -22,6 +22,24 @@ else if(href_list["stickyban"]) stickyban(href_list["stickyban"],href_list) + else if(href_list["getplaytimewindow"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locate(href_list["getplaytimewindow"]) in GLOB.mob_list + if(!M) + to_chat(usr, "ERROR: Mob not found.") + return + cmd_show_exp_panel(M.client) + + else if(href_list["toggleexempt"]) + if(!check_rights(R_ADMIN)) + return + var/client/C = locate(href_list["toggleexempt"]) in GLOB.clients + if(!C) + to_chat(usr, "ERROR: Client not found.") + return + toggle_exempt_status(C) + else if(href_list["makeAntag"]) if (!SSticker.mode) to_chat(usr, "Not until the round starts!") diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 3d89cd49f5..62f3cab1d1 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1,1213 +1,1260 @@ -/client/proc/cmd_admin_drop_everything(mob/M in GLOB.mob_list) - set category = null - set name = "Drop Everything" - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - - var/confirm = alert(src, "Make [M] drop everything?", "Message", "Yes", "No") - if(confirm != "Yes") - return - - for(var/obj/item/W in M) - if(!M.dropItemToGround(W)) - qdel(W) - M.regenerate_icons() - - log_admin("[key_name(usr)] made [key_name(M)] drop everything!") - var/msg = "[key_name_admin(usr)] made [key_name_admin(M)] drop everything!" - message_admins(msg) - admin_ticket_log(M, msg) - SSblackbox.add_details("admin_verb","Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list) - set category = "Special Verbs" - set name = "Subtle Message" - - if(!ismob(M)) - return - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - message_admins("[key_name_admin(src)] has started answering [key_name(M.key, 0, 0)]'s prayer.") - var/msg = input("Message:", text("Subtle PM to [M.key]")) as text - - if (!msg) - message_admins("[key_name_admin(src)] decided not to answer [key_name(M.key, 0, 0)]'s prayer") - return - if(usr) - if (usr.client) - if(usr.client.holder) - to_chat(M, "You hear a voice in your head... [msg]") - - log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") - msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" - message_admins(msg) - admin_ticket_log(M, msg) - SSblackbox.add_details("admin_verb","Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_world_narrate() - set category = "Special Verbs" - set name = "Global Narrate" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - var/msg = input("Message:", text("Enter the text you wish to appear to everyone:")) as text - - if (!msg) - return - to_chat(world, "[msg]") - log_admin("GlobalNarrate: [key_name(usr)] : [msg]") - message_admins("[key_name_admin(usr)] Sent a global narrate") - SSblackbox.add_details("admin_verb","Global Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_direct_narrate(mob/M) - set category = "Special Verbs" - set name = "Direct Narrate" - - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - - if(!M) - M = input("Direct narrate to whom?", "Active Players") as null|anything in GLOB.player_list - - if(!M) - return - - var/msg = input("Message:", text("Enter the text you wish to appear to your target:")) as text - - if( !msg ) - return - - to_chat(M, msg) - log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]") - msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
" - message_admins(msg) - admin_ticket_log(M, msg) - SSblackbox.add_details("admin_verb","Direct Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_local_narrate(atom/A) - set category = "Special Verbs" - set name = "Local Narrate" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - if(!A) - return - var/range = input("Range:", "Narrate to mobs within how many tiles:", 7) as num - if(!range) - return - var/msg = input("Message:", text("Enter the text you wish to appear to everyone within view:")) as text - if (!msg) - return - for(var/mob/M in view(range,A)) - to_chat(M, msg) - - log_admin("LocalNarrate: [key_name(usr)] at [get_area(A)][COORD(A)]: [msg]") - message_admins(" LocalNarrate: [key_name_admin(usr)] at [get_area(A)][ADMIN_JMP(A)]: [msg]
") - SSblackbox.add_details("admin_verb","Local Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_godmode(mob/M in GLOB.mob_list) - set category = "Special Verbs" - set name = "Godmode" - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - M.status_flags ^= GODMODE - to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") - - log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") - var/msg = "[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" - message_admins(msg) - admin_ticket_log(M, msg) - SSblackbox.add_details("admin_toggle","Godmode|[M.status_flags & GODMODE]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/proc/cmd_admin_mute(whom, mute_type, automute = 0) - if(!whom) - return - - var/muteunmute - var/mute_string - var/feedback_string - switch(mute_type) - if(MUTE_IC) - mute_string = "IC (say and emote)" - feedback_string = "IC" - if(MUTE_OOC) - mute_string = "OOC" - feedback_string = "OOC" - if(MUTE_PRAY) - mute_string = "pray" - feedback_string = "Pray" - if(MUTE_ADMINHELP) - mute_string = "adminhelp, admin PM and ASAY" - feedback_string = "Adminhelp" - if(MUTE_DEADCHAT) - mute_string = "deadchat and DSAY" - feedback_string = "Deadchat" - if(MUTE_ALL) - mute_string = "everything" - feedback_string = "Everything" - else - return - - var/client/C - if(istype(whom, /client)) - C = whom - else if(istext(whom)) - C = GLOB.directory[whom] - else - return - - var/datum/preferences/P - if(C) - P = C.prefs - else - P = GLOB.preferences_datums[whom] - if(!P) - return - - if(automute) - if(!config.automute_on) - return - else - if(!check_rights()) - return - - if(automute) - muteunmute = "auto-muted" - P.muted |= mute_type - log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(whom)] from [mute_string]") - message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(whom)] from [mute_string].") - if(C) - to_chat(C, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") - SSblackbox.add_details("admin_toggle","Auto Mute [feedback_string]|1") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - - if(P.muted & mute_type) - muteunmute = "unmuted" - P.muted &= ~mute_type - else - muteunmute = "muted" - P.muted |= mute_type - - log_admin("[key_name(usr)] has [muteunmute] [key_name(whom)] from [mute_string]") - message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(whom)] from [mute_string].") - if(C) - to_chat(C, "You have been [muteunmute] from [mute_string] by [key_name(usr, include_name = FALSE)].") - SSblackbox.add_details("admin_toggle","Mute [feedback_string]|[P.muted & mute_type]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -//I use this proc for respawn character too. /N -/proc/create_xeno(ckey) - if(!ckey) - var/list/candidates = list() - for(var/mob/M in GLOB.player_list) - if(M.stat != DEAD) - continue //we are not dead! - if(!(ROLE_ALIEN in M.client.prefs.be_special)) - continue //we don't want to be an alium - if(M.client.is_afk()) - continue //we are afk - if(M.mind && M.mind.current && M.mind.current.stat != DEAD) - continue //we have a live body we are tied to - candidates += M.ckey - if(candidates.len) - ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in candidates - else - to_chat(usr, "Error: create_xeno(): no suitable candidates.") - if(!istext(ckey)) - return 0 - - var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Praetorian","Hunter","Sentinel","Drone","Larva") - var/obj/effect/landmark/spawn_here = GLOB.xeno_spawn.len ? pick(GLOB.xeno_spawn) : null - var/mob/living/carbon/alien/new_xeno - switch(alien_caste) - if("Queen") - new_xeno = new /mob/living/carbon/alien/humanoid/royal/queen(spawn_here) - if("Praetorian") - new_xeno = new /mob/living/carbon/alien/humanoid/royal/praetorian(spawn_here) - if("Hunter") - new_xeno = new /mob/living/carbon/alien/humanoid/hunter(spawn_here) - if("Sentinel") - new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(spawn_here) - if("Drone") - new_xeno = new /mob/living/carbon/alien/humanoid/drone(spawn_here) - if("Larva") - new_xeno = new /mob/living/carbon/alien/larva(spawn_here) - else - return 0 - if(!spawn_here) - SSjob.SendToLateJoin(new_xeno, FALSE) - - new_xeno.ckey = ckey - var/msg = "[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste]." - message_admins(msg) - admin_ticket_log(new_xeno, msg) - return 1 - -/* -If a guy was gibbed and you want to revive him, this is a good way to do so. -Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. -Traitors and the like can also be revived with the previous role mostly intact. -/N */ -/client/proc/respawn_character() - set category = "Special Verbs" - set name = "Respawn Character" - set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into." - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - var/input = ckey(input(src, "Please specify which key will be respawned.", "Key", "")) - if(!input) - return - - var/mob/dead/observer/G_found - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.ckey == input) - G_found = G - break - - if(!G_found)//If a ghost was not found. - to_chat(usr, "There is no active key like that in the game or the person is not currently a ghost.") - return - - if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something - //Check if they were an alien - if(G_found.mind.assigned_role=="Alien") - if(alert("This character appears to have been an alien. Would you like to respawn them as such?",,"Yes","No")=="Yes") - var/turf/T - if(GLOB.xeno_spawn.len) - T = pick(GLOB.xeno_spawn) - - var/mob/living/carbon/alien/new_xeno - switch(G_found.mind.special_role)//If they have a mind, we can determine which caste they were. - if("Hunter") - new_xeno = new /mob/living/carbon/alien/humanoid/hunter(T) - if("Sentinel") - new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(T) - if("Drone") - new_xeno = new /mob/living/carbon/alien/humanoid/drone(T) - if("Praetorian") - new_xeno = new /mob/living/carbon/alien/humanoid/royal/praetorian(T) - if("Queen") - new_xeno = new /mob/living/carbon/alien/humanoid/royal/queen(T) - else//If we don't know what special role they have, for whatever reason, or they're a larva. - create_xeno(G_found.ckey) - return - - if(!T) - SSjob.SendToLateJoin(new_xeno, FALSE) - - //Now to give them their mind back. - G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use - new_xeno.key = G_found.key - to_chat(new_xeno, "You have been fully respawned. Enjoy the game.") - var/msg = "[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno." - message_admins(msg) - admin_ticket_log(new_xeno, msg) - return //all done. The ghost is auto-deleted - - //check if they were a monkey - else if(findtext(G_found.real_name,"monkey")) - if(alert("This character appears to have been a monkey. Would you like to respawn them as such?",,"Yes","No")=="Yes") - var/mob/living/carbon/monkey/new_monkey = new - SSjob.SendToLateJoin(new_monkey) - G_found.mind.transfer_to(new_monkey) //be careful when doing stuff like this! I've already checked the mind isn't in use - new_monkey.key = G_found.key - to_chat(new_monkey, "You have been fully respawned. Enjoy the game.") - var/msg = "[key_name_admin(usr)] has respawned [new_monkey.key] as a filthy xeno." - message_admins(msg) - admin_ticket_log(new_monkey, msg) - return //all done. The ghost is auto-deleted - - - //Ok, it's not a xeno or a monkey. So, spawn a human. - var/mob/living/carbon/human/new_character = new//The mob being spawned. - SSjob.SendToLateJoin(new_character) - - var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character. - if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something - /*Try and locate a record for the person being respawned through GLOB.data_core. - This isn't an exact science but it does the trick more often than not.*/ - var/id = md5("[G_found.real_name][G_found.mind.assigned_role]") - - record_found = find_record("id", id, GLOB.data_core.locked) - - if(record_found)//If they have a record we can determine a few things. - new_character.real_name = record_found.fields["name"] - new_character.gender = record_found.fields["sex"] - new_character.age = record_found.fields["age"] - new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], record_found.fields["name"], record_found.fields["blood_type"], record_found.fields["species"], record_found.fields["features"]) - else - var/datum/preferences/A = new() - A.copy_to(new_character) - A.real_name = G_found.real_name - new_character.dna.update_dna_identity() - - new_character.name = new_character.real_name - - if(G_found.mind && !G_found.mind.active) - G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use - else - new_character.mind_initialize() - if(!new_character.mind.assigned_role) - new_character.mind.assigned_role = "Assistant"//If they somehow got a null assigned role. - - new_character.key = G_found.key - - /* - The code below functions with the assumption that the mob is already a traitor if they have a special role. - So all it does is re-equip the mob with powers and/or items. Or not, if they have no special role. - If they don't have a mind, they obviously don't have a special role. - */ - - //Two variables to properly announce later on. - var/admin = key_name_admin(src) - var/player_key = G_found.key - - //Now for special roles and equipment. - var/datum/antagonist/traitor/traitordatum = new_character.mind.has_antag_datum(ANTAG_DATUM_TRAITOR) - if(traitordatum) - SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1) - traitordatum.equip() - - - switch(new_character.mind.special_role) - if("Wizard") - new_character.loc = pick(GLOB.wizardstart) - //SSticker.mode.learn_basic_spells(new_character) - SSticker.mode.equip_wizard(new_character) - if("Syndicate") - var/obj/effect/landmark/synd_spawn = locate("landmark*Syndicate-Spawn") - if(synd_spawn) - new_character.loc = get_turf(synd_spawn) - call(/datum/game_mode/proc/equip_syndicate)(new_character) - if("Space Ninja") - var/list/ninja_spawn = list() - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name=="carpspawn") - ninja_spawn += L - var/datum/antagonist/ninja/ninjadatum = new_character.mind.has_antag_datum(ANTAG_DATUM_NINJA) - ninjadatum.equip_space_ninja() - if(ninja_spawn.len) - var/obj/effect/landmark/ninja_spawn_here = pick(ninja_spawn) - new_character.loc = ninja_spawn_here.loc - - else//They may also be a cyborg or AI. - switch(new_character.mind.assigned_role) - if("Cyborg")//More rigging to make em' work and check if they're traitor. - new_character = new_character.Robotize() - if("AI") - new_character = new_character.AIize() - else - SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them. - - //Announces the character on all the systems, based on the record. - if(!issilicon(new_character))//If they are not a cyborg/AI. - if(!record_found&&new_character.mind.assigned_role!=new_character.mind.special_role)//If there are no records for them. If they have a record, this info is already in there. MODE people are not announced anyway. - //Power to the user! - if(alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") - GLOB.data_core.manifest_inject(new_character) - - if(alert(new_character,"Would you like an active AI to announce this character?",,"No","Yes")=="Yes") - AnnounceArrival(new_character, new_character.mind.assigned_role) - - var/msg = "[admin] has respawned [player_key] as [new_character.real_name]." - message_admins(msg) - admin_ticket_log(new_character, msg) - - to_chat(new_character, "You have been fully respawned. Enjoy the game.") - - SSblackbox.add_details("admin_verb","Respawn Character") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return new_character - -/client/proc/cmd_admin_add_freeform_ai_law() - set category = "Fun" - set name = "Add Custom AI law" - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - var/input = input(usr, "Please enter anything you want the AI to do. Anything. Serious.", "What?", "") as text|null - if(!input) - return - - log_admin("Admin [key_name(usr)] has added a new AI law - [input]") - message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]") - - var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") - var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) - - var/datum/round_event/ion_storm/add_law_only/ion = new() - ion.announceEvent = announce_ion_laws - ion.ionMessage = input - - SSblackbox.add_details("admin_verb","Add Custom AI Law") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_rejuvenate(mob/living/M in GLOB.mob_list) - set category = "Special Verbs" - set name = "Rejuvenate" - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - if(!mob) - return - if(!istype(M)) - alert("Cannot revive a ghost") - return - M.revive(full_heal = 1, admin_revive = 1) - - log_admin("[key_name(usr)] healed / revived [key_name(M)]") - var/msg = "Admin [key_name_admin(usr)] healed / revived [key_name_admin(M)]!" - message_admins(msg) - admin_ticket_log(M, msg) - SSblackbox.add_details("admin_verb","Rejuvinate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_create_centcom_report() - set category = "Special Verbs" - set name = "Create Command Report" - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - var/input = input(usr, "Please enter anything you want. Anything. Serious.", "What?", "") as message|null - if(!input) - return - - var/confirm = alert(src, "Do you want to announce the contents of the report to the crew?", "Announce", "Yes", "No", "Cancel") - var/announce_command_report = TRUE - switch(confirm) - if("Yes") - priority_announce(input, null, 'sound/ai/commandreport.ogg') - announce_command_report = FALSE - if("Cancel") - return - - print_command_report(input, "[announce_command_report ? "Classified " : ""][command_name()] Update", announce_command_report) - - log_admin("[key_name(src)] has created a command report: [input]") - message_admins("[key_name_admin(src)] has created a command report") - SSblackbox.add_details("admin_verb","Create Command Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_change_command_name() - set category = "Special Verbs" - set name = "Change Command Name" - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - var/input = input(usr, "Please input a new name for Central Command.", "What?", "") as text|null - if(!input) - return - change_command_name(input) - message_admins("[key_name_admin(src)] has changed Central Command's name to [input]") - log_admin("[key_name(src)] has changed the Central Command name to: [input]") - -/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in world) - set category = "Admin" - set name = "Delete" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - admin_delete(A) - -/client/proc/admin_delete(datum/D) - var/atom/A = D - var/coords = istype(A) ? " at ([A.x], [A.y], [A.z])" : "" - if (alert(src, "Are you sure you want to delete:\n[D]\nat[coords]?", "Confirmation", "Yes", "No") == "Yes") - log_admin("[key_name(usr)] deleted [D][coords]") - message_admins("[key_name_admin(usr)] deleted [D][coords]") - SSblackbox.add_details("admin_verb","Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(isturf(D)) - var/turf/T = D - T.ChangeTurf(T.baseturf) - else - qdel(D) - -/client/proc/cmd_admin_list_open_jobs() - set category = "Admin" - set name = "Manage Job Slots" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - holder.manage_free_slots() - SSblackbox.add_details("admin_verb","Manage Job Slots") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world) - set category = "Special Verbs" - set name = "Explosion" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - var/devastation = input("Range of total devastation. -1 to none", text("Input")) as num|null - if(devastation == null) return - var/heavy = input("Range of heavy impact. -1 to none", text("Input")) as num|null - if(heavy == null) return - var/light = input("Range of light impact. -1 to none", text("Input")) as num|null - if(light == null) return - var/flash = input("Range of flash. -1 to none", text("Input")) as num|null - if(flash == null) return - var/flames = input("Range of flames. -1 to none", text("Input")) as num|null - if(flames == null) return - - if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1) || (flames != -1)) - if ((devastation > 20) || (heavy > 20) || (light > 20) || (flames > 20)) - if (alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", "Yes", "No") == "No") - return - - explosion(O, devastation, heavy, light, flash, null, null,flames) - log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") - SSblackbox.add_details("admin_verb","Explosion") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - else - return - -/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world) - set category = "Special Verbs" - set name = "EM Pulse" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - var/heavy = input("Range of heavy pulse.", text("Input")) as num|null - if(heavy == null) return - var/light = input("Range of light pulse.", text("Input")) as num|null - if(light == null) return - - if (heavy || light) - - empulse(O, heavy, light) - log_admin("[key_name(usr)] created an EM Pulse ([heavy],[light]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an EM Pulse ([heavy],[light]) at ([O.x],[O.y],[O.z])") - SSblackbox.add_details("admin_verb","EM Pulse") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - return - else - return - -/client/proc/cmd_admin_gib(mob/M in GLOB.mob_list) - set category = "Special Verbs" - set name = "Gib" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - var/confirm = alert(src, "Drop a brain?", "Confirm", "Yes", "No","Cancel") - if(confirm == "Cancel") - return - //Due to the delay here its easy for something to have happened to the mob - if(!M) - return - - log_admin("[key_name(usr)] has gibbed [key_name(M)]") - message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]") - - if(isobserver(M)) +/client/proc/cmd_admin_drop_everything(mob/M in GLOB.mob_list) + set category = null + set name = "Drop Everything" + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/confirm = alert(src, "Make [M] drop everything?", "Message", "Yes", "No") + if(confirm != "Yes") + return + + for(var/obj/item/W in M) + if(!M.dropItemToGround(W)) + qdel(W) + M.regenerate_icons() + + log_admin("[key_name(usr)] made [key_name(M)] drop everything!") + var/msg = "[key_name_admin(usr)] made [key_name_admin(M)] drop everything!" + message_admins(msg) + admin_ticket_log(M, msg) + SSblackbox.add_details("admin_verb","Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list) + set category = "Special Verbs" + set name = "Subtle Message" + + if(!ismob(M)) + return + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + message_admins("[key_name_admin(src)] has started answering [key_name(M.key, 0, 0)]'s prayer.") + var/msg = input("Message:", text("Subtle PM to [M.key]")) as text + + if (!msg) + message_admins("[key_name_admin(src)] decided not to answer [key_name(M.key, 0, 0)]'s prayer") + return + if(usr) + if (usr.client) + if(usr.client.holder) + to_chat(M, "You hear a voice in your head... [msg]") + + log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") + msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" + message_admins(msg) + admin_ticket_log(M, msg) + SSblackbox.add_details("admin_verb","Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_world_narrate() + set category = "Special Verbs" + set name = "Global Narrate" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/msg = input("Message:", text("Enter the text you wish to appear to everyone:")) as text + + if (!msg) + return + to_chat(world, "[msg]") + log_admin("GlobalNarrate: [key_name(usr)] : [msg]") + message_admins("[key_name_admin(usr)] Sent a global narrate") + SSblackbox.add_details("admin_verb","Global Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_direct_narrate(mob/M) + set category = "Special Verbs" + set name = "Direct Narrate" + + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + + if(!M) + M = input("Direct narrate to whom?", "Active Players") as null|anything in GLOB.player_list + + if(!M) + return + + var/msg = input("Message:", text("Enter the text you wish to appear to your target:")) as text + + if( !msg ) + return + + to_chat(M, msg) + log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]") + msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
" + message_admins(msg) + admin_ticket_log(M, msg) + SSblackbox.add_details("admin_verb","Direct Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_local_narrate(atom/A) + set category = "Special Verbs" + set name = "Local Narrate" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + if(!A) + return + var/range = input("Range:", "Narrate to mobs within how many tiles:", 7) as num + if(!range) + return + var/msg = input("Message:", text("Enter the text you wish to appear to everyone within view:")) as text + if (!msg) + return + for(var/mob/M in view(range,A)) + to_chat(M, msg) + + log_admin("LocalNarrate: [key_name(usr)] at [get_area(A)][COORD(A)]: [msg]") + message_admins(" LocalNarrate: [key_name_admin(usr)] at [get_area(A)][ADMIN_JMP(A)]: [msg]
") + SSblackbox.add_details("admin_verb","Local Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_godmode(mob/M in GLOB.mob_list) + set category = "Special Verbs" + set name = "Godmode" + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + M.status_flags ^= GODMODE + to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") + + log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") + var/msg = "[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" + message_admins(msg) + admin_ticket_log(M, msg) + SSblackbox.add_details("admin_toggle","Godmode|[M.status_flags & GODMODE]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/proc/cmd_admin_mute(whom, mute_type, automute = 0) + if(!whom) + return + + var/muteunmute + var/mute_string + var/feedback_string + switch(mute_type) + if(MUTE_IC) + mute_string = "IC (say and emote)" + feedback_string = "IC" + if(MUTE_OOC) + mute_string = "OOC" + feedback_string = "OOC" + if(MUTE_PRAY) + mute_string = "pray" + feedback_string = "Pray" + if(MUTE_ADMINHELP) + mute_string = "adminhelp, admin PM and ASAY" + feedback_string = "Adminhelp" + if(MUTE_DEADCHAT) + mute_string = "deadchat and DSAY" + feedback_string = "Deadchat" + if(MUTE_ALL) + mute_string = "everything" + feedback_string = "Everything" + else + return + + var/client/C + if(istype(whom, /client)) + C = whom + else if(istext(whom)) + C = GLOB.directory[whom] + else + return + + var/datum/preferences/P + if(C) + P = C.prefs + else + P = GLOB.preferences_datums[whom] + if(!P) + return + + if(automute) + if(!config.automute_on) + return + else + if(!check_rights()) + return + + if(automute) + muteunmute = "auto-muted" + P.muted |= mute_type + log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(whom)] from [mute_string]") + message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(whom)] from [mute_string].") + if(C) + to_chat(C, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") + SSblackbox.add_details("admin_toggle","Auto Mute [feedback_string]|1") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + + if(P.muted & mute_type) + muteunmute = "unmuted" + P.muted &= ~mute_type + else + muteunmute = "muted" + P.muted |= mute_type + + log_admin("[key_name(usr)] has [muteunmute] [key_name(whom)] from [mute_string]") + message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(whom)] from [mute_string].") + if(C) + to_chat(C, "You have been [muteunmute] from [mute_string] by [key_name(usr, include_name = FALSE)].") + SSblackbox.add_details("admin_toggle","Mute [feedback_string]|[P.muted & mute_type]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +//I use this proc for respawn character too. /N +/proc/create_xeno(ckey) + if(!ckey) + var/list/candidates = list() + for(var/mob/M in GLOB.player_list) + if(M.stat != DEAD) + continue //we are not dead! + if(!(ROLE_ALIEN in M.client.prefs.be_special)) + continue //we don't want to be an alium + if(M.client.is_afk()) + continue //we are afk + if(M.mind && M.mind.current && M.mind.current.stat != DEAD) + continue //we have a live body we are tied to + candidates += M.ckey + if(candidates.len) + ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in candidates + else + to_chat(usr, "Error: create_xeno(): no suitable candidates.") + if(!istext(ckey)) + return 0 + + var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Praetorian","Hunter","Sentinel","Drone","Larva") + var/obj/effect/landmark/spawn_here = GLOB.xeno_spawn.len ? pick(GLOB.xeno_spawn) : null + var/mob/living/carbon/alien/new_xeno + switch(alien_caste) + if("Queen") + new_xeno = new /mob/living/carbon/alien/humanoid/royal/queen(spawn_here) + if("Praetorian") + new_xeno = new /mob/living/carbon/alien/humanoid/royal/praetorian(spawn_here) + if("Hunter") + new_xeno = new /mob/living/carbon/alien/humanoid/hunter(spawn_here) + if("Sentinel") + new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(spawn_here) + if("Drone") + new_xeno = new /mob/living/carbon/alien/humanoid/drone(spawn_here) + if("Larva") + new_xeno = new /mob/living/carbon/alien/larva(spawn_here) + else + return 0 + if(!spawn_here) + SSjob.SendToLateJoin(new_xeno, FALSE) + + new_xeno.ckey = ckey + var/msg = "[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste]." + message_admins(msg) + admin_ticket_log(new_xeno, msg) + return 1 + +/* +If a guy was gibbed and you want to revive him, this is a good way to do so. +Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. +Traitors and the like can also be revived with the previous role mostly intact. +/N */ +/client/proc/respawn_character() + set category = "Special Verbs" + set name = "Respawn Character" + set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into." + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + var/input = ckey(input(src, "Please specify which key will be respawned.", "Key", "")) + if(!input) + return + + var/mob/dead/observer/G_found + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.ckey == input) + G_found = G + break + + if(!G_found)//If a ghost was not found. + to_chat(usr, "There is no active key like that in the game or the person is not currently a ghost.") + return + + if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something + //Check if they were an alien + if(G_found.mind.assigned_role=="Alien") + if(alert("This character appears to have been an alien. Would you like to respawn them as such?",,"Yes","No")=="Yes") + var/turf/T + if(GLOB.xeno_spawn.len) + T = pick(GLOB.xeno_spawn) + + var/mob/living/carbon/alien/new_xeno + switch(G_found.mind.special_role)//If they have a mind, we can determine which caste they were. + if("Hunter") + new_xeno = new /mob/living/carbon/alien/humanoid/hunter(T) + if("Sentinel") + new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(T) + if("Drone") + new_xeno = new /mob/living/carbon/alien/humanoid/drone(T) + if("Praetorian") + new_xeno = new /mob/living/carbon/alien/humanoid/royal/praetorian(T) + if("Queen") + new_xeno = new /mob/living/carbon/alien/humanoid/royal/queen(T) + else//If we don't know what special role they have, for whatever reason, or they're a larva. + create_xeno(G_found.ckey) + return + + if(!T) + SSjob.SendToLateJoin(new_xeno, FALSE) + + //Now to give them their mind back. + G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use + new_xeno.key = G_found.key + to_chat(new_xeno, "You have been fully respawned. Enjoy the game.") + var/msg = "[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno." + message_admins(msg) + admin_ticket_log(new_xeno, msg) + return //all done. The ghost is auto-deleted + + //check if they were a monkey + else if(findtext(G_found.real_name,"monkey")) + if(alert("This character appears to have been a monkey. Would you like to respawn them as such?",,"Yes","No")=="Yes") + var/mob/living/carbon/monkey/new_monkey = new + SSjob.SendToLateJoin(new_monkey) + G_found.mind.transfer_to(new_monkey) //be careful when doing stuff like this! I've already checked the mind isn't in use + new_monkey.key = G_found.key + to_chat(new_monkey, "You have been fully respawned. Enjoy the game.") + var/msg = "[key_name_admin(usr)] has respawned [new_monkey.key] as a filthy xeno." + message_admins(msg) + admin_ticket_log(new_monkey, msg) + return //all done. The ghost is auto-deleted + + + //Ok, it's not a xeno or a monkey. So, spawn a human. + var/mob/living/carbon/human/new_character = new//The mob being spawned. + SSjob.SendToLateJoin(new_character) + + var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character. + if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something + /*Try and locate a record for the person being respawned through GLOB.data_core. + This isn't an exact science but it does the trick more often than not.*/ + var/id = md5("[G_found.real_name][G_found.mind.assigned_role]") + + record_found = find_record("id", id, GLOB.data_core.locked) + + if(record_found)//If they have a record we can determine a few things. + new_character.real_name = record_found.fields["name"] + new_character.gender = record_found.fields["sex"] + new_character.age = record_found.fields["age"] + new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], record_found.fields["name"], record_found.fields["blood_type"], record_found.fields["species"], record_found.fields["features"]) + else + var/datum/preferences/A = new() + A.copy_to(new_character) + A.real_name = G_found.real_name + new_character.dna.update_dna_identity() + + new_character.name = new_character.real_name + + if(G_found.mind && !G_found.mind.active) + G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use + else + new_character.mind_initialize() + if(!new_character.mind.assigned_role) + new_character.mind.assigned_role = "Assistant"//If they somehow got a null assigned role. + + new_character.key = G_found.key + + /* + The code below functions with the assumption that the mob is already a traitor if they have a special role. + So all it does is re-equip the mob with powers and/or items. Or not, if they have no special role. + If they don't have a mind, they obviously don't have a special role. + */ + + //Two variables to properly announce later on. + var/admin = key_name_admin(src) + var/player_key = G_found.key + + //Now for special roles and equipment. + var/datum/antagonist/traitor/traitordatum = new_character.mind.has_antag_datum(ANTAG_DATUM_TRAITOR) + if(traitordatum) + SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1) + traitordatum.equip() + + + switch(new_character.mind.special_role) + if("Wizard") + new_character.loc = pick(GLOB.wizardstart) + //SSticker.mode.learn_basic_spells(new_character) + SSticker.mode.equip_wizard(new_character) + if("Syndicate") + var/obj/effect/landmark/synd_spawn = locate("landmark*Syndicate-Spawn") + if(synd_spawn) + new_character.loc = get_turf(synd_spawn) + call(/datum/game_mode/proc/equip_syndicate)(new_character) + if("Space Ninja") + var/list/ninja_spawn = list() + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name=="carpspawn") + ninja_spawn += L + var/datum/antagonist/ninja/ninjadatum = new_character.mind.has_antag_datum(ANTAG_DATUM_NINJA) + ninjadatum.equip_space_ninja() + if(ninja_spawn.len) + var/obj/effect/landmark/ninja_spawn_here = pick(ninja_spawn) + new_character.loc = ninja_spawn_here.loc + + else//They may also be a cyborg or AI. + switch(new_character.mind.assigned_role) + if("Cyborg")//More rigging to make em' work and check if they're traitor. + new_character = new_character.Robotize() + if("AI") + new_character = new_character.AIize() + else + SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them. + + //Announces the character on all the systems, based on the record. + if(!issilicon(new_character))//If they are not a cyborg/AI. + if(!record_found&&new_character.mind.assigned_role!=new_character.mind.special_role)//If there are no records for them. If they have a record, this info is already in there. MODE people are not announced anyway. + //Power to the user! + if(alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") + GLOB.data_core.manifest_inject(new_character) + + if(alert(new_character,"Would you like an active AI to announce this character?",,"No","Yes")=="Yes") + AnnounceArrival(new_character, new_character.mind.assigned_role) + + var/msg = "[admin] has respawned [player_key] as [new_character.real_name]." + message_admins(msg) + admin_ticket_log(new_character, msg) + + to_chat(new_character, "You have been fully respawned. Enjoy the game.") + + SSblackbox.add_details("admin_verb","Respawn Character") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return new_character + +/client/proc/cmd_admin_add_freeform_ai_law() + set category = "Fun" + set name = "Add Custom AI law" + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + var/input = input(usr, "Please enter anything you want the AI to do. Anything. Serious.", "What?", "") as text|null + if(!input) + return + + log_admin("Admin [key_name(usr)] has added a new AI law - [input]") + message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]") + + var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") + var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) + + var/datum/round_event/ion_storm/add_law_only/ion = new() + ion.announceEvent = announce_ion_laws + ion.ionMessage = input + + SSblackbox.add_details("admin_verb","Add Custom AI Law") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_rejuvenate(mob/living/M in GLOB.mob_list) + set category = "Special Verbs" + set name = "Rejuvenate" + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + if(!mob) + return + if(!istype(M)) + alert("Cannot revive a ghost") + return + M.revive(full_heal = 1, admin_revive = 1) + + log_admin("[key_name(usr)] healed / revived [key_name(M)]") + var/msg = "Admin [key_name_admin(usr)] healed / revived [key_name_admin(M)]!" + message_admins(msg) + admin_ticket_log(M, msg) + SSblackbox.add_details("admin_verb","Rejuvinate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_create_centcom_report() + set category = "Special Verbs" + set name = "Create Command Report" + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + var/input = input(usr, "Please enter anything you want. Anything. Serious.", "What?", "") as message|null + if(!input) + return + + var/confirm = alert(src, "Do you want to announce the contents of the report to the crew?", "Announce", "Yes", "No", "Cancel") + var/announce_command_report = TRUE + switch(confirm) + if("Yes") + priority_announce(input, null, 'sound/ai/commandreport.ogg') + announce_command_report = FALSE + if("Cancel") + return + + print_command_report(input, "[announce_command_report ? "Classified " : ""][command_name()] Update", announce_command_report) + + log_admin("[key_name(src)] has created a command report: [input]") + message_admins("[key_name_admin(src)] has created a command report") + SSblackbox.add_details("admin_verb","Create Command Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_change_command_name() + set category = "Special Verbs" + set name = "Change Command Name" + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + var/input = input(usr, "Please input a new name for Central Command.", "What?", "") as text|null + if(!input) + return + change_command_name(input) + message_admins("[key_name_admin(src)] has changed Central Command's name to [input]") + log_admin("[key_name(src)] has changed the Central Command name to: [input]") + +/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in world) + set category = "Admin" + set name = "Delete" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + admin_delete(A) + +/client/proc/admin_delete(datum/D) + var/atom/A = D + var/coords = istype(A) ? " at ([A.x], [A.y], [A.z])" : "" + if (alert(src, "Are you sure you want to delete:\n[D]\nat[coords]?", "Confirmation", "Yes", "No") == "Yes") + log_admin("[key_name(usr)] deleted [D][coords]") + message_admins("[key_name_admin(usr)] deleted [D][coords]") + SSblackbox.add_details("admin_verb","Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(isturf(D)) + var/turf/T = D + T.ChangeTurf(T.baseturf) + else + qdel(D) + +/client/proc/cmd_admin_list_open_jobs() + set category = "Admin" + set name = "Manage Job Slots" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + holder.manage_free_slots() + SSblackbox.add_details("admin_verb","Manage Job Slots") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world) + set category = "Special Verbs" + set name = "Explosion" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/devastation = input("Range of total devastation. -1 to none", text("Input")) as num|null + if(devastation == null) return + var/heavy = input("Range of heavy impact. -1 to none", text("Input")) as num|null + if(heavy == null) return + var/light = input("Range of light impact. -1 to none", text("Input")) as num|null + if(light == null) return + var/flash = input("Range of flash. -1 to none", text("Input")) as num|null + if(flash == null) return + var/flames = input("Range of flames. -1 to none", text("Input")) as num|null + if(flames == null) return + + if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1) || (flames != -1)) + if ((devastation > 20) || (heavy > 20) || (light > 20) || (flames > 20)) + if (alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", "Yes", "No") == "No") + return + + explosion(O, devastation, heavy, light, flash, null, null,flames) + log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") + SSblackbox.add_details("admin_verb","Explosion") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + else + return + +/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world) + set category = "Special Verbs" + set name = "EM Pulse" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/heavy = input("Range of heavy pulse.", text("Input")) as num|null + if(heavy == null) return + var/light = input("Range of light pulse.", text("Input")) as num|null + if(light == null) return + + if (heavy || light) + + empulse(O, heavy, light) + log_admin("[key_name(usr)] created an EM Pulse ([heavy],[light]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an EM Pulse ([heavy],[light]) at ([O.x],[O.y],[O.z])") + SSblackbox.add_details("admin_verb","EM Pulse") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + return + else + return + +/client/proc/cmd_admin_gib(mob/M in GLOB.mob_list) + set category = "Special Verbs" + set name = "Gib" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/confirm = alert(src, "Drop a brain?", "Confirm", "Yes", "No","Cancel") + if(confirm == "Cancel") + return + //Due to the delay here its easy for something to have happened to the mob + if(!M) + return + + log_admin("[key_name(usr)] has gibbed [key_name(M)]") + message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]") + + if(isobserver(M)) new /obj/effect/gibspawner/generic(get_turf(M)) - return - if(confirm == "Yes") - M.gib() - else - M.gib(1) - SSblackbox.add_details("admin_verb","Gib") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_gib_self() - set name = "Gibself" - set category = "Fun" - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm == "Yes") - log_admin("[key_name(usr)] used gibself.") - message_admins("[key_name_admin(usr)] used gibself.") - SSblackbox.add_details("admin_verb","Gib Self") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - mob.gib(1, 1, 1) - -/client/proc/cmd_admin_check_contents(mob/living/M in GLOB.mob_list) - set category = "Special Verbs" - set name = "Check Contents" - - var/list/L = M.get_contents() - for(var/t in L) - to_chat(usr, "[t]") - SSblackbox.add_details("admin_verb","Check Contents") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_view_range() - set category = "Special Verbs" - set name = "Change View Range" - set desc = "switches between 1x and custom views" - - if(view == world.view) + return + if(confirm == "Yes") + M.gib() + else + M.gib(1) + SSblackbox.add_details("admin_verb","Gib") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_gib_self() + set name = "Gibself" + set category = "Fun" + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm == "Yes") + log_admin("[key_name(usr)] used gibself.") + message_admins("[key_name_admin(usr)] used gibself.") + SSblackbox.add_details("admin_verb","Gib Self") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + mob.gib(1, 1, 1) + +/client/proc/cmd_admin_check_contents(mob/living/M in GLOB.mob_list) + set category = "Special Verbs" + set name = "Check Contents" + + var/list/L = M.get_contents() + for(var/t in L) + to_chat(usr, "[t]") + SSblackbox.add_details("admin_verb","Check Contents") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_view_range() + set category = "Special Verbs" + set name = "Change View Range" + set desc = "switches between 1x and custom views" + + if(view == world.view) change_view(input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128)) - else + else change_view(world.view) - - log_admin("[key_name(usr)] changed their view range to [view].") - //message_admins("\blue [key_name_admin(usr)] changed their view range to [view].") //why? removed by order of XSI - - SSblackbox.add_details("admin_toggle","Change View Range|[view]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/admin_call_shuttle() - - set category = "Admin" - set name = "Call Shuttle" - - if(EMERGENCY_AT_LEAST_DOCKED) - return - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm != "Yes") - return - - SSshuttle.emergency.request() - SSblackbox.add_details("admin_verb","Call Shuttle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-called the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.") - return - -/client/proc/admin_cancel_shuttle() - set category = "Admin" - set name = "Cancel Shuttle" - if(!check_rights(0)) - return - if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") - return - - if(EMERGENCY_AT_LEAST_DOCKED) - return - - SSshuttle.emergency.cancel() - SSblackbox.add_details("admin_verb","Cancel Shuttle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.") - - return - -/client/proc/everyone_random() - set category = "Fun" - set name = "Make Everyone Random" - set desc = "Make everyone have a random appearance. You can only use this before rounds!" - - if(SSticker.HasRoundStarted()) - to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") - return - - if(config.force_random_names) - config.force_random_names = 0 - message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.") - to_chat(usr, "Disabled.") - return - - - var/notifyplayers = alert(src, "Do you want to notify the players?", "Options", "Yes", "No", "Cancel") - if(notifyplayers == "Cancel") - return - - log_admin("Admin [key_name(src)] has forced the players to have random appearances.") - message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.") - - if(notifyplayers == "Yes") - to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!") - - to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") - - config.force_random_names = 1 - SSblackbox.add_details("admin_verb","Make Everyone Random") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/toggle_random_events() - set category = "Server" - set name = "Toggle random events on/off" - set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" - if(!config.allow_random_events) - config.allow_random_events = 1 - to_chat(usr, "Random events enabled") - message_admins("Admin [key_name_admin(usr)] has enabled random events.") - else - config.allow_random_events = 0 - to_chat(usr, "Random events disabled") - message_admins("Admin [key_name_admin(usr)] has disabled random events.") - SSblackbox.add_details("admin_toggle","Toggle Random Events|[config.allow_random_events]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/admin_change_sec_level() - set category = "Special Verbs" - set name = "Set Security Level" - set desc = "Changes the security level. Announcement only, i.e. setting to Delta won't activate nuke" - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - var/level = input("Select security level to change to","Set Security Level") as null|anything in list("green","blue","red","delta") - if(level) - set_security_level(level) - - log_admin("[key_name(usr)] changed the security level to [level]") - message_admins("[key_name_admin(usr)] changed the security level to [level]") - SSblackbox.add_details("admin_verb","Set Security Level [capitalize(level)]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_nuke(obj/machinery/nuclearbomb/N in GLOB.nuke_list) - set name = "Toggle Nuke" - set category = "Fun" - set popup_menu = 0 - if(!check_rights(R_DEBUG)) - return - - if(!N.timing) - var/newtime = input(usr, "Set activation timer.", "Activate Nuke", "[N.timer_set]") as num - if(!newtime) - return - N.timer_set = newtime - N.set_safety() - N.set_active() - - log_admin("[key_name(usr)] [N.timing ? "activated" : "deactivated"] a nuke at ([N.x],[N.y],[N.z]).") - message_admins("[ADMIN_LOOKUPFLW(usr)] [N.timing ? "activated" : "deactivated"] a nuke at [ADMIN_COORDJMP(N)].") - SSblackbox.add_details("admin_toggle","Toggle Nuke|[N.timing]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits - -/client/proc/create_outfits() - set category = "Debug" - set name = "Create Custom Outfit" - - if(!check_rights(R_DEBUG)) - return - - holder.create_outfit() - -/datum/admins/proc/create_outfit() - var/list/uniforms = typesof(/obj/item/clothing/under) - var/list/suits = typesof(/obj/item/clothing/suit) - var/list/gloves = typesof(/obj/item/clothing/gloves) - var/list/shoes = typesof(/obj/item/clothing/shoes) - var/list/headwear = typesof(/obj/item/clothing/head) - var/list/glasses = typesof(/obj/item/clothing/glasses) - var/list/masks = typesof(/obj/item/clothing/mask) - var/list/ids = typesof(/obj/item/card/id) - - var/uniform_select = "" - - var/suit_select = "" - - var/gloves_select = "" - - var/shoes_select = "" - - var/head_select = "" - - var/glasses_select = "" - - var/mask_select = "" - - var/id_select = "" - - var/dat = {" - Create Outfit -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name: - -
Uniform: - [uniform_select] -
Suit: - [suit_select] -
Back: - -
Belt: - -
Gloves: - [gloves_select] -
Shoes: - [shoes_select] -
Head: - [head_select] -
Mask: - [mask_select] -
Ears: - -
Glasses: - [glasses_select] -
ID: - [id_select] -
Left Pocket: - -
Right Pocket: - -
Suit Store: - -
Right Hand: - -
Left Hand: - -
-
- -
- "} - usr << browse(dat, "window=dressup;size=550x600") - -/client/proc/toggle_antag_hud() - set category = "Admin" - set name = "Toggle AntagHUD" - set desc = "Toggles the Admin AntagHUD" - - if(!holder) return - - var/adding_hud = !has_antag_hud() - - for(var/datum/atom_hud/H in GLOB.huds) - if(istype(H, /datum/atom_hud/antag)) - (adding_hud) ? H.add_hud_to(usr) : H.remove_hud_from(usr) - - for(var/datum/gang/G in SSticker.mode.gangs) - var/datum/atom_hud/antag/H = G.ganghud - (adding_hud) ? H.add_hud_to(usr) : H.remove_hud_from(usr) - - to_chat(usr, "You toggled your admin antag HUD [adding_hud ? "ON" : "OFF"].") - message_admins("[key_name_admin(usr)] toggled their admin antag HUD [adding_hud ? "ON" : "OFF"].") - log_admin("[key_name(usr)] toggled their admin antag HUD [adding_hud ? "ON" : "OFF"].") - SSblackbox.add_details("admin_toggle","Toggle Antag HUD|[adding_hud]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/has_antag_hud() - var/datum/atom_hud/A = GLOB.huds[ANTAG_HUD_TRAITOR] - return A.hudusers[mob] - -/client/proc/open_shuttle_manipulator() - set category = "Admin" - set name = "Shuttle Manipulator" - set desc = "Opens the shuttle manipulator UI." - - for(var/obj/machinery/shuttle_manipulator/M in GLOB.machines) - M.ui_interact(usr) - -/client/proc/mass_zombie_infection() - set category = "Fun" - set name = "Mass Zombie Infection" - set desc = "Infects all humans with a latent organ that will zombify \ - them on death." - - if(!holder) - return - - var/confirm = alert(src, "Please confirm you want to add latent zombie organs in all humans?", "Confirm Zombies", "Yes", "No") - if(confirm != "Yes") - return - - for(var/mob/living/carbon/human/H in GLOB.mob_list) - new /obj/item/organ/zombie_infection(H) - - message_admins("[key_name_admin(usr)] added a latent zombie infection to all humans.") - log_admin("[key_name(usr)] added a latent zombie infection to all humans.") - SSblackbox.add_details("admin_verb","Mass Zombie Infection") - -/client/proc/mass_zombie_cure() - set category = "Fun" - set name = "Mass Zombie Cure" - set desc = "Removes the zombie infection from all humans, returning them to normal." - if(!holder) - return - - var/confirm = alert(src, "Please confirm you want to cure all zombies?", "Confirm Zombie Cure", "Yes", "No") - if(confirm != "Yes") - return - - for(var/obj/item/organ/zombie_infection/I in GLOB.zombie_infection_list) - qdel(I) - - message_admins("[key_name_admin(usr)] cured all zombies.") - log_admin("[key_name(usr)] cured all zombies.") - SSblackbox.add_details("admin_verb","Mass Zombie Cure") - -/client/proc/polymorph_all() - set category = "Fun" - set name = "Polymorph All" - set desc = "Applies the effects of the bolt of change to every single mob." - - if(!holder) - return - - var/confirm = alert(src, "Please confirm you want polymorph all mobs?", "Confirm Polymorph", "Yes", "No") - if(confirm != "Yes") - return - - var/list/mobs = shuffle(GLOB.living_mob_list.Copy()) // might change while iterating - var/who_did_it = key_name_admin(usr) - - message_admins("[key_name_admin(usr)] started polymorphed all living mobs.") - log_admin("[key_name(usr)] polymorphed all living mobs.") - SSblackbox.add_details("admin_verb","Polymorph All") - - for(var/mob/living/M in mobs) - CHECK_TICK - - if(!M) - continue - - M.audible_message("...wabbajack...wabbajack...") - playsound(M.loc, 'sound/magic/staff_change.ogg', 50, 1, -1) - - wabbajack(M) - - message_admins("Mass polymorph started by [who_did_it] is complete.") - - -/client/proc/show_tip() - set category = "Admin" - set name = "Show Tip" - set desc = "Sends a tip (that you specify) to all players. After all \ - you're the experienced player here." - - if(!holder) - return - - var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null - if(!input) - return - - if(!SSticker) - return - - SSticker.selected_tip = input - - // If we've already tipped, then send it straight away. - if(SSticker.tipped) - SSticker.send_tip_of_the_round() - - - message_admins("[key_name_admin(usr)] sent a tip of the round.") - log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.") - SSblackbox.add_details("admin_verb","Show Tip") - -#define ON_PURRBATION(H) (!(H.dna.features["tail_human"] == "None" && H.dna.features["ears"] == "None")) - -/proc/mass_purrbation() - for(var/M in GLOB.mob_list) - if(ishumanbasic(M)) - purrbation_apply(M) - CHECK_TICK - -/proc/mass_remove_purrbation() - for(var/M in GLOB.mob_list) - if(ishumanbasic(M)) - purrbation_remove(M) - CHECK_TICK - -/proc/purrbation_toggle(mob/living/carbon/human/H) - if(!ishumanbasic(H)) - return - if(!ON_PURRBATION(H)) - purrbation_apply(H) - . = TRUE - else - purrbation_remove(H) - . = FALSE - -/proc/purrbation_apply(mob/living/carbon/human/H) - if(!ishuman(H)) - return - if(ON_PURRBATION(H)) - return - to_chat(H, "Something is nya~t right.") - H.dna.features["tail_human"] = "Cat" - H.dna.features["ears"] = "Cat" - H.regenerate_icons() - playsound(get_turf(H), 'sound/effects/meow1.ogg', 50, 1, -1) - -/proc/purrbation_remove(mob/living/carbon/human/H) - if(!ishuman(H)) - return - if(!ON_PURRBATION(H)) - return - to_chat(H, "You are no longer a cat.") - H.dna.features["tail_human"] = "None" - H.dna.features["ears"] = "None" - H.regenerate_icons() - -#undef ON_PURRBATION - -/client/proc/modify_goals() - set category = "Debug" - set name = "Modify goals" - - if(!check_rights(R_ADMIN)) - return - - holder.modify_goals() - -/datum/admins/proc/modify_goals() - var/dat = "" - for(var/datum/station_goal/S in SSticker.mode.station_goals) - dat += "[S.name] - Announce | Remove
" - dat += "
Add New Goal" - usr << browse(dat, "window=goals;size=400x400") - - -/client/proc/toggle_hub() - set category = "Server" - set name = "Toggle Hub" - + log_admin("[key_name(usr)] changed their view range to [view].") + //message_admins("\blue [key_name_admin(usr)] changed their view range to [view].") //why? removed by order of XSI + + SSblackbox.add_details("admin_toggle","Change View Range|[view]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/admin_call_shuttle() + + set category = "Admin" + set name = "Call Shuttle" + + if(EMERGENCY_AT_LEAST_DOCKED) + return + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm != "Yes") + return + + SSshuttle.emergency.request() + SSblackbox.add_details("admin_verb","Call Shuttle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-called the emergency shuttle.") + message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.") + return + +/client/proc/admin_cancel_shuttle() + set category = "Admin" + set name = "Cancel Shuttle" + if(!check_rights(0)) + return + if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") + return + + if(EMERGENCY_AT_LEAST_DOCKED) + return + + SSshuttle.emergency.cancel() + SSblackbox.add_details("admin_verb","Cancel Shuttle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") + message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.") + + return + +/client/proc/everyone_random() + set category = "Fun" + set name = "Make Everyone Random" + set desc = "Make everyone have a random appearance. You can only use this before rounds!" + + if(SSticker.HasRoundStarted()) + to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") + return + + if(config.force_random_names) + config.force_random_names = 0 + message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.") + to_chat(usr, "Disabled.") + return + + + var/notifyplayers = alert(src, "Do you want to notify the players?", "Options", "Yes", "No", "Cancel") + if(notifyplayers == "Cancel") + return + + log_admin("Admin [key_name(src)] has forced the players to have random appearances.") + message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.") + + if(notifyplayers == "Yes") + to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!") + + to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") + + config.force_random_names = 1 + SSblackbox.add_details("admin_verb","Make Everyone Random") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/toggle_random_events() + set category = "Server" + set name = "Toggle random events on/off" + set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" + if(!config.allow_random_events) + config.allow_random_events = 1 + to_chat(usr, "Random events enabled") + message_admins("Admin [key_name_admin(usr)] has enabled random events.") + else + config.allow_random_events = 0 + to_chat(usr, "Random events disabled") + message_admins("Admin [key_name_admin(usr)] has disabled random events.") + SSblackbox.add_details("admin_toggle","Toggle Random Events|[config.allow_random_events]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/admin_change_sec_level() + set category = "Special Verbs" + set name = "Set Security Level" + set desc = "Changes the security level. Announcement only, i.e. setting to Delta won't activate nuke" + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + var/level = input("Select security level to change to","Set Security Level") as null|anything in list("green","blue","red","delta") + if(level) + set_security_level(level) + + log_admin("[key_name(usr)] changed the security level to [level]") + message_admins("[key_name_admin(usr)] changed the security level to [level]") + SSblackbox.add_details("admin_verb","Set Security Level [capitalize(level)]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_nuke(obj/machinery/nuclearbomb/N in GLOB.nuke_list) + set name = "Toggle Nuke" + set category = "Fun" + set popup_menu = 0 + if(!check_rights(R_DEBUG)) + return + + if(!N.timing) + var/newtime = input(usr, "Set activation timer.", "Activate Nuke", "[N.timer_set]") as num + if(!newtime) + return + N.timer_set = newtime + N.set_safety() + N.set_active() + + log_admin("[key_name(usr)] [N.timing ? "activated" : "deactivated"] a nuke at ([N.x],[N.y],[N.z]).") + message_admins("[ADMIN_LOOKUPFLW(usr)] [N.timing ? "activated" : "deactivated"] a nuke at [ADMIN_COORDJMP(N)].") + SSblackbox.add_details("admin_toggle","Toggle Nuke|[N.timing]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits + +/client/proc/create_outfits() + set category = "Debug" + set name = "Create Custom Outfit" + + if(!check_rights(R_DEBUG)) + return + + holder.create_outfit() + +/datum/admins/proc/create_outfit() + var/list/uniforms = typesof(/obj/item/clothing/under) + var/list/suits = typesof(/obj/item/clothing/suit) + var/list/gloves = typesof(/obj/item/clothing/gloves) + var/list/shoes = typesof(/obj/item/clothing/shoes) + var/list/headwear = typesof(/obj/item/clothing/head) + var/list/glasses = typesof(/obj/item/clothing/glasses) + var/list/masks = typesof(/obj/item/clothing/mask) + var/list/ids = typesof(/obj/item/card/id) + + var/uniform_select = "" + + var/suit_select = "" + + var/gloves_select = "" + + var/shoes_select = "" + + var/head_select = "" + + var/glasses_select = "" + + var/mask_select = "" + + var/id_select = "" + + var/dat = {" + Create Outfit +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name: + +
Uniform: + [uniform_select] +
Suit: + [suit_select] +
Back: + +
Belt: + +
Gloves: + [gloves_select] +
Shoes: + [shoes_select] +
Head: + [head_select] +
Mask: + [mask_select] +
Ears: + +
Glasses: + [glasses_select] +
ID: + [id_select] +
Left Pocket: + +
Right Pocket: + +
Suit Store: + +
Right Hand: + +
Left Hand: + +
+
+ +
+ "} + usr << browse(dat, "window=dressup;size=550x600") + +/client/proc/toggle_antag_hud() + set category = "Admin" + set name = "Toggle AntagHUD" + set desc = "Toggles the Admin AntagHUD" + + if(!holder) return + + var/adding_hud = !has_antag_hud() + + for(var/datum/atom_hud/H in GLOB.huds) + if(istype(H, /datum/atom_hud/antag)) + (adding_hud) ? H.add_hud_to(usr) : H.remove_hud_from(usr) + + for(var/datum/gang/G in SSticker.mode.gangs) + var/datum/atom_hud/antag/H = G.ganghud + (adding_hud) ? H.add_hud_to(usr) : H.remove_hud_from(usr) + + to_chat(usr, "You toggled your admin antag HUD [adding_hud ? "ON" : "OFF"].") + message_admins("[key_name_admin(usr)] toggled their admin antag HUD [adding_hud ? "ON" : "OFF"].") + log_admin("[key_name(usr)] toggled their admin antag HUD [adding_hud ? "ON" : "OFF"].") + SSblackbox.add_details("admin_toggle","Toggle Antag HUD|[adding_hud]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/has_antag_hud() + var/datum/atom_hud/A = GLOB.huds[ANTAG_HUD_TRAITOR] + return A.hudusers[mob] + +/client/proc/open_shuttle_manipulator() + set category = "Admin" + set name = "Shuttle Manipulator" + set desc = "Opens the shuttle manipulator UI." + + for(var/obj/machinery/shuttle_manipulator/M in GLOB.machines) + M.ui_interact(usr) + +/client/proc/mass_zombie_infection() + set category = "Fun" + set name = "Mass Zombie Infection" + set desc = "Infects all humans with a latent organ that will zombify \ + them on death." + + if(!holder) + return + + var/confirm = alert(src, "Please confirm you want to add latent zombie organs in all humans?", "Confirm Zombies", "Yes", "No") + if(confirm != "Yes") + return + + for(var/mob/living/carbon/human/H in GLOB.mob_list) + new /obj/item/organ/zombie_infection(H) + + message_admins("[key_name_admin(usr)] added a latent zombie infection to all humans.") + log_admin("[key_name(usr)] added a latent zombie infection to all humans.") + SSblackbox.add_details("admin_verb","Mass Zombie Infection") + +/client/proc/mass_zombie_cure() + set category = "Fun" + set name = "Mass Zombie Cure" + set desc = "Removes the zombie infection from all humans, returning them to normal." + if(!holder) + return + + var/confirm = alert(src, "Please confirm you want to cure all zombies?", "Confirm Zombie Cure", "Yes", "No") + if(confirm != "Yes") + return + + for(var/obj/item/organ/zombie_infection/I in GLOB.zombie_infection_list) + qdel(I) + + message_admins("[key_name_admin(usr)] cured all zombies.") + log_admin("[key_name(usr)] cured all zombies.") + SSblackbox.add_details("admin_verb","Mass Zombie Cure") + +/client/proc/polymorph_all() + set category = "Fun" + set name = "Polymorph All" + set desc = "Applies the effects of the bolt of change to every single mob." + + if(!holder) + return + + var/confirm = alert(src, "Please confirm you want polymorph all mobs?", "Confirm Polymorph", "Yes", "No") + if(confirm != "Yes") + return + + var/list/mobs = shuffle(GLOB.living_mob_list.Copy()) // might change while iterating + var/who_did_it = key_name_admin(usr) + + message_admins("[key_name_admin(usr)] started polymorphed all living mobs.") + log_admin("[key_name(usr)] polymorphed all living mobs.") + SSblackbox.add_details("admin_verb","Polymorph All") + + for(var/mob/living/M in mobs) + CHECK_TICK + + if(!M) + continue + + M.audible_message("...wabbajack...wabbajack...") + playsound(M.loc, 'sound/magic/staff_change.ogg', 50, 1, -1) + + wabbajack(M) + + message_admins("Mass polymorph started by [who_did_it] is complete.") + + +/client/proc/show_tip() + set category = "Admin" + set name = "Show Tip" + set desc = "Sends a tip (that you specify) to all players. After all \ + you're the experienced player here." + + if(!holder) + return + + var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null + if(!input) + return + + if(!SSticker) + return + + SSticker.selected_tip = input + + // If we've already tipped, then send it straight away. + if(SSticker.tipped) + SSticker.send_tip_of_the_round() + + + message_admins("[key_name_admin(usr)] sent a tip of the round.") + log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.") + SSblackbox.add_details("admin_verb","Show Tip") + +#define ON_PURRBATION(H) (!(H.dna.features["tail_human"] == "None" && H.dna.features["ears"] == "None")) + +/proc/mass_purrbation() + for(var/M in GLOB.mob_list) + if(ishumanbasic(M)) + purrbation_apply(M) + CHECK_TICK + +/proc/mass_remove_purrbation() + for(var/M in GLOB.mob_list) + if(ishumanbasic(M)) + purrbation_remove(M) + CHECK_TICK + +/proc/purrbation_toggle(mob/living/carbon/human/H) + if(!ishumanbasic(H)) + return + if(!ON_PURRBATION(H)) + purrbation_apply(H) + . = TRUE + else + purrbation_remove(H) + . = FALSE + +/proc/purrbation_apply(mob/living/carbon/human/H) + if(!ishuman(H)) + return + if(ON_PURRBATION(H)) + return + to_chat(H, "Something is nya~t right.") + H.dna.features["tail_human"] = "Cat" + H.dna.features["ears"] = "Cat" + H.regenerate_icons() + playsound(get_turf(H), 'sound/effects/meow1.ogg', 50, 1, -1) + +/proc/purrbation_remove(mob/living/carbon/human/H) + if(!ishuman(H)) + return + if(!ON_PURRBATION(H)) + return + to_chat(H, "You are no longer a cat.") + H.dna.features["tail_human"] = "None" + H.dna.features["ears"] = "None" + H.regenerate_icons() + +#undef ON_PURRBATION + +/client/proc/modify_goals() + set category = "Debug" + set name = "Modify goals" + + if(!check_rights(R_ADMIN)) + return + + holder.modify_goals() + +/datum/admins/proc/modify_goals() + var/dat = "" + for(var/datum/station_goal/S in SSticker.mode.station_goals) + dat += "[S.name] - Announce | Remove
" + dat += "
Add New Goal" + usr << browse(dat, "window=goals;size=400x400") + + +/client/proc/toggle_hub() + set category = "Server" + set name = "Toggle Hub" world.update_hub_visibility(!GLOB.hub_visibility) - + log_admin("[key_name(usr)] has toggled the server's hub status for the round, it is now [(GLOB.hub_visibility?"on":"off")] the hub.") message_admins("[key_name_admin(usr)] has toggled the server's hub status for the round, it is now [(GLOB.hub_visibility?"on":"off")] the hub.") if (GLOB.hub_visibility && !world.reachable) - message_admins("WARNING: The server will not show up on the hub because byond is detecting that a filewall is blocking incoming connections.") - + message_admins("WARNING: The server will not show up on the hub because byond is detecting that a filewall is blocking incoming connections.") + SSblackbox.add_details("admin_toggle","Toggled Hub Visibility|[GLOB.hub_visibility]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/smite(mob/living/carbon/human/target as mob) - set name = "Smite" - set category = "Fun" - if(!holder) - return - - var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA) - - var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list - - if(QDELETED(target) || !punishment) - return - - switch(punishment) - if(ADMIN_PUNISHMENT_LIGHTNING) - var/turf/T = get_step(get_step(target, NORTH), NORTH) - T.Beam(target, icon_state="lightning[rand(1,12)]", time = 5) - target.adjustFireLoss(75) - target.electrocution_animation(40) - to_chat(target, "The gods have punished you for your sins!") - if(ADMIN_PUNISHMENT_BRAINDAMAGE) - target.adjustBrainLoss(75) - if(ADMIN_PUNISHMENT_GIB) - target.gib(FALSE) - if(ADMIN_PUNISHMENT_BSA) - bluespace_artillery(target) - - var/msg = "[key_name_admin(usr)] punished [key_name_admin(target)] with [punishment]." - message_admins(msg) - admin_ticket_log(target, msg) - log_admin("[key_name(usr)] punished [key_name(target)] with [punishment].") - - -/client/proc/trigger_centcom_recall() - if(!holder) - return - var/message = pick(GLOB.admiral_messages) - message = input("Enter message from the on-call admiral to be put in the recall report.", "Admiral Message", message) as text|null - - if(!message) - return - + +/client/proc/smite(mob/living/carbon/human/target as mob) + set name = "Smite" + set category = "Fun" + if(!holder) + return + + var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA) + + var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list + + if(QDELETED(target) || !punishment) + return + + switch(punishment) + if(ADMIN_PUNISHMENT_LIGHTNING) + var/turf/T = get_step(get_step(target, NORTH), NORTH) + T.Beam(target, icon_state="lightning[rand(1,12)]", time = 5) + target.adjustFireLoss(75) + target.electrocution_animation(40) + to_chat(target, "The gods have punished you for your sins!") + if(ADMIN_PUNISHMENT_BRAINDAMAGE) + target.adjustBrainLoss(75) + if(ADMIN_PUNISHMENT_GIB) + target.gib(FALSE) + if(ADMIN_PUNISHMENT_BSA) + bluespace_artillery(target) + + var/msg = "[key_name_admin(usr)] punished [key_name_admin(target)] with [punishment]." + message_admins(msg) + admin_ticket_log(target, msg) + log_admin("[key_name(usr)] punished [key_name(target)] with [punishment].") + + +/client/proc/trigger_centcom_recall() + if(!holder) + return + var/message = pick(GLOB.admiral_messages) + message = input("Enter message from the on-call admiral to be put in the recall report.", "Admiral Message", message) as text|null + + if(!message) + return + message_admins("[key_name_admin(usr)] triggered a CentCom recall, with the admiral message of: [message]") log_game("[key_name(usr)] triggered a CentCom recall, with the message of: [message]") - SSshuttle.centcom_recall(SSshuttle.emergency.timer, message) + SSshuttle.centcom_recall(SSshuttle.emergency.timer, message) + +/client/proc/cmd_admin_check_player_exp() //Allows admins to determine who the newer players are. + set category = "Admin" + set name = "Player Playtime" + if(!check_rights(R_ADMIN)) + return + + var/list/msg = list() + msg += "Playtime ReportPlaytime:
" + src << browse(msg.Join(), "window=Player_playtime_check") + +/datum/admins/proc/cmd_show_exp_panel(client/C) + if(!check_rights(R_ADMIN)) + return + if(!C) + to_chat(usr, "ERROR: Client not found.") + return + + var/list/body = list() + body += "Playtime for [C.key]
Playtime:" + body += C.get_exp_report() + body += "Toggle Exempt status" + body += "" + usr << browse(body.Join(), "window=playerplaytime[C.ckey];size=550x615") + +/datum/admins/proc/toggle_exempt_status(client/C) + if(!check_rights(R_ADMIN)) + return + if(!C) + to_chat(usr, "ERROR: Client not found.") + return + + if(!C.set_db_player_flags()) + to_chat(usr, "ERROR: Unable read player flags from database. Please check logs.") + var/dbflags = C.prefs.db_flags + var/newstate = FALSE + if(dbflags & DB_FLAG_EXEMPT) + newstate = FALSE + else + newstate = TRUE + + if(C.update_flag_db(DB_FLAG_EXEMPT, newstate)) + to_chat(usr, "ERROR: Unable to update player flags. Please check logs.") + else + message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]") + log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]") diff --git a/code/modules/admin/verbs/randomverbs.dm.rej b/code/modules/admin/verbs/randomverbs.dm.rej new file mode 100644 index 0000000000..9941251566 --- /dev/null +++ b/code/modules/admin/verbs/randomverbs.dm.rej @@ -0,0 +1,25 @@ +diff a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm (rejected hunks) +@@ -1246,7 +1246,8 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits + to_chat(usr, "ERROR: Client not found.") + return + +- C.set_db_player_flags() ++ if(!C.set_db_player_flags()) ++ to_chat(usr, "ERROR: Unable read player flags from database. Please check logs.") + var/dbflags = C.prefs.db_flags + var/newstate = FALSE + if(dbflags & DB_FLAG_EXEMPT) +@@ -1254,6 +1255,8 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits + else + newstate = TRUE + +- message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]") +- log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]") +- C.update_flag_db(DB_FLAG_EXEMPT, newstate) +\ No newline at end of file ++ if(C.update_flag_db(DB_FLAG_EXEMPT, newstate)) ++ to_chat(usr, "ERROR: Unable to update player flags. Please check logs.") ++ else ++ message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]") ++ log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]") +\ No newline at end of file diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 9104b3d767..1df28192db 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1,1649 +1,1653 @@ - - -GLOBAL_LIST_EMPTY(preferences_datums) - - - -/datum/preferences - var/client/parent - //doohickeys for savefiles - var/path - var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used - var/max_save_slots = 10 - - //non-preference stuff - var/muted = 0 - var/last_ip - var/last_id - - //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change - var/ooccolor = null - var/enable_tips = TRUE - var/tip_delay = 500 //tip delay in milliseconds - - //Antag preferences - var/list/be_special = list() //Special role selection - var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more - //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, - //autocorrected this round, not that you'd need to check that. - - - var/UI_style = "Midnight" - var/buttons_locked = FALSE - var/hotkeys = FALSE - var/tgui_fancy = TRUE - var/tgui_lock = TRUE - var/windowflashing = TRUE - var/toggles = TOGGLES_DEFAULT - var/chat_toggles = TOGGLES_DEFAULT_CHAT - var/ghost_form = "ghost" - var/ghost_orbit = GHOST_ORBIT_CIRCLE - var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION - var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION - var/ghost_hud = 1 - var/inquisitive_ghost = 1 - var/allow_midround_antag = 1 - var/preferred_map = null - - var/uses_glasses_colour = 0 - - //character preferences - var/real_name //our character's name - var/be_random_name = 0 //whether we'll have a random name every round - var/be_random_body = 0 //whether we'll have a random body every round - var/gender = MALE //gender of character (well duh) - var/age = 30 //age of character - var/underwear = "Nude" //underwear type - var/undershirt = "Nude" //undershirt type - var/socks = "Nude" //socks type - var/backbag = DBACKPACK //backpack type - var/hair_style = "Bald" //Hair type - var/hair_color = "000" //Hair color - var/facial_hair_style = "Shaved" //Face hair type - var/facial_hair_color = "000" //Facial hair color - var/skin_tone = "caucasian1" //Skin color - var/eye_color = "000" //Eye color - var/datum/species/pref_species = new /datum/species/human() //Mutant race - var/list/features = list("mcolor" = "FFF", - "mcolor2" = "FFF", - "mcolor3" = "FFF", - "tail_lizard" = "Smooth", - "tail_human" = "None", - "snout" = "Round", - "horns" = "None", - "ears" = "None", - "wings" = "None", - "frills" = "None", - "spines" = "None", - "body_markings" = "None", - "mam_body_markings" = "None", - "mam_ears" = "None", - "mam_tail" = "None", - "mam_tail_animated" = "None", - "xenodorsal" = "None", - "xenohead" = "None", - "xenotail" = "None", - "legs" = "Normal Legs", - "taur" = "None", - "exhibitionist" = FALSE, - "genitals_use_skintone" = FALSE, - "has_cock" = FALSE, - "cock_shape" = "Human", - "cock_length" = 6, - "cock_girth_ratio" = COCK_GIRTH_RATIO_DEF, - "cock_color" = "fff", - "has_sheath" = FALSE, - "sheath_color" = "fff", - "has_balls" = FALSE, - "balls_internal" = FALSE, - "balls_color" = "fff", - "balls_amount" = 2, - "balls_sack_size" = BALLS_SACK_SIZE_DEF, - "balls_size" = BALLS_SIZE_DEF, - "balls_cum_rate" = CUM_RATE, - "balls_cum_mult" = CUM_RATE_MULT, - "balls_efficiency" = CUM_EFFICIENCY, - "balls_fluid" = "semen", - "has_ovi" = FALSE, - "ovi_shape" = "knotted", - "ovi_length" = 6, - "ovi_color" = "fff", - "has_eggsack" = FALSE, - "eggsack_internal" = TRUE, - "eggsack_color" = "fff", - "eggsack_size" = BALLS_SACK_SIZE_DEF, - "eggsack_egg_color" = "fff", - "eggsack_egg_size" = EGG_GIRTH_DEF, - "has_breasts" = FALSE, - "breasts_color" = "fff", - "breasts_size" = "C", - "breasts_shape" = "Pair", - "breasts_fluid" = "milk", - "has_vag" = FALSE, - "vag_shape" = "Human", - "vag_color" = "fff", - "vag_clits" = 1, - "vag_clit_diam" = 0.25, - "vag_clit_len" = 0.25, - "has_womb" = FALSE, - "womb_cum_rate" = CUM_RATE, - "womb_cum_mult" = CUM_RATE_MULT, - "womb_efficiency" = CUM_EFFICIENCY, - "womb_fluid" = "femcum", - "flavor_text" = "" - )//MAKE SURE TO UPDATE THE LIST IN MOBS.DM IF YOU'RE GOING TO ADD TO THIS LIST, OTHERWISE THINGS MIGHT GET FUCKEY - - var/list/custom_names = list("clown", "mime", "ai", "cyborg", "religion", "deity") - var/prefered_security_department = SEC_DEPT_RANDOM - - //Mob preview - var/icon/preview_icon = null - - //Jobs, uses bitflags - var/job_civilian_high = 0 - var/job_civilian_med = 0 - var/job_civilian_low = 0 - - var/job_medsci_high = 0 - var/job_medsci_med = 0 - var/job_medsci_low = 0 - - var/job_engsec_high = 0 - var/job_engsec_med = 0 - var/job_engsec_low = 0 - - // Want randomjob if preferences already filled - Donkie - var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants - - // 0 = character settings, 1 = game preferences, 2 = character appearance - var/current_tab = 0 - - // OOC Metadata: - var/metadata = "" - - var/unlock_content = 0 - - var/list/ignoring = list() - - var/clientfps = 0 - - var/parallax - - var/uplink_spawn_loc = UPLINK_PDA - - var/list/menuoptions - - //citadel code - var/arousable = TRUE //Allows players to disable arousal from the character creation menu - -/datum/preferences/New(client/C) - parent = C - custom_names["ai"] = pick(GLOB.ai_names) - custom_names["cyborg"] = pick(GLOB.ai_names) - custom_names["clown"] = pick(GLOB.clown_names) - custom_names["mime"] = pick(GLOB.mime_names) - if(istype(C)) - if(!IsGuestKey(C.key)) - load_path(C.ckey) - unlock_content = C.IsByondMember() - if(unlock_content) - max_save_slots = 16 - var/loaded_preferences_successfully = load_preferences() - if(loaded_preferences_successfully) - if(load_character()) - return - //we couldn't load character data so just randomize the character appearance + name - random_character() //let's create a random character then - rather than a fat, bald and naked man. - real_name = pref_species.random_name(gender,1) - if(!loaded_preferences_successfully) - save_preferences() - save_character() //let's save this new random character so it doesn't keep generating new ones. - menuoptions = list() - return - -/datum/preferences/proc/ShowChoices(mob/user) - if(!user || !user.client) - return - if(current_tab == 2) - update_preview_icon(nude=TRUE) - else - update_preview_icon(nude=FALSE) - user << browse_rsc(preview_icon, "previewicon.png") - var/dat = "
" - - dat += "Character Settings" - dat += "Character Appearance" - dat += "Game Preferences" - - if(!path) - dat += "
Please create an account to save your preferences
" - - dat += "
" - - dat += "
" - - switch(current_tab) - if (0) // Character Settings# - if(path) - var/savefile/S = new /savefile(path) - if(S) - dat += "
" - var/name - for(var/i=1, i<=max_save_slots, i++) - S.cd = "/character[i]" - S["real_name"] >> name - if(!name) - name = "Character[i]" - //if(i!=1) dat += " | " - dat += "[name] " - dat += "
" - - dat += "

Occupation Choices

" - dat += "Set Occupation Preferences
" - dat += "

Identity

" - dat += "" - - dat += "
" - if(jobban_isbanned(user, "appearance")) - dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" - dat += "Random Name " - dat += "Always Random Name: [be_random_name ? "Yes" : "No"]
" - - dat += "Name: " - dat += "[real_name]
" - - dat += "Gender: [gender == MALE ? "Male" : "Female"]
" - dat += "Age: [age]
" - dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" - dat += "Exhibitionist:[features["exhibitionist"] == TRUE ? "Yes" : "No"]
" - dat += "Special Names:
" - dat += "Clown: [custom_names["clown"]] " - dat += "Mime:[custom_names["mime"]]
" - dat += "AI: [custom_names["ai"]] " - dat += "Cyborg: [custom_names["cyborg"]]
" - dat += "Chaplain religion: [custom_names["religion"]] " - dat += "Chaplain deity: [custom_names["deity"]]
" - - dat += "Custom job preferences:
" - dat += "Prefered security department: [prefered_security_department]
" - - dat += "
" - - dat += "
" -// dat += "Size: [character_size]
" - dat += "
" - - if (1) // Game Preferences - dat += "
" - dat += "

General Settings

" - dat += "UI Style: [UI_style]
" - dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
" - dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
" - dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" - dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
" - dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"]
" - dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" - dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" - dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" - dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" - dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
" - dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"]
" - dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
" - dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]
" - dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]
" - if(config.allow_Metadata) - dat += "OOC Notes: Edit
" - - if(user.client) - if(user.client.holder) - dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"]
" - dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"]
" - - if(unlock_content || check_rights_for(user.client, R_ADMIN)) - dat += "OOC:     Change
" - - if(unlock_content) - dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" - dat += "Ghost Form: [ghost_form]
" - dat += "Ghost Orbit: [ghost_orbit]
" - - var/button_name = "If you see this something went wrong." - switch(ghost_accs) - if(GHOST_ACCS_FULL) - button_name = GHOST_ACCS_FULL_NAME - if(GHOST_ACCS_DIR) - button_name = GHOST_ACCS_DIR_NAME - if(GHOST_ACCS_NONE) - button_name = GHOST_ACCS_NONE_NAME - - dat += "Ghost Accessories: [button_name]
" - - switch(ghost_others) - if(GHOST_OTHERS_THEIR_SETTING) - button_name = GHOST_OTHERS_THEIR_SETTING_NAME - if(GHOST_OTHERS_DEFAULT_SPRITE) - button_name = GHOST_OTHERS_DEFAULT_SPRITE_NAME - if(GHOST_OTHERS_SIMPLE) - button_name = GHOST_OTHERS_SIMPLE_NAME - - dat += "Ghosts of Others: [button_name]
" - - if (config.maprotation) - var/p_map = preferred_map - if (!p_map) - p_map = "Default" - if (config.defaultmap) - p_map += " ([config.defaultmap.map_name])" - else - if (p_map in config.maplist) - var/datum/map_config/VM = config.maplist[p_map] - if (!VM) - p_map += " (No longer exists)" - else - p_map = VM.map_name - else - p_map += " (No longer exists)" - if(config.allow_map_voting) - dat += "Preferred Map: [p_map]
" - - dat += "FPS: [clientfps]
" - - dat += "Parallax (Fancy Space): " - switch (parallax) - if (PARALLAX_LOW) - dat += "Low" - if (PARALLAX_MED) - dat += "Medium" - if (PARALLAX_INSANE) - dat += "Insane" - if (PARALLAX_DISABLE) - dat += "Disabled" - else - dat += "High" - dat += "
" - - dat += "
" - - dat += "

Special Role Settings

" - - if(jobban_isbanned(user, "Syndicate")) - dat += "You are banned from antagonist roles." - src.be_special = list() - - - for (var/i in GLOB.special_roles) - if(jobban_isbanned(user, i)) - dat += "Be [capitalize(i)]: BANNED
" - else - var/days_remaining = null - if(config.use_age_restriction_for_jobs && ispath(GLOB.special_roles[i])) //If it's a game mode antag, check if the player meets the minimum age - var/mode_path = GLOB.special_roles[i] - var/datum/game_mode/temp_mode = new mode_path - days_remaining = temp_mode.get_remaining_days(user.client) - - if(days_remaining) - dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" - else - dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"]
" - - dat += "
" - - //Character Appearance - if(2) - dat += "" - */ - - - dat += "
" - dat += "

" - dat += "Set Flavor Text
" - if(lentext(features["flavor_text"]) <= 40) - if(!lentext(features["flavor_text"])) - dat += "\[...\]" - else - dat += "[features["flavor_text"]]" - else - dat += "[TextPreview(features["flavor_text"])]...
" - if(config.mutant_races)//really don't need this check, but fuck un-tabbing all those lines - dat += "

Body

" - dat += "Gender: [gender == MALE ? "Male" : "Female"]
" - dat += "Species:[pref_species.id]
" - dat += "Random Body
" - dat += "Always Random Body: [be_random_body ? "Yes" : "No"]
" - if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits)) - dat += "Primary Color:     Change
" - dat += "Secondary Color:     Change
" - dat += "Tertiary Color:     Change
" - if(pref_species.use_skintones) - dat += "Skin Tone: [skin_tone]
" - dat += "Genitals Use Skintone:[features["genitals_use_skintone"] == TRUE ? "Enabled" : "Disabled"]
" - - if(HAIR in pref_species.species_traits) - dat += "Hair Style: [hair_style]
" - dat += "Hair Color:     Change
" - dat += "Facial Hair Style: [facial_hair_style]
" - dat += "Facial Hair Color:     Change
" - if(EYECOLOR in pref_species.species_traits) - dat += "Eye Color:     Change
" - if("tail_lizard" in pref_species.mutant_bodyparts) - dat += "Tail: [features["tail_lizard"]]
" - else if("mam_tail" in pref_species.mutant_bodyparts) - dat += "Tail: [features["mam_tail"]]
" - else if("tail_human" in pref_species.mutant_bodyparts) - dat += "Tail: [features["tail_human"]]
" - if("snout" in pref_species.mutant_bodyparts) - dat += "Snout: [features["snout"]]
" - if("horns" in pref_species.mutant_bodyparts) - dat += "Horns: [features["horns"]]
" - if("frills" in pref_species.mutant_bodyparts) - dat += "Frills: [features["frills"]]
" - if("spines" in pref_species.mutant_bodyparts) - dat += "Spines: [features["spines"]]
" - if("body_markings" in pref_species.mutant_bodyparts) - dat += "Body Markings: [features["body_markings"]]
" - else if("mam_body_markings" in pref_species.mutant_bodyparts) - dat += "Body Markings: [features["mam_body_markings"]]
" - if("mam_ears" in pref_species.mutant_bodyparts) - dat += "Ears: [features["mam_ears"]]
" - else if("ears" in pref_species.mutant_bodyparts) - dat += "Ears: [features["ears"]]
" - if("legs" in pref_species.mutant_bodyparts) - dat += "Legs: [features["legs"]]
" - if("taur" in pref_species.mutant_bodyparts) - dat += "Taur: [features["taur"]]
" - if("wings" in pref_species.mutant_bodyparts && GLOB.r_wings_list.len >1) - dat += "Wings: [features["wings"]]
" - if("xenohead" in pref_species.mutant_bodyparts) - dat += "Caste: [features["xenohead"]]
" - if("xenotail" in pref_species.mutant_bodyparts) - dat += "Tail: [features["xenotail"]]
" - if("xenodorsal" in pref_species.mutant_bodyparts) - dat += "Dorsal Tubes: [features["xenodorsal"]]
" - - dat += "
" - - - dat += "

Clothing & Equipment

" -//underwear will be refactored later so it fits in with other wearable equipment and isn't just an overlay -// dat += "Underwear:[underwear]
" -// dat += "Undershirt:[undershirt]
" -// dat += "Socks:[socks]
" - dat += "Backpack:[backbag]
" - dat += "Uplink Location:[uplink_spawn_loc]
" - - dat += "

Genitals

" - if(NOGENITALS in pref_species.species_traits) - dat += "Your species ([pref_species.name]) does not support genitals!
" - else - dat += "Has Penis:[features["has_cock"] == TRUE ? "Yes" : "No"]
" - if(features["has_cock"] == TRUE) - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Penis Color:   (Skin tone overriding)
" - else - dat += "Penis Color:    Change
" -// dat += "
" - dat += "Penis Shape: [features["cock_shape"]]
" - dat += "Penis Length: [features["cock_length"]] inch(es)
" - dat += "Has Testicles:[features["has_balls"] == TRUE ? "Yes" : "No"]
" - if(features["has_balls"] == TRUE) - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Testicles Color:   (Skin tone overriding)
" - else - dat += "Testicles Color:    Change
" - dat += "Has Vagina:[features["has_vag"] == TRUE ? "Yes" : "No"]
" - if(features["has_vag"]) - dat += "Vagina Type: [features["vag_shape"]]
" - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Vagina Color:   (Skin tone overriding)
" - else - dat += "Vagina Color:    Change
" - dat += "Has Womb:[features["has_womb"] == TRUE ? "Yes" : "No"]
" - dat += "Has Breasts:[features["has_breasts"] == TRUE ? "Yes" : "No"]
" - if(features["has_breasts"]) - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Color:   (Skin tone overriding)
" - else - dat += "Color:    Change
" - dat += "Cup Size:[features["breasts_size"]]
" - dat += "Breast Shape:[features["breasts_shape"]]
" - /* - dat += "

Ovipositor

" - dat += "Has Ovipositor:[features["has_ovi"] == TRUE ? "Yes" : "No"]" - if(features["has_ovi"]) - dat += "Ovi Color:    Change" - dat += "

Eggsack

" - dat += "Has Eggsack:[features["has_eggsack"] == TRUE ? "Yes" : "No"]
" - if(features["has_eggsack"] == TRUE) - dat += "Color:    Change" - dat += "Egg Color:    Change" - dat += "Egg Size:[features["eggsack_egg_size"]]\" Diameter" - - dat += "
" - dat += "
" - - if(!IsGuestKey(user.key)) - dat += "Undo " - dat += "Save Setup " - - dat += "Reset Setup" - dat += "
" - - var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 640, 770) - popup.set_content(dat) - popup.open(0) - -/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) - if(!SSjob) - return - - //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. - //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. - //widthPerColumn - Screen's width for every column. - //height - Screen's height. - - var/width = widthPerColumn - - var/HTML = "
" - if(SSjob.occupations.len <= 0) - HTML += "The job ticker is not yet finished creating jobs, please try again later" - HTML += "
Done

" // Easier to press up here. - - else - HTML += "Choose occupation chances
" - HTML += "
Left-click to raise an occupation preference, right-click to lower it.
" - HTML += "
Done

" // Easier to press up here. - HTML += "" - HTML += "
" // Table within a table for alignment, also allows you to easily add more colomns. - HTML += "" - var/index = -1 - - //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. - var/datum/job/lastJob - - for(var/datum/job/job in SSjob.occupations) - - index += 1 - if((index >= limit) || (job.title in splitJobs)) - width += widthPerColumn - if((index < limit) && (lastJob != null)) - //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with - //the last job's selection color. Creating a rather nice effect. - for(var/i = 0, i < (limit - index), i += 1) - HTML += "" - HTML += "
  
" - index = 0 - - HTML += "" - continue - if(!job.player_old_enough(user.client)) - var/available_in_days = job.available_in_days(user.client) - HTML += "[rank]" - continue - if((job_civilian_low & ASSISTANT) && (rank != "Assistant") && !jobban_isbanned(user, "Assistant")) - HTML += "[rank]" - continue - if(config.enforce_human_authority && !user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) - if(user.client.prefs.pref_species.id == "human") - HTML += "[rank]" - else - HTML += "[rank]" - continue - if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs - HTML += "[rank]" - else - HTML += "[rank]" - - HTML += "" - continue - - HTML += "[prefLevelLabel]" - HTML += "" - - for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even - HTML += "" - - HTML += "
" - var/rank = job.title - lastJob = job - if(jobban_isbanned(user, rank)) - HTML += "[rank] BANNED
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" - - var/prefLevelLabel = "ERROR" - var/prefLevelColor = "pink" - var/prefUpperLevel = -1 // level to assign on left click - var/prefLowerLevel = -1 // level to assign on right click - - if(GetJobDepartment(job, 1) & job.flag) - prefLevelLabel = "High" - prefLevelColor = "slateblue" - prefUpperLevel = 4 - prefLowerLevel = 2 - else if(GetJobDepartment(job, 2) & job.flag) - prefLevelLabel = "Medium" - prefLevelColor = "green" - prefUpperLevel = 1 - prefLowerLevel = 3 - else if(GetJobDepartment(job, 3) & job.flag) - prefLevelLabel = "Low" - prefLevelColor = "orange" - prefUpperLevel = 2 - prefLowerLevel = 4 - else - prefLevelLabel = "NEVER" - prefLevelColor = "red" - prefUpperLevel = 3 - prefLowerLevel = 1 - - - HTML += "" - - if(rank == "Assistant")//Assistant is special - if(job_civilian_low & ASSISTANT) - HTML += "Yes" - else - HTML += "No" - HTML += "
  
" - HTML += "
" - - var/message = "Be an Assistant if preferences unavailable" - if(joblessrole == BERANDOMJOB) - message = "Get random job if preferences unavailable" - else if(joblessrole == RETURNTOLOBBY) - message = "Return to lobby if preferences unavailable" - HTML += "

[message]
" - HTML += "
Reset Preferences
" - - user << browse(null, "window=preferences") - var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) - popup.set_window_options("can_close=0") - popup.set_content(HTML) - popup.open(0) - return - -/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) - if (!job) - return 0 - - if (level == 1) // to high - // remove any other job(s) set to high - job_civilian_med |= job_civilian_high - job_engsec_med |= job_engsec_high - job_medsci_med |= job_medsci_high - job_civilian_high = 0 - job_engsec_high = 0 - job_medsci_high = 0 - - if (job.department_flag == CIVILIAN) - job_civilian_low &= ~job.flag - job_civilian_med &= ~job.flag - job_civilian_high &= ~job.flag - - switch(level) - if (1) - job_civilian_high |= job.flag - if (2) - job_civilian_med |= job.flag - if (3) - job_civilian_low |= job.flag - - return 1 - else if (job.department_flag == ENGSEC) - job_engsec_low &= ~job.flag - job_engsec_med &= ~job.flag - job_engsec_high &= ~job.flag - - switch(level) - if (1) - job_engsec_high |= job.flag - if (2) - job_engsec_med |= job.flag - if (3) - job_engsec_low |= job.flag - - return 1 - else if (job.department_flag == MEDSCI) - job_medsci_low &= ~job.flag - job_medsci_med &= ~job.flag - job_medsci_high &= ~job.flag - - switch(level) - if (1) - job_medsci_high |= job.flag - if (2) - job_medsci_med |= job.flag - if (3) - job_medsci_low |= job.flag - - return 1 - - return 0 - -/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) - if(!SSjob || SSjob.occupations.len <= 0) - return - var/datum/job/job = SSjob.GetJob(role) - - if(!job) - user << browse(null, "window=mob_occupation") - ShowChoices(user) - return - - if (!isnum(desiredLvl)) - to_chat(user, "UpdateJobPreference - desired level was not a number. Please notify coders!") - ShowChoices(user) - return - - if(role == "Assistant") - if(job_civilian_low & job.flag) - job_civilian_low &= ~job.flag - else - job_civilian_low |= job.flag - SetChoices(user) - return 1 - - SetJobPreferenceLevel(job, desiredLvl) - SetChoices(user) - - return 1 - - -/datum/preferences/proc/ResetJobs() - - job_civilian_high = 0 - job_civilian_med = 0 - job_civilian_low = 0 - - job_medsci_high = 0 - job_medsci_med = 0 - job_medsci_low = 0 - - job_engsec_high = 0 - job_engsec_med = 0 - job_engsec_low = 0 - - -/datum/preferences/proc/GetJobDepartment(datum/job/job, level) - if(!job || !level) - return 0 - switch(job.department_flag) - if(CIVILIAN) - switch(level) - if(1) - return job_civilian_high - if(2) - return job_civilian_med - if(3) - return job_civilian_low - if(MEDSCI) - switch(level) - if(1) - return job_medsci_high - if(2) - return job_medsci_med - if(3) - return job_medsci_low - if(ENGSEC) - switch(level) - if(1) - return job_engsec_high - if(2) - return job_engsec_med - if(3) - return job_engsec_low - return 0 - -/datum/preferences/proc/process_link(mob/user, list/href_list) - if(href_list["jobbancheck"]) - var/job = sanitizeSQL(href_list["jobbancheck"]) - var/sql_ckey = sanitizeSQL(user.ckey) - var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'") - if(!query_get_jobban.warn_execute()) - return - if(query_get_jobban.NextRow()) - var/reason = query_get_jobban.item[1] - var/bantime = query_get_jobban.item[2] - var/duration = query_get_jobban.item[3] - var/expiration_time = query_get_jobban.item[4] - var/a_ckey = query_get_jobban.item[5] - var/text - text = "You, or another user of this computer, ([user.ckey]) is banned from playing [job]. The ban reason is:
[reason]
This ban was applied by [a_ckey] on [bantime]" - if(text2num(duration) > 0) - text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" - text += ".
" - to_chat(user, text) - return - - if(href_list["preference"] == "job") - switch(href_list["task"]) - if("close") - user << browse(null, "window=mob_occupation") - ShowChoices(user) - if("reset") - ResetJobs() - SetChoices(user) - if("random") - switch(joblessrole) - if(RETURNTOLOBBY) - if(jobban_isbanned(user, "Assistant")) - joblessrole = BERANDOMJOB - else - joblessrole = BEASSISTANT - if(BEASSISTANT) - joblessrole = BERANDOMJOB - if(BERANDOMJOB) - joblessrole = RETURNTOLOBBY - SetChoices(user) - if("setJobLevel") - UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) - else - SetChoices(user) - return 1 - - switch(href_list["task"]) - if("random") - switch(href_list["preference"]) - if("name") - real_name = pref_species.random_name(gender,1) - if("age") - age = rand(AGE_MIN, AGE_MAX) - if("hair") - hair_color = random_short_color() - if("hair_style") - hair_style = random_hair_style(gender) - if("facial") - facial_hair_color = random_short_color() - if("facial_hair_style") - facial_hair_style = random_facial_hair_style(gender) - if("underwear") - underwear = random_underwear(gender) - if("undershirt") - undershirt = random_undershirt(gender) - if("socks") - socks = random_socks() - if("eyes") - eye_color = random_eye_color() - if("s_tone") - skin_tone = random_skin_tone() - if("bag") - backbag = pick(GLOB.backbaglist) - if("all") - random_character() - - if("input") - switch(href_list["preference"]) - if("ghostform") - if(unlock_content) - var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in GLOB.ghost_forms - if(new_form) - ghost_form = new_form - if("ghostorbit") - if(unlock_content) - var/new_orbit = input(user, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND", null) as null|anything in GLOB.ghost_orbits - if(new_orbit) - ghost_orbit = new_orbit - - if("ghostaccs") - var/new_ghost_accs = alert("Do you want your ghost to show full accessories where possible, hide accessories but still use the directional sprites where possible, or also ignore the directions and stick to the default sprites?",,GHOST_ACCS_FULL_NAME, GHOST_ACCS_DIR_NAME, GHOST_ACCS_NONE_NAME) - switch(new_ghost_accs) - if(GHOST_ACCS_FULL_NAME) - ghost_accs = GHOST_ACCS_FULL - if(GHOST_ACCS_DIR_NAME) - ghost_accs = GHOST_ACCS_DIR - if(GHOST_ACCS_NONE_NAME) - ghost_accs = GHOST_ACCS_NONE - - if("ghostothers") - var/new_ghost_others = alert("Do you want the ghosts of others to show up as their own setting, as their default sprites or always as the default white ghost?",,GHOST_OTHERS_THEIR_SETTING_NAME, GHOST_OTHERS_DEFAULT_SPRITE_NAME, GHOST_OTHERS_SIMPLE_NAME) - switch(new_ghost_others) - if(GHOST_OTHERS_THEIR_SETTING_NAME) - ghost_others = GHOST_OTHERS_THEIR_SETTING - if(GHOST_OTHERS_DEFAULT_SPRITE_NAME) - ghost_others = GHOST_OTHERS_DEFAULT_SPRITE - if(GHOST_OTHERS_SIMPLE_NAME) - ghost_others = GHOST_OTHERS_SIMPLE - - if("name") - var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) - if(new_name) - real_name = new_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("age") - var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null - if(new_age) - age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) - - if("flavor_text") - var/msg = input(usr,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(features["flavor_text"])) as message - if(msg != null) - msg = copytext(msg, 1, MAX_MESSAGE_LEN) - msg = html_encode(msg) - features["flavor_text"] = msg - - if("metadata") - var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null - if(new_metadata) - metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) - - if("hair") - var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as null|color - if(new_hair) - hair_color = sanitize_hexcolor(new_hair) - - - if("hair_style") - var/new_hair_style - if(gender == MALE) - new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_male_list - else - new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_female_list - if(new_hair_style) - hair_style = new_hair_style - - if("next_hair_style") - if (gender == MALE) - hair_style = next_list_item(hair_style, GLOB.hair_styles_male_list) - else - hair_style = next_list_item(hair_style, GLOB.hair_styles_female_list) - - if("previous_hair_style") - if (gender == MALE) - hair_style = previous_list_item(hair_style, GLOB.hair_styles_male_list) - else - hair_style = previous_list_item(hair_style, GLOB.hair_styles_female_list) - - if("facial") - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as null|color - if(new_facial) - facial_hair_color = sanitize_hexcolor(new_facial) - - if("facial_hair_style") - var/new_facial_hair_style - if(gender == MALE) - new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_male_list - else - new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_female_list - if(new_facial_hair_style) - facial_hair_style = new_facial_hair_style - - if("next_facehair_style") - if (gender == MALE) - facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) - else - facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) - - if("previous_facehair_style") - if (gender == MALE) - facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) - else - facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) - - if("underwear") - var/new_underwear - if(gender == MALE) - new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_m - else - new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_f - if(new_underwear) - underwear = new_underwear - - if("undershirt") - var/new_undershirt - if(gender == MALE) - new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_m - else - new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_f - if(new_undershirt) - undershirt = new_undershirt - - if("socks") - var/new_socks - new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in GLOB.socks_list - if(new_socks) - socks = new_socks - - if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null - if(new_eyes) - eye_color = sanitize_hexcolor(new_eyes) - - if("species") - - var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_species - - if(result) - var/newtype = GLOB.roundstart_species[result] - pref_species = new newtype() - //Now that we changed our species, we must verify that the mutant colour is still allowed. - var/temp_hsv = RGBtoHSV(features["mcolor"]) - if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) - features["mcolor"] = pref_species.default_color - if(features["mcolor2"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) - features["mcolor2"] = pref_species.default_color - if(features["mcolor3"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) - features["mcolor3"] = pref_species.default_color - - if("mutant_color") - var/new_mutantcolor = input(user, "Choose your character's primary alien/mutant color:", "Character Preference") as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - features["mcolor"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin - features["mcolor"] = sanitize_hexcolor(new_mutantcolor) - else - to_chat(user, "Invalid color. Your color is not bright enough.") - - if("mutant_color2") - var/new_mutantcolor = input(user, "Choose your character's secondary alien/mutant color:", "Character Preference") as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - features["mcolor2"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin - features["mcolor2"] = sanitize_hexcolor(new_mutantcolor) - else - to_chat(user, "Invalid color. Your color is not bright enough.") - - if("mutant_color3") - var/new_mutantcolor = input(user, "Choose your character's tertiary alien/mutant color:", "Character Preference") as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - features["mcolor3"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin - features["mcolor3"] = sanitize_hexcolor(new_mutantcolor) - else - to_chat(user, "Invalid color. Your color is not bright enough.") - - if("tail_lizard") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard - if(new_tail) - features["tail_lizard"] = new_tail - if(new_tail != "None") - features["taur"] = "None" - - if("tail_human") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_human - if(new_tail) - features["tail_human"] = new_tail - if(new_tail != "None") - features["taur"] = "None" - if("mam_tail") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.mam_tails_list - if(new_tail) - features["mam_tail"] = new_tail - if(new_tail != "None") - features["taur"] = "None" - - if("taur") - var/new_taur - new_taur = input(user, "Choose your character's tauric body:", "Character Preference") as null|anything in GLOB.taur_list - if(new_taur) - features["taur"] = new_taur - if(new_taur != "None") - features["mam_tail"] = "None" - features["xenotail"] = "None" - -/* Doesn't exist yet. will include facial overlays to mimic 5th port species heads. - if("mam_snout") - var/new_snout - new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.mam_snouts_list - if(new_snout) - features["snout"] = new_snout -*/ - - if("snout") - var/new_snout - new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.snouts_list - if(new_snout) - features["snout"] = new_snout - - if("horns") - var/new_horns - new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in GLOB.horns_list - if(new_horns) - features["horns"] = new_horns - - if("mam_ears") - var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.mam_ears_list - if(new_ears) - features["mam_ears"] = new_ears - - if("ears") - var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.ears_list - if(new_ears) - features["ears"] = new_ears - - if("wings") - var/new_wings - new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list - if(new_wings) - features["wings"] = new_wings - - if("frills") - var/new_frills - new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in GLOB.frills_list - if(new_frills) - features["frills"] = new_frills - - if("spines") - var/new_spines - new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in GLOB.spines_list - if(new_spines) - features["spines"] = new_spines - - if("body_markings") - var/new_body_markings - new_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.body_markings_list - if(new_body_markings) - features["body_markings"] = new_body_markings - - if("mam_body_markings") - var/new_mam_body_markings - new_mam_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.mam_body_markings_list - if(new_mam_body_markings) - features["mam_body_markings"] = new_mam_body_markings - - //Xeno Bodyparts - if("xenohead")//Head or caste type - var/new_head - new_head = input(user, "Choose your character's caste:", "Character Preference") as null|anything in GLOB.xeno_head_list - if(new_head) - features["xenohead"] = new_head - - if("xenotail")//Currently one one type, more maybe later if someone sprites them. Might include animated variants in the future. - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.xeno_tail_list - if(new_tail) - features["xenotail"] = new_tail - - if("xenodorsal") - var/new_dors - new_dors = input(user, "Choose your character's dorsal tube type:", "Character Preference") as null|anything in GLOB.xeno_dorsal_list - if(new_dors) - features["xenodorsal"] = new_dors - - if("legs") - var/new_legs - new_legs = input(user, "Choose your character's legs:", "Character Preference") as null|anything in GLOB.legs_list - if(new_legs) - features["legs"] = new_legs - - if("s_tone") - var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in GLOB.skin_tones - if(new_s_tone) - skin_tone = new_s_tone - - if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null - if(new_ooccolor) - ooccolor = sanitize_ooccolor(new_ooccolor) - - if("bag") - var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist - if(new_backbag) - backbag = new_backbag - - if("uplink_loc") - var/new_loc = input(user, "Choose your character's traitor uplink spawn location:", "Character Preference") as null|anything in GLOB.uplink_spawn_loc_list - if(new_loc) - uplink_spawn_loc = new_loc - - if("clown_name") - var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null ) - if(new_clown_name) - custom_names["clown"] = new_clown_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("mime_name") - var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null ) - if(new_mime_name) - custom_names["mime"] = new_mime_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("ai_name") - var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 ) - if(new_ai_name) - custom_names["ai"] = new_ai_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") - - if("cyborg_name") - var/new_cyborg_name = reject_bad_name( input(user, "Choose your character's cyborg name:", "Character Preference") as text|null, 1 ) - if(new_cyborg_name) - custom_names["cyborg"] = new_cyborg_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") - - if("religion_name") - var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null ) - if(new_religion_name) - custom_names["religion"] = new_religion_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("deity_name") - var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null ) - if(new_deity_name) - custom_names["deity"] = new_deity_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("sec_dept") - var/department = input(user, "Choose your prefered security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs - if(department) - prefered_security_department = department - - if ("preferred_map") - var/maplist = list() - var/default = "Default" - if (config.defaultmap) - default += " ([config.defaultmap.map_name])" - for (var/M in config.maplist) - var/datum/map_config/VM = config.maplist[M] - var/friendlyname = "[VM.map_name] " - if (VM.voteweight <= 0) - friendlyname += " (disabled)" - maplist[friendlyname] = VM.map_name - maplist[default] = null - var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in maplist - if (pickedmap) - preferred_map = maplist[pickedmap] - - if ("clientfps") - var/version_message - if (user.client && user.client.byond_version < 511) - version_message = "\nYou need to be using byond version 511 or later to take advantage of this feature, your version of [user.client.byond_version] is too low" - if (world.byond_version < 511) - version_message += "\nThis server does not currently support client side fps. You can set now for when it does." - var/desiredfps = input(user, "Choose your desired fps.[version_message]\n(0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num - if (!isnull(desiredfps)) - clientfps = desiredfps - if (world.byond_version >= 511 && user.client && user.client.byond_version >= 511) - user.client.vars["fps"] = clientfps - if("ui") - var/pickedui = input(user, "Choose your UI style.", "Character Preference") as null|anything in list("Midnight", "Plasmafire", "Retro", "Slimecore", "Operative", "Clockwork") - if(pickedui) - UI_style = pickedui - - //citadel code - if("cock_color") - var/new_cockcolor = input(user, "Penis color:", "Character Preference") as color|null - if(new_cockcolor) - var/temp_hsv = RGBtoHSV(new_cockcolor) - if(new_cockcolor == "#000000") - features["cock_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["cock_color"] = sanitize_hexcolor(new_cockcolor) - else - user << "Invalid color. Your color is not bright enough." - - if("cock_length") - var/new_length = input(user, "Penis length in inches:\n([COCK_SIZE_MIN]-[COCK_SIZE_MAX])", "Character Preference") as num|null - if(new_length) - features["cock_length"] = max(min( round(text2num(new_length)), COCK_SIZE_MAX),COCK_SIZE_MIN) - - if("cock_shape") - var/new_shape - new_shape = input(user, "Penis shape:", "Character Preference") as null|anything in GLOB.cock_shapes_list - if(new_shape) - features["cock_shape"] = new_shape - - if("balls_color") - var/new_ballscolor = input(user, "Testicle Color:", "Character Preference") as color|null - if(new_ballscolor) - var/temp_hsv = RGBtoHSV(new_ballscolor) - if(new_ballscolor == "#000000") - features["balls_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["balls_color"] = sanitize_hexcolor(new_ballscolor) - else - user << "Invalid color. Your color is not bright enough." - - if("egg_size") - var/new_size - var/list/egg_sizes = list(1,2,3) - new_size = input(user, "Egg Diameter(inches):", "Egg Size") as null|anything in egg_sizes - if(new_size) - features["eggsack_egg_size"] = new_size - - if("egg_color") - var/new_egg_color = input(user, "Egg Color:", "Character Preference") as color|null - if(new_egg_color) - var/temp_hsv = RGBtoHSV(new_egg_color) - if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["eggsack_egg_color"] = sanitize_hexcolor(new_egg_color) - else - user << "Invalid color. Your color is not bright enough." - if("breasts_size") - var/new_size - new_size = input(user, "Breast Size", "Character Preference") as null|anything in GLOB.breasts_size_list - if(new_size) - features["breasts_size"] = new_size - - if("breasts_shape") - var/new_shape - new_shape = input(user, "Breast Shape", "Character Preference") as null|anything in GLOB.breasts_shapes_list - if(new_shape) - features["breasts_shape"] = new_shape - - if("breasts_color") - var/new_breasts_color = input(user, "Breast Color:", "Character Preference") as color|null - if(new_breasts_color) - var/temp_hsv = RGBtoHSV(new_breasts_color) - if(new_breasts_color == "#000000") - features["breasts_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["breasts_color"] = sanitize_hexcolor(new_breasts_color) - else - user << "Invalid color. Your color is not bright enough." - if("vag_shape") - var/new_shape - new_shape = input(user, "Vagina Type", "Character Preference") as null|anything in GLOB.vagina_shapes_list - if(new_shape) - features["vag_shape"] = new_shape - if("vag_color") - var/new_vagcolor = input(user, "Vagina color:", "Character Preference") as color|null - if(new_vagcolor) - var/temp_hsv = RGBtoHSV(new_vagcolor) - if(new_vagcolor == "#000000") - features["vag_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["vag_color"] = sanitize_hexcolor(new_vagcolor) - else - user << "Invalid color. Your color is not bright enough." - - else - switch(href_list["preference"]) - - //citadel code - if("genital_colour") - switch(features["genitals_use_skintone"]) - if(TRUE) - features["genitals_use_skintone"] = FALSE - if(FALSE) - features["genitals_use_skintone"] = TRUE - else - features["genitals_use_skintone"] = FALSE - if("arousable") - switch(arousable) - if(TRUE) - arousable = FALSE - if(FALSE) - arousable = TRUE - else//failsafe - arousable = FALSE - if("has_cock") - switch(features["has_cock"]) - if(TRUE) - features["has_cock"] = FALSE - if(FALSE) - features["has_cock"] = TRUE - features["has_ovi"] = FALSE - features["has_eggsack"] = FALSE - else - features["has_cock"] = FALSE - features["has_ovi"] = FALSE - if("has_balls") - switch(features["has_balls"]) - if(TRUE) - features["has_balls"] = FALSE - if(FALSE) - features["has_balls"] = TRUE - features["has_eggsack"] = FALSE - else - features["has_balls"] = FALSE - features["has_eggsack"] = FALSE - - if("has_ovi") - switch(features["has_ovi"]) - if(TRUE) - features["has_ovi"] = FALSE - if(FALSE) - features["has_ovi"] = TRUE - features["has_cock"] = FALSE - features["has_balls"] = FALSE - else - features["has_ovi"] = FALSE - features["has_cock"] = FALSE - - if("has_eggsack") - switch(features["has_eggsack"]) - if(TRUE) - features["has_eggsack"] = FALSE - if(FALSE) - features["has_eggsack"] = TRUE - features["has_balls"] = FALSE - else - features["has_eggsack"] = FALSE - features["has_balls"] = FALSE - - if("balls_internal") - switch(features["balls_internal"]) - if(TRUE) - features["balls_internal"] = FALSE - if(FALSE) - features["balls_internal"] = TRUE - features["eggsack_internal"] = FALSE - else - features["balls_internal"] = FALSE - features["eggsack_internal"] = FALSE - - if("eggsack_internal") - switch(features["eggsack_internal"]) - if(TRUE) - features["eggsack_internal"] = FALSE - if(FALSE) - features["eggsack_internal"] = TRUE - features["balls_internal"] = FALSE - else - features["eggsack_internal"] = FALSE - features["balls_internal"] = FALSE - - if("has_breasts") - switch(features["has_breasts"]) - if(TRUE) - features["has_breasts"] = FALSE - if(FALSE) - features["has_breasts"] = TRUE - else - features["has_breasts"] = FALSE - if("has_vag") - switch(features["has_vag"]) - if(TRUE) - features["has_vag"] = FALSE - if(FALSE) - features["has_vag"] = TRUE - else - features["has_vag"] = FALSE - if("has_womb") - switch(features["has_womb"]) - if(TRUE) - features["has_womb"] = FALSE - if(FALSE) - features["has_womb"] = TRUE - else - features["has_womb"] = FALSE - if("exhibitionist") - switch(features["exhibitionist"]) - if(TRUE) - features["exhibitionist"] = FALSE - if(FALSE) - features["exhibitionist"] = TRUE - else - features["exhibitionist"] = FALSE - - if("publicity") - if(unlock_content) - toggles ^= MEMBER_PUBLIC - if("gender") - if(gender == MALE) - gender = FEMALE - else - gender = MALE - underwear = "Nude" - undershirt = "Nude" - socks = "Nude" - facial_hair_style = "Shaved" - hair_style = "Bald" - - if("hotkeys") - hotkeys = !hotkeys - if("action_buttons") - buttons_locked = !buttons_locked - if("tgui_fancy") - tgui_fancy = !tgui_fancy - if("tgui_lock") - tgui_lock = !tgui_lock - if("winflash") - windowflashing = !windowflashing - if("hear_adminhelps") - toggles ^= SOUND_ADMINHELP - if("announce_login") - toggles ^= ANNOUNCE_LOGIN - - if("be_special") - var/be_special_type = href_list["be_special_type"] - if(be_special_type in be_special) - be_special -= be_special_type - else - be_special += be_special_type - - if("name") - be_random_name = !be_random_name - - if("all") - be_random_body = !be_random_body - - if("hear_midis") - toggles ^= SOUND_MIDI - - if("lobby_music") - toggles ^= SOUND_LOBBY - if((toggles & SOUND_LOBBY) && user.client) - user.client.playtitlemusic() - else - user.stop_sound_channel(CHANNEL_LOBBYMUSIC) - - if("ghost_ears") - chat_toggles ^= CHAT_GHOSTEARS - - if("ghost_sight") - chat_toggles ^= CHAT_GHOSTSIGHT - - if("ghost_whispers") - chat_toggles ^= CHAT_GHOSTWHISPER - - if("ghost_radio") - chat_toggles ^= CHAT_GHOSTRADIO - - if("ghost_pda") - chat_toggles ^= CHAT_GHOSTPDA - - if("pull_requests") - chat_toggles ^= CHAT_PULLR - - if("allow_midround_antag") - toggles ^= MIDROUND_ANTAG - - if("parallaxup") - parallax = Wrap(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) - if (parent && parent.mob && parent.mob.hud_used) - parent.mob.hud_used.update_parallax_pref(parent.mob) - - if("parallaxdown") - parallax = Wrap(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) - if (parent && parent.mob && parent.mob.hud_used) - parent.mob.hud_used.update_parallax_pref(parent.mob) - - if("save") - save_preferences() - save_character() - - if("load") - load_preferences() - load_character() - attempt_vr(parent.prefs_vr,"load_vore","") - - if("changeslot") - attempt_vr(parent.prefs_vr,"load_vore","") - if(!load_character(text2num(href_list["num"]))) - random_character() - real_name = random_unique_name(gender) - save_character() - - if("tab") - if (href_list["tab"]) - current_tab = text2num(href_list["tab"]) - - ShowChoices(user) - return 1 - -/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) - if(be_random_name) - real_name = pref_species.random_name(gender) - - if(be_random_body) - random_character(gender) - - if(config.humans_need_surnames) - var/firstspace = findtext(real_name, " ") - var/name_length = length(real_name) - if(!firstspace) //we need a surname - real_name += " [pick(GLOB.last_names)]" - else if(firstspace == name_length) - real_name += "[pick(GLOB.last_names)]" - - character.real_name = real_name - character.name = character.real_name - - character.gender = gender - character.age = age - - character.eye_color = eye_color - var/obj/item/organ/eyes/organ_eyes = character.getorgan(/obj/item/organ/eyes) - if(organ_eyes) - if(!initial(organ_eyes.eye_color)) - organ_eyes.eye_color = eye_color - organ_eyes.old_eye_color = eye_color - character.hair_color = hair_color - character.facial_hair_color = facial_hair_color - - character.skin_tone = skin_tone - character.hair_style = hair_style - character.facial_hair_style = facial_hair_style - character.underwear = underwear - character.undershirt = undershirt - character.socks = socks - - character.backbag = backbag - - character.dna.features = features.Copy() //Flavor text is now a DNA feature - character.dna.real_name = character.real_name - var/datum/species/chosen_species - if(pref_species != /datum/species/human && config.mutant_races) - chosen_species = pref_species.type - else - chosen_species = /datum/species/human - character.set_species(chosen_species, icon_update=0) - - //citadel code - character.give_genitals() - character.flavor_text = features["flavor_text"] //Let's update their flavor_text at least initially - character.canbearoused = arousable - - if(icon_updates) - character.update_body() - character.update_hair() - character.update_body_parts() - character.update_genitals() +GLOBAL_LIST_EMPTY(preferences_datums) + + + +/datum/preferences + var/client/parent + //doohickeys for savefiles + var/path + var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used + var/max_save_slots = 10 + + //non-preference stuff + var/muted = 0 + var/last_ip + var/last_id + + //game-preferences + var/lastchangelog = "" //Saved changlog filesize to detect if there was a change + var/ooccolor = null + var/enable_tips = TRUE + var/tip_delay = 500 //tip delay in milliseconds + + //Antag preferences + var/list/be_special = list() //Special role selection + var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more + //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, + //autocorrected this round, not that you'd need to check that. + + + var/UI_style = "Midnight" + var/buttons_locked = FALSE + var/hotkeys = FALSE + var/tgui_fancy = TRUE + var/tgui_lock = TRUE + var/windowflashing = TRUE + var/toggles = TOGGLES_DEFAULT + var/db_flags + var/chat_toggles = TOGGLES_DEFAULT_CHAT + var/ghost_form = "ghost" + var/ghost_orbit = GHOST_ORBIT_CIRCLE + var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION + var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION + var/ghost_hud = 1 + var/inquisitive_ghost = 1 + var/allow_midround_antag = 1 + var/preferred_map = null + + var/uses_glasses_colour = 0 + + //character preferences + var/real_name //our character's name + var/be_random_name = 0 //whether we'll have a random name every round + var/be_random_body = 0 //whether we'll have a random body every round + var/gender = MALE //gender of character (well duh) + var/age = 30 //age of character + var/underwear = "Nude" //underwear type + var/undershirt = "Nude" //undershirt type + var/socks = "Nude" //socks type + var/backbag = DBACKPACK //backpack type + var/hair_style = "Bald" //Hair type + var/hair_color = "000" //Hair color + var/facial_hair_style = "Shaved" //Face hair type + var/facial_hair_color = "000" //Facial hair color + var/skin_tone = "caucasian1" //Skin color + var/eye_color = "000" //Eye color + var/datum/species/pref_species = new /datum/species/human() //Mutant race + var/list/features = list("mcolor" = "FFF", + "mcolor2" = "FFF", + "mcolor3" = "FFF", + "tail_lizard" = "Smooth", + "tail_human" = "None", + "snout" = "Round", + "horns" = "None", + "ears" = "None", + "wings" = "None", + "frills" = "None", + "spines" = "None", + "body_markings" = "None", + "mam_body_markings" = "None", + "mam_ears" = "None", + "mam_tail" = "None", + "mam_tail_animated" = "None", + "xenodorsal" = "None", + "xenohead" = "None", + "xenotail" = "None", + "legs" = "Normal Legs", + "taur" = "None", + "exhibitionist" = FALSE, + "genitals_use_skintone" = FALSE, + "has_cock" = FALSE, + "cock_shape" = "Human", + "cock_length" = 6, + "cock_girth_ratio" = COCK_GIRTH_RATIO_DEF, + "cock_color" = "fff", + "has_sheath" = FALSE, + "sheath_color" = "fff", + "has_balls" = FALSE, + "balls_internal" = FALSE, + "balls_color" = "fff", + "balls_amount" = 2, + "balls_sack_size" = BALLS_SACK_SIZE_DEF, + "balls_size" = BALLS_SIZE_DEF, + "balls_cum_rate" = CUM_RATE, + "balls_cum_mult" = CUM_RATE_MULT, + "balls_efficiency" = CUM_EFFICIENCY, + "balls_fluid" = "semen", + "has_ovi" = FALSE, + "ovi_shape" = "knotted", + "ovi_length" = 6, + "ovi_color" = "fff", + "has_eggsack" = FALSE, + "eggsack_internal" = TRUE, + "eggsack_color" = "fff", + "eggsack_size" = BALLS_SACK_SIZE_DEF, + "eggsack_egg_color" = "fff", + "eggsack_egg_size" = EGG_GIRTH_DEF, + "has_breasts" = FALSE, + "breasts_color" = "fff", + "breasts_size" = "C", + "breasts_shape" = "Pair", + "breasts_fluid" = "milk", + "has_vag" = FALSE, + "vag_shape" = "Human", + "vag_color" = "fff", + "vag_clits" = 1, + "vag_clit_diam" = 0.25, + "vag_clit_len" = 0.25, + "has_womb" = FALSE, + "womb_cum_rate" = CUM_RATE, + "womb_cum_mult" = CUM_RATE_MULT, + "womb_efficiency" = CUM_EFFICIENCY, + "womb_fluid" = "femcum", + "flavor_text" = "" + )//MAKE SURE TO UPDATE THE LIST IN MOBS.DM IF YOU'RE GOING TO ADD TO THIS LIST, OTHERWISE THINGS MIGHT GET FUCKEY + + var/list/custom_names = list("clown", "mime", "ai", "cyborg", "religion", "deity") + var/prefered_security_department = SEC_DEPT_RANDOM + + //Mob preview + var/icon/preview_icon = null + + //Jobs, uses bitflags + var/job_civilian_high = 0 + var/job_civilian_med = 0 + var/job_civilian_low = 0 + + var/job_medsci_high = 0 + var/job_medsci_med = 0 + var/job_medsci_low = 0 + + var/job_engsec_high = 0 + var/job_engsec_med = 0 + var/job_engsec_low = 0 + + // Want randomjob if preferences already filled - Donkie + var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants + + // 0 = character settings, 1 = game preferences, 2 = character appearance + var/current_tab = 0 + + // OOC Metadata: + var/metadata = "" + + var/unlock_content = 0 + + var/list/ignoring = list() + + var/clientfps = 0 + + var/parallax + + var/uplink_spawn_loc = UPLINK_PDA + + var/list/exp + var/list/menuoptions + + //citadel code + var/arousable = TRUE //Allows players to disable arousal from the character creation menu + +/datum/preferences/New(client/C) + parent = C + custom_names["ai"] = pick(GLOB.ai_names) + custom_names["cyborg"] = pick(GLOB.ai_names) + custom_names["clown"] = pick(GLOB.clown_names) + custom_names["mime"] = pick(GLOB.mime_names) + if(istype(C)) + if(!IsGuestKey(C.key)) + load_path(C.ckey) + unlock_content = C.IsByondMember() + if(unlock_content) + max_save_slots = 16 + var/loaded_preferences_successfully = load_preferences() + if(loaded_preferences_successfully) + if(load_character()) + return + //we couldn't load character data so just randomize the character appearance + name + random_character() //let's create a random character then - rather than a fat, bald and naked man. + real_name = pref_species.random_name(gender,1) + if(!loaded_preferences_successfully) + save_preferences() + save_character() //let's save this new random character so it doesn't keep generating new ones. + menuoptions = list() + return + +/datum/preferences/proc/ShowChoices(mob/user) + if(!user || !user.client) + return + if(current_tab == 2) + update_preview_icon(nude=TRUE) + else + update_preview_icon(nude=FALSE) + user << browse_rsc(preview_icon, "previewicon.png") + var/dat = "
" + + dat += "Character Settings" + dat += "Character Appearance" + dat += "Game Preferences" + + if(!path) + dat += "
Please create an account to save your preferences
" + + dat += "
" + + dat += "
" + + switch(current_tab) + if (0) // Character Settings# + if(path) + var/savefile/S = new /savefile(path) + if(S) + dat += "
" + var/name + for(var/i=1, i<=max_save_slots, i++) + S.cd = "/character[i]" + S["real_name"] >> name + if(!name) + name = "Character[i]" + //if(i!=1) dat += " | " + dat += "[name] " + dat += "
" + + dat += "

Occupation Choices

" + dat += "Set Occupation Preferences
" + dat += "

Identity

" + dat += "" + + dat += "
" + if(jobban_isbanned(user, "appearance")) + dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" + dat += "Random Name " + dat += "Always Random Name: [be_random_name ? "Yes" : "No"]
" + + dat += "Name: " + dat += "[real_name]
" + + dat += "Gender: [gender == MALE ? "Male" : "Female"]
" + dat += "Age: [age]
" + dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" + dat += "Exhibitionist:[features["exhibitionist"] == TRUE ? "Yes" : "No"]
" + dat += "Special Names:
" + dat += "Clown: [custom_names["clown"]] " + dat += "Mime:[custom_names["mime"]]
" + dat += "AI: [custom_names["ai"]] " + dat += "Cyborg: [custom_names["cyborg"]]
" + dat += "Chaplain religion: [custom_names["religion"]] " + dat += "Chaplain deity: [custom_names["deity"]]
" + + dat += "Custom job preferences:
" + dat += "Prefered security department: [prefered_security_department]
" + + dat += "
" + + dat += "
" +// dat += "Size: [character_size]
" + dat += "
" + + if (1) // Game Preferences + dat += "
" + dat += "

General Settings

" + dat += "UI Style: [UI_style]
" + dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
" + dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
" + dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" + dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
" + dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"]
" + dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" + dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" + dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" + dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" + dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
" + dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"]
" + dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
" + dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]
" + dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]
" + if(config.allow_Metadata) + dat += "OOC Notes: Edit
" + + if(user.client) + if(user.client.holder) + dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"]
" + dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"]
" + + if(unlock_content || check_rights_for(user.client, R_ADMIN)) + dat += "OOC:     Change
" + + if(unlock_content) + dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" + dat += "Ghost Form: [ghost_form]
" + dat += "Ghost Orbit: [ghost_orbit]
" + + var/button_name = "If you see this something went wrong." + switch(ghost_accs) + if(GHOST_ACCS_FULL) + button_name = GHOST_ACCS_FULL_NAME + if(GHOST_ACCS_DIR) + button_name = GHOST_ACCS_DIR_NAME + if(GHOST_ACCS_NONE) + button_name = GHOST_ACCS_NONE_NAME + + dat += "Ghost Accessories: [button_name]
" + + switch(ghost_others) + if(GHOST_OTHERS_THEIR_SETTING) + button_name = GHOST_OTHERS_THEIR_SETTING_NAME + if(GHOST_OTHERS_DEFAULT_SPRITE) + button_name = GHOST_OTHERS_DEFAULT_SPRITE_NAME + if(GHOST_OTHERS_SIMPLE) + button_name = GHOST_OTHERS_SIMPLE_NAME + + dat += "Ghosts of Others: [button_name]
" + + if (config.maprotation) + var/p_map = preferred_map + if (!p_map) + p_map = "Default" + if (config.defaultmap) + p_map += " ([config.defaultmap.map_name])" + else + if (p_map in config.maplist) + var/datum/map_config/VM = config.maplist[p_map] + if (!VM) + p_map += " (No longer exists)" + else + p_map = VM.map_name + else + p_map += " (No longer exists)" + if(config.allow_map_voting) + dat += "Preferred Map: [p_map]
" + + dat += "FPS: [clientfps]
" + + dat += "Parallax (Fancy Space): " + switch (parallax) + if (PARALLAX_LOW) + dat += "Low" + if (PARALLAX_MED) + dat += "Medium" + if (PARALLAX_INSANE) + dat += "Insane" + if (PARALLAX_DISABLE) + dat += "Disabled" + else + dat += "High" + dat += "
" + + dat += "
" + + dat += "

Special Role Settings

" + + if(jobban_isbanned(user, "Syndicate")) + dat += "You are banned from antagonist roles." + src.be_special = list() + + + for (var/i in GLOB.special_roles) + if(jobban_isbanned(user, i)) + dat += "Be [capitalize(i)]: BANNED
" + else + var/days_remaining = null + if(config.use_age_restriction_for_jobs && ispath(GLOB.special_roles[i])) //If it's a game mode antag, check if the player meets the minimum age + var/mode_path = GLOB.special_roles[i] + var/datum/game_mode/temp_mode = new mode_path + days_remaining = temp_mode.get_remaining_days(user.client) + + if(days_remaining) + dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" + else + dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"]
" + + dat += "
" + + //Character Appearance + if(2) + dat += "" + */ + + + dat += "
" + dat += "

" + dat += "Set Flavor Text
" + if(lentext(features["flavor_text"]) <= 40) + if(!lentext(features["flavor_text"])) + dat += "\[...\]" + else + dat += "[features["flavor_text"]]" + else + dat += "[TextPreview(features["flavor_text"])]...
" + if(config.mutant_races)//really don't need this check, but fuck un-tabbing all those lines + dat += "

Body

" + dat += "Gender: [gender == MALE ? "Male" : "Female"]
" + dat += "Species:[pref_species.id]
" + dat += "Random Body
" + dat += "Always Random Body: [be_random_body ? "Yes" : "No"]
" + if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits)) + dat += "Primary Color:     Change
" + dat += "Secondary Color:     Change
" + dat += "Tertiary Color:     Change
" + if(pref_species.use_skintones) + dat += "Skin Tone: [skin_tone]
" + dat += "Genitals Use Skintone:[features["genitals_use_skintone"] == TRUE ? "Enabled" : "Disabled"]
" + + if(HAIR in pref_species.species_traits) + dat += "Hair Style: [hair_style]
" + dat += "Hair Color:     Change
" + dat += "Facial Hair Style: [facial_hair_style]
" + dat += "Facial Hair Color:     Change
" + if(EYECOLOR in pref_species.species_traits) + dat += "Eye Color:     Change
" + if("tail_lizard" in pref_species.mutant_bodyparts) + dat += "Tail: [features["tail_lizard"]]
" + else if("mam_tail" in pref_species.mutant_bodyparts) + dat += "Tail: [features["mam_tail"]]
" + else if("tail_human" in pref_species.mutant_bodyparts) + dat += "Tail: [features["tail_human"]]
" + if("snout" in pref_species.mutant_bodyparts) + dat += "Snout: [features["snout"]]
" + if("horns" in pref_species.mutant_bodyparts) + dat += "Snout: [features["horns"]]
" + if("frills" in pref_species.mutant_bodyparts) + dat += "Frills: [features["frills"]]
" + if("spines" in pref_species.mutant_bodyparts) + dat += "Spines: [features["spines"]]
" + if("body_markings" in pref_species.mutant_bodyparts) + dat += "Body Markings: [features["body_markings"]]
" + else if("mam_body_markings" in pref_species.mutant_bodyparts) + dat += "Body Markings: [features["mam_body_markings"]]
" + if("mam_ears" in pref_species.mutant_bodyparts) + dat += "Ears: [features["mam_ears"]]
" + else if("ears" in pref_species.mutant_bodyparts) + dat += "Ears: [features["ears"]]
" + if("legs" in pref_species.mutant_bodyparts) + dat += "Legs: [features["legs"]]
" + if("taur" in pref_species.mutant_bodyparts) + dat += "Taur: [features["taur"]]
" + if("wings" in pref_species.mutant_bodyparts && GLOB.r_wings_list.len >1) + dat += "Wings: [features["wings"]]
" + if("xenohead" in pref_species.mutant_bodyparts) + dat += "Caste: [features["xenohead"]]
" + if("xenotail" in pref_species.mutant_bodyparts) + dat += "Tail: [features["xenotail"]]
" + if("xenodorsal" in pref_species.mutant_bodyparts) + dat += "Dorsal Tubes: [features["xenodorsal"]]
" + + dat += "
" + + + dat += "

Clothing & Equipment

" +//underwear will be refactored later so it fits in with other wearable equipment and isn't just an overlay +// dat += "Underwear:[underwear]
" +// dat += "Undershirt:[undershirt]
" +// dat += "Socks:[socks]
" + dat += "Backpack:[backbag]
" + dat += "Uplink Location:[uplink_spawn_loc]
" + + dat += "

Genitals

" + if(NOGENITALS in pref_species.species_traits) + dat += "Your species ([pref_species.name]) does not support genitals!
" + else + dat += "Has Penis:[features["has_cock"] == TRUE ? "Yes" : "No"]
" + if(features["has_cock"] == TRUE) + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Penis Color:   (Skin tone overriding)
" + else + dat += "Penis Color:    Change
" +// dat += "
" + dat += "Penis Shape: [features["cock_shape"]]
" + dat += "Penis Length: [features["cock_length"]] inch(es)
" + dat += "Has Testicles:[features["has_balls"] == TRUE ? "Yes" : "No"]
" + if(features["has_balls"] == TRUE) + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Testicles Color:   (Skin tone overriding)
" + else + dat += "Testicles Color:    Change
" + dat += "Has Vagina:[features["has_vag"] == TRUE ? "Yes" : "No"]
" + if(features["has_vag"]) + dat += "Vagina Type: [features["vag_shape"]]
" + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Vagina Color:   (Skin tone overriding)
" + else + dat += "Vagina Color:    Change
" + dat += "Has Womb:[features["has_womb"] == TRUE ? "Yes" : "No"]
" + dat += "Has Breasts:[features["has_breasts"] == TRUE ? "Yes" : "No"]
" + if(features["has_breasts"]) + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Color:   (Skin tone overriding)
" + else + dat += "Color:    Change
" + dat += "Cup Size:[features["breasts_size"]]
" + dat += "Breast Shape:[features["breasts_shape"]]
" + /* + dat += "

Ovipositor

" + dat += "Has Ovipositor:[features["has_ovi"] == TRUE ? "Yes" : "No"]" + if(features["has_ovi"]) + dat += "Ovi Color:    Change" + dat += "

Eggsack

" + dat += "Has Eggsack:[features["has_eggsack"] == TRUE ? "Yes" : "No"]
" + if(features["has_eggsack"] == TRUE) + dat += "Color:    Change" + dat += "Egg Color:    Change" + dat += "Egg Size:[features["eggsack_egg_size"]]\" Diameter" + + dat += "
" + dat += "
" + + if(!IsGuestKey(user.key)) + dat += "Undo " + dat += "Save Setup " + + dat += "Reset Setup" + dat += "
" + + var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 640, 770) + popup.set_content(dat) + popup.open(0) + +/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) + if(!SSjob) + return + + //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. + //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. + //widthPerColumn - Screen's width for every column. + //height - Screen's height. + + var/width = widthPerColumn + + var/HTML = "
" + if(SSjob.occupations.len <= 0) + HTML += "The job ticker is not yet finished creating jobs, please try again later" + HTML += "
Done

" // Easier to press up here. + + else + HTML += "Choose occupation chances
" + HTML += "
Left-click to raise an occupation preference, right-click to lower it.
" + HTML += "
Done

" // Easier to press up here. + HTML += "" + HTML += "
" // Table within a table for alignment, also allows you to easily add more colomns. + HTML += "" + var/index = -1 + + //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. + var/datum/job/lastJob + + for(var/datum/job/job in SSjob.occupations) + + index += 1 + if((index >= limit) || (job.title in splitJobs)) + width += widthPerColumn + if((index < limit) && (lastJob != null)) + //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with + //the last job's selection color. Creating a rather nice effect. + for(var/i = 0, i < (limit - index), i += 1) + HTML += "" + HTML += "
  
" + index = 0 + + HTML += "" + continue + var/required_playtime_remaining = job.required_playtime_remaining(user.client) + if(required_playtime_remaining) + HTML += "[rank]" + continue + if(!job.player_old_enough(user.client)) + var/available_in_days = job.available_in_days(user.client) + HTML += "[rank]" + continue + if((job_civilian_low & ASSISTANT) && (rank != "Assistant") && !jobban_isbanned(user, "Assistant")) + HTML += "[rank]" + continue + if(config.enforce_human_authority && !user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) + if(user.client.prefs.pref_species.id == "human") + HTML += "[rank]" + else + HTML += "[rank]" + continue + if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs + HTML += "[rank]" + else + HTML += "[rank]" + + HTML += "" + continue + + HTML += "[prefLevelLabel]" + HTML += "" + + for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even + HTML += "" + + HTML += "
" + var/rank = job.title + lastJob = job + if(jobban_isbanned(user, rank)) + HTML += "[rank] BANNED
\[ [get_exp_format(required_playtime_remaining)] as [job.get_exp_req_type()] \]
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" + + var/prefLevelLabel = "ERROR" + var/prefLevelColor = "pink" + var/prefUpperLevel = -1 // level to assign on left click + var/prefLowerLevel = -1 // level to assign on right click + + if(GetJobDepartment(job, 1) & job.flag) + prefLevelLabel = "High" + prefLevelColor = "slateblue" + prefUpperLevel = 4 + prefLowerLevel = 2 + else if(GetJobDepartment(job, 2) & job.flag) + prefLevelLabel = "Medium" + prefLevelColor = "green" + prefUpperLevel = 1 + prefLowerLevel = 3 + else if(GetJobDepartment(job, 3) & job.flag) + prefLevelLabel = "Low" + prefLevelColor = "orange" + prefUpperLevel = 2 + prefLowerLevel = 4 + else + prefLevelLabel = "NEVER" + prefLevelColor = "red" + prefUpperLevel = 3 + prefLowerLevel = 1 + + + HTML += "" + + if(rank == "Assistant")//Assistant is special + if(job_civilian_low & ASSISTANT) + HTML += "Yes" + else + HTML += "No" + HTML += "
  
" + HTML += "
" + + var/message = "Be an Assistant if preferences unavailable" + if(joblessrole == BERANDOMJOB) + message = "Get random job if preferences unavailable" + else if(joblessrole == RETURNTOLOBBY) + message = "Return to lobby if preferences unavailable" + HTML += "

[message]
" + HTML += "
Reset Preferences
" + + user << browse(null, "window=preferences") + var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) + popup.set_window_options("can_close=0") + popup.set_content(HTML) + popup.open(0) + return + +/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) + if (!job) + return 0 + + if (level == 1) // to high + // remove any other job(s) set to high + job_civilian_med |= job_civilian_high + job_engsec_med |= job_engsec_high + job_medsci_med |= job_medsci_high + job_civilian_high = 0 + job_engsec_high = 0 + job_medsci_high = 0 + + if (job.department_flag == CIVILIAN) + job_civilian_low &= ~job.flag + job_civilian_med &= ~job.flag + job_civilian_high &= ~job.flag + + switch(level) + if (1) + job_civilian_high |= job.flag + if (2) + job_civilian_med |= job.flag + if (3) + job_civilian_low |= job.flag + + return 1 + else if (job.department_flag == ENGSEC) + job_engsec_low &= ~job.flag + job_engsec_med &= ~job.flag + job_engsec_high &= ~job.flag + + switch(level) + if (1) + job_engsec_high |= job.flag + if (2) + job_engsec_med |= job.flag + if (3) + job_engsec_low |= job.flag + + return 1 + else if (job.department_flag == MEDSCI) + job_medsci_low &= ~job.flag + job_medsci_med &= ~job.flag + job_medsci_high &= ~job.flag + + switch(level) + if (1) + job_medsci_high |= job.flag + if (2) + job_medsci_med |= job.flag + if (3) + job_medsci_low |= job.flag + + return 1 + + return 0 + +/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) + if(!SSjob || SSjob.occupations.len <= 0) + return + var/datum/job/job = SSjob.GetJob(role) + + if(!job) + user << browse(null, "window=mob_occupation") + ShowChoices(user) + return + + if (!isnum(desiredLvl)) + to_chat(user, "UpdateJobPreference - desired level was not a number. Please notify coders!") + ShowChoices(user) + return + + if(role == "Assistant") + if(job_civilian_low & job.flag) + job_civilian_low &= ~job.flag + else + job_civilian_low |= job.flag + SetChoices(user) + return 1 + + SetJobPreferenceLevel(job, desiredLvl) + SetChoices(user) + + return 1 + + +/datum/preferences/proc/ResetJobs() + + job_civilian_high = 0 + job_civilian_med = 0 + job_civilian_low = 0 + + job_medsci_high = 0 + job_medsci_med = 0 + job_medsci_low = 0 + + job_engsec_high = 0 + job_engsec_med = 0 + job_engsec_low = 0 + + +/datum/preferences/proc/GetJobDepartment(datum/job/job, level) + if(!job || !level) + return 0 + switch(job.department_flag) + if(CIVILIAN) + switch(level) + if(1) + return job_civilian_high + if(2) + return job_civilian_med + if(3) + return job_civilian_low + if(MEDSCI) + switch(level) + if(1) + return job_medsci_high + if(2) + return job_medsci_med + if(3) + return job_medsci_low + if(ENGSEC) + switch(level) + if(1) + return job_engsec_high + if(2) + return job_engsec_med + if(3) + return job_engsec_low + return 0 + +/datum/preferences/proc/process_link(mob/user, list/href_list) + if(href_list["jobbancheck"]) + var/job = sanitizeSQL(href_list["jobbancheck"]) + var/sql_ckey = sanitizeSQL(user.ckey) + var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'") + if(!query_get_jobban.warn_execute()) + return + if(query_get_jobban.NextRow()) + var/reason = query_get_jobban.item[1] + var/bantime = query_get_jobban.item[2] + var/duration = query_get_jobban.item[3] + var/expiration_time = query_get_jobban.item[4] + var/a_ckey = query_get_jobban.item[5] + var/text + text = "You, or another user of this computer, ([user.ckey]) is banned from playing [job]. The ban reason is:
[reason]
This ban was applied by [a_ckey] on [bantime]" + if(text2num(duration) > 0) + text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" + text += ".
" + to_chat(user, text) + return + + if(href_list["preference"] == "job") + switch(href_list["task"]) + if("close") + user << browse(null, "window=mob_occupation") + ShowChoices(user) + if("reset") + ResetJobs() + SetChoices(user) + if("random") + switch(joblessrole) + if(RETURNTOLOBBY) + if(jobban_isbanned(user, "Assistant")) + joblessrole = BERANDOMJOB + else + joblessrole = BEASSISTANT + if(BEASSISTANT) + joblessrole = BERANDOMJOB + if(BERANDOMJOB) + joblessrole = RETURNTOLOBBY + SetChoices(user) + if("setJobLevel") + UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) + else + SetChoices(user) + return 1 + + switch(href_list["task"]) + if("random") + switch(href_list["preference"]) + if("name") + real_name = pref_species.random_name(gender,1) + if("age") + age = rand(AGE_MIN, AGE_MAX) + if("hair") + hair_color = random_short_color() + if("hair_style") + hair_style = random_hair_style(gender) + if("facial") + facial_hair_color = random_short_color() + if("facial_hair_style") + facial_hair_style = random_facial_hair_style(gender) + if("underwear") + underwear = random_underwear(gender) + if("undershirt") + undershirt = random_undershirt(gender) + if("socks") + socks = random_socks() + if("eyes") + eye_color = random_eye_color() + if("s_tone") + skin_tone = random_skin_tone() + if("bag") + backbag = pick(GLOB.backbaglist) + if("all") + random_character() + + if("input") + switch(href_list["preference"]) + if("ghostform") + if(unlock_content) + var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in GLOB.ghost_forms + if(new_form) + ghost_form = new_form + if("ghostorbit") + if(unlock_content) + var/new_orbit = input(user, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND", null) as null|anything in GLOB.ghost_orbits + if(new_orbit) + ghost_orbit = new_orbit + + if("ghostaccs") + var/new_ghost_accs = alert("Do you want your ghost to show full accessories where possible, hide accessories but still use the directional sprites where possible, or also ignore the directions and stick to the default sprites?",,GHOST_ACCS_FULL_NAME, GHOST_ACCS_DIR_NAME, GHOST_ACCS_NONE_NAME) + switch(new_ghost_accs) + if(GHOST_ACCS_FULL_NAME) + ghost_accs = GHOST_ACCS_FULL + if(GHOST_ACCS_DIR_NAME) + ghost_accs = GHOST_ACCS_DIR + if(GHOST_ACCS_NONE_NAME) + ghost_accs = GHOST_ACCS_NONE + + if("ghostothers") + var/new_ghost_others = alert("Do you want the ghosts of others to show up as their own setting, as their default sprites or always as the default white ghost?",,GHOST_OTHERS_THEIR_SETTING_NAME, GHOST_OTHERS_DEFAULT_SPRITE_NAME, GHOST_OTHERS_SIMPLE_NAME) + switch(new_ghost_others) + if(GHOST_OTHERS_THEIR_SETTING_NAME) + ghost_others = GHOST_OTHERS_THEIR_SETTING + if(GHOST_OTHERS_DEFAULT_SPRITE_NAME) + ghost_others = GHOST_OTHERS_DEFAULT_SPRITE + if(GHOST_OTHERS_SIMPLE_NAME) + ghost_others = GHOST_OTHERS_SIMPLE + + if("name") + var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) + if(new_name) + real_name = new_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("age") + var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null + if(new_age) + age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) + + if("flavor_text") + var/msg = input(usr,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(features["flavor_text"])) as message + if(msg != null) + msg = copytext(msg, 1, MAX_MESSAGE_LEN) + msg = html_encode(msg) + features["flavor_text"] = msg + + if("metadata") + var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null + if(new_metadata) + metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) + + if("hair") + var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as null|color + if(new_hair) + hair_color = sanitize_hexcolor(new_hair) + + + if("hair_style") + var/new_hair_style + if(gender == MALE) + new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_male_list + else + new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_female_list + if(new_hair_style) + hair_style = new_hair_style + + if("next_hair_style") + if (gender == MALE) + hair_style = next_list_item(hair_style, GLOB.hair_styles_male_list) + else + hair_style = next_list_item(hair_style, GLOB.hair_styles_female_list) + + if("previous_hair_style") + if (gender == MALE) + hair_style = previous_list_item(hair_style, GLOB.hair_styles_male_list) + else + hair_style = previous_list_item(hair_style, GLOB.hair_styles_female_list) + + if("facial") + var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as null|color + if(new_facial) + facial_hair_color = sanitize_hexcolor(new_facial) + + if("facial_hair_style") + var/new_facial_hair_style + if(gender == MALE) + new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_male_list + else + new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_female_list + if(new_facial_hair_style) + facial_hair_style = new_facial_hair_style + + if("next_facehair_style") + if (gender == MALE) + facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) + else + facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) + + if("previous_facehair_style") + if (gender == MALE) + facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) + else + facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) + + if("underwear") + var/new_underwear + if(gender == MALE) + new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_m + else + new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_f + if(new_underwear) + underwear = new_underwear + + if("undershirt") + var/new_undershirt + if(gender == MALE) + new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_m + else + new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_f + if(new_undershirt) + undershirt = new_undershirt + + if("socks") + var/new_socks + new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in GLOB.socks_list + if(new_socks) + socks = new_socks + + if("eyes") + var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null + if(new_eyes) + eye_color = sanitize_hexcolor(new_eyes) + + if("species") + + var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_species + + if(result) + var/newtype = GLOB.roundstart_species[result] + pref_species = new newtype() + //Now that we changed our species, we must verify that the mutant colour is still allowed. + var/temp_hsv = RGBtoHSV(features["mcolor"]) + if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) + features["mcolor"] = pref_species.default_color + if(features["mcolor2"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) + features["mcolor2"] = pref_species.default_color + if(features["mcolor3"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) + features["mcolor3"] = pref_species.default_color + + if("mutant_color") + var/new_mutantcolor = input(user, "Choose your character's primary alien/mutant color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(new_mutantcolor == "#000000") + features["mcolor"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin + features["mcolor"] = sanitize_hexcolor(new_mutantcolor) + else + to_chat(user, "Invalid color. Your color is not bright enough.") + + if("mutant_color2") + var/new_mutantcolor = input(user, "Choose your character's secondary alien/mutant color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(new_mutantcolor == "#000000") + features["mcolor2"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin + features["mcolor2"] = sanitize_hexcolor(new_mutantcolor) + else + to_chat(user, "Invalid color. Your color is not bright enough.") + + if("mutant_color3") + var/new_mutantcolor = input(user, "Choose your character's tertiary alien/mutant color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(new_mutantcolor == "#000000") + features["mcolor3"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin + features["mcolor3"] = sanitize_hexcolor(new_mutantcolor) + else + to_chat(user, "Invalid color. Your color is not bright enough.") + + if("tail_lizard") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard + if(new_tail) + features["tail_lizard"] = new_tail + if(new_tail != "None") + features["taur"] = "None" + + if("tail_human") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_human + if(new_tail) + features["tail_human"] = new_tail + if(new_tail != "None") + features["taur"] = "None" + if("mam_tail") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.mam_tails_list + if(new_tail) + features["mam_tail"] = new_tail + if(new_tail != "None") + features["taur"] = "None" + + if("taur") + var/new_taur + new_taur = input(user, "Choose your character's tauric body:", "Character Preference") as null|anything in GLOB.taur_list + if(new_taur) + features["taur"] = new_taur + if(new_taur != "None") + features["mam_tail"] = "None" + features["xenotail"] = "None" + +/* Doesn't exist yet. will include facial overlays to mimic 5th port species heads. + if("mam_snout") + var/new_snout + new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.mam_snouts_list + if(new_snout) + features["snout"] = new_snout +*/ + + if("snout") + var/new_snout + new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.snouts_list + if(new_snout) + features["snout"] = new_snout + + if("horns") + var/new_horns + new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in GLOB.horns_list + if(new_horns) + features["horns"] = new_horns + + if("mam_ears") + var/new_ears + new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.mam_ears_list + if(new_ears) + features["mam_ears"] = new_ears + + if("ears") + var/new_ears + new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.ears_list + if(new_ears) + features["ears"] = new_ears + + if("wings") + var/new_wings + new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list + if(new_wings) + features["wings"] = new_wings + + if("frills") + var/new_frills + new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in GLOB.frills_list + if(new_frills) + features["frills"] = new_frills + + if("spines") + var/new_spines + new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in GLOB.spines_list + if(new_spines) + features["spines"] = new_spines + + if("body_markings") + var/new_body_markings + new_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.body_markings_list + if(new_body_markings) + features["body_markings"] = new_body_markings + + if("mam_body_markings") + var/new_mam_body_markings + new_mam_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.mam_body_markings_list + if(new_mam_body_markings) + features["mam_body_markings"] = new_mam_body_markings + + //Xeno Bodyparts + if("xenohead")//Head or caste type + var/new_head + new_head = input(user, "Choose your character's caste:", "Character Preference") as null|anything in GLOB.xeno_head_list + if(new_head) + features["xenohead"] = new_head + + if("xenotail")//Currently one one type, more maybe later if someone sprites them. Might include animated variants in the future. + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.xeno_tail_list + if(new_tail) + features["xenotail"] = new_tail + + if("xenodorsal") + var/new_dors + new_dors = input(user, "Choose your character's dorsal tube type:", "Character Preference") as null|anything in GLOB.xeno_dorsal_list + if(new_dors) + features["xenodorsal"] = new_dors + + if("legs") + var/new_legs + new_legs = input(user, "Choose your character's legs:", "Character Preference") as null|anything in GLOB.legs_list + if(new_legs) + features["legs"] = new_legs + + if("s_tone") + var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in GLOB.skin_tones + if(new_s_tone) + skin_tone = new_s_tone + + if("ooccolor") + var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null + if(new_ooccolor) + ooccolor = sanitize_ooccolor(new_ooccolor) + + if("bag") + var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist + if(new_backbag) + backbag = new_backbag + + if("uplink_loc") + var/new_loc = input(user, "Choose your character's traitor uplink spawn location:", "Character Preference") as null|anything in GLOB.uplink_spawn_loc_list + if(new_loc) + uplink_spawn_loc = new_loc + + if("clown_name") + var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null ) + if(new_clown_name) + custom_names["clown"] = new_clown_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("mime_name") + var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null ) + if(new_mime_name) + custom_names["mime"] = new_mime_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("ai_name") + var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 ) + if(new_ai_name) + custom_names["ai"] = new_ai_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") + + if("cyborg_name") + var/new_cyborg_name = reject_bad_name( input(user, "Choose your character's cyborg name:", "Character Preference") as text|null, 1 ) + if(new_cyborg_name) + custom_names["cyborg"] = new_cyborg_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") + + if("religion_name") + var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null ) + if(new_religion_name) + custom_names["religion"] = new_religion_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("deity_name") + var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null ) + if(new_deity_name) + custom_names["deity"] = new_deity_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("sec_dept") + var/department = input(user, "Choose your prefered security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs + if(department) + prefered_security_department = department + + if ("preferred_map") + var/maplist = list() + var/default = "Default" + if (config.defaultmap) + default += " ([config.defaultmap.map_name])" + for (var/M in config.maplist) + var/datum/map_config/VM = config.maplist[M] + var/friendlyname = "[VM.map_name] " + if (VM.voteweight <= 0) + friendlyname += " (disabled)" + maplist[friendlyname] = VM.map_name + maplist[default] = null + var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in maplist + if (pickedmap) + preferred_map = maplist[pickedmap] + + if ("clientfps") + var/version_message + if (user.client && user.client.byond_version < 511) + version_message = "\nYou need to be using byond version 511 or later to take advantage of this feature, your version of [user.client.byond_version] is too low" + if (world.byond_version < 511) + version_message += "\nThis server does not currently support client side fps. You can set now for when it does." + var/desiredfps = input(user, "Choose your desired fps.[version_message]\n(0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num + if (!isnull(desiredfps)) + clientfps = desiredfps + if (world.byond_version >= 511 && user.client && user.client.byond_version >= 511) + user.client.vars["fps"] = clientfps + if("ui") + var/pickedui = input(user, "Choose your UI style.", "Character Preference") as null|anything in list("Midnight", "Plasmafire", "Retro", "Slimecore", "Operative", "Clockwork") + if(pickedui) + UI_style = pickedui + + //citadel code + if("cock_color") + var/new_cockcolor = input(user, "Penis color:", "Character Preference") as color|null + if(new_cockcolor) + var/temp_hsv = RGBtoHSV(new_cockcolor) + if(new_cockcolor == "#000000") + features["cock_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["cock_color"] = sanitize_hexcolor(new_cockcolor) + else + user << "Invalid color. Your color is not bright enough." + + if("cock_length") + var/new_length = input(user, "Penis length in inches:\n([COCK_SIZE_MIN]-[COCK_SIZE_MAX])", "Character Preference") as num|null + if(new_length) + features["cock_length"] = max(min( round(text2num(new_length)), COCK_SIZE_MAX),COCK_SIZE_MIN) + + if("cock_shape") + var/new_shape + new_shape = input(user, "Penis shape:", "Character Preference") as null|anything in GLOB.cock_shapes_list + if(new_shape) + features["cock_shape"] = new_shape + + if("balls_color") + var/new_ballscolor = input(user, "Testicle Color:", "Character Preference") as color|null + if(new_ballscolor) + var/temp_hsv = RGBtoHSV(new_ballscolor) + if(new_ballscolor == "#000000") + features["balls_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["balls_color"] = sanitize_hexcolor(new_ballscolor) + else + user << "Invalid color. Your color is not bright enough." + + if("egg_size") + var/new_size + var/list/egg_sizes = list(1,2,3) + new_size = input(user, "Egg Diameter(inches):", "Egg Size") as null|anything in egg_sizes + if(new_size) + features["eggsack_egg_size"] = new_size + + if("egg_color") + var/new_egg_color = input(user, "Egg Color:", "Character Preference") as color|null + if(new_egg_color) + var/temp_hsv = RGBtoHSV(new_egg_color) + if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["eggsack_egg_color"] = sanitize_hexcolor(new_egg_color) + else + user << "Invalid color. Your color is not bright enough." + if("breasts_size") + var/new_size + new_size = input(user, "Breast Size", "Character Preference") as null|anything in GLOB.breasts_size_list + if(new_size) + features["breasts_size"] = new_size + + if("breasts_shape") + var/new_shape + new_shape = input(user, "Breast Shape", "Character Preference") as null|anything in GLOB.breasts_shapes_list + if(new_shape) + features["breasts_shape"] = new_shape + + if("breasts_color") + var/new_breasts_color = input(user, "Breast Color:", "Character Preference") as color|null + if(new_breasts_color) + var/temp_hsv = RGBtoHSV(new_breasts_color) + if(new_breasts_color == "#000000") + features["breasts_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["breasts_color"] = sanitize_hexcolor(new_breasts_color) + else + user << "Invalid color. Your color is not bright enough." + if("vag_shape") + var/new_shape + new_shape = input(user, "Vagina Type", "Character Preference") as null|anything in GLOB.vagina_shapes_list + if(new_shape) + features["vag_shape"] = new_shape + if("vag_color") + var/new_vagcolor = input(user, "Vagina color:", "Character Preference") as color|null + if(new_vagcolor) + var/temp_hsv = RGBtoHSV(new_vagcolor) + if(new_vagcolor == "#000000") + features["vag_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["vag_color"] = sanitize_hexcolor(new_vagcolor) + else + user << "Invalid color. Your color is not bright enough." + + else + switch(href_list["preference"]) + + //citadel code + if("genital_colour") + switch(features["genitals_use_skintone"]) + if(TRUE) + features["genitals_use_skintone"] = FALSE + if(FALSE) + features["genitals_use_skintone"] = TRUE + else + features["genitals_use_skintone"] = FALSE + if("arousable") + switch(arousable) + if(TRUE) + arousable = FALSE + if(FALSE) + arousable = TRUE + else//failsafe + arousable = FALSE + if("has_cock") + switch(features["has_cock"]) + if(TRUE) + features["has_cock"] = FALSE + if(FALSE) + features["has_cock"] = TRUE + features["has_ovi"] = FALSE + features["has_eggsack"] = FALSE + else + features["has_cock"] = FALSE + features["has_ovi"] = FALSE + if("has_balls") + switch(features["has_balls"]) + if(TRUE) + features["has_balls"] = FALSE + if(FALSE) + features["has_balls"] = TRUE + features["has_eggsack"] = FALSE + else + features["has_balls"] = FALSE + features["has_eggsack"] = FALSE + + if("has_ovi") + switch(features["has_ovi"]) + if(TRUE) + features["has_ovi"] = FALSE + if(FALSE) + features["has_ovi"] = TRUE + features["has_cock"] = FALSE + features["has_balls"] = FALSE + else + features["has_ovi"] = FALSE + features["has_cock"] = FALSE + + if("has_eggsack") + switch(features["has_eggsack"]) + if(TRUE) + features["has_eggsack"] = FALSE + if(FALSE) + features["has_eggsack"] = TRUE + features["has_balls"] = FALSE + else + features["has_eggsack"] = FALSE + features["has_balls"] = FALSE + + if("balls_internal") + switch(features["balls_internal"]) + if(TRUE) + features["balls_internal"] = FALSE + if(FALSE) + features["balls_internal"] = TRUE + features["eggsack_internal"] = FALSE + else + features["balls_internal"] = FALSE + features["eggsack_internal"] = FALSE + + if("eggsack_internal") + switch(features["eggsack_internal"]) + if(TRUE) + features["eggsack_internal"] = FALSE + if(FALSE) + features["eggsack_internal"] = TRUE + features["balls_internal"] = FALSE + else + features["eggsack_internal"] = FALSE + features["balls_internal"] = FALSE + + if("has_breasts") + switch(features["has_breasts"]) + if(TRUE) + features["has_breasts"] = FALSE + if(FALSE) + features["has_breasts"] = TRUE + else + features["has_breasts"] = FALSE + if("has_vag") + switch(features["has_vag"]) + if(TRUE) + features["has_vag"] = FALSE + if(FALSE) + features["has_vag"] = TRUE + else + features["has_vag"] = FALSE + if("has_womb") + switch(features["has_womb"]) + if(TRUE) + features["has_womb"] = FALSE + if(FALSE) + features["has_womb"] = TRUE + else + features["has_womb"] = FALSE + if("exhibitionist") + switch(features["exhibitionist"]) + if(TRUE) + features["exhibitionist"] = FALSE + if(FALSE) + features["exhibitionist"] = TRUE + else + features["exhibitionist"] = FALSE + + if("publicity") + if(unlock_content) + toggles ^= MEMBER_PUBLIC + if("gender") + if(gender == MALE) + gender = FEMALE + else + gender = MALE + underwear = "Nude" + undershirt = "Nude" + socks = "Nude" + facial_hair_style = "Shaved" + hair_style = "Bald" + + if("hotkeys") + hotkeys = !hotkeys + if("action_buttons") + buttons_locked = !buttons_locked + if("tgui_fancy") + tgui_fancy = !tgui_fancy + if("tgui_lock") + tgui_lock = !tgui_lock + if("winflash") + windowflashing = !windowflashing + if("hear_adminhelps") + toggles ^= SOUND_ADMINHELP + if("announce_login") + toggles ^= ANNOUNCE_LOGIN + + if("be_special") + var/be_special_type = href_list["be_special_type"] + if(be_special_type in be_special) + be_special -= be_special_type + else + be_special += be_special_type + + if("name") + be_random_name = !be_random_name + + if("all") + be_random_body = !be_random_body + + if("hear_midis") + toggles ^= SOUND_MIDI + + if("lobby_music") + toggles ^= SOUND_LOBBY + if((toggles & SOUND_LOBBY) && user.client) + user.client.playtitlemusic() + else + user.stop_sound_channel(CHANNEL_LOBBYMUSIC) + + if("ghost_ears") + chat_toggles ^= CHAT_GHOSTEARS + + if("ghost_sight") + chat_toggles ^= CHAT_GHOSTSIGHT + + if("ghost_whispers") + chat_toggles ^= CHAT_GHOSTWHISPER + + if("ghost_radio") + chat_toggles ^= CHAT_GHOSTRADIO + + if("ghost_pda") + chat_toggles ^= CHAT_GHOSTPDA + + if("pull_requests") + chat_toggles ^= CHAT_PULLR + + if("allow_midround_antag") + toggles ^= MIDROUND_ANTAG + + if("parallaxup") + parallax = Wrap(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) + if (parent && parent.mob && parent.mob.hud_used) + parent.mob.hud_used.update_parallax_pref(parent.mob) + + if("parallaxdown") + parallax = Wrap(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) + if (parent && parent.mob && parent.mob.hud_used) + parent.mob.hud_used.update_parallax_pref(parent.mob) + + if("save") + save_preferences() + save_character() + + if("load") + load_preferences() + load_character() + attempt_vr(parent.prefs_vr,"load_vore","") + + if("changeslot") + attempt_vr(parent.prefs_vr,"load_vore","") + if(!load_character(text2num(href_list["num"]))) + random_character() + real_name = random_unique_name(gender) + save_character() + + if("tab") + if (href_list["tab"]) + current_tab = text2num(href_list["tab"]) + + ShowChoices(user) + return 1 + +/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) + if(be_random_name) + real_name = pref_species.random_name(gender) + + if(be_random_body) + random_character(gender) + + if(config.humans_need_surnames) + var/firstspace = findtext(real_name, " ") + var/name_length = length(real_name) + if(!firstspace) //we need a surname + real_name += " [pick(GLOB.last_names)]" + else if(firstspace == name_length) + real_name += "[pick(GLOB.last_names)]" + + character.real_name = real_name + character.name = character.real_name + + character.gender = gender + character.age = age + + character.eye_color = eye_color + var/obj/item/organ/eyes/organ_eyes = character.getorgan(/obj/item/organ/eyes) + if(organ_eyes) + if(!initial(organ_eyes.eye_color)) + organ_eyes.eye_color = eye_color + organ_eyes.old_eye_color = eye_color + character.hair_color = hair_color + character.facial_hair_color = facial_hair_color + + character.skin_tone = skin_tone + character.hair_style = hair_style + character.facial_hair_style = facial_hair_style + character.underwear = underwear + character.undershirt = undershirt + character.socks = socks + + character.backbag = backbag + + character.dna.features = features.Copy() //Flavor text is now a DNA feature + character.dna.real_name = character.real_name + var/datum/species/chosen_species + if(pref_species != /datum/species/human && config.mutant_races) + chosen_species = pref_species.type + else + chosen_species = /datum/species/human + character.set_species(chosen_species, icon_update=0) + + //citadel code + character.give_genitals() + character.flavor_text = features["flavor_text"] //Let's update their flavor_text at least initially + character.canbearoused = arousable + + if(icon_updates) + character.update_body() + character.update_hair() + character.update_body_parts() + character.update_genitals() diff --git a/code/modules/client/preferences.dm.rej b/code/modules/client/preferences.dm.rej new file mode 100644 index 0000000000..02e6ad2d76 --- /dev/null +++ b/code/modules/client/preferences.dm.rej @@ -0,0 +1,14 @@ +diff a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm (rejected hunks) +@@ -130,12 +130,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) + menuoptions = list() + return + +-/datum/preferences/vv_edit_var(var_name, var_value) +- var/static/list/banned_edits = list("exp") +- if(var_name in banned_edits) +- return FALSE +- return ..() +- + /datum/preferences/proc/ShowChoices(mob/user) + if(!user || !user.client) + return diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm new file mode 100644 index 0000000000..fc61fcf40b --- /dev/null +++ b/code/modules/jobs/job_exp.dm @@ -0,0 +1,279 @@ +GLOBAL_LIST_EMPTY(exp_to_update) +GLOBAL_PROTECT(exp_to_update) + + +// Procs +/datum/job/proc/required_playtime_remaining(client/C) + if(!C) + return 0 + if(!config.use_exp_tracking) + return 0 + if(!exp_requirements || !exp_type) + return 0 + if(!job_is_xp_locked(src.title)) + return 0 + if(config.use_exp_restrictions_admin_bypass && check_rights(R_ADMIN, FALSE, C.mob)) + return 0 + var/isexempt = C.prefs.db_flags & DB_FLAG_EXEMPT + if(isexempt) + return 0 + var/my_exp = C.calc_exp_type(get_exp_req_type()) + var/job_requirement = get_exp_req_amount() + if(my_exp >= job_requirement) + return 0 + else + return (job_requirement - my_exp) + +/datum/job/proc/get_exp_req_amount() + if(title in GLOB.command_positions) + if(config.use_exp_restrictions_heads_hours) + return config.use_exp_restrictions_heads_hours * 60 + return exp_requirements + +/datum/job/proc/get_exp_req_type() + if(title in GLOB.command_positions) + if(config.use_exp_restrictions_heads_department && exp_type_department) + return exp_type_department + return exp_type + +/proc/job_is_xp_locked(jobtitle) + if(!config.use_exp_restrictions_heads && jobtitle in GLOB.command_positions) + return FALSE + if(!config.use_exp_restrictions_other && !(jobtitle in GLOB.command_positions)) + return FALSE + return TRUE + +/client/proc/calc_exp_type(exptype) + var/list/explist = prefs.exp.Copy() + var/amount = 0 + var/list/typelist = GLOB.exp_jobsmap[exptype] + if(!typelist) + return -1 + for(var/job in typelist["titles"]) + if(job in explist) + amount += explist[job] + return amount + +/client/proc/get_exp_report() + if(!config.use_exp_tracking) + return "Tracking is disabled in the server configuration file." + var/list/play_records = prefs.exp + if(!play_records.len) + set_exp_from_db() + play_records = prefs.exp + if(!play_records.len) + return "[key] has no records." + var/return_text = list() + return_text += "" + var/list/jobs_locked = list() + var/list/jobs_unlocked = list() + for(var/datum/job/job in SSjob.occupations) + if(job.exp_requirements && job.exp_type) + if(!job_is_xp_locked(job.title)) + continue + else if(!job.required_playtime_remaining(mob.client)) + jobs_unlocked += job.title + else + var/xp_req = job.get_exp_req_amount() + jobs_locked += "[job.title] [get_exp_format(text2num(calc_exp_type(job.get_exp_req_type())))] / [get_exp_format(xp_req)] as [job.get_exp_req_type()])" + if(jobs_unlocked.len) + return_text += "

Jobs Unlocked:" + if(jobs_locked.len) + return_text += "

Jobs Not Unlocked:" + return return_text + + +/client/proc/get_exp_living() + if(!prefs.exp) + return "No data" + var/exp_living = text2num(prefs.exp[EXP_TYPE_LIVING]) + return get_exp_format(exp_living) + +/proc/get_exp_format(expnum) + if(expnum > 60) + return num2text(round(expnum / 60)) + "h" + else if(expnum > 0) + return num2text(expnum) + "m" + else + return "0h" + +/datum/controller/subsystem/blackbox/proc/update_exp(mins, ann = FALSE) + if(!SSdbcore.Connect()) + return -1 + for(var/client/L in GLOB.clients) + if(L.is_afk()) + continue + addtimer(CALLBACK(L,/client/proc/update_exp_list,mins,ann),10) + +/datum/controller/subsystem/blackbox/proc/update_exp_db() + SSdbcore.MassInsert(format_table_name("role_time"),GLOB.exp_to_update,TRUE) + LAZYCLEARLIST(GLOB.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 -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()) + return -1 + var/list/play_records = list() + while(exp_read.NextRow()) + play_records[exp_read.item[1]] = text2num(exp_read.item[2]) + + for(var/rtype in SSjob.name_occupations) + if(!play_records[rtype]) + play_records[rtype] = 0 + for(var/rtype in GLOB.exp_specialmap) + if(!play_records[rtype]) + play_records[rtype] = 0 + + prefs.exp = play_records + + +//updates player db flags +/client/proc/update_flag_db(newflag, state = FALSE) + + if(!SSdbcore.Connect()) + return -1 + + if(!set_db_player_flags()) + return -1 + + 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()) + return -1 + + +/client/proc/update_exp_list(minutes, announce_changes = FALSE) + if(!config.use_exp_tracking) + 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()) + return -1 + var/list/play_records = list() + while(exp_read.NextRow()) + play_records[exp_read.item[1]] = text2num(exp_read.item[2]) + + for(var/rtype in SSjob.name_occupations) + if(!play_records[rtype]) + play_records[rtype] = 0 + for(var/rtype in GLOB.exp_specialmap) + if(!play_records[rtype]) + play_records[rtype] = 0 + var/list/old_records = play_records.Copy() + if(isliving(mob)) + if(mob.stat != DEAD) + var/rolefound = FALSE + play_records[EXP_TYPE_LIVING] += minutes + if(announce_changes) + to_chat(src,"You got: [minutes] Living EXP!") + if(mob.mind.assigned_role) + for(var/job in SSjob.name_occupations) + if(mob.mind.assigned_role == job) + rolefound = TRUE + play_records[job] += minutes + if(announce_changes) + to_chat(src,"You got: [minutes] [job] EXP!") + if(!rolefound) + for(var/role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) + if(mob.mind.assigned_role == role) + rolefound = TRUE + play_records[role] += minutes + if(announce_changes) + to_chat(mob,"You got: [minutes] [role] EXP!") + if(mob.mind.special_role && !mob.mind.var_edited) + 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] [trackedrole] EXP!") + if(!rolefound) + play_records["Unknown"] += minutes + else + play_records[EXP_TYPE_GHOST] += minutes + if(announce_changes) + to_chat(src,"You got: [minutes] Ghost EXP!") + else if(isobserver(mob)) + play_records[EXP_TYPE_GHOST] += minutes + if(announce_changes) + to_chat(src,"You got: [minutes] Ghost EXP!") + else if(minutes) //Let "refresh" checks go through + return + prefs.exp = play_records + + for(var/jtype in play_records) + if(play_records[jtype] != old_records[jtype]) + LAZYINITLIST(GLOB.exp_to_update) + GLOB.exp_to_update.Add(list(list( + "job" = "'[sanitizeSQL(jtype)]'", + "ckey" = "'[sanitizeSQL(ckey)]'", + "minutes" = play_records[jtype]))) + addtimer(CALLBACK(SSblackbox,/datum/controller/subsystem/blackbox/proc/update_exp_db),20,TIMER_OVERRIDE|TIMER_UNIQUE) + + +//ALWAYS call this at beginning to any proc touching player flags, or your database admin will probably be mad +/client/proc/set_db_player_flags() + if(!SSdbcore.Connect()) + return FALSE + + var/datum/DBQuery/flags_read = SSdbcore.NewQuery("SELECT flags FROM [format_table_name("player")] WHERE ckey='[ckey]'") + + if(!flags_read.Execute()) + 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 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..94d2753fc5 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 @@ -90,6 +93,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 +129,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 +165,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/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/config/config.txt b/config/config.txt index b344482679..ae2cc1e43a 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 @@ -315,4 +328,4 @@ 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 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/tgstation.dme b/tgstation.dme index 4a1a0f3925..ee43dcafd7 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -1462,6 +1462,7 @@ #include "code\modules\hydroponics\grown\tomato.dm" #include "code\modules\hydroponics\grown\towercap.dm" #include "code\modules\jobs\access.dm" +#include "code\modules\jobs\job_exp.dm" #include "code\modules\jobs\jobs.dm" #include "code\modules\jobs\job_types\assistant.dm" #include "code\modules\jobs\job_types\captain.dm"