diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 2f58d4e9ebb..a8a0459bcb1 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -7,4 +7,4 @@ assignees: ''
---
-Please place all features requests here: https://nanotrasen.se/forum/60-suggestions/
+Please place all features requests here: https://www.paradisestation.org/forum/60-suggestions/
diff --git a/.gitignore b/.gitignore
index a4634f3fc84..d9f4a187400 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,4 +21,5 @@ data/
*.db
stddef.dm
.atom-build.json
-*.vscode/*
\ No newline at end of file
+*.vscode/*
+!/.vscode/extensions.json
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 464e01c8f53..5a731170a79 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,8 +18,8 @@ addons:
env:
global:
- - BYOND_MAJOR="512"
- - BYOND_MINOR="1454"
+ - BYOND_MAJOR="513"
+ - BYOND_MINOR="1505"
- BYOND_MACRO_COUNT=4
matrix:
- DM_MAPFILE="cyberiad"
@@ -69,4 +69,4 @@ jobs:
- npm install --loglevel=error
before_script:
script:
- - gulp
+ - node node_modules/gulp/bin/gulp.js --require less-loader
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000000..9099488143b
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,6 @@
+{
+ "recommendations": [
+ "gbasood.byond-dm-language-support",
+ "platymuus.dm-langclient"
+ ]
+}
diff --git a/README.md b/README.md
index d60fe8006c8..eafdb5ffe91 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
[](http://forthebadge.com)
[](http://forthebadge.com)
-[Website](https://nanotrasen.se/) - [Code](https://github.com/ParadiseSS13/Paradise) - [Discord](https://discordapp.com/invite/YJDsXFE)
+[Website](https://www.paradisestation.org/) - [Code](https://github.com/ParadiseSS13/Paradise) - [Discord](https://discordapp.com/invite/YJDsXFE)
---
@@ -104,7 +104,7 @@ Your server details go in /config/dbconfig.txt,
and the SQL schema is in /SQL/paradise_schema.sql or /SQL/paradise_schema_prefix.sql,
depending on if you want table prefixes.
More detailed setup instructions are located on our wiki:
-https://nanotrasen.se/wiki/index.php/Setting_up_the_Database
+https://www.paradisestation.org/wiki/index.php/Setting_up_the_Database
---
diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 1e4692b7e3b..837f8f440d6 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -97,7 +97,7 @@ CREATE TABLE `customuseritems` (
`cuiPropAdjust` text,
`cuiJobMask` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -124,7 +124,7 @@ CREATE TABLE `death` (
`fireloss` int(11) NOT NULL,
`oxyloss` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -160,7 +160,7 @@ CREATE TABLE `admin` (
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -177,7 +177,7 @@ CREATE TABLE `admin_log` (
`adminip` varchar(18) NOT NULL,
`log` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -212,7 +212,7 @@ CREATE TABLE `ban` (
`unbanned_computerid` varchar(32) DEFAULT NULL,
`unbanned_ip` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -230,7 +230,7 @@ CREATE TABLE `feedback` (
`var_value` int(16) DEFAULT NULL,
`details` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -272,7 +272,7 @@ CREATE TABLE `player` (
`parallax` tinyint(1) DEFAULT '8',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
-) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -293,7 +293,7 @@ CREATE TABLE `poll_option` (
`descmid` varchar(32) DEFAULT NULL,
`descmax` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -314,7 +314,7 @@ CREATE TABLE `poll_question` (
`createdby_ckey` varchar(45) NULL DEFAULT NULL,
`createdby_ip` varchar(45) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -333,7 +333,7 @@ CREATE TABLE `poll_textreply` (
`replytext` text NOT NULL,
`adminrank` varchar(32) NOT NULL DEFAULT 'Player',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -353,7 +353,7 @@ CREATE TABLE `poll_vote` (
`adminrank` varchar(32) NOT NULL,
`rating` int(2) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -369,7 +369,7 @@ CREATE TABLE `privacy` (
`consent` bit(1) NOT NULL,
PRIMARY KEY (`ckey`),
UNIQUE KEY `ckey_UNIQUE` (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -391,7 +391,7 @@ CREATE TABLE `karma` (
`spenderip` text NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -407,7 +407,7 @@ CREATE TABLE `karmatotals` (
`karma` int(11) NOT NULL,
`karmaspent` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -426,7 +426,7 @@ CREATE TABLE `library` (
`ckey` varchar(45) NOT NULL,
`flagged` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -442,7 +442,7 @@ CREATE TABLE `legacy_population` (
`admincount` int(11) DEFAULT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -458,7 +458,7 @@ CREATE TABLE `whitelist` (
`job` text,
`species` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -485,7 +485,7 @@ CREATE TABLE `watch` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -505,8 +505,9 @@ CREATE TABLE `notes` (
`last_editor` varchar(32),
`edits` text,
`server` varchar(50) NOT NULL,
+ `crew_playtime` mediumint(8) UNSIGNED DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -523,7 +524,7 @@ CREATE TABLE `memo` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -537,7 +538,7 @@ CREATE TABLE `ipintel` (
`date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
`intel` real NOT NULL DEFAULT '0',
PRIMARY KEY (`ip`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -550,7 +551,7 @@ CREATE TABLE `vpn_whitelist` (
`ckey` varchar(32) NOT NULL,
`reason` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -563,5 +564,34 @@ CREATE TABLE `oauth_tokens` (
`ckey` varchar(32) NOT NULL,
`token` varchar(32) NOT NULL,
PRIMARY KEY (`token`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
+
+
+--
+-- Table structure for table `playtime_history`
+--
+DROP TABLE IF EXISTS `playtime_history`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `playtime_history` (
+ `ckey` varchar(32) NOT NULL,
+ `date` DATE NOT NULL,
+ `time_living` SMALLINT NOT NULL,
+ `time_ghost` SMALLINT NOT NULL,
+ PRIMARY KEY (`ckey`, `date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+--
+-- Table structure for table `connection_log`
+--
+DROP TABLE IF EXISTS `connection_log`;
+CREATE TABLE `connection_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `datetime` datetime NOT NULL,
+ `ckey` varchar(32) NOT NULL,
+ `ip` varchar(32) NOT NULL,
+ `computerid` varchar(32) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql
index aa5d5895e56..b43141f6784 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -97,7 +97,7 @@ CREATE TABLE `SS13_customuseritems` (
`cuiPropAdjust` text,
`cuiJobMask` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -124,7 +124,7 @@ CREATE TABLE `SS13_death` (
`fireloss` int(11) NOT NULL,
`oxyloss` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -159,7 +159,7 @@ CREATE TABLE `SS13_admin` (
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -176,7 +176,7 @@ CREATE TABLE `SS13_admin_log` (
`adminip` varchar(18) NOT NULL,
`log` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -211,7 +211,7 @@ CREATE TABLE `SS13_ban` (
`unbanned_computerid` varchar(32) DEFAULT NULL,
`unbanned_ip` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -229,7 +229,7 @@ CREATE TABLE `SS13_feedback` (
`var_value` int(16) DEFAULT NULL,
`details` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -271,7 +271,7 @@ CREATE TABLE `SS13_player` (
`parallax` tinyint(1) DEFAULT '8',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
-) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -292,7 +292,7 @@ CREATE TABLE `SS13_poll_option` (
`descmid` varchar(32) DEFAULT NULL,
`descmax` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -313,7 +313,7 @@ CREATE TABLE `SS13_poll_question` (
`createdby_ckey` varchar(45) NULL DEFAULT NULL,
`createdby_ip` varchar(45) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -332,7 +332,7 @@ CREATE TABLE `SS13_poll_textreply` (
`replytext` text NOT NULL,
`adminrank` varchar(32) NOT NULL DEFAULT 'Player',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -352,7 +352,7 @@ CREATE TABLE `SS13_poll_vote` (
`adminrank` varchar(32) NOT NULL,
`rating` int(2) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -368,7 +368,7 @@ CREATE TABLE `ss13_privacy` (
`consent` bit(1) NOT NULL,
PRIMARY KEY (`ckey`),
UNIQUE KEY `ckey_UNIQUE` (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -390,7 +390,7 @@ CREATE TABLE `SS13_karma` (
`spenderip` text NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -406,7 +406,7 @@ CREATE TABLE `SS13_karmatotals` (
`karma` int(11) NOT NULL,
`karmaspent` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -425,7 +425,7 @@ CREATE TABLE `SS13_library` (
`ckey` varchar(45) NOT NULL,
`flagged` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -441,7 +441,7 @@ CREATE TABLE `SS13_legacy_population` (
`admincount` int(11) DEFAULT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -457,7 +457,7 @@ CREATE TABLE `SS13_whitelist` (
`job` text,
`species` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -484,7 +484,7 @@ CREATE TABLE `SS13_watch` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -504,8 +504,9 @@ CREATE TABLE `SS13_notes` (
`last_editor` varchar(32),
`edits` text,
`server` varchar(50) NOT NULL,
+ `crew_playtime` mediumint(8) UNSIGNED DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -522,7 +523,7 @@ CREATE TABLE `SS13_memo` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -536,7 +537,7 @@ CREATE TABLE `SS13_ipintel` (
`date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
`intel` real NOT NULL DEFAULT '0',
PRIMARY key (`ip`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -549,7 +550,7 @@ CREATE TABLE `SS13_vpn_whitelist` (
`ckey` varchar(32) NOT NULL,
`reason` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -562,5 +563,32 @@ CREATE TABLE `SS13_oauth_tokens` (
`ckey` varchar(32) NOT NULL,
`token` varchar(32) NOT NULL,
PRIMARY KEY (`token`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `SS13_playtime_history`
+--
+DROP TABLE IF EXISTS `SS13_playtime_history`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `SS13_playtime_history` (
+ `ckey` varchar(32) NOT NULL,
+ `date` DATE NOT NULL,
+ `time_living` SMALLINT NOT NULL,
+ `time_ghost` SMALLINT NOT NULL,
+ PRIMARY KEY (`ckey`, `date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `SS13_connection_log`
+--
+DROP TABLE IF EXISTS `SS13_connection_log`;
+CREATE TABLE `SS13_connection_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `datetime` datetime NOT NULL,
+ `ckey` varchar(32) NOT NULL,
+ `ip` varchar(32) NOT NULL,
+ `computerid` varchar(32) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
diff --git a/SQL/updates/10-11.sql b/SQL/updates/10-11.sql
new file mode 100644
index 00000000000..9564d3efaad
--- /dev/null
+++ b/SQL/updates/10-11.sql
@@ -0,0 +1,41 @@
+# Updating SQL from ver 10 to 11
+# Changes tables from latin1 to utf8mb4
+# Based on: https://github.com/BeeStation/BeeStation-Hornet/pull/1074/files
+# -Kyet
+
+# Generation (not necessary to run unless your DB has extra tables)
+SELECT CONCAT("ALTER TABLE ", TABLE_SCHEMA, '.', TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") AS ExecuteTheString
+FROM INFORMATION_SCHEMA.TABLES
+WHERE TABLE_SCHEMA="feedback"
+AND TABLE_TYPE="BASE TABLE";
+
+# Actual table conversion stuff
+ALTER TABLE feedback.admin CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.admin_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.ban CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.characters CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.connection_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.customuseritems CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.death CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.donators CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.erro_privacy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.feedback CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.ipintel CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.karma CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.karmatotals CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.legacy_population CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.library CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.memo CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.notes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.oauth_tokens CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.player CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.playtime_history CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_option CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_question CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_textreply CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_vote CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.population CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.privacy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.vpn_whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.watch CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
\ No newline at end of file
diff --git a/SQL/updates/9-10.sql b/SQL/updates/9-10.sql
new file mode 100644
index 00000000000..942aca1b541
--- /dev/null
+++ b/SQL/updates/9-10.sql
@@ -0,0 +1,24 @@
+# Updating SQL from ver 9 to 10 - Kyet
+
+# Add the 'playtime_history' table that tracks playtime per player per day
+CREATE TABLE `playtime_history` (
+ `ckey` varchar(32) NOT NULL,
+ `date` DATE NOT NULL,
+ `time_living` SMALLINT NOT NULL,
+ `time_ghost` SMALLINT NOT NULL,
+ PRIMARY KEY (`ckey`, `date`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+# Add the 'connection_log' table, which is used to log all connections to the server
+DROP TABLE IF EXISTS `connection_log`;
+CREATE TABLE `connection_log` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `datetime` datetime NOT NULL,
+ `ckey` varchar(32) NOT NULL,
+ `ip` varchar(32) NOT NULL,
+ `computerid` varchar(32) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+# Add the 'crew_playtime' field to the 'notes' table, which gives admins some idea of how many hours have passed for a player since they got a note
+ALTER TABLE `notes` ADD `crew_playtime` mediumint(8) UNSIGNED DEFAULT '0' AFTER `server`;
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 7dd5fc5a609..51b895ff12d 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -319,7 +319,7 @@
#define INVESTIGATE_BOMB "bombs"
// The SQL version required by this version of the code
-#define SQL_VERSION 9
+#define SQL_VERSION 11
// Vending machine stuff
#define CAT_NORMAL 1
@@ -384,7 +384,7 @@
#define EXPLOSIVE_WALL_GROUP_SYNDICATE_BASE "syndicate_base"
// Filters
-#define FILTER_AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, border=4, color="#04080FAA")
+#define FILTER_AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, color="#04080FAA")
//Fullscreen overlay resolution in tiles.
#define FULLSCREEN_OVERLAY_RESOLUTION_X 15
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 5b93e570222..34ad4d69e54 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -10,7 +10,6 @@
// justice if someone's abusing your role
#define ROLE_SYNDICATE "Syndicate"
#define ROLE_TRAITOR "traitor"
-#define ROLE_MINDSLAVE "mindslave"
#define ROLE_OPERATIVE "operative"
#define ROLE_CHANGELING "changeling"
#define ROLE_WIZARD "wizard"
diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm
index 4fd2c390fa7..16b6e475b8e 100644
--- a/code/__DEFINES/typeids.dm
+++ b/code/__DEFINES/typeids.dm
@@ -2,5 +2,5 @@
#define TYPEID_NULL "0"
#define TYPEID_NORMAL_LIST "f"
//helper macros
-#define GET_TYPEID(ref) ( ( (lentext(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, lentext(ref) - 6) ) )
+#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref) - 6) ) )
#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST)
\ No newline at end of file
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 65fc87c3b34..7a91c3bf9bc 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -1,5 +1,5 @@
//location of the rust-g library
-#define RUST_G "rust_g"
+#define RUST_G (world.system_type == UNIX ? "./librust_g.so" : "./rust_g.dll")
// On Linux/Unix systems the line endings are LF, on windows it's CRLF, admins that don't use notepad++
// will get logs that are one big line if the system is Linux and they are using notepad. This solves it by adding CR to every line ending
@@ -140,6 +140,9 @@
/proc/log_asset(text)
WRITE_LOG(GLOB.world_asset_log, "ASSET: [text]")
+/proc/log_runtime_summary(text)
+ WRITE_LOG(GLOB.runtime_summary_log, "[text]")
+
/**
* Standardized method for tracking startup times.
*/
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index 49983658d48..5ffe891d95b 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -69,11 +69,6 @@
return list[index]
return
-/proc/islist(list/list)
- if(istype(list))
- return 1
- return 0
-
//Return either pick(list) or null if list is not of type /list or is empty
/proc/safepick(list/list)
if(!islist(list) || !list.len)
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index f3b76064517..0f356aa4f4a 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -20,7 +20,7 @@
if(!istext(t))
t = "[t]" // Just quietly assume any non-texts are supposed to be text
var/sqltext = dbcon.Quote(t);
- return copytext(sqltext, 2, lentext(sqltext));//Quote() adds quotes around input, we already do that
+ return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that
/proc/format_table_name(table as text)
return sqlfdbktableprefix + table
@@ -333,9 +333,9 @@ proc/checkhtml(var/t)
//is in the other string at the same spot (assuming it is not a replace char).
//This is used for fingerprints
var/newtext = text
- if(lentext(text) != lentext(compare))
+ if(length(text) != length(compare))
return 0
- for(var/i = 1, i < lentext(text), i++)
+ for(var/i = 1, i < length(text), i++)
var/a = copytext(text,i,i+1)
var/b = copytext(compare,i,i+1)
//if it isn't both the same letter, or if they are both the replacement character
@@ -355,7 +355,7 @@ proc/checkhtml(var/t)
if(!text || !character)
return 0
var/count = 0
- for(var/i = 1, i <= lentext(text), i++)
+ for(var/i = 1, i <= length(text), i++)
var/a = copytext(text,i,i+1)
if(a == character)
count++
@@ -400,8 +400,8 @@ proc/checkhtml(var/t)
//Used in preferences' SetFlavorText and human's set_flavor verb
//Previews a string of len or less length
/proc/TextPreview(var/string,var/len=40)
- if(lentext(string) <= len)
- if(!lentext(string))
+ if(length(string) <= len)
+ if(!length(string))
return "\[...\]"
else
return html_encode(string) //NO DECODED HTML YOU CHUCKLEFUCKS
@@ -541,7 +541,7 @@ proc/checkhtml(var/t)
text = "[text]"
else
text = "[text]"
-
+
text = copytext(text, 1, MAX_PAPER_MESSAGE_LEN)
return text
diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm
index 472caea4490..7c1d2257e27 100644
--- a/code/__HELPERS/traits.dm
+++ b/code/__HELPERS/traits.dm
@@ -62,6 +62,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
//mob traits
#define TRAIT_PACIFISM "pacifism"
+#define TRAIT_WATERBREATH "waterbreathing"
// common trait sources
#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention
\ No newline at end of file
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 82295bc9d80..f1611b38fd9 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -540,9 +540,20 @@ Returns 1 if the chain up to the area contains the given typepath
/proc/between(var/low, var/middle, var/high)
return max(min(middle, high), low)
-proc/arctan(x)
+
+
+#if DM_VERSION > 513
+#warn 513 is definitely stable now, remove this
+#endif
+#if DM_VERSION < 513
+/proc/arctan(x)
var/y=arcsin(x/sqrt(1+x*x))
return y
+/proc/islist(list/list)
+ if(istype(list))
+ return 1
+ return 0
+#endif
//returns random gauss number
proc/GaussRand(var/sigma)
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 43482937afb..10c7d74c752 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -12,6 +12,8 @@ GLOBAL_VAR(world_href_log)
GLOBAL_PROTECT(world_href_log)
GLOBAL_VAR(world_asset_log)
GLOBAL_PROTECT(world_asset_log)
+GLOBAL_VAR(runtime_summary_log)
+GLOBAL_PROTECT(runtime_summary_log)
var/list/jobMax = list()
var/list/admin_log = list ( )
diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm
index a5586578b3f..9c0f498cd83 100644
--- a/code/_onclick/hud/plane_master.dm
+++ b/code/_onclick/hud/plane_master.dm
@@ -16,7 +16,7 @@
filters += filter(type = "outline", size = _size, color = _color)
/obj/screen/plane_master/proc/shadow(_size, _border, _offset = 0, _x = 0, _y = 0, _color = "#04080FAA")
- filters += filter(type = "drop_shadow", x = _x, y = _y, color = _color, size = _size, offset = _offset, border = _border)
+ filters += filter(type = "drop_shadow", x = _x, y = _y, color = _color, size = _size, offset = _offset)
/obj/screen/plane_master/proc/clear_filters()
filters = list()
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 8ae35d52179..86883654997 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -79,7 +79,7 @@
var/auto_cryo_ssd_mins = 0
var/ssd_warning = 0
-
+
var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb
var/traitor_objectives_amount = 2
@@ -102,6 +102,7 @@
var/donationsurl = "http://example.org"
var/repositoryurl = "http://example.org"
var/discordurl = "http://example.org"
+ var/discordforumurl = "http://example.org"
var/overflow_server_url
var/forbid_singulo_possession = 0
@@ -144,6 +145,7 @@
var/ipintel_detailsurl = "https://iphub.info/?ip="
var/forum_link_url
+ var/forum_playerinfo_url
var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt
var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt
@@ -244,7 +246,7 @@
//cube monkey limit
var/cubemonkeycap = 20
-
+
// Makes gamemodes respect player limits
var/enable_gamemode_player_limit = 0
@@ -354,6 +356,9 @@
if("forum_link_url")
config.forum_link_url = value
+ if("forum_playerinfo_url")
+ config.forum_playerinfo_url = value
+
if("log_ooc")
config.log_ooc = 1
@@ -483,6 +488,9 @@
if("discordurl")
config.discordurl = value
+ if("discordforumurl")
+ config.discordforumurl = value
+
if("donationsurl")
config.donationsurl = value
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index 166083b36ff..504a2d74a2c 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -421,23 +421,23 @@ SUBSYSTEM_DEF(jobs)
to_chat(H, "You are the [alt_title ? alt_title : rank].")
to_chat(H, "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.")
- to_chat(H, "For more information on how the station works, see Standard Operating Procedure (SOP)")
+ to_chat(H, "For more information on how the station works, see Standard Operating Procedure (SOP)")
if(job.is_service)
- to_chat(H, "As a member of Service, make sure to read up on your Department SOP")
+ to_chat(H, "As a member of Service, make sure to read up on your Department SOP")
if(job.is_supply)
- to_chat(H, "As a member of Supply, make sure to read up on your Department SOP")
+ to_chat(H, "As a member of Supply, make sure to read up on your Department SOP")
if(job.is_command)
- to_chat(H, "As an important member of Command, read up on your Department SOP")
+ to_chat(H, "As an important member of Command, read up on your Department SOP")
if(job.is_legal)
- to_chat(H, "Your job requires complete knowledge of Space Law and Legal Standard Operating Procedure")
+ to_chat(H, "Your job requires complete knowledge of Space Law and Legal Standard Operating Procedure")
if(job.is_engineering)
- to_chat(H, "As a member of Engineering, make sure to read up on your Department SOP")
+ to_chat(H, "As a member of Engineering, make sure to read up on your Department SOP")
if(job.is_medical)
- to_chat(H, "As a member of Medbay, make sure to read up on your Department SOP")
+ to_chat(H, "As a member of Medbay, make sure to read up on your Department SOP")
if(job.is_science)
- to_chat(H, "As a member of Science, make sure to read up on your Department SOP")
+ to_chat(H, "As a member of Science, make sure to read up on your Department SOP")
if(job.is_security)
- to_chat(H, "As a member of Security, you are to know Space Law, Legal Standard Operating Procedure, as well as your Department SOP")
+ to_chat(H, "As a member of Security, you are to know Space Law, Legal Standard Operating Procedure, as well as your Department SOP")
if(job.req_admin_notify)
to_chat(H, "You are playing a job that is important for the game progression. If you have to disconnect, please notify the admins via adminhelp.")
diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm
index 24be623573e..058569250ec 100644
--- a/code/controllers/subsystem/tickets/tickets.dm
+++ b/code/controllers/subsystem/tickets/tickets.dm
@@ -23,7 +23,7 @@ SUBSYSTEM_DEF(tickets)
flags = SS_BACKGROUND
- var/list/allTickets
+ var/list/allTickets = list() //make it here because someone might ahelp before the system has initialized
var/ticketCounter = 1
@@ -31,7 +31,6 @@ SUBSYSTEM_DEF(tickets)
close_messages = list("- [ticket_name] Rejected! -",
"Please try to be calm, clear, and descriptive in admin helps, do not assume the staff member has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.",
"Your [ticket_name] has now been closed.")
- LAZYINITLIST(allTickets)
return ..()
/datum/controller/subsystem/tickets/fire()
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index e5b948f4f26..aca505e6a03 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -41,6 +41,10 @@
use_delay = use_delay_override
/datum/component/squeak/proc/play_squeak()
+ if(ismob(parent))
+ var/mob/M = parent
+ if(M.stat == DEAD)
+ return
if(prob(squeak_chance))
if(!override_squeak_sounds)
playsound(parent, pickweight(default_squeak_sounds), volume, 1, -1)
diff --git a/code/datums/spells/area_teleport.dm b/code/datums/spells/area_teleport.dm
index 2f07f106d2a..4aa7eafbed6 100644
--- a/code/datums/spells/area_teleport.dm
+++ b/code/datums/spells/area_teleport.dm
@@ -25,10 +25,13 @@
var/A = null
if(!randomise_selection)
- A = input("Area to teleport to", "Teleport", A) in teleportlocs
+ A = input("Area to teleport to", "Teleport", A) as null|anything in teleportlocs
else
A = pick(teleportlocs)
+ if(!A)
+ return
+
var/area/thearea = teleportlocs[A]
if(thearea.tele_proof && !istype(thearea, /area/wizard_station))
diff --git a/code/datums/spells/cluwne.dm b/code/datums/spells/cluwne.dm
index 6a87419886c..58a33e31a44 100644
--- a/code/datums/spells/cluwne.dm
+++ b/code/datums/spells/cluwne.dm
@@ -17,7 +17,7 @@
var/obj/item/organ/internal/brain/cluwne/idiot_brain = new
idiot_brain.insert(src, make_cluwne = 0)
idiot_brain.dna = dna.Clone()
- setBrainLoss(80)
+ setBrainLoss(80, use_brain_mod = FALSE)
set_nutrition(9000)
overeatduration = 9000
Confused(30)
diff --git a/code/game/gamemodes/autotraitor/autotraitor.dm b/code/game/gamemodes/autotraitor/autotraitor.dm
index cc0cc307ed5..6c89ab45434 100644
--- a/code/game/gamemodes/autotraitor/autotraitor.dm
+++ b/code/game/gamemodes/autotraitor/autotraitor.dm
@@ -52,7 +52,7 @@
if(!traitor || !istype(traitor))
pre_traitors.Remove(traitor)
continue
- if(istype(traitor))
+ if(istype(traitor))
traitor.special_role = SPECIAL_ROLE_TRAITOR
traitor.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm
index e1cef880e0a..cc7203d9c40 100644
--- a/code/game/gamemodes/blob/blobs/blob_mobs.dm
+++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm
@@ -186,6 +186,7 @@
else
adjustBruteLoss(0.2) // If you are at full health, you won't lose health. You'll need it. However the moment anybody sneezes on you, the decaying will begin.
adjustFireLoss(0.2)
+ ..()
/mob/living/simple_animal/hostile/blob/blobbernaut/New()
..()
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 1cc55d99f0c..113487dc1dc 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -9,24 +9,28 @@ var/global/list/all_cults = list()
/proc/is_convertable_to_cult(datum/mind/mind)
if(!mind)
- return 0
+ return FALSE
if(!mind.current)
- return 0
+ return FALSE
if(iscultist(mind.current))
- return 1 //If they're already in the cult, assume they are convertable
+ return TRUE //If they're already in the cult, assume they are convertable
if(ishuman(mind.current) && (mind.assigned_role in list("Captain", "Chaplain")))
- return 0
+ return FALSE
if(ishuman(mind.current))
var/mob/living/carbon/human/H = mind.current
- if(ismindshielded(H))
- return 0
+ if(ismindshielded(H)) //mindshield protects against conversions unless removed
+ return FALSE
+// if(mind.offstation_role) cant convert offstation roles such as ghost spawns
+// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew
if(issilicon(mind.current))
- return 0 //can't convert machines, that's ratvar's thing
+ return FALSE //can't convert machines, that's ratvar's thing
if(isguardian(mind.current))
var/mob/living/simple_animal/hostile/guardian/G = mind.current
if(!iscultist(G.summoner))
- return 0 //can't convert it unless the owner is converted
- return 1
+ return FALSE //can't convert it unless the owner is converted
+ if(isgolem(mind.current))
+ return FALSE
+ return TRUE
/proc/is_sacrifice_target(datum/mind/mind)
if(SSticker.mode.name == "cult")
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index d30ac4f8a68..3489a087ec0 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -198,7 +198,7 @@ var/list/blacklisted_pylon_turfs = typecacheof(list(
if(last_heal <= world.time)
last_heal = world.time + heal_delay
for(var/mob/living/L in range(5, src))
- if(iscultist(L) || istype(L, /mob/living/simple_animal/shade) || istype(L, /mob/living/simple_animal/hostile/construct))
+ if(iscultist(L) || iswizard(L) || istype(L, /mob/living/simple_animal/shade) || istype(L, /mob/living/simple_animal/hostile/construct))
if(L.health != L.maxHealth)
new /obj/effect/temp_visual/heal(get_turf(src), "#960000")
if(ishuman(L))
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 2f2f0cae02e..d918bead0de 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -340,144 +340,100 @@ var/list/teleport_runes = list()
fail_invoke()
-//Rite of Enlightenment: Converts a normal crewmember to the cult. Faster for every cultist nearby.
+//Rite of Offering: Converts a normal crewmember to the cult or sacrifices mindshielded and sacrifice targets.
/obj/effect/rune/convert
- cultist_name = "Rite of Enlightenment"
- cultist_desc = "converts a normal crewmember on top of it to the cult. Does not work on mindshielded crew."
+ cultist_name = "Rite of Offering"
+ cultist_desc = "Offers a non-cultists on top of it to your deity, either converting or sacrificing them."
invocation = "Mah'weyh pleggh at e'ntrath!"
icon_state = "3"
- req_cultists = 2
-
+ req_cultists = 1
+ allow_excess_invokers = TRUE
+ rune_in_use = FALSE
+
/obj/effect/rune/convert/do_invoke_glow()
return
/obj/effect/rune/convert/invoke(var/list/invokers)
- var/list/convertees = list()
- var/turf/T = get_turf(src)
-
- for(var/mob/living/M in T.contents)
- if(!iscultist(M) && !ismindshielded(M) && !isgolem(M) && ishuman(M))
- convertees.Add(M)
- if(!convertees.len)
- fail_invoke()
- log_game("Convert rune failed - no eligible convertees")
- return
- var/mob/living/new_cultist = pick(convertees)
- if(!is_convertable_to_cult(new_cultist.mind) || new_cultist.null_rod_check())
- for(var/M in invokers)
- to_chat(M, "Something is shielding [new_cultist]'s mind!")
- if(is_sacrifice_target(new_cultist.mind))
- to_chat(M, "\"I desire this one for myself. SACRIFICE THEM!\"")
- fail_invoke()
- log_game("Convert rune failed - convertee could not be converted")
- return
- ..()
-
- new_cultist.visible_message("[new_cultist] writhes in pain as the markings below them glow a bloody red!", \
- "AAAAAAAAAAAAAA-")
- SSticker.mode.add_cultist(new_cultist.mind, 1)
- new /obj/item/tome(get_turf(src))
- new_cultist.mind.special_role = "Cultist"
- to_chat(new_cultist, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \
- and something evil takes root.")
- to_chat(new_cultist, "Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve [SSticker.cultdat.entity_title3] above all else. Bring it back.\
- ")
-
-//Rite of Tribute: Sacrifices a crew member to Nar-Sie. Places them into a soul shard if they're in their body.
-/obj/effect/rune/sacrifice
- cultist_name = "Rite of Tribute"
- cultist_desc = "sacrifices a crew member to your god. May place them into a soul shard if their spirit remains in their body."
- icon_state = "3"
- allow_excess_invokers = 1
- invocation = "Barhah hra zar'garis!"
- rune_in_use = 0
-
-/obj/effect/rune/sacrifice/New()
- ..()
- cultist_desc = "sacrifices a crew member to [SSticker.cultdat.entity_title3]. May place them into a soul shard if their spirit remains in their body."
-
-/obj/effect/rune/sacrifice/invoke(var/list/invokers)
if(rune_in_use)
return
- rune_in_use = 1
- var/mob/living/user = invokers[1] //the first invoker is always the user
+ var/list/myriad_targets = list()
var/turf/T = get_turf(src)
- var/list/possible_targets = list()
- for(var/mob/living/M in T.contents)
- if(M.mind)
- if(M.mind in sacrificed)
- continue
+ for(var/mob/living/M in T)
if(!iscultist(M))
- possible_targets.Add(M)
- var/mob/offering
- if(possible_targets.len > 1) //If there's more than one target, allow choice
- offering = input(user, "Choose an offering to sacrifice.", "Unholy Tribute") as null|anything in possible_targets
- if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
- return
- else if(possible_targets.len) //Otherwise, if there's a target at all, pick the only one
- offering = possible_targets[possible_targets.len]
- if(!offering)
- rune_in_use = 0
+ myriad_targets |= M
+ if(!myriad_targets.len)
+ fail_invoke()
+ log_game("Offer rune failed - no eligible targets")
+ rune_in_use = FALSE
return
+
+ rune_in_use = TRUE
+ var/mob/living/new_cultist = pick(myriad_targets)
+ if(is_convertable_to_cult(new_cultist.mind) && !new_cultist.null_rod_check() && !is_sacrifice_target(new_cultist.mind) && new_cultist.stat != DEAD && new_cultist.client != null)
+ invocation = "Mah'weyh pleggh at e'ntrath!"
+ ..()
+ do_convert(new_cultist, invokers)
+ else
+ invocation = "Barhah hra zar'garis!"
+ ..()
+ do_sacrifice(new_cultist, invokers)
+ rune_in_use = FALSE
+
+/obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers)
+ if(invokers.len < 2)
+ fail_invoke()
+ for(var/I in invokers)
+ to_chat(I, "You need at least two invokers to convert!")
+ return
+ else
+ convertee.visible_message("[convertee] writhes in pain as the markings below them glow a bloody red!", \
+ "AAAAAAAAAAAAAA-")
+ SSticker.mode.add_cultist(convertee.mind, 1)
+ new /obj/item/tome(get_turf(src))
+ convertee.mind.special_role = "Cultist"
+ to_chat(convertee, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \
+ and something evil takes root.")
+ to_chat(convertee, "Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve [SSticker.cultdat.entity_title3] above all else. Bring it back.\
+ ")
+ return
+
+/obj/effect/rune/convert/proc/do_sacrifice(mob/living/offering, list/invokers)
+ var/mob/living/user = invokers[1] //the first invoker is always the user
if(((ishuman(offering) || isrobot(offering)) && offering.stat != DEAD) || is_sacrifice_target(offering.mind)) //Requires three people to sacrifice living targets
if(invokers.len < 3)
for(var/M in invokers)
to_chat(M, "[offering] is too greatly linked to the world! You need three acolytes!")
fail_invoke()
log_game("Sacrifice rune failed - not enough acolytes and target is living")
- rune_in_use = 0
return
- visible_message("[src] pulses blood red!")
- color = rgb(126, 23, 23)
- ..()
- sac(invokers, offering)
- color = initial(color)
-/obj/effect/rune/sacrifice/proc/sac(var/list/invokers, mob/living/T)
var/sacrifice_fulfilled
var/datum/game_mode/cult/cult_mode = SSticker.mode
- if(T)
- if(isdog(T)) // Doggos are the best, but not cats
- for(var/M in invokers)
- var/mob/living/L = M
- to_chat(L, "\"Even I have standards, such as they are!\"")
- if(L.reagents)
- L.reagents.add_reagent("hell_water", 2)
- if(T.mind)
- sacrificed.Add(T.mind)
- if(is_sacrifice_target(T.mind))
- sacrifice_fulfilled = 1
- new /obj/effect/temp_visual/cult/sac(loc)
- if(SSticker && SSticker.mode && SSticker.mode.name == "cult")
-
- cult_mode.harvested++
- for(var/M in invokers)
- if(sacrifice_fulfilled)
- to_chat(M, "\"Yes! This is the one I desire! You have done well.\"")
- cult_mode.additional_phase()
- else
- if(ishuman(T) || isrobot(T))
- to_chat(M, "\"I accept this sacrifice.\"")
- else
- to_chat(M, "\"I accept this meager sacrifice.\"")
- if(T.mind)
- var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src))
- stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul()
- if(!stone.transfer_soul("VICTIM", T, usr)) //If it cannot be added
- qdel(stone)
- if(stone)
- stone.invisibility = 0
- if(!T)
- rune_in_use = 0
- return
- if(isrobot(T))
- playsound(T, 'sound/effects/EMPulse.ogg', 100, 1)
- T.dust() //To prevent the MMI from remaining
+ if(offering.mind)
+ sacrificed.Add(offering.mind)
+ if(is_sacrifice_target(offering.mind))
+ sacrifice_fulfilled = TRUE
+ new /obj/effect/temp_visual/cult/sac(loc)
+ if(SSticker && SSticker.mode && SSticker.mode.name == "cult")
+ cult_mode.harvested++
+ for(var/M in invokers)
+ if(sacrifice_fulfilled)
+ to_chat(M, "\"Yes! This is the one I desire! You have done well.\"")
+ cult_mode.additional_phase()
else
- playsound(T, 'sound/magic/disintegrate.ogg', 100, 1)
- T.gib()
- rune_in_use = 0
+ if(ishuman(offering) || isrobot(offering))
+ to_chat(M, "\"I accept this sacrifice.\"")
+ else
+ to_chat(M, "\"I accept this meager sacrifice.\"")
+ playsound(offering, 'sound/misc/demon_consume.ogg', 100, 1)
+ if((ishuman(offering) || isrobot(offering)) && offering.client_mobs_in_contents?.len)
+ var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src))
+ stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul()
+ stone.transfer_soul("FORCE", offering, user) //If it cannot be added
+ stone.invisibility = 0
+ else
+ offering.dust()
//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
/obj/effect/rune/narsie
@@ -707,6 +663,7 @@ var/list/teleport_runes = list()
/obj/effect/rune/raise_dead/fail_invoke()
..()
+ rune_in_use = FALSE
for(var/mob/living/M in range(1,src))
if(M.stat == DEAD)
M.visible_message("[M] twitches.")
diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm
index 908eaa5360f..9fce3cc6e24 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction.dm
@@ -184,7 +184,7 @@
var/obj/machinery/abductor/console/con = get_team_console(team_number)
var/datum/objective/objective = team_objectives[team_number]
if(con.experiment.points >= objective.target_amount)
- SSshuttle.emergency.request(null, 0.5)
+ SSshuttle.emergency.request(null, 0.5, reason = "Large amount of abnormal thought patterns detected. All crew are recalled for mandatory evaluation and reconditioning.")
SSshuttle.emergency.canRecall = FALSE
finished = 1
return ..()
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index b761af6db92..f86f018757e 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -162,7 +162,7 @@
to_chat(src, "You are invincible and invisible to everyone but other ghosts. Most abilities will reveal you, rendering you vulnerable.")
to_chat(src, "To function, you are to drain the life essence from humans. This essence is a resource, as well as your health, and will power all of your abilities.")
to_chat(src, "You do not remember anything of your past lives, nor will you remember anything about this one after your death.")
- to_chat(src, "Be sure to read the wiki page at http://nanotrasen.se/wiki/index.php/Revenant to learn more.")
+ to_chat(src, "Be sure to read the wiki page at http://www.paradisestation.org/wiki/index.php/Revenant to learn more.")
var/datum/objective/revenant/objective = new
objective.owner = mind
mind.objectives += objective
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 214c349978a..89ecb1d103b 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -1,4 +1,4 @@
-var/global/list/all_objectives = list()
+GLOBAL_LIST_EMPTY(all_objectives)
var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datum/theft_objective/steal - /datum/theft_objective/number - /datum/theft_objective/unique
@@ -10,13 +10,13 @@ var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datu
var/completed = 0 //currently only used for custom objectives.
var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
-/datum/objective/New(var/text)
- all_objectives |= src
+/datum/objective/New(text)
+ GLOB.all_objectives += src
if(text)
explanation_text = text
/datum/objective/Destroy()
- all_objectives -= src
+ GLOB.all_objectives -= src
return ..()
/datum/objective/proc/check_completion()
diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm
index b580f88c0b7..b4b4b265711 100644
--- a/code/game/gamemodes/shadowling/shadowling.dm
+++ b/code/game/gamemodes/shadowling/shadowling.dm
@@ -126,7 +126,7 @@ Made by Xhuis
to_chat(shadow.current, "Currently, you are disguised as an employee aboard [world.name].")
to_chat(shadow.current, "In your limited state, you have two abilities: Hatch and Shadowling Hivemind (:8).")
to_chat(shadow.current, "Any other shadowlings are your allies. You must assist them as they shall assist you.")
- to_chat(shadow.current, "If you are new to shadowling, or want to read about abilities, check the wiki page at http://nanotrasen.se/wiki/index.php/Shadowling
")
+ to_chat(shadow.current, "If you are new to shadowling, or want to read about abilities, check the wiki page at https://www.paradisestation.org/wiki/index.php/Shadowling
")
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index cca11dc0a98..2edbb41cb95 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -205,24 +205,19 @@
/obj/item/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
switch(choice)
if("FORCE")
- var/obj/item/soulstone/C = src
- if(!iscarbon(target)) //TODO: Add sacrifice stoning for non-organics, just because you have no body doesnt mean you dont have a soul
- return 0
- if(contents.len)
- return 0
- var/mob/living/carbon/T = target
+ var/obj/item/soulstone/SS = src
+ var/mob/living/T = target
if(T.client != null)
- for(var/obj/item/W in T)
- T.unEquip(W)
- C.init_shade(T, U)
- return 1
+ SS.init_shade(T, U)
else
to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...")
- return C.getCultGhost(T,U)
+ T.Paralyse(20)
+ if(!SS.getCultGhost(T,U))
+ T.dust() //If we can't get a ghost, kill the sacrifice anyway.
if("VICTIM")
var/mob/living/carbon/human/T = target
- var/obj/item/soulstone/C = src
+ var/obj/item/soulstone/SS = src
if(T.stat == 0)
to_chat(U, "Capture failed!: Kill or maim the victim first!")
else
@@ -231,94 +226,67 @@
else
if(T.client == null)
to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...")
- C.getCultGhost(T,U)
+ SS.getCultGhost(T,U)
else
- if(C.contents.len)
+ if(SS.contents.len)
to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
else
- for(var/obj/item/W in T)
- T.unEquip(W)
- C.init_shade(T, U, vic = 1)
- qdel(T)
+ SS.init_shade(T, U, vic = 1)
+
if("SHADE")
var/mob/living/simple_animal/shade/T = target
- var/obj/item/soulstone/C = src
- if(!C.can_use(U))
+ var/obj/item/soulstone/SS = src
+ if(!SS.can_use(U))
U.Paralyse(5)
to_chat(U, "Your body is wracked with debilitating pain!")
return
if(T.stat == DEAD)
to_chat(U, "Capture failed!: The shade has already been banished!")
else
- if(C.contents.len)
+ if(SS.contents.len)
to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.")
else
- T.loc = C //put shade in stone
+ T.loc = SS //put shade in stone
T.status_flags |= GODMODE
T.canmove = 0
T.health = T.maxHealth
T.faction |= "\ref[U]"
- C.icon_state = "soulstone2"
+ SS.icon_state = "soulstone2"
to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
to_chat(U, "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone.")
if("CONSTRUCT")
var/obj/structure/constructshell/T = target
- var/obj/item/soulstone/C = src
- var/mob/living/simple_animal/shade/A = locate() in C
- if(A)
+ var/obj/item/soulstone/SS = src
+ var/mob/living/simple_animal/shade/SH = locate() in SS
+ if(SH)
var/construct_class = alert(U, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
switch(construct_class)
if("Juggernaut")
- var/mob/living/simple_animal/hostile/construct/armoured/Z = new /mob/living/simple_animal/hostile/construct/armoured (get_turf(T.loc))
- Z.key = A.key
- Z.faction |= "\ref[U]"
- if(iscultist(U))
- if(SSticker.mode.name == "cult")
- SSticker.mode:add_cultist(Z.mind)
- else
- SSticker.mode.cult+=Z.mind
- SSticker.mode.update_cult_icons_added(Z.mind)
- qdel(T)
- to_chat(Z, "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, create shield walls and even deflect energy weapons, and rip apart enemies and walls alike.")
- to_chat(Z, "You are still bound to serve your creator, follow [U.p_their()] orders and help [U.p_them()] complete [U.p_their()] goals at all costs.")
- Z.cancel_camera()
- qdel(C)
+ var/mob/living/simple_animal/hostile/construct/armoured/C = new /mob/living/simple_animal/hostile/construct/armoured (get_turf(T.loc))
+ to_chat(C, "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, create shield walls and even deflect energy weapons, and rip apart enemies and walls alike.")
+ init_construct(C,SH,SS,T)
if("Wraith")
- var/mob/living/simple_animal/hostile/construct/wraith/Z = new /mob/living/simple_animal/hostile/construct/wraith (get_turf(T.loc))
- Z.key = A.key
- Z.faction |= "\ref[U]"
- if(iscultist(U))
- if(SSticker.mode.name == "cult")
- SSticker.mode:add_cultist(Z.mind)
- else
- SSticker.mode.cult+=Z.mind
- SSticker.mode.update_cult_icons_added(Z.mind)
- qdel(T)
- to_chat(Z, "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.")
- to_chat(Z, "You are still bound to serve your creator, follow [U.p_their()] orders and help [U.p_them()] complete [U.p_their()] goals at all costs.")
- Z.cancel_camera()
- qdel(C)
+ var/mob/living/simple_animal/hostile/construct/wraith/C = new /mob/living/simple_animal/hostile/construct/wraith (get_turf(T.loc))
+ to_chat(C, "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.")
+ init_construct(C,SH,SS,T)
if("Artificer")
- var/mob/living/simple_animal/hostile/construct/builder/Z = new /mob/living/simple_animal/hostile/construct/builder (get_turf(T.loc))
- Z.key = A.key
- Z.faction |= "\ref[U]"
- if(iscultist(U))
- if(SSticker.mode.name == "cult")
- SSticker.mode:add_cultist(Z.mind)
- else
- SSticker.mode.cult+=Z.mind
- SSticker.mode.update_cult_icons_added(Z.mind)
- qdel(T)
- to_chat(Z, "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, use magic missile, repair allied constructs (by clicking on them), and most important of all create new constructs (Use your Artificer spell to summon a new construct shell and Summon Soulstone to create a new soulstone).")
- to_chat(Z, "You are still bound to serve your creator, follow [U.p_their()] orders and help [U.p_them()] complete [U.p_their()] goals at all costs.")
- Z.cancel_camera()
- qdel(C)
+ var/mob/living/simple_animal/hostile/construct/builder/C = new /mob/living/simple_animal/hostile/construct/builder (get_turf(T.loc))
+ to_chat(C, "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, use magic missile, repair allied constructs (by clicking on them), and most important of all create new constructs (Use your Artificer spell to summon a new construct shell and Summon Soulstone to create a new soulstone).")
+ init_construct(C,SH,SS,T)
else
to_chat(U, "Creation failed!: The soul stone is empty! Go kill someone!")
return
+/proc/init_construct(mob/living/simple_animal/hostile/construct/C, mob/living/simple_animal/shade/SH, obj/item/soulstone/SS, obj/structure/constructshell/T)
+ SH.mind.transfer_to(C)
+ qdel(T)
+ qdel(SH)
+ to_chat(C, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
+ C.cancel_camera()
+ qdel(SS)
+
/proc/makeNewConstruct(var/mob/living/simple_animal/hostile/construct/ctype, var/mob/target, var/mob/stoner = null, cultoverride = 0)
if(jobban_isbanned(target, "cultist") || jobban_isbanned(target, "Syndicate"))
return
@@ -332,14 +300,14 @@
SSticker.mode.cult+=newstruct.mind
SSticker.mode.update_cult_icons_added(newstruct.mind)
if(stoner && iswizard(stoner))
- to_chat(newstruct, "You are still bound to serve your creator, follow [stoner.p_their()] orders and help [stoner.p_them()] complete [stoner.p_their()] goals at all costs.")
+ to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
else if(stoner && iscultist(stoner))
- to_chat(newstruct, "You are still bound to serve the cult, follow [stoner.p_their()] orders and help [stoner.p_them()] complete [stoner.p_their()] goals at all costs.")
+ to_chat(newstruct, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.")
else
- to_chat(newstruct, "You are still bound to serve your creator, follow [stoner.p_their()] orders and help [stoner.p_them()] complete [stoner.p_their()] goals at all costs.")
+ to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.")
newstruct.cancel_camera()
-/obj/item/soulstone/proc/init_shade(mob/living/carbon/human/T, mob/U, vic = 0)
+/obj/item/soulstone/proc/init_shade(mob/living/T, mob/U, vic = 0)
new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
T.invisibility = 101
var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc )
@@ -355,19 +323,30 @@
S.name = "Shade of [T.real_name]"
S.real_name = "Shade of [T.real_name]"
S.key = T.key
- if(U)
- S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation
- if(U && iscultist(U))
- SSticker.mode.add_cultist(S.mind, 0)
S.cancel_camera()
name = "soulstone: Shade of [T.real_name]"
icon_state = "soulstone2"
- if(U && iswizard(U))
- to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help [U.p_them()] succeed in their goals at all costs.")
- else if(U && iscultist(U))
- to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help [U.p_them()] succeed in their goals at all costs.")
+ if(U)
+ S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation
+ if(iswizard(U))
+ SSticker.mode.update_wiz_icons_added(S.mind)
+ S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE
+ if(iscultist(U))
+ SSticker.mode.add_cultist(S.mind, 0)
+ S.mind.special_role = SPECIAL_ROLE_CULTIST
+ S.mind.store_memory("Serve the cult's will.")
+ to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.")
+ else
+ S.mind.store_memory("Serve [U.real_name], your creator.")
+ to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help them succeed in their goals at all costs.")
if(vic && U)
to_chat(U, "Capture successful!: [T.real_name]'s soul has been ripped from [U.p_their()] body and stored within the soul stone.")
+ if(isrobot(T))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn.
+ T.dust()
+ else
+ for(var/obj/item/W in T)
+ T.unEquip(W)
+ qdel(T)
/obj/item/soulstone/proc/get_shade_type()
return /mob/living/simple_animal/shade/cult
@@ -375,7 +354,7 @@
/obj/item/soulstone/anybody/get_shade_type()
return /mob/living/simple_animal/shade
-/obj/item/soulstone/proc/getCultGhost(mob/living/carbon/human/T, mob/U)
+/obj/item/soulstone/proc/getCultGhost(mob/living/T, mob/U)
var/mob/dead/observer/chosen_ghost
for(var/mob/dead/observer/ghost in GLOB.player_list) //We put them back in their body
@@ -395,8 +374,5 @@
if(contents.len) //If they used the soulstone on someone else in the meantime
return 0
T.ckey = chosen_ghost.ckey
- for(var/obj/item/W in T)
- T.unEquip(W)
init_shade(T, U)
- qdel(T)
return 1
diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm
index 359981ded4f..b69dbfd3ed4 100644
--- a/code/game/jobs/job_exp.dm
+++ b/code/game/jobs/job_exp.dm
@@ -263,8 +263,11 @@ var/global/list/role_playtime_requirements = list(
myrole = mob.mind.playtime_role
else if(mob.mind.assigned_role)
myrole = mob.mind.assigned_role
+ var/added_living = 0
+ var/added_ghost = 0
if(mob.stat == CONSCIOUS && myrole)
play_records[EXP_TYPE_LIVING] += minutes
+ added_living += minutes
if(announce_changes)
to_chat(mob,"You got: [minutes] Living EXP!")
for(var/category in exp_jobsmap)
@@ -279,6 +282,7 @@ var/global/list/role_playtime_requirements = list(
to_chat(mob,"You got: [minutes] Special EXP!")
else if(isobserver(mob))
play_records[EXP_TYPE_GHOST] += minutes
+ added_ghost += minutes
if(announce_changes)
to_chat(mob,"You got: [minutes] Ghost EXP!")
else
@@ -286,9 +290,15 @@ var/global/list/role_playtime_requirements = list(
var/new_exp = list2params(play_records)
prefs.exp = new_exp
new_exp = sanitizeSQL(new_exp)
- var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]' WHERE ckey='[ckey]'")
+ var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]',lastseen = Now() WHERE ckey='[ckey]'")
if(!update_query.Execute())
var/err = update_query.ErrorMsg()
- log_game("SQL ERROR during exp_update_client write. Error : \[[err]\]\n")
- message_admins("SQL ERROR during exp_update_client write. Error : \[[err]\]\n")
+ log_game("SQL ERROR during exp_update_client write 1. Error : \[[err]\]\n")
+ message_admins("SQL ERROR during exp_update_client write 1. Error : \[[err]\]\n")
return
+ var/DBQuery/update_query_history = dbcon.NewQuery("INSERT INTO [format_table_name("playtime_history")] (ckey, date, time_living, time_ghost) VALUES ('[ckey]',CURDATE(),[added_living],[added_ghost]) ON DUPLICATE KEY UPDATE time_living=time_living+VALUES(time_living),time_ghost=time_ghost+VALUES(time_ghost)")
+ if(!update_query_history.Execute())
+ var/err = update_query_history.ErrorMsg()
+ log_game("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n")
+ message_admins("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n")
+ return
\ No newline at end of file
diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm
index 672ebed32e4..bcabdeb511e 100644
--- a/code/game/machinery/computer/prisoner.dm
+++ b/code/game/machinery/computer/prisoner.dm
@@ -71,8 +71,8 @@
health_display = "DEAD"
else if(total_loss)
health_display = "HURT ([total_loss])"
- if(is_station_level(M.z) && !istype(M.loc, /turf/space))
- loc_display = "[get_area(M)]"
+ if(is_station_level(Tr.z) && !istype(Tr.loc, /turf/space))
+ loc_display = "[get_area(Tr)]"
dat += "ID: [T.id]
Subject: [M]
Location: [loc_display]
Health: [health_display]
"
dat += "(Message Holder) |
"
dat += "********************************
"
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index ae8b3f5829c..abf6dc725b3 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -380,7 +380,7 @@
cult_mode.bypass_phase()
//Update any existing objectives involving this mob.
- for(var/datum/objective/O in all_objectives)
+ for(var/datum/objective/O in GLOB.all_objectives)
// We don't want revs to get objectives that aren't for heads of staff. Letting
// them win or lose based on cryo is silly so we remove the objective.
if(istype(O,/datum/objective/mutiny) && O.target == occupant.mind)
@@ -395,7 +395,7 @@
if(!O) return
O.find_target()
if(!(O.target))
- all_objectives -= O
+ GLOB.all_objectives -= O
O.owner.objectives -= O
qdel(O)
if(occupant.mind && occupant.mind.assigned_role)
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index f23734a0079..8bd7af5dad4 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -444,7 +444,7 @@
//Stolen from status_display
/obj/machinery/door_timer/proc/texticon(tn, px = 0, py = 0)
var/image/I = image('icons/obj/status_display.dmi', "blank")
- var/len = lentext(tn)
+ var/len = length(tn)
for(var/d = 1 to len)
var/char = copytext(tn, len-d+1, len-d+2)
diff --git a/code/game/machinery/poolcontroller.dm b/code/game/machinery/poolcontroller.dm
index 4b512297ffd..12804cfcc88 100644
--- a/code/game/machinery/poolcontroller.dm
+++ b/code/game/machinery/poolcontroller.dm
@@ -28,21 +28,21 @@
/obj/machinery/poolcontroller/invisible/sea
name = "Sea Controller"
desc = "A controller for the underwater portion of the sea. Players shouldn't see this."
- deep_water = TRUE
+ deep_water = TRUE
-/obj/machinery/poolcontroller/Initialize(mapload)
- var/contents_loop = linked_area
+/obj/machinery/poolcontroller/Initialize(mapload)
+ var/contents_loop = linked_area
if(!linked_area)
contents_loop = range(srange, src)
for(var/turf/T in contents_loop)
if(istype(T, /turf/simulated/floor/beach/water))
var/turf/simulated/floor/beach/water/W = T
- W.linkedcontroller = src
+ W.linkedcontroller = src
linkedturfs += T
else if(istype(T, /turf/unsimulated/beach/water))
var/turf/unsimulated/beach/water/W = T
- W.linkedcontroller = src
+ W.linkedcontroller = src
linkedturfs += T
. = ..()
@@ -121,7 +121,7 @@
return //Has internals, no drowning
if((NO_BREATHE in drownee.dna.species.species_traits) || (BREATHLESS in drownee.mutations))
return //doesn't breathe, no drowning
- if(isskrell(drownee) || isneara(drownee))
+ if(HAS_TRAIT(drownee,TRAIT_WATERBREATH))
return //fish things don't drown
if(drownee.stat == DEAD) //Dead spacemen don't drown more
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index eb3052dd909..eecdc0e4f4a 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -8,6 +8,7 @@ var/const/SAFETY_COOLDOWN = 100
layer = MOB_LAYER+1 // Overhead
anchored = 1
density = 1
+ damage_deflection = 10
var/safety_mode = 0 // Temporarily stops machine if it detects a mob
var/icon_name = "grinder-o"
var/blood = 0
diff --git a/code/game/machinery/supply_display.dm b/code/game/machinery/supply_display.dm
index d08f68b2914..1fe7ee6e4cb 100644
--- a/code/game/machinery/supply_display.dm
+++ b/code/game/machinery/supply_display.dm
@@ -14,7 +14,7 @@
else
message1 = "CARGO"
message2 = SSshuttle.supply.getTimerStr()
- if(lentext(message2) > CHARS_PER_LINE)
+ if(length(message2) > CHARS_PER_LINE)
message2 = "Error"
update_display(message1, message2)
diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm
index 5b5f483b94f..aacd605b730 100644
--- a/code/game/mecha/equipment/tools/medical_tools.dm
+++ b/code/game/mecha/equipment/tools/medical_tools.dm
@@ -316,40 +316,38 @@
var/mob/originaloccupant = chassis.occupant
spawn(0)
src = null //if src is deleted, still process the syringe
- for(var/i=0, i<6, i++)
+ var/max_range = 6
+ for(var/i=0, i [M] was hit by the syringe!")
- if(M.can_inject(null, TRUE))
- if(mechsyringe.reagents)
- for(var/datum/reagent/A in mechsyringe.reagents.reagent_list)
- R += A.id + " ("
- R += num2text(A.volume) + "),"
- add_attack_logs(originaloccupant, M, "Shot with [src] containing [R], transferred [mechsyringe.reagents.total_volume] units")
- mechsyringe.icon_state = initial(mechsyringe.icon_state)
- mechsyringe.icon = initial(mechsyringe.icon)
- mechsyringe.reagents.reaction(M, INGEST)
- mechsyringe.reagents.trans_to(M, mechsyringe.reagents.total_volume)
- M.take_organ_damage(2)
- break
- else if(mechsyringe.loc == trg)
- mechsyringe.icon_state = initial(mechsyringe.icon_state)
- mechsyringe.icon = initial(mechsyringe.icon)
- mechsyringe.update_icon()
- break
- else
- mechsyringe.icon_state = initial(mechsyringe.icon_state)
- mechsyringe.icon = initial(mechsyringe.icon)
- mechsyringe.update_icon()
+ if(!step_towards(mechsyringe,trg))
+ break
+
+ var/list/mobs = new
+ for(var/mob/living/carbon/M in mechsyringe.loc)
+ mobs += M
+ var/mob/living/carbon/M = safepick(mobs)
+ if(M)
+ var/R
+ mechsyringe.visible_message(" [M] was hit by the syringe!")
+ if(M.can_inject(null, TRUE))
+ if(mechsyringe.reagents)
+ for(var/datum/reagent/A in mechsyringe.reagents.reagent_list)
+ R += A.id + " ("
+ R += num2text(A.volume) + "),"
+ add_attack_logs(originaloccupant, M, "Shot with [src] containing [R], transferred [mechsyringe.reagents.total_volume] units")
+ mechsyringe.reagents.reaction(M, INGEST)
+ mechsyringe.reagents.trans_to(M, mechsyringe.reagents.total_volume)
+ M.take_organ_damage(2)
+ break
+ else if(mechsyringe.loc == trg)
break
sleep(1)
+ if(mechsyringe)
+ // Revert the syringe icon to normal one once it stops flying.
+ mechsyringe.icon_state = initial(mechsyringe.icon_state)
+ mechsyringe.icon = initial(mechsyringe.icon)
+ mechsyringe.update_icon()
return 1
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 7dc1a97a735..9f8c877aa24 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -12,7 +12,7 @@
var/heal_brute = 0
var/heal_burn = 0
var/self_delay = 20
- var/unique_handling = 0 //some things give a special prompt, do we want to bypass some checks in parent?
+ var/unique_handling = FALSE //some things give a special prompt, do we want to bypass some checks in parent?
var/stop_bleeding = 0
var/healverb = "bandage"
@@ -31,28 +31,20 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected)
if(!H.can_inject(user, TRUE))
- return 1
+ return TRUE
if(!affecting)
to_chat(user, "That limb is missing!")
- return 1
+ return TRUE
if(affecting.is_robotic())
to_chat(user, "This can't be used on a robotic limb.")
- return 1
-
- if(stop_bleeding)
- if(H.bleedsuppress)
- to_chat(user, "[H]'s bleeding is already bandaged!")
- return 1
- else if(!H.bleed_rate)
- to_chat(user, "[H] isn't bleeding!")
- return 1
+ return TRUE
if(M == user && !unique_handling)
user.visible_message("[user] starts to apply [src] on [H]...")
if(!do_mob(user, H, self_delay))
- return 1
+ return TRUE
return
if(isanimal(M))
@@ -123,6 +115,7 @@
desc = "Some sterile gauze to wrap around bloody stumps."
icon_state = "gauze"
origin_tech = "biotech=2"
+ heal_brute = 10
stop_bleeding = 1800
/obj/item/stack/medical/bruise_pack/attackby(obj/item/I, mob/user, params)
@@ -140,13 +133,13 @@
/obj/item/stack/medical/bruise_pack/attack(mob/living/M, mob/user)
if(..())
- return 1
+ return TRUE
if(ishuman(M))
var/mob/living/carbon/human/H = M
var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected)
- if(affecting.open == 0)
+ if(affecting.open == FALSE)
affecting.germ_level = 0
if(stop_bleeding)
@@ -187,6 +180,7 @@
icon_state = "ointment"
origin_tech = "biotech=2"
healverb = "salve"
+ heal_burn = 10
/obj/item/stack/medical/ointment/attack(mob/living/M, mob/user)
if(..())
@@ -196,7 +190,7 @@
var/mob/living/carbon/human/H = M
var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected)
- if(affecting.open == 0)
+ if(affecting.open == FALSE)
affecting.germ_level = 0
heal(H, user)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 71d5e36badf..0ad64e24f23 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1069,6 +1069,22 @@ obj/item/toy/cards/deck/syndicate/black
name = "tuxedo cat plushie"
icon_state = "tuxedocat"
+/obj/item/toy/plushie/voxplushie
+ name = "vox plushie"
+ desc = "A stitched-together vox, fresh from the skipjack. Press its belly to hear it skree!"
+ icon_state = "plushie_vox"
+ item_state = "plushie_vox"
+ var/cooldown = 0
+
+/obj/item/toy/plushie/voxplushie/attack_self(mob/user)
+ if(!cooldown)
+ playsound(user, 'sound/voice/shriek1.ogg', 10, 0)
+ visible_message("Skreee!")
+ cooldown = 1
+ spawn(30) cooldown = 0
+ return
+ ..()
+
//New generation TG plushies
/obj/item/toy/plushie/lizardplushie
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index 4d36c640784..4cc4cb948ce 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -18,7 +18,7 @@
-
+