Update, 19MAR2017

New lights
SQL Migrations
A tonne of minor fixes and tweaks
Some stuff left to be added
This commit is contained in:
skull132
2017-03-19 21:26:42 +02:00
committed by GitHub
793 changed files with 20575 additions and 11942 deletions
BIN
View File
Binary file not shown.
+11 -15
View File
@@ -1,11 +1,11 @@
# baystation12
# Aurorastation
[Website](http://baystation12.net/) - [Code](http://github.com/Baystation12/Baystation12/) - [IRC](http://baystation12.net/forums/viewtopic.php?f=12&t=5088)
[Website](https://aurorastation.org/) - [Code](https://github.com/Aurorastation/Aurora.3)
---
### LICENSE
Baystation12 is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE-AGPL3.txt.
Aurorastation is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE-AGPL3.txt.
Commits with a git authorship date prior to `1420675200 +0000` (2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt.
@@ -18,11 +18,11 @@ See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for more informatio
### GETTING THE CODE
The simplest way to obtain the code is using the github .zip feature.
Click [here](https://github.com/Baystation12/Baystation12/archive/master.zip) to get the latest code as a .zip file, then unzip it to wherever you want.
Click [here](https://github.com/Aurorastation/Aurora.3/archive/master.zip) to get the latest stable code as a .zip file, then unzip it to wherever you want.
The more complicated and easier to update method is using git. You'll need to download git or some client from [here](http://git-scm.com/). When that's installed, right click in any folder and click on "Git Bash". When that opens, type in:
git clone https://github.com/Baystation12/Baystation12.git
git clone https://github.com/Aurorastation/Aurora.3.git
(hint: hold down ctrl and press insert to paste into git bash)
@@ -30,7 +30,7 @@ This will take a while to download, but it provides an easier method for updatin
Once the repository is in place, run this command:
```bash
cd Baystation12
cd Aurora.3
git update-index --assume-unchanged baystation12.int
```
Now git will ignore changes to the file baystation12.int.
@@ -45,7 +45,7 @@ This is a sourcecode-only release, so the next step is to compile the server fil
baystation12.dmb - 0 errors, 0 warnings
If you see any errors or warnings, something has gone wrong - possibly a corrupt download or the files extracted wrong, or a code issue on the main repo. Ask on IRC.
If you see any errors or warnings, something has gone wrong - possibly a corrupt download or the files extracted wrong, or a code issue on the main repo. Ask on the server Discord if you're completely lost.
Once that's done, open up the config folder. You'll want to edit config.txt to set the probabilities for different gamemodes in Secret and to set your server location so that all your players don't get disconnected at the end of each round. It's recommended you don't turn on the gamemodes with probability 0, as they have various issues and aren't currently being tested, so they may have unknown and bizarre bugs.
@@ -78,16 +78,12 @@ When you have done this, you'll need to recompile the code, but then it should w
### Configuration
For a basic setup, simply copy every file from config/example to config.
For a basic setup, simply copy every file from config/example to config.
For more advanced setups, setting the server `tick_lag` in the config as well as configuring SQL are good first steps.
---
### SQL Setup
The SQL backend for the library and stats tracking requires a MySQL server. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql. More detailed setup instructions arecoming soon, for now ask in our IRC channel.
---
### IRC Bot Setup
Included in the repo is an IRC bot capable of relaying adminhelps to a specified IRC channel/server (thanks to Skibiliano). Instructions for bot setup are included in the /bot/ folder along with the bot/relay script itself.
The SQL backend for the library and stats tracking requires a MySQL server, as does the optional SQL saves system. Your server details go in config/dbconfig.txt, and initial database setup is done with [flyway](https://flywaydb.org/). Detailed instructions can be found [here](https://github.com/Aurorastation/Aurora.3/tree/master/SQL).
+81
View File
@@ -0,0 +1,81 @@
### Prerequisites
The server connects to a mysql-compatible server (mysql, mariadb, percona), so you'll need one of those with a database and user/password pair ready.
We use [flyway](https://flywaydb.org/) to manage database migrations. To set up the database, you'll need to [download flyway](https://flywaydb.org/getstarted/download.html).
You'll also need some proficiency with the command line.
----
### Attribution
Credit to Mloc from Baystation12 for the initial readme.
---
### Creating migrations
As a coder, creating migrations is relatively easy. And they're a lot more flexible than just updating the initial schema would be.
First, figure out the changes you need to make. From table alteration and creation commands, to simply update and insert statements.
Write them into a .sql file in the SQL/migrate folder, in a valid order of execution. Name the file in the following format:
Vxxx__Description_goes_here.sql
Where `xxx` is the next version number from the last existing file (include the 0s), and the descrption is a short description for the migration, with spaces replaced by underscores.
Push this to your branch, and you're done!
---
### Initial setup
In the root project directory, run:
path/to/flyway migrate -user=USER -password=PASSWORD -url=jdbc:mysql://HOST/DATABASE
Where USER is your mysql username, PASSWORD is your mysql password, HOST is the hostname of the mysql server and DATABASE is the database to use.
---
### Migrating
Use the same command as above. Handy, isn't it?
---
### Using a pre-flyway database
**Note that this is not recommended!**
You may run into issues with some migrations, due to improper versioning. The best way to utilize this system is to set everything up on an empty schema.
The next alternative is to make sure your database structure matches the V001 file within the migrate folder by manually modifying the structure to avoid dataloss, and then doing the steps described below.
If you're using a database since before we moved to flyway, it's a bit more involved to get migrations working.
In the root project directory, run:
path/to/flyway baseline -user=USER -password=PASSWORD -url=jdbc:mysql://HOST/DATABASE -baselineVersion=001 -baselineDescription="Initial schema"
From there, you can run migrations as normal.
---
### Configuration file
Instead of putting -user, -password and -url in the command line every time you execute flyway, you can use a config file. Create it somewhere in the root of your project (we're calling it 'db.conf'):
flyway.url=jdbc:mysql://HOST/DATABASE
flyway.user=USER
flyway.password=PASSWORD
Now you can just run `flyway migrate -configFile=db.conf`, and the settings will be loaded from config.
---
### Misc tables
We included a set of miscellanious tables in the misc folder. These are primarily used for debugging and are not meant to be pushed into production. As such, they're not included in the migration folder.
Ignoring or implementing them should not cause issues with the system.
@@ -1,15 +1,92 @@
CREATE DATABASE `spacestation13` /*!40100 DEFAULT CHARACTER SET utf8 */;
-- ------------------------------------------------------------
-- These are BOREALIS' tables. Some are actively used ingame,
-- hence why they're bundled in here.
-- ------------------------------------------------------------
CREATE TABLE `ss13_admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) CHARACTER SET latin1 NOT NULL,
`rank` varchar(32) CHARACTER SET latin1 NOT NULL DEFAULT 'Administrator',
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
`discord_id` varchar(45) NOT NULL DEFAULT '',
--
-- BOREALIS' table for bans
--
CREATE TABLE `discord_bans` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(45) NOT NULL,
`user_name` varchar(45) NOT NULL,
`server_id` varchar(45) NOT NULL,
`ban_type` varchar(45) NOT NULL,
`ban_duration` int(11) NOT NULL DEFAULT '-1',
`ban_reason` longtext,
`ban_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expiration_time` datetime NOT NULL,
`admin_id` varchar(45) DEFAULT NULL,
`admin_name` varchar(45) DEFAULT 'BOREALIS',
`ban_lifted` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- BOREALIS' table for channels
--
CREATE TABLE `discord_channels` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`channel_group` varchar(32) NOT NULL,
`channel_id` text NOT NULL,
`pin_flag` int(11) NOT NULL DEFAULT '0',
`server_id` varchar(45) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- BOREALIS' table for logs
--
CREATE TABLE `discord_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`action_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`action` text NOT NULL,
`admin_id` varchar(45) DEFAULT NULL,
`user_id` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- BOREALIS' table for strikes
--
CREATE TABLE `discord_strikes` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(45) NOT NULL,
`user_name` varchar(45) NOT NULL,
`action_type` varchar(45) NOT NULL DEFAULT 'WARNING',
`strike_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`admin_id` varchar(45) NOT NULL,
`admin_name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- BOREALIS' table for subscriptions
--
CREATE TABLE `discord_subscribers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(45) NOT NULL,
`once` tinyint(1) NOT NULL DEFAULT '0',
`subscribed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expired_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ------------------------------------------------------------
-- SS13 tables begin here. All of these are actively used.
-- Note that some are used by the WebInterface, but if so,
-- it's done in support of ingame systems.
-- ------------------------------------------------------------
--
-- Table for admin rank alterations
--
CREATE TABLE `ss13_admin_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
@@ -19,43 +96,50 @@ CREATE TABLE `ss13_admin_log` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_api_commands` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`command` VARCHAR(50) NOT NULL COLLATE 'utf8_bin',
`description` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_bin',
PRIMARY KEY (`id`),
UNIQUE INDEX `UNIQUE command` (`command`)
)
COLLATE='utf8_bin'
ENGINE=InnoDB;
--
-- Table for the server-supported API/world.Topic commands
--
CREATE TABLE `ss13_api_commands` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`command` varchar(50) COLLATE utf8_bin NOT NULL,
`description` varchar(255) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQUE command` (`command`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
--
-- Table for auth tokens for world.Topic
--
CREATE TABLE `ss13_api_tokens` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`token` VARCHAR(100) NOT NULL COLLATE 'utf8_bin',
`ip` VARCHAR(16) NULL DEFAULT NULL COLLATE 'utf8_bin',
`creator` VARCHAR(50) NOT NULL COLLATE 'utf8_bin',
`description` VARCHAR(100) NOT NULL COLLATE 'utf8_bin',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_bin'
ENGINE=InnoDB;
`id` int(11) NOT NULL AUTO_INCREMENT,
`token` varchar(100) COLLATE utf8_bin NOT NULL,
`ip` varchar(16) COLLATE utf8_bin DEFAULT NULL,
`creator` varchar(50) COLLATE utf8_bin NOT NULL,
`description` varchar(100) COLLATE utf8_bin NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
--
-- Table to describe which tokens can access what commands for world.Topic
--
CREATE TABLE `ss13_api_token_command` (
`command_id` INT(11) NOT NULL,
`token_id` INT(11) NOT NULL,
PRIMARY KEY (`command_id`, `token_id`),
INDEX `token_id` (`token_id`),
CONSTRAINT `function_id` FOREIGN KEY (`command_id`) REFERENCES `ss13_api_commands` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `token_id` FOREIGN KEY (`token_id`) REFERENCES `ss13_api_tokens` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_bin'
ENGINE=InnoDB;
`command_id` int(11) NOT NULL,
`token_id` int(11) NOT NULL,
PRIMARY KEY (`command_id`,`token_id`),
KEY `token_id` (`token_id`),
CONSTRAINT `function_id` FOREIGN KEY (`command_id`) REFERENCES `ss13_api_commands` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `token_id` FOREIGN KEY (`token_id`) REFERENCES `ss13_api_tokens` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
--
-- Table for bans issued
--
CREATE TABLE `ss13_ban` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -78,13 +162,17 @@ CREATE TABLE `ss13_ban` (
`edits` text CHARACTER SET latin1,
`unbanned` tinyint(1) DEFAULT NULL,
`unbanned_datetime` datetime DEFAULT NULL,
`unbanned_reason` text CHARACTER SET latin1 DEFAULT NULL,
`unbanned_reason` text,
`unbanned_ckey` varchar(32) CHARACTER SET latin1 DEFAULT NULL,
`unbanned_computerid` varchar(32) CHARACTER SET latin1 DEFAULT NULL,
`unbanned_ip` varchar(32) CHARACTER SET latin1 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for ban-mirroring, used to catch ban dodgers
--
CREATE TABLE `ss13_ban_mirrors` (
`ban_mirror_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ban_id` int(10) unsigned NOT NULL,
@@ -95,6 +183,24 @@ CREATE TABLE `ss13_ban_mirrors` (
PRIMARY KEY (`ban_mirror_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for housing CCIA notices to be sent mid-round
--
CREATE TABLE `ss13_ccia_general_notice_list` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`message` text COLLATE utf8_unicode_ci NOT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for player information
--
CREATE TABLE `ss13_player` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) CHARACTER SET latin1 NOT NULL,
@@ -109,95 +215,237 @@ CREATE TABLE `ss13_player` (
UNIQUE KEY `ckey` (`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for linking requests between the Web-Interface and game server
--
CREATE TABLE `ss13_player_linking` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`forum_id` int(11) NOT NULL,
`forum_username_short` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`forum_username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`player_ckey` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`status` enum('new','confirmed','rejected','linked') COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for player personal-AI preferences
--
CREATE TABLE `ss13_player_pai` (
`ckey` varchar(32) NOT NULL,
`name` varchar(50) DEFAULT NULL,
`description` text,
`role` text,
`comments` text,
PRIMARY KEY (`ckey`),
CONSTRAINT `player_pai_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table for general player preferences
--
CREATE TABLE `ss13_player_preferences` (
`ckey` varchar(32) NOT NULL,
`ooccolor` text,
`lastchangelog` text,
`UI_style` text,
`current_character` int(11) DEFAULT '0',
`toggles` int(11) DEFAULT '0',
`UI_style_color` text,
`UI_style_alpha` int(11) DEFAULT '255',
`asfx_togs` int(11) DEFAULT '0',
`lastmotd` text,
`lastmemo` text,
`language_prefixes` text,
PRIMARY KEY (`ckey`),
CONSTRAINT `player_preferences_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table for holding admin ranks
--
CREATE TABLE `ss13_admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) CHARACTER SET latin1 NOT NULL,
`rank` varchar(32) CHARACTER SET latin1 NOT NULL DEFAULT 'Administrator',
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
`discord_id` varchar(45) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQUE` (`ckey`),
CONSTRAINT `ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for initial character data
--
CREATE TABLE `ss13_characters` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
`name` varchar(128) NULL DEFAULT NULL,
`metadata` varchar(512) NULL DEFAULT NULL,
`be_special_role` text NULL DEFAULT NULL,
`gender` varchar(32) NULL DEFAULT NULL,
`age` int(11) NULL DEFAULT NULL,
`species` varchar(32) NULL DEFAULT NULL,
`language` text NULL DEFAULT NULL,
`hair_colour` varchar(7) NULL DEFAULT NULL,
`facial_colour` varchar(7) NULL DEFAULT NULL,
`skin_tone` int(11) NULL DEFAULT NULL,
`skin_colour` varchar(7) NULL DEFAULT NULL,
`hair_style` varchar(32) NULL DEFAULT NULL,
`facial_style` varchar(32) NULL DEFAULT NULL,
`eyes_colour` varchar(7) NULL DEFAULT NULL,
`underwear` varchar(32) NULL DEFAULT NULL,
`undershirt` varchar(32) NULL DEFAULT NULL,
`socks` varchar(32) NULL DEFAULT NULL,
`backbag` int(11) NULL DEFAULT NULL,
`b_type` varchar(32) NULL DEFAULT NULL,
`spawnpoint` varchar(32) NULL DEFAULT NULL,
`jobs` text NULL DEFAULT NULL,
`name` varchar(128) DEFAULT NULL,
`metadata` varchar(512) DEFAULT NULL,
`be_special_role` text,
`gender` varchar(32) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`species` varchar(32) DEFAULT NULL,
`language` varchar(50) DEFAULT NULL,
`hair_colour` varchar(7) DEFAULT NULL,
`facial_colour` varchar(7) DEFAULT NULL,
`skin_tone` int(11) DEFAULT NULL,
`skin_colour` varchar(7) DEFAULT NULL,
`hair_style` varchar(32) DEFAULT NULL,
`facial_style` varchar(32) DEFAULT NULL,
`eyes_colour` varchar(7) DEFAULT NULL,
`underwear` varchar(32) DEFAULT NULL,
`undershirt` varchar(32) DEFAULT NULL,
`socks` varchar(32) DEFAULT NULL,
`backbag` int(11) DEFAULT NULL,
`b_type` varchar(32) DEFAULT NULL,
`spawnpoint` varchar(32) DEFAULT NULL,
`jobs` text,
`alternate_option` tinyint(1) DEFAULT NULL,
`alternate_titles` text NULL DEFAULT NULL,
`alternate_titles` text,
`disabilities` int(11) DEFAULT '0',
`skills` text NULL DEFAULT NULL,
`skill_specialization` text NULL DEFAULT NULL,
`home_system` text NULL DEFAULT NULL,
`citizenship` text NULL DEFAULT NULL,
`faction` text NULL DEFAULT NULL,
`religion` text NULL DEFAULT NULL,
`nt_relation` text NULL DEFAULT NULL,
`uplink_location` text NULL DEFAULT NULL,
`organs_data` text NULL DEFAULT NULL,
`organs_robotic` text NULL DEFAULT NULL,
`gear` text NULL DEFAULT NULL,
`skills` text,
`skill_specialization` text,
`home_system` text,
`citizenship` text,
`faction` text,
`religion` text,
`nt_relation` text,
`uplink_location` text,
`organs_data` text,
`organs_robotic` text,
`gear` text,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deleted_at` datetime NULL DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ss13_characters_ckey` (`ckey`),
KEY `ss13_characteres_name` (`name`),
CONSTRAINT `ss13_characters_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table for character flavour text
--
CREATE TABLE `ss13_characters_flavour` (
`char_id` int(11) NOT NULL,
`records_employment` text NULL DEFAULT NULL,
`records_medical` text NULL DEFAULT NULL,
`records_security` text NULL DEFAULT NULL,
`records_exploit` text NULL DEFAULT NULL,
`records_ccia` text NULL DEFAULT NULL,
`flavour_general` text NULL DEFAULT NULL,
`flavour_head` text NULL DEFAULT NULL,
`flavour_face` text NULL DEFAULT NULL,
`flavour_eyes` text NULL DEFAULT NULL,
`flavour_torso` text NULL DEFAULT NULL,
`flavour_arms` text NULL DEFAULT NULL,
`flavour_hands` text NULL DEFAULT NULL,
`flavour_legs` text NULL DEFAULT NULL,
`flavour_feet` text NULL DEFAULT NULL,
`robot_default` text NULL DEFAULT NULL,
`robot_standard` text NULL DEFAULT NULL,
`robot_engineering` text NULL DEFAULT NULL,
`robot_construction` text NULL DEFAULT NULL,
`robot_medical` text NULL DEFAULT NULL,
`robot_rescue` text NULL DEFAULT NULL,
`robot_miner` text NULL DEFAULT NULL,
`robot_custodial` text NULL DEFAULT NULL,
`robot_service` text NULL DEFAULT NULL,
`robot_clerical` text NULL DEFAULT NULL,
`robot_security` text NULL DEFAULT NULL,
`robot_research` text NULL DEFAULT NULL,
`records_employment` text,
`records_medical` text,
`records_security` text,
`records_exploit` text,
`records_ccia` text,
`flavour_general` text,
`flavour_head` text,
`flavour_face` text,
`flavour_eyes` text,
`flavour_torso` text,
`flavour_arms` text,
`flavour_hands` text,
`flavour_legs` text,
`flavour_feet` text,
`robot_default` text,
`robot_standard` text,
`robot_engineering` text,
`robot_construction` text,
`robot_medical` text,
`robot_rescue` text,
`robot_miner` text,
`robot_custodial` text,
`robot_service` text,
`robot_clerical` text,
`robot_security` text,
`robot_research` text,
PRIMARY KEY (`char_id`),
CONSTRAINT `ss13_flavour_fk_char_id` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
--
-- Table for housing IC criminal records
--
CREATE TABLE `ss13_character_incidents` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`char_id` int(11) NOT NULL,
`UID` varchar(32) COLLATE utf8_bin NOT NULL,
`datetime` varchar(50) COLLATE utf8_bin NOT NULL,
`notes` text COLLATE utf8_bin NOT NULL,
`charges` text COLLATE utf8_bin NOT NULL,
`evidence` text COLLATE utf8_bin NOT NULL,
`arbiters` text COLLATE utf8_bin NOT NULL,
`brig_sentence` int(11) NOT NULL DEFAULT '0',
`fine` int(11) NOT NULL DEFAULT '0',
`felony` int(11) NOT NULL DEFAULT '0',
`created_by` varchar(50) COLLATE utf8_bin DEFAULT NULL,
`deleted_by` varchar(50) COLLATE utf8_bin DEFAULT NULL,
`game_id` varchar(50) COLLATE utf8_bin NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UID_char_id` (`char_id`,`UID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
--
-- Table for logging which characters have joined rounds when
--
CREATE TABLE `ss13_characters_log` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`char_id` INT(11) NOT NULL,
`game_id` VARCHAR(50) NOT NULL,
`datetime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`job_name` VARCHAR(32) NOT NULL,
`special_role` VARCHAR(32) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `ss13_charlog_fk_char_id` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`char_id` int(11) NOT NULL,
`game_id` varchar(50) NOT NULL,
`datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`job_name` varchar(32) DEFAULT NULL,
`special_role` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ss13_charlog_fk_char_id` (`char_id`),
CONSTRAINT `ss13_charlog_fk_char_id` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for CCIA actions taken against characters
--
CREATE TABLE `ss13_ccia_actions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_unicode_ci NOT NULL,
`type` enum('injunction','suspension','warning','other') COLLATE utf8_unicode_ci NOT NULL,
`issuedby` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`details` text COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`expires_at` date DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Bridge table between `ss13_ccia_actions` and `ss13_characters`
--
CREATE TABLE `ss13_ccia_action_char` (
`action_id` int(10) unsigned NOT NULL,
`char_id` int(11) NOT NULL,
PRIMARY KEY (`action_id`,`char_id`),
KEY `ccia_action_char_char_id_foreign` (`char_id`),
CONSTRAINT `ccia_action_char_action_id_foreign` FOREIGN KEY (`action_id`) REFERENCES `ss13_ccia_actions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `ccia_action_char_char_id_foreign` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for logging player connections
--
CREATE TABLE `ss13_connection_log` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
@@ -208,12 +456,20 @@ CREATE TABLE `ss13_connection_log` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for antag contest participants
--
CREATE TABLE `ss13_contest_participants` (
`player_ckey` varchar(32) NOT NULL,
`character_id` int(10) unsigned NOT NULL,
`contest_faction` enum('INDEP','SLF','BIS','ASI','PSIS','HSH','TCD') NOT NULL DEFAULT 'INDEP'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for antag contenst reports
--
CREATE TABLE `ss13_contest_reports` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`player_ckey` varchar(32) NOT NULL,
@@ -223,17 +479,13 @@ CREATE TABLE `ss13_contest_reports` (
`objective_side` enum('pro_synth','anti_synth') NOT NULL,
`objective_outcome` tinyint(1) DEFAULT '0',
`objective_datetime` datetime NOT NULL,
`duplicate` int(1) unsigned DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_customitems` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) CHARACTER SET latin1 NOT NULL,
`real_name` varchar(32) CHARACTER SET latin1 NOT NULL,
`item` varchar(124) CHARACTER SET latin1 NOT NULL,
`job` text CHARACTER SET latin1,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for death statistics
--
CREATE TABLE `ss13_death` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -254,6 +506,10 @@ CREATE TABLE `ss13_death` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for housing station directives
--
CREATE TABLE `ss13_directives` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) CHARACTER SET latin1 NOT NULL,
@@ -261,6 +517,10 @@ CREATE TABLE `ss13_directives` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for round statistics
--
CREATE TABLE `ss13_feedback` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime NOT NULL,
@@ -271,6 +531,10 @@ CREATE TABLE `ss13_feedback` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for ingame forms
--
CREATE TABLE `ss13_forms` (
`form_id` int(11) NOT NULL AUTO_INCREMENT,
`id` varchar(4) CHARACTER SET latin1 NOT NULL,
@@ -281,6 +545,22 @@ CREATE TABLE `ss13_forms` (
PRIMARY KEY (`form_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for tracking IPC implants
--
CREATE TABLE `ss13_ipc_tracking` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`player_ckey` varchar(32) NOT NULL,
`character_name` varchar(255) NOT NULL,
`tag_status` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table for the ingame library
--
CREATE TABLE `ss13_library` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`author` text CHARACTER SET latin1 NOT NULL,
@@ -292,21 +572,9 @@ CREATE TABLE `ss13_library` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_news` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`publishtime` int(11) NOT NULL,
`channel` varchar(64) CHARACTER SET latin1 NOT NULL,
`author` varchar(64) CHARACTER SET latin1 NOT NULL,
`title` varchar(64) CHARACTER SET latin1 DEFAULT NULL,
`body` text CHARACTER SET latin1 NOT NULL,
`notpublishing` tinyint(1) DEFAULT '0',
`approved` tinyint(1) DEFAULT '0',
`status` tinyint(1) NOT NULL DEFAULT '1',
`uploadip` varchar(18) CHARACTER SET latin1 NOT NULL,
`uploadtime` datetime NOT NULL,
`approvetime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for player notes
--
CREATE TABLE `ss13_notes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -323,35 +591,9 @@ CREATE TABLE `ss13_notes` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_player_linking` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`forum_id` int(11) NOT NULL,
`forum_username_short` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`forum_username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`player_ckey` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`status` enum('new','confirmed','rejected','linked') COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `ss13_player_preferences` (
`ckey` varchar(32) NOT NULL,
`ooccolor` text NULL DEFAULT NULL,
`lastchangelog` text NULL DEFAULT NULL,
`UI_style` text NULL DEFAULT NULL,
`current_character` int(11) NULL DEFAULT NULL,
`toggles` int(11) DEFAULT '0',
`UI_style_color` text NULL DEFAULT NULL,
`UI_style_alpha` int(11) NULL DEFAULT '255',
`asfx_togs` int(11) DEFAULT '0',
`lastmotd` text NULL DEFAULT NULL,
`lastmemo` text NULL DEFAULT NULL,
`language_prefixes` text NULL DEFAULT NULL,
PRIMARY KEY (`ckey`),
CONSTRAINT `player_preferences_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table for ingame poll options
--
CREATE TABLE `ss13_poll_option` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -366,6 +608,10 @@ CREATE TABLE `ss13_poll_option` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for ingame poll questions
--
CREATE TABLE `ss13_poll_question` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`polltype` varchar(16) CHARACTER SET latin1 NOT NULL DEFAULT 'OPTION',
@@ -377,6 +623,10 @@ CREATE TABLE `ss13_poll_question` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for ingame poll text/freeform replies
--
CREATE TABLE `ss13_poll_textreply` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
@@ -388,6 +638,10 @@ CREATE TABLE `ss13_poll_textreply` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for ingame poll votes
--
CREATE TABLE `ss13_poll_vote` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
@@ -400,13 +654,21 @@ CREATE TABLE `ss13_poll_vote` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for tracking ingame population counts
--
CREATE TABLE `ss13_population` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`playercount` int(11) DEFAULT NULL,
`admincount` int(11) DEFAULT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=69279 DEFAULT CHARSET=utf8;
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for player privacy preferences
--
CREATE TABLE `ss13_privacy` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -416,6 +678,27 @@ CREATE TABLE `ss13_privacy` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for ingame santa clause event
--
CREATE TABLE `ss13_santa` (
`character_name` varchar(32) NOT NULL,
`participation_status` tinyint(1) NOT NULL DEFAULT '1',
`is_assigned` tinyint(1) NOT NULL DEFAULT '0',
`mark_name` varchar(32) DEFAULT NULL,
`character_gender` varchar(32) NOT NULL,
`character_species` varchar(32) NOT NULL,
`character_job` varchar(32) NOT NULL,
`character_like` mediumtext NOT NULL,
`gift_assigned` varchar(255) DEFAULT NULL,
PRIMARY KEY (`character_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table for syndicate contracts
--
CREATE TABLE `ss13_syndie_contracts` (
`contract_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`contractee_id` int(11) NOT NULL,
@@ -433,10 +716,14 @@ CREATE TABLE `ss13_syndie_contracts` (
PRIMARY KEY (`contract_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for comments to syndicate contracts
--
CREATE TABLE `ss13_syndie_contracts_comments` (
`comment_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`contract_id` int(11) NOT NULL,
`commentor_id` int(11) NOT NULL,
`contract_id` int(11) unsigned NOT NULL,
`commentor_id` int(11) unsigned NOT NULL,
`commentor_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`comment` text COLLATE utf8_unicode_ci NOT NULL,
@@ -445,9 +732,62 @@ CREATE TABLE `ss13_syndie_contracts_comments` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`comment_id`)
`report_status` enum('waiting-approval','accepted','rejected') COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`comment_id`),
KEY `contract_id` (`contract_id`),
CONSTRAINT `contract_id` FOREIGN KEY (`contract_id`) REFERENCES `ss13_syndie_contracts` (`contract_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Bridge table between contract comment data and player table
--
CREATE TABLE `ss13_syndie_contracts_comments_completers` (
`user_id` int(11) NOT NULL,
`comment_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`comment_id`),
KEY `comment_id` (`comment_id`),
CONSTRAINT `comment_id` FOREIGN KEY (`comment_id`) REFERENCES `ss13_syndie_contracts_comments` (`comment_id`) ON DELETE CASCADE,
CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `ss13_player` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for individual contract objectives
--
CREATE TABLE `ss13_syndie_contracts_objectives` (
`objective_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`contract_id` int(11) NOT NULL,
`status` enum('open','closed','deleted') COLLATE utf8_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`reward_credits` int(11) DEFAULT NULL,
`reward_credits_update` int(11) DEFAULT NULL,
`reward_other` text COLLATE utf8_unicode_ci,
`reward_other_update` text COLLATE utf8_unicode_ci,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`objective_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Bridge table between contract objectives and comments
--
CREATE TABLE `ss13_syndie_contracts_comments_objectives` (
`objective_id` int(10) unsigned NOT NULL,
`comment_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`objective_id`,`comment_id`),
KEY `comments_comment_id` (`comment_id`),
CONSTRAINT `comments_comment_id` FOREIGN KEY (`comment_id`) REFERENCES `ss13_syndie_contracts_comments` (`comment_id`) ON DELETE CASCADE,
CONSTRAINT `objectives_objective_id` FOREIGN KEY (`objective_id`) REFERENCES `ss13_syndie_contracts_objectives` (`objective_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for contract subscribers
--
CREATE TABLE `ss13_syndie_contracts_subscribers` (
`contract_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
@@ -455,6 +795,10 @@ CREATE TABLE `ss13_syndie_contracts_subscribers` (
CONSTRAINT `syndie_contracts_subscribers_contract_id_foreign` FOREIGN KEY (`contract_id`) REFERENCES `ss13_syndie_contracts` (`contract_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for player warnings
--
CREATE TABLE `ss13_warnings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime NOT NULL,
@@ -476,6 +820,10 @@ CREATE TABLE `ss13_warnings` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table for housing tokens for the WebInterface SSO from within the game
--
CREATE TABLE `ss13_web_sso` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ckey` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
@@ -485,6 +833,10 @@ CREATE TABLE `ss13_web_sso` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table for logging whitelist alterations
--
CREATE TABLE `ss13_whitelist_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
@@ -494,93 +846,13 @@ CREATE TABLE `ss13_whitelist_log` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Table for storing whitelist bitkeys
--
CREATE TABLE `ss13_whitelist_statuses` (
`flag` int(10) unsigned NOT NULL,
`status_name` varchar(32) NOT NULL,
`subspecies` tinyint(4) NOT NULL DEFAULT '1',
PRIMARY KEY (`status_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `ss13_stats_ie` (
`ckey` varchar(32) NOT NULL,
`IsIE` tinyint(4) NOT NULL,
`IsEdge` tinyint(4) NOT NULL,
`EdgeHtmlVersion` int(11) NOT NULL,
`TrueVersion` tinyint(4) NOT NULL,
`ActingVersion` tinyint(4) NOT NULL,
`CompatibilityMode` tinyint(4) NOT NULL,
`DateUpdated` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_character_incidents` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`char_id` INT(11) NOT NULL,
`UID` VARCHAR(32) NOT NULL COLLATE 'utf8_bin',
`datetime` VARCHAR(50) NOT NULL COLLATE 'utf8_bin',
`notes` TEXT NOT NULL COLLATE 'utf8_bin',
`charges` TEXT NOT NULL COLLATE 'utf8_bin',
`evidence` TEXT NOT NULL COLLATE 'utf8_bin',
`arbiters` TEXT NOT NULL COLLATE 'utf8_bin',
`brig_sentence` INT(11) NOT NULL DEFAULT '0',
`fine` INT(11) NOT NULL DEFAULT '0',
`felony` INT(11) NOT NULL DEFAULT '0',
`created_by` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_bin',
`deleted_by` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_bin',
`game_id` VARCHAR(50) NOT NULL COLLATE 'utf8_bin',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `UID_char_id` (`char_id`, `UID`)
) COLLATE='utf8_bin' ENGINE=InnoDB;
CREATE TABLE `discord_channels` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`channel_group` varchar(32) NOT NULL,
`channel_id` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ss13_ccia_actions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_unicode_ci NOT NULL,
`type` enum('injunction','suspension','warning','other') COLLATE utf8_unicode_ci NOT NULL,
`issuedby` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`details` text COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`expires_at` date DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `ss13_ccia_action_char` (
`action_id` int(10) unsigned NOT NULL,
`char_id` int(11) NOT NULL,
PRIMARY KEY (`action_id`,`char_id`),
KEY `ccia_action_char_char_id_foreign` (`char_id`),
CONSTRAINT `ccia_action_char_action_id_foreign` FOREIGN KEY (`action_id`) REFERENCES `ss13_ccia_actions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `ccia_action_char_char_id_foreign` FOREIGN KEY (`char_id`) REFERENCES `ss13_characters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `ss13_ccia_general_notice_list` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`message` text COLLATE utf8_unicode_ci NOT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `ss13_player_pai` (
`ckey` VARCHAR(32) NOT NULL,
`name` VARCHAR(50) NULL DEFAULT NULL,
`description` TEXT NULL DEFAULT NULL,
`role` TEXT NULL DEFAULT NULL,
`comments` TEXT NULL DEFAULT NULL,
PRIMARY KEY (`ckey`),
CONSTRAINT `player_pai_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
@@ -0,0 +1,7 @@
--
-- Adds parallax related preferences toggles for the player preferences table.
--
ALTER TABLE `ss13_player_preferences`
ADD `parallax_toggles` INT(11) NULL DEFAULT NULL,
ADD `parallax_speed` INT(11) NULL DEFAULT NULL;
+18
View File
@@ -0,0 +1,18 @@
-- These are tables used only for debugging/profiling.
-- They are not needed for normal server operation.
CREATE TABLE `ss13dbg_lighting` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`time` INT(11) UNSIGNED NULL DEFAULT NULL COMMENT 'World time (in ticks)',
`tick_usage` DOUBLE UNSIGNED NULL DEFAULT NULL,
`type` VARCHAR(32) NULL DEFAULT NULL COMMENT 'The type of the update.' COLLATE 'latin1_bin',
`name` VARCHAR(50) NULL DEFAULT NULL COMMENT 'The callee\'s name.' COLLATE 'latin1_bin',
`loc_name` VARCHAR(50) NULL DEFAULT NULL COMMENT 'The callee\'s location.' COLLATE 'latin1_bin',
`x` SMALLINT(6) NULL DEFAULT NULL,
`y` SMALLINT(6) NULL DEFAULT NULL,
`z` SMALLINT(6) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
)
COLLATE='latin1_bin'
ENGINE=MEMORY
ROW_FORMAT=FIXED;
+105 -24
View File
@@ -23,11 +23,13 @@
#include "code\__defines\chemistry.dm"
#include "code\__defines\damage_organs.dm"
#include "code\__defines\dna.dm"
#include "code\__defines\dview.dm"
#include "code\__defines\gamemode.dm"
#include "code\__defines\items_clothing.dm"
#include "code\__defines\lighting.dm"
#include "code\__defines\machinery.dm"
#include "code\__defines\math_physics.dm"
#include "code\__defines\mining.dm"
#include "code\__defines\misc.dm"
#include "code\__defines\mobs.dm"
#include "code\__defines\process_scheduler.dm"
@@ -51,6 +53,7 @@
#include "code\_helpers\matrices.dm"
#include "code\_helpers\mobs.dm"
#include "code\_helpers\names.dm"
#include "code\_helpers\overlay.dm"
#include "code\_helpers\sanitize_values.dm"
#include "code\_helpers\spawn_sync.dm"
#include "code\_helpers\text.dm"
@@ -78,6 +81,7 @@
#include "code\_onclick\hud\human.dm"
#include "code\_onclick\hud\movable_screen_objects.dm"
#include "code\_onclick\hud\other_mobs.dm"
#include "code\_onclick\hud\parallax.dm"
#include "code\_onclick\hud\robot.dm"
#include "code\_onclick\hud\screen_objects.dm"
#include "code\_onclick\hud\spell_screen_objects.dm"
@@ -121,6 +125,7 @@
#include "code\controllers\emergency_shuttle_controller.dm"
#include "code\controllers\hooks-defs.dm"
#include "code\controllers\hooks.dm"
#include "code\controllers\law.dm"
#include "code\controllers\master_controller.dm"
#include "code\controllers\shuttle_controller.dm"
#include "code\controllers\subsystems.dm"
@@ -130,12 +135,13 @@
#include "code\controllers\Processes\alarm.dm"
#include "code\controllers\Processes\chemistry.dm"
#include "code\controllers\Processes\disease.dm"
#include "code\controllers\Processes\effects.dm"
#include "code\controllers\Processes\emergencyShuttle.dm"
#include "code\controllers\Processes\event.dm"
#include "code\controllers\Processes\explosives.dm"
#include "code\controllers\Processes\garbage.dm"
#include "code\controllers\Processes\inactivity.dm"
#include "code\controllers\Processes\law.dm"
#include "code\controllers\Processes\lighting.dm"
#include "code\controllers\Processes\machinery.dm"
#include "code\controllers\Processes\mob.dm"
#include "code\controllers\Processes\modifier.dm"
@@ -165,7 +171,7 @@
#include "code\datums\mind.dm"
#include "code\datums\mixed.dm"
#include "code\datums\modules.dm"
#include "code\datums\recipe.dm"
#include "code\datums\scheduled_task.dm"
#include "code\datums\server_greeting.dm"
#include "code\datums\sun.dm"
#include "code\datums\supplypacks.dm"
@@ -329,6 +335,7 @@
#include "code\game\gamemodes\setupgame.dm"
#include "code\game\gamemodes\calamity\calamity.dm"
#include "code\game\gamemodes\changeling\changeling.dm"
#include "code\game\gamemodes\changeling\changeling_items.dm"
#include "code\game\gamemodes\changeling\changeling_powers.dm"
#include "code\game\gamemodes\changeling\modularchangling.dm"
#include "code\game\gamemodes\cult\cult.dm"
@@ -459,6 +466,8 @@
#include "code\game\machinery\wall_frames.dm"
#include "code\game\machinery\washing_machine.dm"
#include "code\game\machinery\wishgranter.dm"
#include "code\game\machinery\abstract\abstract.dm"
#include "code\game\machinery\abstract\intercom_listener.dm"
#include "code\game\machinery\atmoalter\area_atmos_computer.dm"
#include "code\game\machinery\atmoalter\canister.dm"
#include "code\game\machinery\atmoalter\meter.dm"
@@ -527,10 +536,13 @@
#include "code\game\machinery\kitchen\icecream.dm"
#include "code\game\machinery\kitchen\microwave.dm"
#include "code\game\machinery\kitchen\smartfridge.dm"
#include "code\game\machinery\kitchen\cooking_machines\_appliance.dm"
#include "code\game\machinery\kitchen\cooking_machines\_cooker.dm"
#include "code\game\machinery\kitchen\cooking_machines\_cooker_output.dm"
#include "code\game\machinery\kitchen\cooking_machines\_mixer.dm"
#include "code\game\machinery\kitchen\cooking_machines\candy.dm"
#include "code\game\machinery\kitchen\cooking_machines\cereal.dm"
#include "code\game\machinery\kitchen\cooking_machines\container.dm"
#include "code\game\machinery\kitchen\cooking_machines\fryer.dm"
#include "code\game\machinery\kitchen\cooking_machines\grill.dm"
#include "code\game\machinery\kitchen\cooking_machines\oven.dm"
@@ -630,6 +642,8 @@
#include "code\game\objects\items\devices\flashlight.dm"
#include "code\game\objects\items\devices\floor_painter.dm"
#include "code\game\objects\items\devices\hacktool.dm"
#include "code\game\objects\items\devices\holowarrant.dm"
#include "code\game\objects\items\devices\lightmeter.dm"
#include "code\game\objects\items\devices\lightreplacer.dm"
#include "code\game\objects\items\devices\magnetic_lock.dm"
#include "code\game\objects\items\devices\megaphone.dm"
@@ -1017,7 +1031,6 @@
#include "code\modules\client\preferences.dm"
#include "code\modules\client\preferences_ambience.dm"
#include "code\modules\client\preferences_factions.dm"
#include "code\modules\client\preferences_gear.dm"
#include "code\modules\client\preferences_notification.dm"
#include "code\modules\client\preferences_savefile.dm"
#include "code\modules\client\preferences_spawnpoints.dm"
@@ -1037,6 +1050,22 @@
#include "code\modules\client\preference_setup\global\02_settings.dm"
#include "code\modules\client\preference_setup\global\03_language.dm"
#include "code\modules\client\preference_setup\global\04_pai.dm"
#include "code\modules\client\preference_setup\loadout\gear_tweaks.dm"
#include "code\modules\client\preference_setup\loadout\loadout.dm"
#include "code\modules\client\preference_setup\loadout\loadout_accessories.dm"
#include "code\modules\client\preference_setup\loadout\loadout_cosmetics.dm"
#include "code\modules\client\preference_setup\loadout\loadout_ears.dm"
#include "code\modules\client\preference_setup\loadout\loadout_eyes.dm"
#include "code\modules\client\preference_setup\loadout\loadout_general.dm"
#include "code\modules\client\preference_setup\loadout\loadout_gloves.dm"
#include "code\modules\client\preference_setup\loadout\loadout_head.dm"
#include "code\modules\client\preference_setup\loadout\loadout_mask.dm"
#include "code\modules\client\preference_setup\loadout\loadout_shoes.dm"
#include "code\modules\client\preference_setup\loadout\loadout_smoking.dm"
#include "code\modules\client\preference_setup\loadout\loadout_suit.dm"
#include "code\modules\client\preference_setup\loadout\loadout_uniform.dm"
#include "code\modules\client\preference_setup\loadout\loadout_utility.dm"
#include "code\modules\client\preference_setup\loadout\loadout_xeno.dm"
#include "code\modules\client\preference_setup\occupation\occupation.dm"
#include "code\modules\client\preference_setup\other\01_incidents.dm"
#include "code\modules\client\preference_setup\skills\skills.dm"
@@ -1144,6 +1173,11 @@
#include "code\modules\economy\Events.dm"
#include "code\modules\economy\Events_Mundane.dm"
#include "code\modules\economy\TradeDestinations.dm"
#include "code\modules\effects\effect_system.dm"
#include "code\modules\effects\visual_effect.dm"
#include "code\modules\effects\sparks\procs.dm"
#include "code\modules\effects\sparks\spawner.dm"
#include "code\modules\effects\sparks\visuals.dm"
#include "code\modules\events\apc_damage.dm"
#include "code\modules\events\bear_attack.dm"
#include "code\modules\events\blob.dm"
@@ -1195,7 +1229,10 @@
#include "code\modules\flufftext\Dreaming.dm"
#include "code\modules\flufftext\Hallucination.dm"
#include "code\modules\flufftext\TextFilters.dm"
#include "code\modules\food\recipe.dm"
#include "code\modules\food\recipes_fryer.dm"
#include "code\modules\food\recipes_microwave.dm"
#include "code\modules\food\recipes_oven.dm"
#include "code\modules\games\boardgame.dm"
#include "code\modules\games\cardemon.dm"
#include "code\modules\games\cards.dm"
@@ -1240,13 +1277,15 @@
#include "code\modules\library\lib_items.dm"
#include "code\modules\library\lib_machines.dm"
#include "code\modules\library\lib_readme.dm"
#include "code\modules\lighting\_lighting_defs.dm"
#include "code\modules\lighting\light_source.dm"
#include "code\modules\lighting\lighting_area.dm"
#include "code\modules\lighting\lighting_atom.dm"
#include "code\modules\lighting\lighting_corner.dm"
#include "code\modules\lighting\lighting_overlay.dm"
#include "code\modules\lighting\lighting_process.dm"
#include "code\modules\lighting\lighting_system.dm"
#include "code\modules\lighting\lighting_profiler.dm"
#include "code\modules\lighting\lighting_setup.dm"
#include "code\modules\lighting\lighting_source.dm"
#include "code\modules\lighting\lighting_turf.dm"
#include "code\modules\lighting\lighting_verbs.dm"
#include "code\modules\lighting\~lighting_undefs.dm"
#include "code\modules\liquid\splash_simulation.dm"
#include "code\modules\maps\dmm_suite.dm"
@@ -1276,6 +1315,7 @@
#include "code\modules\mining\drilling\scanner.dm"
#include "code\modules\mob\animations.dm"
#include "code\modules\mob\death.dm"
#include "code\modules\mob\dview.dm"
#include "code\modules\mob\emote.dm"
#include "code\modules\mob\gender.dm"
#include "code\modules\mob\hear_say.dm"
@@ -1505,6 +1545,7 @@
#include "code\modules\mob\living\simple_animal\borer\say.dm"
#include "code\modules\mob\living\simple_animal\constructs\constructs.dm"
#include "code\modules\mob\living\simple_animal\constructs\soulstone.dm"
#include "code\modules\mob\living\simple_animal\familiars\familiars.dm"
#include "code\modules\mob\living\simple_animal\friendly\cat.dm"
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"
#include "code\modules\mob\living\simple_animal\friendly\crab.dm"
@@ -1531,6 +1572,9 @@
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
#include "code\modules\mob\living\simple_animal\hostile\syndicate.dm"
#include "code\modules\mob\living\simple_animal\hostile\tree.dm"
#include "code\modules\mob\living\simple_animal\hostile\commanded\_command_defines.dm"
#include "code\modules\mob\living\simple_animal\hostile\commanded\bear_companion.dm"
#include "code\modules\mob\living\simple_animal\hostile\commanded\commanded.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm"
@@ -1542,23 +1586,27 @@
#include "code\modules\mob\new_player\skill.dm"
#include "code\modules\mob\new_player\sprite_accessories.dm"
#include "code\modules\modular_computers\laptop_vendor.dm"
#include "code\modules\modular_computers\computers\item\modular_computer.dm"
#include "code\modules\modular_computers\computers\item\processor.dm"
#include "code\modules\modular_computers\computers\item\tablet.dm"
#include "code\modules\modular_computers\computers\item\tablet_presets.dm"
#include "code\modules\modular_computers\computers\machinery\console_presets.dm"
#include "code\modules\modular_computers\computers\machinery\modular_computer.dm"
#include "code\modules\modular_computers\computers\machinery\modular_console.dm"
#include "code\modules\modular_computers\computers\machinery\modular_laptop.dm"
#include "code\modules\modular_computers\computers\modular_computer\core.dm"
#include "code\modules\modular_computers\computers\modular_computer\damage.dm"
#include "code\modules\modular_computers\computers\modular_computer\hardware.dm"
#include "code\modules\modular_computers\computers\modular_computer\interaction.dm"
#include "code\modules\modular_computers\computers\modular_computer\power.dm"
#include "code\modules\modular_computers\computers\modular_computer\ui.dm"
#include "code\modules\modular_computers\computers\modular_computer\variables.dm"
#include "code\modules\modular_computers\computers\subtypes\dev_console.dm"
#include "code\modules\modular_computers\computers\subtypes\dev_laptop.dm"
#include "code\modules\modular_computers\computers\subtypes\dev_tablet.dm"
#include "code\modules\modular_computers\computers\subtypes\dev_telescreen.dm"
#include "code\modules\modular_computers\computers\subtypes\preset_console.dm"
#include "code\modules\modular_computers\computers\subtypes\preset_tablet.dm"
#include "code\modules\modular_computers\computers\subtypes\preset_telescreen.dm"
#include "code\modules\modular_computers\file_system\computer_file.dm"
#include "code\modules\modular_computers\file_system\data.dm"
#include "code\modules\modular_computers\file_system\news_article.dm"
#include "code\modules\modular_computers\file_system\program.dm"
#include "code\modules\modular_computers\file_system\program_events.dm"
#include "code\modules\modular_computers\file_system\programs\_program.dm"
#include "code\modules\modular_computers\file_system\programs\configurator.dm"
#include "code\modules\modular_computers\file_system\programs\file_browser.dm"
#include "code\modules\modular_computers\file_system\programs\ntdownloader.dm"
#include "code\modules\modular_computers\file_system\programs\app_presets.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\hacked_camera.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm"
@@ -1571,11 +1619,16 @@
#include "code\modules\modular_computers\file_system\programs\generic\news_browser.dm"
#include "code\modules\modular_computers\file_system\programs\generic\ntnrc_client.dm"
#include "code\modules\modular_computers\file_system\programs\generic\nttransfer.dm"
#include "code\modules\modular_computers\file_system\programs\medical\_medical.dm"
#include "code\modules\modular_computers\file_system\programs\research\_research.dm"
#include "code\modules\modular_computers\file_system\programs\medical\suit_sensors.dm"
#include "code\modules\modular_computers\file_system\programs\research\ai_restorer.dm"
#include "code\modules\modular_computers\file_system\programs\research\exosuit_monitor.dm"
#include "code\modules\modular_computers\file_system\programs\research\ntmonitor.dm"
#include "code\modules\modular_computers\file_system\programs\security\camera.dm"
#include "code\modules\modular_computers\file_system\programs\security\digitalwarrant.dm"
#include "code\modules\modular_computers\file_system\programs\system\client_manager.dm"
#include "code\modules\modular_computers\file_system\programs\system\configurator.dm"
#include "code\modules\modular_computers\file_system\programs\system\file_browser.dm"
#include "code\modules\modular_computers\file_system\programs\system\ntdownloader.dm"
#include "code\modules\modular_computers\hardware\ai_slot.dm"
#include "code\modules\modular_computers\hardware\battery_module.dm"
#include "code\modules\modular_computers\hardware\card_slot.dm"
@@ -1596,9 +1649,6 @@
#include "code\modules\multiz\pipes.dm"
#include "code\modules\multiz\structures.dm"
#include "code\modules\multiz\turf.dm"
#include "code\modules\nano\_JSON.dm"
#include "code\modules\nano\JSON Reader.dm"
#include "code\modules\nano\JSON Writer.dm"
#include "code\modules\nano\nanoexternal.dm"
#include "code\modules\nano\nanomanager.dm"
#include "code\modules\nano\nanomapgen.dm"
@@ -1718,6 +1768,7 @@
#include "code\modules\projectiles\guns\projectile.dm"
#include "code\modules\projectiles\guns\energy\laser.dm"
#include "code\modules\projectiles\guns\energy\lawgiver.dm"
#include "code\modules\projectiles\guns\energy\magic.dm"
#include "code\modules\projectiles\guns\energy\nuclear.dm"
#include "code\modules\projectiles\guns\energy\pulse.dm"
#include "code\modules\projectiles\guns\energy\rifle.dm"
@@ -1804,6 +1855,7 @@
#include "code\modules\reagents\reagent_containers\food\cans.dm"
#include "code\modules\reagents\reagent_containers\food\condiment.dm"
#include "code\modules\reagents\reagent_containers\food\drinks.dm"
#include "code\modules\reagents\reagent_containers\food\lunch.dm"
#include "code\modules\reagents\reagent_containers\food\sandwich.dm"
#include "code\modules\reagents\reagent_containers\food\snacks.dm"
#include "code\modules\reagents\reagent_containers\food\drinks\bottle.dm"
@@ -1924,11 +1976,15 @@
#include "code\modules\shuttles\shuttles_multi.dm"
#include "code\modules\spells\artifacts.dm"
#include "code\modules\spells\construct_spells.dm"
#include "code\modules\spells\contracts.dm"
#include "code\modules\spells\monster_manual.dm"
#include "code\modules\spells\no_clothes.dm"
#include "code\modules\spells\spell_code.dm"
#include "code\modules\spells\spell_projectile.dm"
#include "code\modules\spells\spell_verb.dm"
#include "code\modules\spells\spellbook.dm"
#include "code\modules\spells\spells.dm"
#include "code\modules\spells\storage.dm"
#include "code\modules\spells\aoe_turf\aoe_turf.dm"
#include "code\modules\spells\aoe_turf\blink.dm"
#include "code\modules\spells\aoe_turf\charge.dm"
@@ -1938,23 +1994,48 @@
#include "code\modules\spells\aoe_turf\summons.dm"
#include "code\modules\spells\aoe_turf\conjure\conjure.dm"
#include "code\modules\spells\aoe_turf\conjure\construct.dm"
#include "code\modules\spells\aoe_turf\conjure\druidic_spells.dm"
#include "code\modules\spells\aoe_turf\conjure\forcewall.dm"
#include "code\modules\spells\aoe_turf\conjure\grove.dm"
#include "code\modules\spells\general\area_teleport.dm"
#include "code\modules\spells\general\contract_spells.dm"
#include "code\modules\spells\general\mark_recall.dm"
#include "code\modules\spells\general\return_master.dm"
#include "code\modules\spells\general\rune_write.dm"
#include "code\modules\spells\hand\hand.dm"
#include "code\modules\spells\hand\hand_item.dm"
#include "code\modules\spells\spellbook\battlemage.dm"
#include "code\modules\spells\spellbook\cleric.dm"
#include "code\modules\spells\spellbook\druid.dm"
#include "code\modules\spells\spellbook\necromancer.dm"
#include "code\modules\spells\spellbook\spatial.dm"
#include "code\modules\spells\spellbook\standard.dm"
#include "code\modules\spells\spellbook\student.dm"
#include "code\modules\spells\targeted\cleric_spells.dm"
#include "code\modules\spells\targeted\entangle.dm"
#include "code\modules\spells\targeted\ethereal_jaunt.dm"
#include "code\modules\spells\targeted\genetic.dm"
#include "code\modules\spells\targeted\harvest.dm"
#include "code\modules\spells\targeted\mind_transfer.dm"
#include "code\modules\spells\targeted\necromancer_spells.dm"
#include "code\modules\spells\targeted\shapeshift.dm"
#include "code\modules\spells\targeted\shift.dm"
#include "code\modules\spells\targeted\subjugate.dm"
#include "code\modules\spells\targeted\swap.dm"
#include "code\modules\spells\targeted\targeted.dm"
#include "code\modules\spells\targeted\torment.dm"
#include "code\modules\spells\targeted\equip\equip.dm"
#include "code\modules\spells\targeted\equip\holy_relic.dm"
#include "code\modules\spells\targeted\equip\horsemask.dm"
#include "code\modules\spells\targeted\equip\remove_horsemask.dm"
#include "code\modules\spells\targeted\equip\party_hardy.dm"
#include "code\modules\spells\targeted\equip\seed.dm"
#include "code\modules\spells\targeted\equip\shield.dm"
#include "code\modules\spells\targeted\projectile\dumbfire.dm"
#include "code\modules\spells\targeted\projectile\fireball.dm"
#include "code\modules\spells\targeted\projectile\magic_missile.dm"
#include "code\modules\spells\targeted\projectile\passage.dm"
#include "code\modules\spells\targeted\projectile\projectile.dm"
#include "code\modules\spells\targeted\projectile\stuncuff.dm"
#include "code\modules\supermatter\setup_supermatter.dm"
#include "code\modules\supermatter\supermatter.dm"
#include "code\modules\surgery\_defines.dm"
@@ -250,7 +250,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -229,7 +229,7 @@ Thus, the two variables affect pump operation are set in New():
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -92,7 +92,7 @@
return 1
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
if(do_after(user, 40))
if(do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -146,7 +146,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -139,7 +139,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -114,7 +114,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
+1 -1
View File
@@ -358,7 +358,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -80,7 +80,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -275,7 +275,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -290,7 +290,7 @@
user << "<span class='danger'>\The [WT] must be turned on!</span>"
else if (WT.remove_fuel(0,user))
user << "<span class='notice'>Now welding \the [src].</span>"
if(do_after(user, 20))
if(do_after(user, 20, act_target = src))
if(!src || !WT.isOn()) return
playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
if(!welded)
+1 -1
View File
@@ -315,7 +315,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
+1 -1
View File
@@ -93,7 +93,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
+4 -4
View File
@@ -137,13 +137,13 @@ turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
if(firelevel > 6)
icon_state = "3"
set_light(7, FIRE_LIGHT_3)
set_light(7, FIRE_LIGHT_3, no_update = TRUE) // We set color later in the proc, that should trigger an update.
else if(firelevel > 2.5)
icon_state = "2"
set_light(5, FIRE_LIGHT_2)
set_light(5, FIRE_LIGHT_2, no_update = TRUE)
else
icon_state = "1"
set_light(3, FIRE_LIGHT_1)
set_light(3, FIRE_LIGHT_1, no_update = TRUE)
for(var/mob/living/L in loc)
L.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the mobs!
@@ -439,4 +439,4 @@ datum/gas_mixture/proc/check_recombustability(list/fuel_objs)
#undef FIRE_LIGHT_1
#undef FIRE_LIGHT_2
#undef FIRE_LIGHT_3
#undef FIRE_LIGHT_3
-26
View File
@@ -1,26 +0,0 @@
/matrix/proc/TurnTo(old_angle, new_angle)
. = new_angle - old_angle
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3)
if(!segments)
return
var/segment = 360/segments
if(!clockwise)
segment = -segment
var/list/matrices = list()
for(var/i in 1 to segments-1)
var/matrix/M = matrix(transform)
M.Turn(segment*i)
matrices += M
var/matrix/last = matrix(transform)
matrices += last
speed /= segments
animate(src, transform = matrices[1], time = speed, loops)
for(var/i in 2 to segments) //2 because 1 is covered above
animate(transform = matrices[i], time = speed)
//doesn't have an object argument because this is "Stacking" with the animate call above
//3 billion% intentional
+1 -1
View File
@@ -1,2 +1,2 @@
#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary.
// 1 will enable set background. 0 will disable set background.
// 1 will enable set background. 0 will disable set background.
+14
View File
@@ -0,0 +1,14 @@
//DVIEW defines
#define FOR_DVIEW(type, range, center, invis_flags) \
dview_mob.loc = center; \
dview_mob.see_invisible = invis_flags; \
for(type in view(range, dview_mob))
#define END_FOR_DVIEW dview_mob.loc = null
#define DVIEW(output, range, center, invis_flags) \
dview_mob.loc = center; \
dview_mob.see_invisible = invis_flags; \
output = view(range, dview_mob); \
dview_mob.loc = null;
+80 -22
View File
@@ -1,34 +1,92 @@
//DVIEW defines
//DVIEW is a hack that uses a mob with darksight in order to find lists of viewable stuff while ignoring darkness
#define LIGHTING_INTERVAL 2 // Frequency, in 1/10ths of a second, of the lighting process.
var/mob/dview/dview_mob = new
#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone
#define LIGHTING_ROUND_VALUE 1 / 128 //Value used to round lumcounts, values smaller than 1/255 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY.
/mob/dview
invisibility = 101
density = 0
#define LIGHTING_ICON 'icons/effects/lighting_overlay.png' // icon used for lighting shading effects
anchored = 1
simulated = 0
#define LIGHTING_SOFT_THRESHOLD 0.001 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays.
see_in_dark = 1e6
// If I were you I'd leave this alone.
#define LIGHTING_BASE_MATRIX \
list \
( \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \
0, 0, 0, 1 \
) \
#define FOR_DVIEW(type, range, center, invis_flags) \
dview_mob.loc = center; \
dview_mob.see_invisible = invis_flags; \
for(type in view(range, dview_mob))
// Helpers so we can (more easily) control the colour matrices.
#define CL_MATRIX_RR 1
#define CL_MATRIX_RG 2
#define CL_MATRIX_RB 3
#define CL_MATRIX_RA 4
#define CL_MATRIX_GR 5
#define CL_MATRIX_GG 6
#define CL_MATRIX_GB 7
#define CL_MATRIX_GA 8
#define CL_MATRIX_BR 9
#define CL_MATRIX_BG 10
#define CL_MATRIX_BB 11
#define CL_MATRIX_BA 12
#define CL_MATRIX_AR 13
#define CL_MATRIX_AG 14
#define CL_MATRIX_AB 15
#define CL_MATRIX_AA 16
#define CL_MATRIX_CR 17
#define CL_MATRIX_CG 18
#define CL_MATRIX_CB 19
#define CL_MATRIX_CA 20
#define END_FOR_DVIEW dview_mob.loc = null
//Some defines to generalise colours used in lighting.
//Important note on colors. Colors can end up significantly different from the basic html picture, especially when saturated
#define LIGHT_COLOR_RED "#FA8282" //Warm but extremely diluted red. rgb(250, 130, 130)
#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100)
#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250)
#define DVIEW(output, range, center, invis_flags) \
dview_mob.loc = center; \
dview_mob.see_invisible = invis_flags; \
output = view(range, dview_mob); \
dview_mob.loc = null ;
#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225)
#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225)
#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125)
#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50)
#define LIGHT_COLOR_ORANGE "#FA9632" //Mostly pure orange. rgb(250, 150, 50)
#define LIGHT_COLOR_PURPLE "#A97FAA" //Soft purple. rgb(169, 127, 170)
//These ones aren't a direct colour like the ones above, because nothing would fit
#define LIGHT_COLOR_FIRE "#FAA019" //Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25)
#define LIGHT_COLOR_FLARE "#FA644B" //Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75)
#define LIGHT_COLOR_SLIME_LAMP "#AFC84B" //Weird color, between yellow and green, very slimy. rgb(175, 200, 75)
#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" //Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175)
#define LIGHT_COLOR_HALOGEN "#F0FAFA" //Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250)
//Defines for lighting status, see power/lighting.dm
#define LIGHT_OK 0
#define LIGHT_EMPTY 1
#define LIGHT_BROKEN 2
#define LIGHT_BURNED 3
// Some angle presets for directional lighting.
#define LIGHT_OMNI null
#define LIGHT_SEMI 180
#define LIGHT_WIDE 90
#define LIGHT_NARROW 45
// Night lighting controller times
// The time (in ticks based on worldtime2ticks()) that various actions trigger
#define MORNING_LIGHT_RESET 252000 // 7am or 07:00 - lighting restores to normal in morning
#define NIGHT_LIGHT_ACTIVE 648000 // 6pm or 18:00 - night lighting mode activates
// The time (in hours based on worldtime2hours()) that various actions trigger
#define MORNING_LIGHT_RESET 7 // 7am or 07:00 - lighting restores to normal in morning
#define NIGHT_LIGHT_ACTIVE 18 // 6pm or 18:00 - night lighting mode activates
// Some brightness/range defines for objects.
#define L_WALLMOUNT_POWER 0.4
#define L_WALLMOUNT_RANGE 2
#define L_WALLMOUNT_HI_POWER 1 // For red/delta alert on fire alarms.
#define L_WALLMOUNT_HI_RANGE 4
// This controls by how much console sprites are dimmed before being overlayed.
#define HOLOSCREEN_ADDITION_FACTOR 1
#define HOLOSCREEN_MULTIPLICATION_FACTOR 0.5
#define HOLOSCREEN_ADDITION_OPACITY 0.8
#define HOLOSCREEN_MULTIPLICATION_OPACITY 1
// Just so we can avoid unneeded proc calls when profiling is disabled.
#define L_PROF(O,T) if (lighting_profiling) {lprof_write(O,T);}
+14 -8
View File
@@ -68,14 +68,6 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
#define STAGE_FIVE 9
#define STAGE_SUPER 11
// computer3 error codes, move lower in the file when it passes dev -Sayu
#define PROG_CRASH 0x1 // Generic crash.
#define MISSING_PERIPHERAL 0x2 // Missing hardware.
#define BUSTED_ASS_COMPUTER 0x4 // Self-perpetuating error. BAC will continue to crash forever.
#define MISSING_PROGRAM 0x8 // Some files try to automatically launch a program. This is that failing.
#define FILE_DRM 0x10 // Some files want to not be copied/moved. This is them complaining that you tried.
#define NETWORK_FAILURE 0x20
// NanoUI flags
#define STATUS_INTERACTIVE 2 // GREEN Visability
#define STATUS_UPDATE 1 // ORANGE Visability
@@ -104,3 +96,17 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
#define ATMOS_DEFAULT_VOLUME_FILTER 200 // L.
#define ATMOS_DEFAULT_VOLUME_MIXER 200 // L.
#define ATMOS_DEFAULT_VOLUME_PIPE 70 // L.
// Misc process flags.
#define M_PROCESSES 0x1
#define M_USES_POWER 0x2
// If this is returned from a machine's process() proc, the machine will stop processing but
// will continue to have power calculations done.
#define M_NO_PROCESS 27
// This controls how much power the AME generates per unit of fuel.
// Assuming 100% efficency, use this equation to figure out power output.
// power_generated = (fuel**2) * AM_POWER_FACTOR
#define AM_POWER_FACTOR 50000
+10
View File
@@ -0,0 +1,10 @@
#define ORE_URANIUM "uranium"
#define ORE_IRON "iron"
#define ORE_COAL "coal"
#define ORE_SAND "sand"
#define ORE_PHORON "phoron"
#define ORE_SILVER "silver"
#define ORE_GOLD "gold"
#define ORE_DIAMOND "diamond"
#define ORE_PLATINUM "platinum"
#define ORE_HYDROGEN "mhydrogen"
+39 -2
View File
@@ -48,6 +48,10 @@
#define SHOW_TYPING 0x4000
#define CHAT_NOICONS 0x8000
#define PARALLAX_SPACE 0x1
#define PARALLAX_DUST 0x2
#define PROGRESS_BARS 0x4
#define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC)
//Sound effects toggles
@@ -200,10 +204,11 @@
#define NTNETSPEED_DOS_AMPLIFICATION 5 // Multiplier for Denial of Service program. Resulting load on NTNet relay is this multiplied by NTNETSPEED of the device
// Program bitflags
#define PROGRAM_ALL 7
#define PROGRAM_ALL 15
#define PROGRAM_CONSOLE 1
#define PROGRAM_LAPTOP 2
#define PROGRAM_TABLET 4
#define PROGRAM_TELESCREEN 8
#define PROGRAM_STATE_KILLED 0
#define PROGRAM_STATE_BACKGROUND 1
@@ -259,4 +264,36 @@
#define LAYER_TABLE 2.8
#define LAYER_UNDER_TABLE 2.79
#define LAYER_ABOVE_TABLE 2.81
#define LAYER_ABOVE_TABLE 2.81
// Stoplag.
#define TICK_LIMIT 80
#define TICK_CHECK ( world.tick_usage > TICK_LIMIT ? stoplag() : 0 )
#define CHECK_TICK if (world.tick_usage > TICK_LIMIT) stoplag()
// Effect Systems.
#define EFFECT_CONTINUE 0 // Keep processing.
#define EFFECT_HALT 1 // Stop processing, but don't qdel.
#define EFFECT_DESTROY 2 // qdel.
// Performance bullshit.
//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
#define RANGE_TURFS(RADIUS, CENTER) \
block( \
locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
)
#define get_turf(A) (get_step(A, 0))
#define QDELETED(TARGET) (!TARGET || TARGET.gcDestroyed)
#define QDEL_NULL(item) qdel(item); item = null
// Shim until addtimer is merged or I figure out if it is safe to use scheduler for this.
#define QDEL_IN(OBJ, TIME) spawn(TIME) qdel(OBJ)
//Recipe type defines. Used to determine what machine makes them
#define MICROWAVE 0x1
#define FRYER 0x2
#define OVEN 0x4
#define CANDYMAKER 0x8
#define CEREALMAKER 0x10
+16
View File
@@ -16,3 +16,19 @@
// SCHECK macros
// This references src directly to work around a weird bug with try/catch
#define SCHECK sleepCheck()
#define F_SCHECK \
calls_since_last_scheck = 0; \
if (killed) CRASH("A killed process is still running somehow..."); \
if (hung) { \
handleHung(); \
CRASH("Process [name] hung and was restarted."); \
} \
if (world.tick_usage > 100 || (world.tick_usage - tick_start) > tick_allowance) { \
sleep(world.tick_lag); \
cpu_defer_count++; \
last_slept = TimeOfHour; \
tick_start = world.tick_usage; \
}
#define PSCHED_CHECK_TICK (world.tick_usage > 100 || (world.tick_usage - tick_start) > tick_allowance)
+128 -98
View File
@@ -1,125 +1,155 @@
//This was made pretty explicity for atmospherics devices which could not delete their datums properly
//Make sure you go around and null out those references to the datum
//It was also pretty explicitly and shamelessly stolen from regular object pooling, thanks esword
//#define DEBUG_DATUM_POOL
#define MAINTAINING_OBJECT_POOL_COUNT 500
// Read-only or compile-time vars and special exceptions.
/var/list/exclude = list("inhand_states", "loc", "locs", "parent_type", "vars", "verbs", "type", "x", "y", "z","group", "animate_movement")
/var/global/list/masterdatumPool = new
/var/global/list/pooledvariables = new
/*
/tg/station13 /atom/movable Pool:
---------------------------------
By RemieRichards
* @args : datum type, normal arguments
* Example call: getFromPool(/datum/pipeline, args)
*/
/proc/getFromPool(var/type, ...)
var/list/B = (args - type)
Creation/Deletion is laggy, so let's reduce reuse and recycle!
if(length(masterdatumPool[type]) <= 0)
*/
#define ATOM_POOL_COUNT 100
// "define DEBUG_ATOM_POOL 1
var/global/list/GlobalPool = list()
//You'll be using this proc 90% of the time.
//It grabs a type from the pool if it can
//And if it can't, it creates one
//The pool is flexible and will expand to fit
//The new created atom when it eventually
//Goes into the pool
//Second argument can be a new location, if the type is /atom/movable
//Or a list of arguments
//Either way it gets passed to new
/proc/PoolOrNew(var/get_type,var/second_arg)
var/datum/D
D = GetFromPool(get_type,second_arg)
if(!D)
// So the GC knows we're pooling this type.
if(!GlobalPool[get_type])
GlobalPool[get_type] = list()
if(islist(second_arg))
return new get_type (arglist(second_arg))
else
return new get_type (second_arg)
return D
/proc/GetFromPool(var/get_type,var/second_arg)
if(isnull(GlobalPool[get_type]))
return 0
if(length(GlobalPool[get_type]) == 0)
return 0
var/datum/D = pick_n_take(GlobalPool[get_type])
if(D)
D.ResetVars()
D.Prepare(second_arg)
return D
return 0
/proc/PlaceInPool(var/datum/D)
if(!istype(D))
return
if(length(GlobalPool[D.type]) > ATOM_POOL_COUNT)
#ifdef DEBUG_ATOM_POOL
world << text("DEBUG_DATUM_POOL: PlaceInPool([]) exceeds []. Discarding.", D.type, ATOM_POOL_COUNT)
#ifdef DEBUG_DATUM_POOL
if(ticker)
to_chat(world, text("DEBUG_DATUM_POOL: new proc has been called ([] | []).", type, list2params(B)))
#endif
if(garbage_collector)
garbage_collector.AddTrash(D)
//so the GC knows we're pooling this type.
if(isnull(masterdatumPool[type]))
masterdatumPool[type] = list()
if(B && B.len)
return new type(arglist(B))
else
del(D)
return new type()
var/datum/O = masterdatumPool[type][1]
masterdatumPool[type] -= O
#ifdef DEBUG_DATUM_POOL
to_chat(world, text("DEBUG_DATUM_POOL: getFromPool([]) [] left arglist([]).", type, length(masterdatumPool[type]), list2params(B)))
#endif
if(!O || !istype(O))
O = new type(arglist(B))
else
if(istype(O, /atom/movable) && B.len) // B.len check so we don't OoB.
var/atom/movable/AM = O
AM.forceMove(B[1], FALSE, TRUE)
if(B && B.len)
O.New(arglist(B))
else
O.New()
return O
/*
* @args
* D, datum instance
*
* Example call: returnToPool(src)
*/
/proc/returnToPool(const/datum/D)
ASSERT(D)
if(istype(D, /atom/movable) && length(masterdatumPool[D.type]) > MAINTAINING_OBJECT_POOL_COUNT)
#ifdef DEBUG_DATUM_POOL
to_chat(world, text("DEBUG_DATUM_POOL: returnToPool([]) exceeds [] discarding...", D.type, MAINTAINING_OBJECT_POOL_COUNT))
#endif
qdel(D)
return
if(D in GlobalPool[D.type])
return
if(!GlobalPool[D.type])
GlobalPool[D.type] = list()
GlobalPool[D.type] += D
if(isnull(masterdatumPool[D.type]))
masterdatumPool[D.type] = list()
D.Destroy()
D.ResetVars()
D.resetVariables()
/proc/IsPooled(var/datum/D)
if(isnull(GlobalPool[D.type]))
return 0
return 1
#ifdef DEBUG_DATUM_POOL
if(D in masterdatumPool[D.type])
to_chat(world, text("returnToPool has been called twice for the same datum of type [] time to panic.", D.type))
#endif
/datum/proc/Prepare(args)
if(islist(args))
New(arglist(args))
else
New(args)
masterdatumPool[D.type] |= D
/atom/movable/Prepare(args)
var/list/args_list = args
if(istype(args_list) && args_list.len)
loc = args[1]
else
loc = args
..()
#ifdef DEBUG_DATUM_POOL
to_chat(world, text("DEBUG_DATUM_POOL: returnToPool([]) [] left.", D.type, length(masterdatumPool[D.type])))
#endif
var/list/excluded_vars = list("animate_movement", "contents", "loc", "locs", "parent_type", "vars", "verbs", "type")
var/list/pooledvariables = list()
//thanks to clusterfack @ /vg/station for these two procs
/datum/proc/createVariables(var/list/excluded)
#undef MAINTAINING_DATUM_POOL_COUNT
#ifdef DEBUG_DATUM_POOL
#undef DEBUG_DATUM_POOL
#endif
/datum/proc/createVariables()
pooledvariables[type] = new/list()
var/list/all_excluded = excluded_vars + excluded
var/list/exclude = global.exclude + args
for(var/key in vars)
if(key in all_excluded)
if(key in exclude)
continue
pooledvariables[type][key] = initial(vars[key])
/datum/proc/ResetVars(var/list/excluded = list())
//RETURNS NULL WHEN INITIALIZED AS A LIST() AND POSSIBLY OTHER DISCRIMINATORS
//IF YOU ARE USING SPECIAL VARIABLES SUCH A LIST() INITIALIZE THEM USING RESET VARIABLES
//SEE http://www.byond.com/forum/?post=76850 AS A REFERENCE ON THIS
/datum/proc/resetVariables()
if(!pooledvariables[type])
createVariables(excluded)
createVariables(args)
for(var/key in pooledvariables[type])
vars[key] = pooledvariables[type][key]
/atom/movable/ResetVars()
..()
loc = null
contents = initial(contents) //something is really wrong if this object still has stuff in it by this point
/proc/isInTypes(atom/Object, types)
if(!Object)
return 0
var/prototype = Object.type
Object = null
/image/ResetVars()
..()
loc = null
for (var/type in params2list(types))
if (ispath(prototype, text2path(type)))
return 1
#undef ATOM_POOL_COUNT
return 0
/client/proc/debug_pooling()
set name = "Debug Pooling Type"
set category = "Debug"
if (!check_rights(R_DEBUG))
return
var/type = input("What is the typepath for the pooled object variables you wish to view?", "Pooled Variables") in pooledvariables|null
if(!type)
return
var/list/L = list()
L += "<b>Stored Variables for Pooling for this type</b><br>"
for(var/key in pooledvariables[type])
if(pooledvariables[type][key])
L += "<br>[key] = [pooledvariables[type][key]]"
else
L += "<br>[key] = null"
usr << browse(jointext(L,""),"window=poolingvariablelogs")
// Shim - this method doesn't natively exist in this implementation.
/proc/IsPooled(var/datum/D)
return "[D.type]" in masterdatumPool
-1
View File
@@ -631,7 +631,6 @@ proc/ColorTone(rgb, tone)
/*
Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays
as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included.
The _flatIcons list is a cache for generated icon files.
*/
proc // Creates a single icon from a given /atom or /image. Only the first argument is required.
+9 -1
View File
@@ -127,4 +127,12 @@
return (val & (val-1)) == 0
/proc/RoundUpToPowerOfTwo(var/val)
return 2 ** -round(-log(2,val))
return 2 ** -round(-log(2,val))
//Written by Lohikar
//Returns the cube root of the input number
/proc/cubert(var/num, var/iterations = 10)
. = num
for (var/i = 0, i < iterations, i++)
. = (1/3) * (num/(.**2)+2*.)
+33
View File
@@ -0,0 +1,33 @@
// Factor/Opacity values are defined in __defines\lighting.dm
/proc/holographic_overlay(obj/target, icon, icon_state)
var/image/multiply = make_screen_overlay(icon, icon_state)
multiply.blend_mode = BLEND_MULTIPLY
multiply.color = list(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_OPACITY
)
target.overlays += multiply
var/image/overlay = make_screen_overlay(icon, icon_state)
overlay.blend_mode = BLEND_ADD
overlay.color = list(
HOLOSCREEN_ADDITION_FACTOR, 0, 0, 0,
0, HOLOSCREEN_ADDITION_FACTOR, 0, 0,
0, 0, HOLOSCREEN_ADDITION_FACTOR, 0,
0, 0, 0, HOLOSCREEN_ADDITION_OPACITY
)
target.overlays += overlay
/proc/make_screen_overlay(icon, icon_state, brightness_factor = null)
var/image/overlay = image(icon, icon_state)
overlay.layer = LIGHTING_LAYER + 0.1
if (brightness_factor)
overlay.color = list(
brightness_factor, 0, 0, 0,
0, brightness_factor, 0, 0,
0, 0, brightness_factor, 0,
0, 0, 0, 1
)
return overlay
+22
View File
@@ -180,6 +180,28 @@
if(start)
return findtextEx(text, suffix, start, null)
//Parses a string into a list
/proc/dd_text2List(text, separator)
var/textlength = lentext(text)
var/separatorlength = lentext(separator)
var/list/textList = new /list()
var/searchPosition = 1
var/findPosition = 1
var/buggyText
while (1) // Loop forever.
findPosition = findtextEx(text, separator, searchPosition, 0)
buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element.
textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext().
searchPosition = findPosition + separatorlength // Skip over separator.
if (findPosition == 0) // Didn't find anything at end of string so stop here.
return textList
else
if (searchPosition > textlength) // Found separator at very end of string.
textList += "" // So add empty element
/*
* Text modification
*/
+5 -3
View File
@@ -10,10 +10,12 @@ proc/worldtime2text(time = world.time, timeshift = 1)
if(!roundstart_hour) roundstart_hour = rand(0, 23)
return timeshift ? time2text(time+(36000*roundstart_hour), "hh:mm") : time2text(time, "hh:mm")
proc/worldtime2ticks(time = world.time)
if(!roundstart_hour)
/proc/worldtime2hours()
if (!roundstart_hour)
worldtime2text()
return ((roundstart_hour * 60 MINUTES) + time) % TICKS_IN_DAY
. = (world.timeofday / (60 MINUTES)) + roundstart_hour
if (. > 24)
. -= 24
proc/worlddate2text()
return num2text(game_year) + "-" + time2text(world.timeofday, "MM-DD")
+101 -44
View File
@@ -646,45 +646,109 @@ proc/GaussRandRound(var/sigma,var/roundto)
else return get_step(ref, base_dir)
/proc/do_mob(var/mob/user, var/mob/target, var/delay = 30, var/numticks = 5, var/needhand = 1) //This is quite an ugly solution but i refuse to use the old request system.
if(!user || !target) return 0
if(numticks == 0) return 0
var/delayfraction = round(delay/numticks)
var/original_user_loc = user.loc
var/original_target_loc = target.loc
/proc/do_mob(mob/user , mob/target, delay = 30, numticks = 10, needhand = TRUE, display_progress = TRUE) //This is quite an ugly solution but i refuse to use the old request system.
if(!user || !target)
return 0
var/user_loc = user.loc
var/target_loc = target.loc
var/holding = user.get_active_hand()
for(var/i = 0, i<numticks, i++)
var/delayfraction = round(delay/numticks)
var/image/progbar
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && display_progress)
if(!progbar)
progbar = image(icon = 'icons/effects/doafter_icon.dmi', loc = target, icon_state = "prog_bar_0")
progbar.layer = 21
progbar.pixel_z = WORLD_ICON_SIZE
progbar.appearance_flags = RESET_TRANSFORM
for (var/i = 1 to numticks)
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && progbar && display_progress)
progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]"
user.client.images |= progbar
sleep(delayfraction)
if(!user || user.stat || user.weakened || user.stunned || user.loc != original_user_loc)
if(!user || !target)
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(!target || target.loc != original_target_loc)
if (user.loc != user_loc || target.loc != target_loc || (needhand && user.get_active_hand() != holding) || user.stat || user.weakened || user.stunned)
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand
return 0
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 1
/proc/do_after(var/mob/user as mob, delay as num, var/numticks = 5, var/needhand = 1)
/proc/do_after(mob/user as mob, delay as num, numticks = 10, needhand = TRUE, atom/movable/act_target = null, use_user_turf = FALSE, display_progress = TRUE)
if(!user || isnull(user))
return 0
if(numticks == 0)
return 0
if (!act_target)
act_target = user
var/delayfraction = round(delay/numticks)
var/original_loc = user.loc
var/Location
if(use_user_turf) //When this is true, do_after() will check whether the user's turf has changed, rather than the user's loc.
Location = get_turf(user)
else
Location = user.loc
var/holding = user.get_active_hand()
for(var/i = 0, i<numticks, i++)
var/image/progbar
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && act_target && display_progress)
if(!progbar)
progbar = image(icon = 'icons/effects/doafter_icon.dmi', loc = act_target, icon_state = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE
progbar.layer = 21
progbar.appearance_flags = RESET_COLOR | RESET_TRANSFORM
for (var/i = 1 to numticks)
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && act_target && display_progress)
if(!progbar)
progbar = image(icon = 'icons/effects/doafter_icon.dmi', loc = act_target, icon_state = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE
progbar.layer = 21
progbar.appearance_flags = RESET_COLOR | RESET_TRANSFORM
progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]"
user.client.images |= progbar
sleep(delayfraction)
if(!user || user.stat || user.weakened || user.stunned || user.loc != original_loc)
var/user_loc_to_check
if(use_user_turf)
user_loc_to_check = get_turf(user)
else
user_loc_to_check = user.loc
if (!user || user.stat || user.weakened || user.stunned || !(user_loc_to_check == Location))
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 1
//Takes: Anything that could possibly have variables and a varname to check.
@@ -849,12 +913,6 @@ proc/GaussRandRound(var/sigma,var/roundto)
if(!istype(M,/mob) || istype(M, /mob/eye)) continue // If we need to check for more mobs, I'll add a variable
M.loc = X
// var/area/AR = X.loc
// if(AR.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn
// X.opacity = !X.opacity
// X.SetOpacity(!X.opacity)
toupdate += X
if(turftoleave)
@@ -1063,13 +1121,6 @@ proc/get_mob_with_client_list()
else if (zone == "r_foot") return "right foot"
else return zone
//gets the turf the atom is located in (or itself, if it is a turf).
//returns null if the atom is not in a turf.
/proc/get_turf(atom/A)
if(!istype(A)) return
for(A, A && !isturf(A), A=A.loc);
return A
/proc/get(atom/loc, type)
while(loc)
if(istype(loc, type))
@@ -1364,15 +1415,6 @@ var/list/WALLITEMS = list(
/atom/movable/proc/stop_orbit()
orbiting = null
/mob/dview/New()
..()
// We don't want to be in any mob lists; we're a dummy not a mob.
mob_list -= src
if(stat == DEAD)
dead_mob_list -= src
else
living_mob_list -= src
// call to generate a stack trace and print to runtime logs
/proc/crash_with(msg)
CRASH(msg)
@@ -1396,3 +1438,18 @@ var/list/WALLITEMS = list(
a = a.loc
return 0//If we get here, we must be buried many layers deep in nested containers. Shouldn't happen
//Increases delay as the server gets more overloaded,
//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
#define DELTA_CALC max((max(world.tick_usage,world.cpu)/100),1)
/proc/stoplag()
. = 0
var/i = 1
do
. += round(i*DELTA_CALC)
sleep(i*world.tick_lag*DELTA_CALC)
i *= 2
while (world.tick_usage > TICK_LIMIT)
#undef DELTA_CALC
+2
View File
@@ -48,3 +48,5 @@
#define to_file(file_entry, file_content) file_entry << file_content
#define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name)
#define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name)
#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE)
+2
View File
@@ -255,6 +255,8 @@ datum/hud/New(mob/owner)
mymob.instantiate_hud(src, ui_style, ui_color, ui_alpha)
update_parallax_existence()
/mob/proc/instantiate_hud(var/datum/hud/HUD, var/ui_style, var/ui_color, var/ui_alpha)
return
+265
View File
@@ -0,0 +1,265 @@
// Until we convert to planes.
#define PLANE_SPACE_BACKGROUND -98
#define PLANE_SPACE_PARALLAX (PLANE_SPACE_BACKGROUND + 1) // -97
#define PLANE_SPACE_DUST (PLANE_SPACE_PARALLAX + 1) // -96
#define PLANE_ABOVE_PARALLAX (PLANE_SPACE_BACKGROUND + 3) // -95
/*
* This file handles all parallax-related business once the parallax itself is initialized with the rest of the HUD
*/
#define PARALLAX_IMAGE_WIDTH 8
#define PARALLAX_IMAGE_TILES (PARALLAX_IMAGE_WIDTH**2)
#define GRID_WIDTH 3
var/list/parallax_on_clients = list()
var/parallax_initialized = 0
var/space_color = "#050505"
var/list/parallax_icon[(GRID_WIDTH**2)*3]
/obj/screen/parallax
var/base_offset_x = 0
var/base_offset_y = 0
mouse_opacity = 0
icon = 'icons/turf/space.dmi'
icon_state = "blank"
name = "space parallax"
screen_loc = "CENTER,CENTER"
blend_mode = BLEND_ADD
layer = AREA_LAYER
plane = PLANE_SPACE_PARALLAX
var/parallax_speed = 0
/obj/screen/plane_master
appearance_flags = PLANE_MASTER
screen_loc = "CENTER,CENTER"
/obj/screen/plane_master/parallax_master
plane = PLANE_SPACE_PARALLAX
blend_mode = BLEND_MULTIPLY
color = list(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,0,
0,0,0,1)
/obj/screen/plane_master/parallax_spacemaster //Turns space white, causing the parallax to only show in areas with opacity. Somehow
plane = PLANE_SPACE_BACKGROUND
color = list(
0,0,0,0,
0,0,0,0,
0,0,0,0,
1,1,1,1)
/obj/screen/plane_master/parallax_spacemaster/New()
..()
overlays += image(icon = 'icons/mob/screen1.dmi', icon_state = "blank")
if(universe)
universe.convert_parallax(src)
/obj/screen/plane_master/parallax_dustmaster
plane = PLANE_SPACE_DUST
color = list(0,0,0,0)
/datum/hud/proc/update_parallax_existence()
if(!parallax_initialized)
return
initialize_parallax()
update_parallax()
update_parallax_values()
/datum/hud/proc/initialize_parallax()
var/client/C = mymob.client
if(!C.parallax_master)
C.parallax_master = new /obj/screen/plane_master/parallax_master
if(!C.parallax_spacemaster)
C.parallax_spacemaster = new /obj/screen/plane_master/parallax_spacemaster
if(!C.parallax_dustmaster)
C.parallax_dustmaster = new /obj/screen/plane_master/parallax_dustmaster
if(!C.parallax.len)
for(var/obj/screen/parallax/bgobj in parallax_icon)
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
parallax_layer.appearance = bgobj.appearance
parallax_layer.base_offset_x = bgobj.base_offset_x
parallax_layer.base_offset_y = bgobj.base_offset_y
parallax_layer.parallax_speed = bgobj.parallax_speed
parallax_layer.screen_loc = bgobj.screen_loc
C.parallax += parallax_layer
if(bgobj.parallax_speed)
C.parallax_movable += parallax_layer
if(!C.parallax_offset.len)
C.parallax_offset["horizontal"] = 0
C.parallax_offset["vertical"] = 0
C.screen |= C.parallax_dustmaster
/datum/hud/proc/update_parallax()
var/client/C = mymob.client
if(C.prefs.parallax_togs & PARALLAX_SPACE)
parallax_on_clients |= C
for(var/obj/screen/parallax/bgobj in C.parallax)
C.screen |= bgobj
C.screen |= C.parallax_master
C.screen |= C.parallax_spacemaster
if(C.prefs.parallax_togs & PARALLAX_DUST)
C.parallax_dustmaster.color = list(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1)
else
C.parallax_dustmaster.color = list(0,0,0,0)
else
for(var/obj/screen/parallax/bgobj in C.parallax)
C.screen -= bgobj
parallax_on_clients -= C
C.screen -= C.parallax_master
C.screen -= C.parallax_spacemaster
C.parallax_dustmaster.color = list(0,0,0,0)
/datum/hud/proc/update_parallax_values()
var/client/C = mymob.client
if(!parallax_initialized)
return
if(!(locate(/turf/space) in trange(C.view,get_turf(C.eye))))
return
//ACTUALLY MOVING THE PARALLAX
var/turf/posobj = get_turf(C.eye)
if(!C.previous_turf || (C.previous_turf.z != posobj.z))
C.previous_turf = posobj
//Doing it this way prevents parallax layers from "jumping" when you change Z-Levels.
var/offsetx = C.parallax_offset["horizontal"] + posobj.x - C.previous_turf.x
var/offsety = C.parallax_offset["vertical"] + posobj.y - C.previous_turf.y
C.parallax_offset["horizontal"] = offsetx
C.parallax_offset["vertical"] = offsety
C.previous_turf = posobj
for(var/obj/screen/parallax/bgobj in C.parallax_movable)
var/accumulated_offset_x = bgobj.base_offset_x - round(offsetx * bgobj.parallax_speed * C.prefs.parallax_speed)
var/accumulated_offset_y = bgobj.base_offset_y - round(offsety * bgobj.parallax_speed * C.prefs.parallax_speed)
if(accumulated_offset_x > PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE)
accumulated_offset_x -= PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE*GRID_WIDTH //3x3 grid, 15 tiles * 64 icon_size * 3 grid size
if(accumulated_offset_x < -(PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE*2))
accumulated_offset_x += PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE*GRID_WIDTH
if(accumulated_offset_y > PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE)
accumulated_offset_y -= PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE*GRID_WIDTH
if(accumulated_offset_y < -(PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE*2))
accumulated_offset_y += PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE*GRID_WIDTH
bgobj.screen_loc = "CENTER:[accumulated_offset_x],CENTER:[accumulated_offset_y]"
//Parallax generation code below
#define PARALLAX4_ICON_NUMBER 20
#define PARALLAX3_ICON_NUMBER 14
#define PARALLAX2_ICON_NUMBER 10
/proc/create_global_parallax_icons()
var/list/plane1 = list()
var/list/plane2 = list()
var/list/plane3 = list()
var/list/pixel_x = list()
var/list/pixel_y = list()
var/index = 1
for(var/i = 0 to (PARALLAX_IMAGE_TILES-1))
for(var/j = 1 to GRID_WIDTH**2)
plane1 += rand(1,26)
plane2 += rand(1,26)
plane3 += rand(1,26)
pixel_x += WORLD_ICON_SIZE * (i%PARALLAX_IMAGE_WIDTH)
pixel_y += WORLD_ICON_SIZE * round(i/PARALLAX_IMAGE_WIDTH)
for(var/i in 0 to ((GRID_WIDTH**2)-1))
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
var/list/L = list()
for(var/j in 1 to PARALLAX_IMAGE_TILES)
if(plane1[j+i*PARALLAX_IMAGE_TILES] <= PARALLAX4_ICON_NUMBER)
var/image/I = image('icons/turf/space_parallax4.dmi',"[plane1[j+i*PARALLAX_IMAGE_TILES]]")
I.pixel_x = pixel_x[j]
I.pixel_y = pixel_y[j]
L += I
parallax_layer.overlays = L
parallax_layer.parallax_speed = 0
parallax_layer.calibrate_parallax(i+1)
parallax_icon[index] = parallax_layer
index++
for(var/i in 0 to ((GRID_WIDTH**2)-1))
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
var/list/L = list()
for(var/j in 1 to PARALLAX_IMAGE_TILES)
if(plane2[j+i*PARALLAX_IMAGE_TILES] <= PARALLAX3_ICON_NUMBER)
var/image/I = image('icons/turf/space_parallax3.dmi',"[plane2[j+i*PARALLAX_IMAGE_TILES]]")
I.pixel_x = pixel_x[j]
I.pixel_y = pixel_y[j]
L += I
parallax_layer.overlays = L
parallax_layer.parallax_speed = 0.5
parallax_layer.calibrate_parallax(i+1)
parallax_icon[index] = parallax_layer
index++
for(var/i in 0 to ((GRID_WIDTH**2)-1))
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
var/list/L = list()
for(var/j in 1 to PARALLAX_IMAGE_TILES)
if(plane3[j+i*PARALLAX_IMAGE_TILES] <= PARALLAX2_ICON_NUMBER)
var/image/I = image('icons/turf/space_parallax2.dmi',"[plane3[j+i*PARALLAX_IMAGE_TILES]]")
I.pixel_x = pixel_x[j]
I.pixel_y = pixel_y[j]
L += I
parallax_layer.overlays = L
parallax_layer.parallax_speed = 1
parallax_layer.calibrate_parallax(i+1)
parallax_icon[index] = parallax_layer
index++
parallax_initialized = 1
/obj/screen/parallax/proc/calibrate_parallax(var/i)
if(!i)
return
/* Placement of screen objects
1 2 3
4 5 6
7 8 9
*/
base_offset_x = -PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE/2
base_offset_y = -PARALLAX_IMAGE_WIDTH*WORLD_ICON_SIZE/2
//TODO: switch to grid size defines... somehow
switch(i)
if(1,4,7) //1 mod grid_size
base_offset_x -= WORLD_ICON_SIZE*PARALLAX_IMAGE_WIDTH
if(3,6,9) //0 mod grid_size
base_offset_x += WORLD_ICON_SIZE*PARALLAX_IMAGE_WIDTH
switch(i)
if(1,2,3) //round(i/grid_size) = 0
base_offset_y += WORLD_ICON_SIZE*PARALLAX_IMAGE_WIDTH
if(7,8,9) //round(i/grid_size) = 2
base_offset_y -= WORLD_ICON_SIZE*PARALLAX_IMAGE_WIDTH
screen_loc = "CENTER:[base_offset_x],CENTER:[base_offset_y]"
#undef PARALLAX4_ICON_NUMBER
#undef PARALLAX3_ICON_NUMBER
#undef PARALLAX2_ICON_NUMBER
#undef PARALLAX_IMAGE_WIDTH
#undef PARALLAX_IMAGE_TILES
+1
View File
@@ -15,6 +15,7 @@
/obj/screen/Destroy()
master = null
screen_loc = null
return ..()
/obj/screen/text
+3 -2
View File
@@ -23,7 +23,7 @@
spell_holder.client.screen -= src
spell_holder = null
/obj/screen/movable/spell_master/ResetVars(var/list/exclude = list())
/obj/screen/movable/spell_master/resetVariables(var/list/exclude = list())
exclude += "spell_objects"
..(exclude)
@@ -93,7 +93,7 @@
if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one
return
var/obj/screen/spell/newscreen = PoolOrNew(/obj/screen/spell)
var/obj/screen/spell/newscreen = getFromPool(/obj/screen/spell)
newscreen.spellmaster = src
newscreen.spell = spell
@@ -125,6 +125,7 @@
/obj/screen/movable/spell_master/proc/silence_spells(var/amount)
for(var/obj/screen/spell/spell in spell_objects)
spell.spell.silenced = amount
spell.spell.process()
spell.update_charge(1)
/obj/screen/movable/spell_master/proc/update_spells(forced = 0, mob/user)
-376
View File
@@ -1,376 +0,0 @@
/atom/DblClick(location, control, params) //TODO: DEFERRED: REWRITE
if(!usr) return
// ------- TIME SINCE LAST CLICK -------
if (world.time <= usr:lastDblClick+1)
return
else
usr:lastDblClick = world.time
//Putting it here for now. It diverts stuff to the mech clicking procs. Putting it here stops us drilling items in our inventory Carn
if(istype(usr.loc,/obj/mecha))
if(usr.client && (src in usr.client.screen))
return
var/obj/mecha/Mech = usr.loc
Mech.click_action(src,usr)
return
// ------- DIR CHANGING WHEN CLICKING ------
if( iscarbon(usr) && !usr.buckled )
if( src.x && src.y && usr.x && usr.y )
var/dx = src.x - usr.x
var/dy = src.y - usr.y
if(dy || dx)
if(abs(dx) < abs(dy))
if(dy > 0) usr.set_dir(NORTH)
else usr.set_dir(SOUTH)
else
if(dx > 0) usr.set_dir(EAST)
else usr.set_dir(WEST)
else
if(pixel_y > 16) usr.set_dir(NORTH)
else if(pixel_y < -16) usr.set_dir(SOUTH)
else if(pixel_x > 16) usr.set_dir(EAST)
else if(pixel_x < -16) usr.set_dir(WEST)
// ------- AI -------
else if (istype(usr, /mob/living/silicon/ai))
var/mob/living/silicon/ai/ai = usr
if (ai.control_disabled)
return
// ------- CYBORG -------
else if (istype(usr, /mob/living/silicon/robot))
var/mob/living/silicon/robot/bot = usr
if (bot.lockcharge) return
..()
// ------- SHIFT-CLICK -------
if(params)
var/parameters = params2list(params)
if(parameters["shift"]){
if(!isAI(usr))
ShiftClick(usr)
else
AIShiftClick(usr)
return
}
// ------- ALT-CLICK -------
if(parameters["alt"]){
if(!isAI(usr))
AltClick(usr)
else
AIAltClick(usr)
return
}
// ------- CTRL-CLICK -------
if(parameters["ctrl"]){
if(!isAI(usr))
CtrlClick(usr)
else
AICtrlClick(usr)
return
}
// ------- MIDDLE-CLICK -------
if(parameters["middle"]){
if(!isAI(usr))
MiddleClick(usr)
return
}
// ------- THROW -------
if(usr.in_throw_mode)
return usr:throw_item(src)
// ------- ITEM IN HAND DEFINED -------
var/obj/item/W = usr.get_active_hand()
/* Now handled by get_active_hand()
// ------- ROBOT -------
if(istype(usr, /mob/living/silicon/robot))
if(!isnull(usr:module_active))
W = usr:module_active
else
W = null
*/
// ------- ATTACK SELF -------
if (W == src && usr.stat == 0)
W.attack_self(usr)
if(usr.hand)
usr.update_inv_l_hand(0) //update in-hand overlays
else
usr.update_inv_r_hand(0)
return
// ------- PARALYSIS, STUN, WEAKENED, DEAD, (And not AI) -------
if (((usr.paralysis || usr.stunned || usr.weakened) && !istype(usr, /mob/living/silicon/ai)) || usr.stat != 0)
return
// ------- CLICKING STUFF IN CONTAINERS -------
if ((!( src in usr.contents ) && (((!( isturf(src) ) && (!( isturf(src.loc) ) && (src.loc && !( isturf(src.loc.loc) )))) || !( isturf(usr.loc) )) && (src.loc != usr.loc && (!( istype(src, /obj/screen) ) && !( usr.contents.Find(src.loc) ))))))
if (istype(usr, /mob/living/silicon/ai))
var/mob/living/silicon/ai/ai = usr
if (ai.control_disabled)
return
else
return
// ------- 1 TILE AWAY -------
var/t5
// ------- AI CAN CLICK ANYTHING -------
if(istype(usr, /mob/living/silicon/ai))
t5 = 1
// ------- CYBORG CAN CLICK ANYTHING WHEN NOT HOLDING STUFF -------
else if(istype(usr, /mob/living/silicon/robot) && !W)
t5 = 1
else
t5 = in_range(src, usr) || src.loc == usr
// world << "according to dblclick(), t5 is [t5]"
// ------- ACTUALLY DETERMINING STUFF -------
if (((t5 || (W && (W.flags & USEDELAY))) && !( istype(src, /obj/screen) )))
// ------- ( CAN USE ITEM OR HAS 1 SECOND USE DELAY ) AND NOT CLICKING ON SCREEN -------
if (usr.next_move < world.time)
usr.prev_move = usr.next_move
usr.next_move = world.time + 10
else
// ------- ALREADY USED ONE ITEM WITH USE DELAY IN THE PREVIOUS SECOND -------
return
// ------- DELAY CHECK PASSED -------
if ((src.loc && (get_dist(src, usr) < 2 || src.loc == usr.loc)))
// ------- CLICKED OBJECT EXISTS IN GAME WORLD, DISTANCE FROM PERSON TO OBJECT IS 1 SQUARE OR THEY'RE ON THE SAME SQUARE -------
var/direct = get_dir(usr, src)
var/obj/item/weapon/dummy/D = new /obj/item/weapon/dummy( usr.loc )
var/ok = 0
if ( (direct - 1) & direct)
// ------- CLICKED OBJECT IS LOCATED IN A DIAGONAL POSITION FROM THE PERSON -------
var/turf/Step_1
var/turf/Step_2
switch(direct)
if(5.0)
Step_1 = get_step(usr, NORTH)
Step_2 = get_step(usr, EAST)
if(6.0)
Step_1 = get_step(usr, SOUTH)
Step_2 = get_step(usr, EAST)
if(9.0)
Step_1 = get_step(usr, NORTH)
Step_2 = get_step(usr, WEST)
if(10.0)
Step_1 = get_step(usr, SOUTH)
Step_2 = get_step(usr, WEST)
else
if(Step_1 && Step_2)
// ------- BOTH CARDINAL DIRECTIONS OF THE DIAGONAL EXIST IN THE GAME WORLD -------
var/check_1 = 0
var/check_2 = 0
if(step_to(D, Step_1))
check_1 = 1
for(var/obj/border_obstacle in Step_1)
if(border_obstacle.flags & ON_BORDER)
if(!border_obstacle.CheckExit(D, src))
check_1 = 0
// ------- YOU TRIED TO CLICK ON AN ITEM THROUGH A WINDOW (OR SIMILAR THING THAT LIMITS ON BORDERS) ON ONE OF THE DIRECITON TILES -------
for(var/obj/border_obstacle in get_turf(src))
if((border_obstacle.flags & ON_BORDER) && (src != border_obstacle))
if(!border_obstacle.CanPass(D, D.loc, 1, 0))
// ------- YOU TRIED TO CLICK ON AN ITEM THROUGH A WINDOW (OR SIMILAR THING THAT LIMITS ON BORDERS) ON THE TILE YOU'RE ON -------
check_1 = 0
D.loc = usr.loc
if(step_to(D, Step_2))
check_2 = 1
for(var/obj/border_obstacle in Step_2)
if(border_obstacle.flags & ON_BORDER)
if(!border_obstacle.CheckExit(D, src))
check_2 = 0
for(var/obj/border_obstacle in get_turf(src))
if((border_obstacle.flags & ON_BORDER) && (src != border_obstacle))
if(!border_obstacle.CanPass(D, D.loc, 1, 0))
check_2 = 0
if(check_1 || check_2)
ok = 1
// ------- YOU CAN REACH THE ITEM THROUGH AT LEAST ONE OF THE TWO DIRECTIONS. GOOD. -------
/*
More info:
If you're trying to click an item in the north-east of your mob, the above section of code will first check if tehre's a tile to the north or you and to the east of you
These two tiles are Step_1 and Step_2. After this, a new dummy object is created on your location. It then tries to move to Step_1, If it succeeds, objects on the turf you're on and
the turf that Step_1 is are checked for items which have the ON_BORDER flag set. These are itmes which limit you on only one tile border. Windows, for the most part.
CheckExit() and CanPass() are use to determine this. The dummy object is then moved back to your location and it tries to move to Step_2. Same checks are performed here.
If at least one of the two checks succeeds, it means you can reach the item and ok is set to 1.
*/
else
// ------- OBJECT IS ON A CARDINAL TILE (NORTH, SOUTH, EAST OR WEST OR THE TILE YOU'RE ON) -------
if(loc == usr.loc)
ok = 1
// ------- OBJECT IS ON THE SAME TILE AS YOU -------
else
ok = 1
//Now, check objects to block exit that are on the border
for(var/obj/border_obstacle in usr.loc)
if(border_obstacle.flags & ON_BORDER)
if(!border_obstacle.CheckExit(D, src))
ok = 0
//Next, check objects to block entry that are on the border
for(var/obj/border_obstacle in get_turf(src))
if((border_obstacle.flags & ON_BORDER) && (src != border_obstacle))
if(!border_obstacle.CanPass(D, D.loc, 1, 0))
ok = 0
/*
See the previous More info, for... more info...
*/
//qdel(D)
// Garbage Collect Dummy
D.loc = null
D = null
// ------- DUMMY OBJECT'S SERVED IT'S PURPOSE, IT'S REWARDED WITH A SWIFT DELETE -------
if (!( ok ))
// ------- TESTS ABOVE DETERMINED YOU CANNOT REACH THE TILE -------
return 0
if (!( usr.restrained() || (usr.lying && usr.buckled!=src) ))
// ------- YOU ARE NOT REASTRAINED -------
if (W)
// ------- YOU HAVE AN ITEM IN YOUR HAND - HANDLE ATTACKBY AND AFTERATTACK -------
var/ignoreAA = 0 //Ignore afterattack(). Surgery uses this.
if (t5)
ignoreAA = src.attackby(W, usr)
if (W && !ignoreAA)
W.afterattack(src, usr, (t5 ? 1 : 0), params)
else
// ------- YOU DO NOT HAVE AN ITEM IN YOUR HAND -------
if (istype(usr, /mob/living/carbon/human))
// ------- YOU ARE HUMAN -------
src.attack_hand(usr, usr.hand)
else
// ------- YOU ARE NOT HUMAN. WHAT ARE YOU - DETERMINED HERE AND PROPER ATTACK_MOBTYPE CALLED -------
if (istype(usr, /mob/living/carbon/monkey))
src.attack_paw(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/alien/humanoid))
if(usr.m_intent == "walk" && istype(usr, /mob/living/carbon/alien/humanoid/hunter))
usr.m_intent = "run"
usr.hud_used.move_intent.icon_state = "running"
usr.update_icons()
src.attack_alien(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/alien/larva))
src.attack_larva(usr)
else if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot))
src.attack_ai(usr, usr.hand)
else if(istype(usr, /mob/living/carbon/slime))
src.attack_slime(usr)
else if(istype(usr, /mob/living/simple_animal))
src.attack_animal(usr)
else
// ------- YOU ARE RESTRAINED. DETERMINE WHAT YOU ARE AND ATTACK WITH THE PROPER HAND_X PROC -------
if (istype(usr, /mob/living/carbon/human))
src.hand_h(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/monkey))
src.hand_p(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/alien/humanoid))
src.hand_al(usr, usr.hand)
else if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot))
src.hand_a(usr, usr.hand)
else
// ------- ITEM INACESSIBLE OR CLICKING ON SCREEN -------
if (istype(src, /obj/screen))
// ------- IT'S THE HUD YOU'RE CLICKING ON -------
usr.prev_move = usr.next_move
usr:lastDblClick = world.time + 2
if (usr.next_move < world.time)
usr.next_move = world.time + 2
else
return
// ------- 2 DECISECOND DELAY FOR CLICKING PASSED -------
if (!( usr.restrained() ))
// ------- YOU ARE NOT RESTRAINED -------
if ((W && !( istype(src, /obj/screen) )))
// ------- IT SHOULD NEVER GET TO HERE, DUE TO THE ISTYPE(SRC, /OBJ/SCREEN) FROM PREVIOUS IF-S - I TESTED IT WITH A DEBUG OUTPUT AND I COULDN'T GET THIST TO SHOW UP. -------
src.attackby(W, usr)
if (W)
W.afterattack(src, usr,, params)
else
// ------- YOU ARE NOT RESTRAINED, AND ARE CLICKING A HUD OBJECT -------
if (istype(usr, /mob/living/carbon/human))
src.attack_hand(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/monkey))
src.attack_paw(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/alien/humanoid))
src.attack_alien(usr, usr.hand)
else
// ------- YOU ARE RESTRAINED CLICKING ON A HUD OBJECT -------
if (istype(usr, /mob/living/carbon/human))
src.hand_h(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/monkey))
src.hand_p(usr, usr.hand)
else if (istype(usr, /mob/living/carbon/alien/humanoid))
src.hand_al(usr, usr.hand)
else
// ------- YOU ARE CLICKING ON AN OBJECT THAT'S INACCESSIBLE TO YOU AND IS NOT YOUR HUD -------
if((LASER in usr:mutations) && usr:a_intent == I_HURT && world.time >= usr.next_move)
// ------- YOU HAVE THE LASER MUTATION, YOUR INTENT SET TO HURT AND IT'S BEEN MORE THAN A DECISECOND SINCE YOU LAS TATTACKED -------
var/turf/T = get_turf(usr)
var/turf/U = get_turf(src)
if(istype(usr, /mob/living/carbon/human))
usr:nutrition -= rand(1,5)
usr:handle_regular_hud_updates()
var/obj/item/projectile/beam/A = new /obj/item/projectile/beam( usr.loc )
A.icon = 'icons/effects/genetics.dmi'
A.icon_state = "eyelasers"
playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1)
A.firer = usr
A.def_zone = usr:get_organ_target()
A.original = src
A.current = T
A.yo = U.y - T.y
A.xo = U.x - T.x
spawn( 1 )
A.process()
usr.next_move = world.time + 6
return
+1 -3
View File
@@ -110,9 +110,7 @@
M.Stun(power)
M.stuttering = max(M.stuttering, power)
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, M)
s.start()
spark(M, 5, alldirs)
if(prob(stunprob) && powerlevel >= 8)
M.adjustFireLoss(powerlevel * rand(6,10))
+10 -6
View File
@@ -74,13 +74,19 @@ var/const/tk_maxrange = 15
var/atom/movable/focus = null
var/mob/living/host = null
/obj/item/tk_grab/Destroy()
if (host)
host.remove_from_mob(src)
host = null
focus = null
return ..()
/obj/item/tk_grab/dropped(mob/user as mob)
if(focus && user && loc != user && loc != user.loc) // drop_item() gets called when you tk-attack a table/closet with an item
if(focus.Adjacent(loc))
focus.loc = loc
loc = null
spawn(1)
qdel(src)
QDEL_IN(src, 1)
return
//stops TK grabs being equipped anywhere but into hands
@@ -157,7 +163,7 @@ var/const/tk_maxrange = 15
/obj/item/tk_grab/proc/apply_focus_overlay()
if(!focus) return
var/obj/effect/overlay/O = PoolOrNew(/obj/effect/overlay, locate(focus.x,focus.y,focus.z))
var/obj/effect/overlay/O = new(locate(focus.x,focus.y,focus.z))
O.name = "sparkles"
O.anchored = 1
O.density = 0
@@ -166,9 +172,7 @@ var/const/tk_maxrange = 15
O.icon = 'icons/effects/effects.dmi'
O.icon_state = "nothing"
flick("empdisable",O)
spawn(5)
qdel(O)
return
QDEL_IN(O, 5)
/obj/item/tk_grab/update_icon()
overlays.Cut()
+1 -1
View File
@@ -22,7 +22,7 @@ var/datum/controller/process/alarm/alarm_manager
for(last_object in all_handlers)
var/datum/alarm_handler/AH = last_object
AH.process()
SCHECK
F_SCHECK
/datum/controller/process/alarm/proc/active_alarms()
var/list/all_alarms = new
+1 -1
View File
@@ -22,7 +22,7 @@ var/datum/controller/process/chemistry/chemistryProcess
var/datum/reagents/holder = last_object
if(!holder.process_reactions())
active_holders -= holder
SCHECK
F_SCHECK
/datum/controller/process/chemistry/proc/mark_for_update(var/datum/reagents/holder)
if(holder in active_holders)
+1 -1
View File
@@ -9,7 +9,7 @@
for(last_object in active_diseases)
var/datum/disease/D = last_object
D.process()
SCHECK
F_SCHECK
/datum/controller/process/disease/statProcess()
..()
+92
View File
@@ -0,0 +1,92 @@
#define STAGE_IDLE 0
#define STAGE_EFFECT 1
#define STAGE_SUBEFFECT 2
var/datum/controller/process/effects/effect_master
/var/list/datum/effect_system/effects_objects = list() // The effect-spawning objects. Shouldn't be many of these.
/var/list/obj/visual_effect/effects_visuals = list() // The visible component of an effect. May be created without an effect object.
/datum/controller/process/effects
var/tmp/list/processing_effects = list()
var/tmp/list/processing_visuals = list()
var/tmp/stage = STAGE_IDLE
/datum/controller/process/effects/setup()
effect_master = src
name = "effects"
schedule_interval = 2
tick_allowance = 15
/datum/controller/process/effects/doWork()
if (stage == STAGE_IDLE)
// Start a new work cycle.
processing_effects = effects_objects
effects_objects = list()
stage = STAGE_EFFECT
while (processing_effects.len)
var/datum/effect_system/E = processing_effects[processing_effects.len]
processing_effects.len--
if (!E || E.gcDestroyed)
continue
switch (E.process())
if (EFFECT_CONTINUE)
effects_objects += E
if (EFFECT_DESTROY)
returnToPool(E)
F_SCHECK
if (stage == STAGE_EFFECT)
processing_visuals = effects_visuals
effects_visuals = list()
stage = STAGE_SUBEFFECT
while (processing_visuals.len)
var/obj/visual_effect/V = processing_visuals[processing_visuals.len]
processing_visuals.len--
if (!V || V.gcDestroyed)
effects_visuals -= V
continue
switch (V.tick())
if (EFFECT_CONTINUE)
effects_visuals += V
if (EFFECT_DESTROY)
effects_visuals -= V
V.end()
returnToPool(V)
F_SCHECK
stage = STAGE_IDLE
// We're done.
if (!processing_effects.len && !processing_visuals.len && !effects_objects.len && !effects_visuals.len)
disable()
/datum/controller/process/effects/proc/queue(var/datum/effect_system/E)
if (!E || E.gcDestroyed)
return
effects_objects += E
enable()
/datum/controller/process/effects/proc/queue_simple(var/obj/visual_effect/V)
if (!V || V.gcDestroyed)
return
effects_visuals += V
enable()
/datum/controller/process/effects/statProcess()
..()
stat(null, "Effect process is [disabled ? "sleeping" : "processing"].")
stat(null, "[effects_objects.len] effects queued, [processing_effects.len] processing")
stat(null, "[effects_visuals.len] visuals queued, [processing_visuals.len] processing")
+2 -2
View File
@@ -6,10 +6,10 @@
for(last_object in event_manager.active_events)
var/datum/event/E = last_object
E.process()
SCHECK
F_SCHECK
for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
last_object = event_manager.event_containers[i]
var/list/datum/event_container/EC = last_object
EC.process()
SCHECK
F_SCHECK
+7 -7
View File
@@ -46,7 +46,7 @@ var/datum/controller/process/explosives/bomb_processor
else
explosion(data)
SCHECK
F_SCHECK
work_queue -= data
lighting_process.enable()
@@ -110,7 +110,7 @@ var/datum/controller/process/explosives/bomb_processor
if (vibration)
for(var/mob/M in player_list)
SCHECK
F_SCHECK
// Double check for client
var/reception = 2//Whether the person can be shaken or hear sound
//2 = BOTH
@@ -176,12 +176,12 @@ var/datum/controller/process/explosives/bomb_processor
else continue
T.ex_act(dist)
SCHECK
F_SCHECK
if(T)
for(var/atom_movable in T.contents) //bypass type checking since only atom/movable can be contained by turfs anyway
var/atom/movable/AM = atom_movable
if(AM && AM.simulated) AM.ex_act(dist)
SCHECK
F_SCHECK
var/took = (world.timeofday-start)/10
//You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare
@@ -214,7 +214,7 @@ var/datum/controller/process/explosives/bomb_processor
for(var/direction in cardinal)
var/turf/T = get_step(epicenter, direction)
explosion_spread(T, power - epicenter.explosion_resistance, direction)
SCHECK
F_SCHECK
//This step applies the ex_act effects for the explosion, as planned in the previous step.
for(var/turf/T in explosion_turfs)
@@ -233,13 +233,13 @@ var/datum/controller/process/explosives/bomb_processor
T = locate(x,y,z)
for(var/atom/A in T)
A.ex_act(severity)
SCHECK
F_SCHECK
explosion_in_progress = 0
// A proc used by recursive explosions. (The actually recursive bit.)
/datum/controller/process/explosives/proc/explosion_spread(turf/s, power, direction)
SCHECK
F_SCHECK
if (istype(s, /turf/unsimulated))
return
if(power <= 0)
+4 -9
View File
@@ -80,7 +80,7 @@ world/loop_checks = 0
tick_dels++
total_dels++
destroyed.Cut(1, 2)
SCHECK
F_SCHECK
#undef GC_FORCE_DEL_PER_TICK
#undef GC_COLLECTION_TIMEOUT
@@ -127,11 +127,6 @@ world/loop_checks = 0
stat(null, "[garbage_collect ? "On" : "Off"], [destroyed.len] queued")
stat(null, "Dels: [total_dels], [soft_dels] soft, [hard_dels] hard, [tick_dels] last run")
// Tests if an atom has been deleted.
/proc/deleted(atom/A)
return !A || !isnull(A.gcDestroyed)
// Should be treated as a replacement for the 'del' keyword.
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
/proc/qdel(var/datum/A)
@@ -151,13 +146,13 @@ world/loop_checks = 0
/datum/proc/finalize_qdel()
if(IsPooled(src))
PlaceInPool(src)
returnToPool(src)
else
del(src)
/atom/finalize_qdel()
if(IsPooled(src))
PlaceInPool(src)
if (IsPooled(src))
returnToPool(src)
else
if(garbage_collector)
garbage_collector.AddTrash(src)
+1 -1
View File
@@ -11,4 +11,4 @@
log_access("AFK: [key_name(C)]")
C << "<SPAN CLASS='warning'>You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.</SPAN>"
del(C) // Don't qdel, cannot override finalize_qdel behaviour for clients.
SCHECK
F_SCHECK
-28
View File
@@ -1,28 +0,0 @@
/var/global/datum/controller/process/law/corp_regs
/datum/controller/process/law
var/list/laws = list() // All laws
var/list/low_severity = list()
var/list/med_severity = list()
var/list/high_severity = list()
/datum/controller/process/law/New()
corp_regs = src
..()
/datum/controller/process/law/setup()
name = "Law"
schedule_interval = 100
for( var/L in subtypesof( /datum/law/low_severity ))
low_severity += new L
for( var/L in subtypesof( /datum/law/med_severity ))
med_severity += new L
for( var/L in subtypesof( /datum/law/high_severity ))
high_severity += new L
laws = low_severity + med_severity + high_severity
+75
View File
@@ -0,0 +1,75 @@
#define STAGE_NONE 0
#define STAGE_SOURCE 1
#define STAGE_CORNER 2
#define STAGE_OVERLAY 3
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
// Probably slow.
/var/lighting_profiling = FALSE
/var/datum/controller/process/lighting/lighting_process
/datum/controller/process/lighting
schedule_interval = LIGHTING_INTERVAL
/datum/controller/process/lighting/setup()
name = "lighting"
lighting_process = src
/datum/controller/process/lighting/statProcess()
..()
stat(null, "Server tick usage is [world.tick_usage].")
stat(null, "[all_lighting_overlays.len] overlays ([all_lighting_corners.len] corners)")
stat(null, "Lights: [lighting_update_lights.len] queued")
stat(null, "Corners: [lighting_update_corners.len] queued")
stat(null, "Overlays: [lighting_update_overlays.len] queued")
/datum/controller/process/lighting/doWork()
var/list/curr_lights = lighting_update_lights
var/list/curr_corners = lighting_update_corners
var/list/curr_overlays = lighting_update_overlays
while (curr_lights.len)
var/datum/light_source/L = curr_lights[curr_lights.len]
curr_lights.len--
if(L.destroyed || L.check() || L.force_update)
L.remove_lum()
if(!L.destroyed)
L.apply_lum()
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
L.vis_update = FALSE
L.force_update = FALSE
L.needs_update = FALSE
F_SCHECK
while (curr_corners.len)
var/datum/lighting_corner/C = curr_corners[curr_corners.len]
curr_corners.len--
C.update_overlays()
C.needs_update = FALSE
F_SCHECK
while (curr_overlays.len)
var/atom/movable/lighting_overlay/O = curr_overlays[curr_overlays.len]
curr_overlays.len--
O.update_overlay()
O.needs_update = FALSE
F_SCHECK
#undef STAGE_NONE
#undef STAGE_SOURCE
#undef STAGE_CORNER
#undef STAGE_OVERLAY
+135 -53
View File
@@ -1,67 +1,149 @@
/var/global/machinery_sort_required = 0
/var/global/machinery_sort_required = 0
var/global/list/power_using_machines = list()
var/global/list/ticking_machines = list()
#define STAGE_NONE 0
#define STAGE_MACHINERY_PROCESS 1
#define STAGE_MACHINERY_POWER 2
#define STAGE_POWERNET 3
#define STAGE_POWERSINK 4
#define STAGE_PIPENET 5
/proc/add_machine(var/obj/machinery/M)
if (QDELETED(M))
return
var/type = M.get_process_type()
if (type)
machines += M
if (type & M_PROCESSES)
ticking_machines += M
if (type & M_USES_POWER)
power_using_machines += M
/proc/remove_machine(var/obj/machinery/M)
machines -= M
power_using_machines -= M
ticking_machines -= M
/datum/controller/process/machinery
var/tmp/list/processing_machinery = list()
var/tmp/list/processing_power_users = list()
var/tmp/list/processing_powernets = list()
var/tmp/list/processing_powersinks = list()
var/tmp/list/processing_pipenets = list()
var/stage = STAGE_NONE
/datum/controller/process/machinery/setup()
name = "machinery"
schedule_interval = 20 // every 2 seconds
schedule_interval = 2 SECONDS
start_delay = 12
/datum/controller/process/machinery/doWork()
internal_sort()
internal_process_pipenets()
internal_process_machinery()
internal_process_power()
internal_process_power_drain()
// If we're starting a new tick, setup.
if (stage == STAGE_NONE)
processing_machinery = ticking_machines.Copy()
stage = STAGE_MACHINERY_PROCESS
// Process machinery.
while (processing_machinery.len)
var/obj/machinery/M = processing_machinery[processing_machinery.len]
processing_machinery.len--
if (QDELETED(M))
remove_machine(M)
continue
switch (M.process())
if (PROCESS_KILL)
remove_machine(M)
if (M_NO_PROCESS)
ticking_machines -= M
F_SCHECK
if (stage == STAGE_MACHINERY_PROCESS)
processing_power_users = power_using_machines.Copy()
stage = STAGE_MACHINERY_POWER
while (processing_power_users.len)
var/obj/machinery/M = processing_power_users[processing_power_users.len]
processing_power_users.len--
if (QDELETED(M))
remove_machine(M)
continue
if (M.use_power)
M.auto_use_power()
F_SCHECK
if (stage == STAGE_MACHINERY_POWER)
processing_powernets = powernets.Copy()
stage = STAGE_POWERNET
while (processing_powernets.len)
var/datum/powernet/PN = processing_powernets[processing_powernets.len]
processing_powernets.len--
if (QDELETED(PN))
powernets -= PN
continue
PN.reset()
F_SCHECK
if (stage == STAGE_POWERNET)
processing_powersinks = processing_power_items.Copy()
stage = STAGE_POWERSINK
while (processing_powersinks.len)
var/obj/item/I = processing_powersinks[processing_powersinks.len]
processing_powersinks.len--
if (QDELETED(I) || !I.pwr_drain())
processing_power_items -= I
F_SCHECK
if (stage == STAGE_POWERSINK)
processing_pipenets = pipe_networks.Copy()
stage = STAGE_PIPENET
while (processing_pipenets.len)
var/datum/pipe_network/PN = processing_pipenets[processing_pipenets.len]
processing_pipenets.len--
if (QDELETED(PN))
pipe_networks -= PN
continue
PN.process()
F_SCHECK
stage = STAGE_NONE
/datum/controller/process/machinery/proc/internal_sort()
if(machinery_sort_required)
machinery_sort_required = 0
machines = dd_sortedObjectList(machines)
/datum/controller/process/machinery/proc/internal_process_machinery()
for(last_object in machines)
var/obj/machinery/M = last_object
if(M && !M.gcDestroyed)
if(M.process() == PROCESS_KILL)
//M.inMachineList = 0 We don't use this debugging function
machines.Remove(M)
continue
if(M && M.use_power)
M.auto_use_power()
SCHECK
/datum/controller/process/machinery/proc/internal_process_power()
for(last_object in powernets)
var/datum/powernet/powerNetwork = last_object
if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed))
powerNetwork.reset()
SCHECK
continue
powernets.Remove(powerNetwork)
/datum/controller/process/machinery/proc/internal_process_power_drain()
// Currently only used by powersinks. These items get priority processed before machinery
for(last_object in processing_power_items)
var/obj/item/I = last_object
if(!I.pwr_drain()) // 0 = Process Kill, remove from processing list.
processing_power_items.Remove(I)
SCHECK
/datum/controller/process/machinery/proc/internal_process_pipenets()
for(last_object in pipe_networks)
var/datum/pipe_network/pipeNetwork = last_object
if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed))
pipeNetwork.process()
SCHECK
continue
pipe_networks.Remove(pipeNetwork)
/datum/controller/process/machinery/statProcess()
..()
stat(null, "[machines.len] machines")
stat(null, "[powernets.len] powernets")
stat(null, "[pipe_networks.len] pipenets")
stat(null, "[processing_power_items.len] power item\s")
stat(null, "[machines.len] total machines")
stat(null, "[ticking_machines.len] ticking machines, [processing_machinery.len] queued")
stat(null, "[power_using_machines.len] power-using machines, [processing_power_users.len] queued")
stat(null, "[powernets.len] powernets, [processing_powernets.len] queued")
stat(null, "[processing_power_items.len] power items, [processing_powersinks.len] queued")
stat(null, "[pipe_networks.len] pipenets, [processing_pipenets.len] queued")
#undef STAGE_NONE
#undef STAGE_MACHINERY_PROCESS
#undef STAGE_MACHINERY_POWER
#undef STAGE_POWERNET
#undef STAGE_POWERSINK
#undef STAGE_PIPENET
+20 -12
View File
@@ -1,9 +1,11 @@
/datum/controller/process/mob
var/tmp/datum/updateQueue/updateQueueInstance
var/tmp/list/queue = list()
var/normal_exit = TRUE
/datum/controller/process/mob/setup()
name = "mob"
schedule_interval = 20 // every 2 seconds
schedule_interval = 2 SECONDS // every 2 seconds
start_delay = 16
/datum/controller/process/mob/started()
@@ -12,18 +14,24 @@
mob_list = list()
/datum/controller/process/mob/doWork()
for(last_object in mob_list)
var/mob/M = last_object
if(M && isnull(M.gcDestroyed))
try
M.Life()
catch(var/exception/e)
catchException(e, M)
SCHECK
else
catchBadType(M)
if (normal_exit)
queue = mob_list.Copy()
normal_exit = FALSE
while (queue.len)
var/mob/M = queue[queue.len]
queue.len--
if (!M || M.gcDestroyed)
mob_list -= M
continue
M.Life()
F_SCHECK
normal_exit = TRUE
/datum/controller/process/mob/statProcess()
..()
stat(null, "[mob_list.len] mobs")
stat(null, "[mob_list.len] mobs, [queue.len] queued")
+26 -2
View File
@@ -1,12 +1,35 @@
/datum/controller/process/nanoui
var/is_idle = TRUE
var/list/queued_uis = list()
/datum/controller/process/nanoui/setup()
name = "nanoui"
schedule_interval = 20 // every 2 seconds
schedule_interval = 2 SECONDS
/datum/controller/process/nanoui/statProcess()
..()
stat(null, "[nanomanager.processing_uis.len] UIs")
stat(null, "[nanomanager.processing_uis.len] UIs, [queued_uis.len] processing")
/datum/controller/process/nanoui/doWork()
if (is_idle)
queued_uis = nanomanager.processing_uis.Copy()
is_idle = FALSE
while (queued_uis.len)
var/datum/nanoui/UI = queued_uis[queued_uis.len]
queued_uis.len--
if (!UI || UI.gcDestroyed)
catchBadType(UI)
nanomanager.processing_uis -= UI
continue
UI.process()
F_SCHECK
is_idle = TRUE
/*datum/controller/process/nanoui/doWork()
for(last_object in nanomanager.processing_uis)
var/datum/nanoui/NUI = last_object
if(istype(NUI) && isnull(NUI.gcDestroyed))
@@ -17,3 +40,4 @@
else
catchBadType(NUI)
nanomanager.processing_uis -= NUI
*/
+18 -26
View File
@@ -10,7 +10,7 @@ var/datum/controller/process/night_lighting/nl_ctrl
/datum/controller/process/night_lighting/setup()
name = "night lighting controller"
schedule_interval = 3600 // Every 5 minutes.
schedule_interval = 5 MINUTES
nl_ctrl = src
@@ -18,34 +18,26 @@ var/datum/controller/process/night_lighting/nl_ctrl
// Stop trying to delete processes. Not how it goes.
disabled = 1
/datum/controller/process/night_lighting/preStart()
switch (worldtime2ticks())
if (0 to config.nl_finish)
deactivate()
if (config.nl_start to TICKS_IN_DAY)
activate()
var/time = worldtime2hours()
if (time <= config.nl_finish || time >= config.nl_start)
activate()
else
deactivate()
/datum/controller/process/night_lighting/doWork()
if (manual_override) // don't automatically change lighting if it was manually changed in-game
return
switch (worldtime2ticks())
if (0 to config.nl_finish)
if (isactive)
command_announcement.Announce("Good morning. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now return the public hallway lighting levels to normal.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
deactivate()
if (config.nl_start to TICKS_IN_DAY)
if (!isactive)
command_announcement.Announce("Good evening. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now dim lighting in the public hallways in order to accommodate the circadian rhythm of some species.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
activate()
else
if (isactive)
deactivate()
var/time = worldtime2hours()
if (time <= config.nl_finish || time >= config.nl_start)
if (!isactive)
command_announcement.Announce("Good evening. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now dim lighting in the public hallways in order to accommodate the circadian rhythm of some species.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
activate()
else
if (isactive)
command_announcement.Announce("Good morning. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now return the public hallway lighting levels to normal.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
deactivate()
// 'whitelisted' areas are areas that have nightmode explicitly enabled
@@ -54,14 +46,14 @@ var/datum/controller/process/night_lighting/nl_ctrl
for (var/obj/machinery/power/apc/APC in get_apc_list(whitelisted_only))
APC.toggle_nightlight("on")
SCHECK
F_SCHECK
/datum/controller/process/night_lighting/proc/deactivate(var/whitelisted_only = 1)
isactive = 0
for (var/obj/machinery/power/apc/APC in get_apc_list(whitelisted_only))
APC.toggle_nightlight("off")
SCHECK
F_SCHECK
/datum/controller/process/night_lighting/proc/get_apc_list(var/whitelisted_only = 1)
var/list/obj/machinery/power/apc/lighting_apcs = list()
@@ -73,6 +65,6 @@ var/datum/controller/process/night_lighting/nl_ctrl
if (B.apc && !B.apc.aidisabled)
lighting_apcs += B.apc
SCHECK
F_SCHECK
return lighting_apcs
+21 -12
View File
@@ -1,6 +1,10 @@
/datum/controller/process/obj
var/normal_exit = TRUE
var/list/queue = list()
/datum/controller/process/obj/setup()
name = "obj"
schedule_interval = 20 // every 2 seconds
schedule_interval = 2 SECONDS
start_delay = 8
/datum/controller/process/obj/started()
@@ -9,18 +13,23 @@
processing_objects = list()
/datum/controller/process/obj/doWork()
for(last_object in processing_objects)
var/datum/O = last_object
if(isnull(O.gcDestroyed))
try
O:process()
catch(var/exception/e)
catchException(e, O)
SCHECK
else
catchBadType(O)
if (normal_exit)
queue = processing_objects.Copy()
normal_exit = FALSE
while (queue.len)
var/datum/O = queue[queue.len]
queue.len--
if (!O || O.gcDestroyed)
processing_objects -= O
continue
O:process()
F_SCHECK
normal_exit = TRUE
/datum/controller/process/obj/statProcess()
..()
stat(null, "[processing_objects.len] objects")
stat(null, "[processing_objects.len] objects, [queue.len] queued")
+118 -85
View File
@@ -4,43 +4,141 @@
* Scheduler *
************/
/datum/controller/process/scheduler
var/list/scheduled_tasks
var/list/datum/scheduled_task/tasks = list()
var/datum/scheduled_task/head
/datum/controller/process/scheduler/setup()
name = "scheduler"
schedule_interval = 3 SECONDS
scheduled_tasks = list()
schedule_interval = 2
scheduler = src
/datum/controller/process/scheduler/doWork()
for(last_object in scheduled_tasks)
var/datum/scheduled_task/scheduled_task = last_object
try
if(world.time > scheduled_task.trigger_time)
unschedule(scheduled_task)
scheduled_task.pre_process()
scheduled_task.process()
scheduled_task.post_process()
catch(var/exception/e)
catchException(e, last_object)
SCHECK
while (head && !PSCHED_CHECK_TICK)
if (head.destroyed)
head.kill()
head = head.next
tasks -= head
continue
if (head.trigger_time >= world.time)
return // Nothing after this will be ready to fire.
// This one's ready to fire, process it.
var/datum/scheduled_task/task = head
head = task.next
task.pre_process()
task.process()
task.post_process()
if (task.destroyed) // post_process probably destroyed it.
tasks -= task
task.kill()
/datum/controller/process/scheduler/proc/queue(datum/scheduled_task/task)
if (!task || !task.trigger_time)
warning("scheduler: Invalid task queued! Ignoring.")
return
// Reset this in-case we're doing a rebuild.
task.next = null
if (!head && !tasks.len)
head = task
tasks += task
return
if (!head) // Head's missing but we still have tasks, rebuild.
tasks += task
rebuild_queue()
return
var/datum/scheduled_task/curr = head
while (curr.next && curr.trigger_time < task.trigger_time)
curr = curr.next
if (!curr.next)
// We're at the end of the queue, just append.
curr.next = task
tasks += task
return
// Inserting midway into the list.
var/old_next = curr.next
curr.next = task
task.next = old_next
tasks += task
// Rebuilds the queue linked-list, removing invalid or destroyed entries.
/datum/controller/process/scheduler/proc/rebuild_queue()
log_debug("scheduler: Rebuilding queue.")
var/list/old_tasks = tasks
tasks = list()
if (!old_tasks.len)
log_debug("scheduler: rebuild was called on empty queue! Aborting.")
return
// Find the head.
for (var/thing in old_tasks)
var/datum/scheduled_task/task = thing
if (QDELETED(task))
old_tasks -= task
continue
if (task.destroyed)
old_tasks -= task
task.kill()
continue
if (!head || task.trigger_time < head.trigger_time)
head = task
if (!head)
log_debug("scheduler: unable to find head! Purging task queue.")
for (var/thing in old_tasks)
var/datum/scheduled_task/task = thing
if (QDELETED(task))
continue
task.kill()
head = null
return
// Don't queue the head.
tasks += head
old_tasks -= head
// Now rebuild the queue.
for (var/thing in old_tasks)
var/datum/scheduled_task/task = thing
queue(task)
log_debug("scheduler: Queue diff is [old_tasks.len - tasks.len].")
/datum/controller/process/scheduler/statProcess()
..()
stat(null, "[scheduled_tasks.len] task\s")
stat(null, "[tasks.len] task\s")
/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st)
scheduled_tasks += st
destroyed_event.register(st, src, /datum/controller/process/scheduler/proc/unschedule)
queue(st)
/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st)
if(st in scheduled_tasks)
scheduled_tasks -= st
destroyed_event.unregister(st, src)
st.destroyed = TRUE
/**********
* Helpers *
**********/
/proc/schedule(source, the_proc, time, ...)
if (time < 0)
return
time += world.time
var/list/the_args
if (length(args) > 3)
the_args = args.Copy(4)
else
the_args = list()
if (source)
return schedule_task_with_source(time, the_proc, the_args)
else
return schedule_task(time, the_proc, the_args)
/proc/schedule_task_in(var/in_time, var/procedure, var/list/arguments = list())
return schedule_task(world.time + in_time, procedure, arguments)
@@ -66,68 +164,3 @@
var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval))
scheduler.schedule(st)
return st
/*************
* Task Datum *
*************/
/datum/scheduled_task
var/trigger_time
var/procedure
var/list/arguments
var/task_after_process
var/list/task_after_process_args
/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
..()
src.trigger_time = trigger_time
src.procedure = procedure
src.arguments = arguments ? arguments : list()
src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task
src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list()
task_after_process_args += src
/datum/scheduled_task/Destroy()
procedure = null
arguments.Cut()
task_after_process = null
task_after_process_args.Cut()
return ..()
/datum/scheduled_task/proc/pre_process()
task_triggered_event.raise_event(list(src))
/datum/scheduled_task/proc/process()
if(procedure)
call(procedure)(arglist(arguments))
/datum/scheduled_task/proc/post_process()
call(task_after_process)(arglist(task_after_process_args))
// Resets the trigger time, has no effect if the task has already triggered
/datum/scheduled_task/proc/trigger_task_in(var/trigger_in)
src.trigger_time = world.time + trigger_in
/datum/scheduled_task/source
var/datum/source
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
src.source = source
destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
/datum/scheduled_task/source/Destroy()
source = null
return ..()
/datum/scheduled_task/source/process()
call(source, procedure)(arglist(arguments))
/datum/scheduled_task/source/proc/source_destroyed()
qdel(src)
/proc/destroy_scheduled_task(var/datum/scheduled_task/st)
qdel(st)
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
st.trigger_time = world.time + trigger_delay
scheduler.schedule(st)
+1 -1
View File
@@ -9,7 +9,7 @@ var/global/list/turf/processing_turfs = list()
var/turf/T = last_object
if(T.process() == PROCESS_KILL)
processing_turfs.Remove(T)
SCHECK
F_SCHECK
/datum/controller/process/turf/statProcess()
..()
+1 -1
View File
@@ -69,4 +69,4 @@ var/datum/controller/process/wireless/wirelessProcess
process_conections -= C
if(!target_found)
unsuccesful_connections += C
SCHECK
F_SCHECK
+10 -4
View File
@@ -177,8 +177,8 @@ var/list/gamemode_cache = list()
var/ghost_interaction = 0
var/night_lighting = 0
var/nl_start = 19 * TICKS_IN_HOUR
var/nl_finish = 8 * TICKS_IN_HOUR
var/nl_start = 19
var/nl_finish = 8
var/comms_password = ""
@@ -187,6 +187,7 @@ var/list/gamemode_cache = list()
var/use_discord_bot = 0
var/discord_bot_host = "localhost"
var/discord_bot_port = 0
var/use_discord_pins = 0
var/python_path = "python" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix
var/use_lib_nudge = 0 //Use the C library nudge instead of the python nudge.
var/use_overmap = 0
@@ -629,10 +630,10 @@ var/list/gamemode_cache = list()
config.night_lighting = 1
if("nl_start_hour")
config.nl_start = text2num(value) * TICKS_IN_HOUR
config.nl_start = text2num(value)
if("nl_finish_hour")
config.nl_finish = text2num(value) * TICKS_IN_HOUR
config.nl_finish = text2num(value)
if("disable_player_mice")
config.disable_player_mice = 1
@@ -652,6 +653,9 @@ var/list/gamemode_cache = list()
if("discord_bot_port")
config.discord_bot_port = value
if("use_discord_pins")
config.use_discord_pins = 1
if("python_path")
if(value)
config.python_path = value
@@ -878,6 +882,8 @@ var/list/gamemode_cache = list()
discord_bot.active = 1
if ("robust_debug")
discord_bot.robust_debug = 1
if ("subscriber")
discord_bot.subscriber_role = value
else
log_misc("Unknown setting in discord configuration: '[name]'")
+24
View File
@@ -0,0 +1,24 @@
/var/global/datum/controller/law/corp_regs
/datum/controller/law
var/list/laws = list() // All laws
var/list/low_severity = list()
var/list/med_severity = list()
var/list/high_severity = list()
/datum/controller/law/New()
corp_regs = src
for (var/L in subtypesof(/datum/law/low_severity))
low_severity += new L
for (var/L in subtypesof(/datum/law/med_severity))
med_severity += new L
for (var/L in subtypesof(/datum/law/high_severity))
high_severity += new L
laws = low_severity + med_severity + high_severity
..()
+11
View File
@@ -76,6 +76,14 @@ datum/controller/game_controller/proc/setup_objects()
T.broadcast_status()
admin_notice(span("danger", "Caching space parallax."))
create_global_parallax_icons()
admin_notice(span("danger", "Done."))
admin_notice(span("danger", "Setting up lighting."))
initialize_lighting()
admin_notice(span("danger", "Lighting Setup Completed."))
//Spawn the contents of the cargo warehouse
sleep(-1)
spawn_cargo_stock()
@@ -86,5 +94,8 @@ datum/controller/game_controller/proc/setup_objects()
//Set up spawn points.
populate_spawn_points()
// Setup laws.
global.corp_regs = new
admin_notice("<span class='danger'>Initializations complete.</span>", R_DEBUG)
sleep(-1)
-94
View File
@@ -1,94 +0,0 @@
var/global/datum/news_controller/news_controller
/datum/news_controller
var/count = 0 //Count of articles pulled and published
var/publish_time //Ingame time for when the stored article is to be published
var/article_id //Stored article's primary-key
var/running = 1 //Boolean value, self-explanitory
var/fails = 0 //Counter for failed queries
var/max_fails = 5 //Maximum amount of failures before the controller is killed
/datum/news_controller/proc/process()
if(!running) //Not running, return.
return
if(fails >= max_fails) //Too many failures, killing.
kill()
return
if(!article_id && !publish_time) //No entry stored. Update and get one.
update()
return
if(publish_time < world.time) //Time to publish the article.
publish()
if(fails)
fails = 0
//Stores a new article for publishing.
/datum/news_controller/proc/update()
establish_db_connection(dbcon)
if(!dbcon.IsConnected())
error("SQL database connection failed. News_controller failed to retreive information.")
fails++
return
var/DBQuery/update_query = dbcon.NewQuery("SELECT id, publishtime FROM ss13_news WHERE status = 2 ORDER BY publishtime ASC LIMIT :count, 1")
update_query.Execute(list(":count" = count))
if(update_query.ErrorMsg())
fails++
return
if(!update_query.RowCount())
kill()
return
while(update_query.NextRow())
article_id = text2num(update_query.item[1])
publish_time = text2num(update_query.item[2]) * 600
count++
//Publish the stored article.
/datum/news_controller/proc/publish()
establish_db_connection(dbcon)
if(!dbcon.IsConnected())
error("SQL database connection failed. News_controller failed to retreive information.")
fails++
return
var/DBQuery/publish_query = dbcon.NewQuery("SELECT channel, author, body FROM ss13_news WHERE id = :article_id")
publish_query.Execute(list(":article_id" = article_id))
if(publish_query.ErrorMsg())
fails++
return
var/article_channel
var/article_author
var/article_content
while(publish_query.NextRow())
article_channel = publish_query.item[1]
article_author = publish_query.item[2]
article_content = publish_query.item[3]
var/channel_found
for(var/datum/feed_channel/FC in news_network.network_channels)
if(FC.channel_name == article_channel)
channel_found = 1
break
if(!channel_found)
news_network.CreateFeedChannel(article_channel, article_author, 0)
news_network.SubmitArticle(article_content, article_author, article_channel)
article_id = null
publish_time = null
update()
//Kills the controller.
/datum/news_controller/proc/kill()
running = 0
return
+7 -1
View File
@@ -25,7 +25,7 @@
usr.client.debug_variables(antag)
message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.")
/client/proc/debug_controller(controller in list("Master","Ticker","Ticker Process","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano","Chemistry","Wireless","Observation"))
/client/proc/debug_controller(controller in list("Master","Ticker","Ticker Process","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano","Chemistry","Wireless","Observation","Lighting","Explosives"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -98,5 +98,11 @@
if("Observation")
debug_variables(all_observable_events)
feedback_add_details("admin_verb", "DObservation")
if("Lighting")
debug_variables(lighting_process)
feedback_add_details("admin_verb", "DLightingP")
if("Explosives")
debug_variables(bomb_processor)
feedback_add_details("admin_verb", "DExplosives")
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
return
+19 -2
View File
@@ -194,8 +194,15 @@ datum/controller/vote
proc/submit_vote(var/ckey, var/vote)
if(mode)
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
return 0
if(config.vote_no_dead && usr && !usr.client.holder)
if (isnewplayer(usr))
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
else if (isobserver(usr))
var/mob/dead/observer/O = usr
if (O.started_as_observer)
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
if(vote && vote >= 1 && vote <= choices.len)
if(current_votes[ckey])
choices[choices[current_votes[ckey]]]--
@@ -211,6 +218,16 @@ datum/controller/vote
// Transfer votes are their own little special snowflake
var/next_allowed_time = 0
if (vote_type == "crew_transfer")
if (config.vote_no_dead && !usr.client.holder)
if (isnewplayer(usr))
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
else if (isobserver(usr))
var/mob/dead/observer/O = usr
if (O.started_as_observer)
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
if (last_transfer_vote)
next_allowed_time = (last_transfer_vote + config.vote_delay)
else
+335 -48
View File
@@ -1,6 +1,13 @@
#define CHAN_ADMIN "channel_admin"
#define CHAN_CCIAA "channel_cciaa"
#define CHAN_ANNOUNCE "channel_announce"
#define CHAN_INVITE "channel_invite"
#define SEND_OK 0
#define SEND_TIMEOUT 1
#define ERROR_PROC 2
#define ERROR_CURL 3
#define ERROR_HTTP 4
var/datum/discord_bot/discord_bot = null
@@ -15,21 +22,34 @@ var/datum/discord_bot/discord_bot = null
discord_bot.update_channels()
if (config.use_discord_pins && server_greeting)
server_greeting.update_pins()
return 1
/datum/discord_bot
var/list/channels = list()
var/list/channels_to_group = list() // Group flag -> list of channel datums map.
var/list/channels = list() // Channel ID -> channel datum map. Will ensure that only one datum per channel ID exists.
var/datum/discord_channel/invite = null // The channel datum where the ingame Join Channel button will link to.
var/active = 0
var/auth_token = ""
var/subscriber_role = ""
var/robust_debug = 0
// Lazy man's rate limiting vars
var/rate_limited_since = 0
var/queue_being_pushed = 0
var/datum/scheduled_task/push_task
var/list/queue = list()
/*
* Proc update_channels
* Used to load channels from the database and construct them with the discord API.
* Wipes all current channels and channel maps.
*
* @return num - Error code. 0 upon success.
*/
/datum/discord_bot/proc/update_channels()
if (!active)
return 1
@@ -38,27 +58,56 @@ var/datum/discord_bot/discord_bot = null
log_debug("BOREALIS: Failed to update channels due to missing database.")
return 2
channels = list()
// Reset the channel lists.
channels_to_group.Cut()
channels.Cut()
var/DBQuery/channel_query = dbcon.NewQuery("SELECT channel_group, channel_id FROM discord_channels")
var/DBQuery/channel_query = dbcon.NewQuery("SELECT channel_group, channel_id, pin_flag, server_id FROM discord_channels")
channel_query.Execute()
var/list/A
while (channel_query.NextRow())
if (isnull(channels[channel_query.item[1]]))
channels[channel_query.item[1]] = list()
// Create the channel map.
if (isnull(channels_to_group[channel_query.item[1]]))
channels_to_group[channel_query.item[1]] = list()
A = channels[channel_query.item[1]]
A += channel_query.item[2]
var/datum/discord_channel/B = channels[channel_query.item[2]]
// We don't have this channel datum yet.
if (isnull(B))
B = new(channel_query.item[2], channel_query.item[4], text2num(channel_query.item[3]))
if (!B)
log_debug("BOREALIS: Bad channel data during update channels. [jointext(channel_query.item, ", ")].")
continue
channels[channel_query.item[2]] = B
if (text2num(channel_query.item[3]))
B.pin_flag |= text2num(channel_query.item[3])
// Add the channel to the required lists.
channels_to_group[channel_query.item[1]] += B
if (!isnull(channels_to_group[CHAN_INVITE]))
invite = channels_to_group[CHAN_INVITE][1]
else if (robust_debug)
log_debug("BOREALIS: No invite channel designated.")
log_debug("BOREALIS: Channels updated successfully.")
return 0
/*
* Proc send_message
* Used to send a message to a specific channel group.
*
* @param text channel_group - The name of the channel group which to target.
* @param text message - The message to send.
*/
/datum/discord_bot/proc/send_message(var/channel_group, var/message)
if (!active || !auth_token)
return
if (!channel_group || !channels.len || isnull(channels[channel_group]))
if (!channel_group || !channels.len || isnull(channels_to_group[channel_group]))
return
if (!message)
@@ -70,18 +119,18 @@ var/datum/discord_bot/discord_bot = null
// Let's run it through the proper JSON encoder, just in case of special characters.
message = json_encode(list("content" = message))
var/list/A = channels[channel_group]
var/list/A = channels_to_group[channel_group]
var/list/sent = list()
for (var/channel in A)
if (send_post_request("https://discordapp.com/api/channels/[channel]/messages", message, "Authorization: Bot [auth_token]", "Content-Type: application/json") == 429)
for (var/B in A)
var/datum/discord_channel/channel = B
if (channel.send_message_to(auth_token, message) == SEND_TIMEOUT)
// Whoopsies, rate limited.
// Set up the queue.
rate_limited_since = world.time
queue.Add(list(message, A - sent))
queue.Add(list(list(message, A - sent)))
// Schedule a push.
spawn (100)
push_queue()
if (!push_task)
push_task = schedule_task_with_source_in(10 SECONDS, src, /datum/discord_bot/proc/push_queue)
// And exit.
return
@@ -89,43 +138,87 @@ var/datum/discord_bot/discord_bot = null
sent += channel
if (robust_debug)
log_debug("BOEALIS: Message sent to [channel_group]. JSON body: '[message]'")
log_debug("BOREALIS: Message sent to [channel_group]. JSON body: '[message]'")
/*
* Proc retreive_pins
* Used to fetch a list of all pins from the designated channels.
*
* @return list - A multilayered list of flags associated with pins. Structure looks like this:
* list("pin_flag" = list(list("author" = author name, "content" = content),
* list("author" = author name, "content" = content)))
*/
/datum/discord_bot/proc/retreive_pins()
if (!active || !auth_token)
return list()
if (!channels.len || isnull(channels_to_group["channel_pins"]))
testing("No group.")
return list()
var/list/output = list()
for (var/A in channels_to_group["channel_pins"])
var/datum/discord_channel/channel = A
if (isnull(output["[channel.pin_flag]"]))
output["[channel.pin_flag]"] = list()
output["[channel.pin_flag]"] += channel.get_pins(auth_token)
return output
/*
* Proc retreive_invite
* Used to retreive the invite to the invite channel.
* One will be created if none exist.
*
* @return text - The invite URL to the designated invite channel.
*/
/datum/discord_bot/proc/retreive_invite()
if (!active || !auth_token)
return ""
if (!invite)
return ""
var/res = invite.get_invite(auth_token)
return isnum(res) ? "" : res
/*
* Proc send_to_admin
* Forwards a message to the admin channels.
*/
/datum/discord_bot/proc/send_to_admins(message)
send_message(CHAN_ADMIN, message)
/*
* Proc send_to_cciaa
* Forwards a message to the CCIAA channels.
*/
/datum/discord_bot/proc/send_to_cciaa(message)
send_message(CHAN_CCIAA, message)
/datum/discord_bot/proc/send_to_announce(message)
/*
* Proc send_to_announce
* Forwards a message to the announcements channels.
*/
/datum/discord_bot/proc/send_to_announce(message, prepend_role = 0)
if (prepend_role && subscriber_role)
message = "<@&[subscriber_role]> " + message
send_message(CHAN_ANNOUNCE, message)
/*
* Proc push_queue
* Handles the queue pushing for the bot. If there is no need to reschedule (all messages get successfully
* pushed), then it deletes push_task and sets it back to null. Otherwise, it simply reschedules it.
*/
/datum/discord_bot/proc/push_queue()
// What facking queue.
if (!queue.len)
if (!queue || !queue.len)
if (robust_debug)
log_debug("BOREALIS: Attempted to push a null length queue.")
if (queue_being_pushed)
queue_being_pushed = 0
return
if (queue_being_pushed)
if (robust_debug)
log_debug("BOREALIS: Attempted to initialize a second queue driver.")
return
if ((world.time - rate_limited_since) < 100)
// Something broke the limit again. Ideally, this wouldn't happen. But sure.
// Use a longer timeout, just in case.
spawn (200)
push_queue()
queue_being_pushed = 0
return
// Async process lock var. No touchy.
queue_being_pushed = 1
// A[1] - message body.
// A[2] - list of channels to send to.
var/message
@@ -134,22 +227,216 @@ var/datum/discord_bot/discord_bot = null
message = A[1]
destinations = A[2]
for (var/channel in destinations)
if (send_post_request("https://discordapp.com/api/channels/[channel]/messages", message, "Authorization: Bot [auth_token]", "Content-Type: application/json") == 429)
// Limited again. Reschedule.
rate_limited_since = world.time
spawn (100)
push_queue()
for (var/B in destinations)
var/datum/discord_channel/channel = B
if (channel.send_message_to(auth_token, message) == SEND_TIMEOUT)
// Tasks nuke themselves after use. So just make a new one! What could possibly go wrong!
push_task = schedule_task_with_source_in(10 SECONDS, src, /datum/discord_bot/proc/push_queue)
queue_being_pushed = 0
return
else
destinations.Remove(channel)
queue.Remove(A)
queue_being_pushed = 0
// A holder class for channels.
/datum/discord_channel
var/id = ""
var/server_id = ""
var/pin_flag = 0
var/invite_url = ""
/*
* Constructor
*
* @param text _id - the discord API id of the channel, as a string.
* @param text _sid - the discord API server id for the channel, as a string.
* @param num _pin - the bitflags of admin permissions which have access to the pins from this channel.
*/
/datum/discord_channel/New(var/_id, var/_sid, var/_pin)
id = _id
server_id = _sid
if (_pin)
pin_flag = _pin
/*
* Proc send_message_to
* Sends a message to the channel.
*
* @param text token - the authorization token to be used for the requests.
* @param text message - the sanitized message content to be sent.
* @return num - a specific return code for the discord_bot to handle.
*/
/datum/discord_channel/proc/send_message_to(var/token, var/message)
if (!token || !message)
return ERROR_PROC
var/res = send_post_request("https://discordapp.com/api/channels/[id]/messages", message, "Authorization: Bot [token]", "Content-Type: application/json")
switch (res)
if (-1)
return ERROR_PROC
if (200)
return SEND_OK
if (429)
return SEND_TIMEOUT
if (0 to 90)
log_debug("BOREALIS: cURL error while forwarding message to Discord API: [res]. Message body: [message].")
return ERROR_CURL
else
log_debug("BOREALIS: HTTP error while forwarding message to Discord API: [res]. Channel: [id]. Message body: [message].")
return ERROR_HTTP
/*
* Proc get_pins
* Retreives a list of pinned messages from the channel.
*
* @param text token - the authorization token to be used for the requests.
* @return mixed - 2d array of content upon success, in format: list(list("author" = text, "content" = text)).
* Num upon failure.
*/
/datum/discord_channel/proc/get_pins(var/token)
var/res = send_get_request("https://discordapp.com/api/channels/[id]/pins", "Authorization: Bot [token]")
// Is a number, so an error code.
if (isnum(res))
switch (res)
if (-1)
return ERROR_PROC
if (0 to 90)
log_debug("BOREALIS: cURL error while fetching pins from the Discord API: [res].")
return ERROR_CURL
if (100 to 600)
log_debug("BOREALIS: HTTP error while fetching pins from the Discord API: [res].")
return ERROR_HTTP
else
log_debug("BOREALIS: Unknown response code while fetching pins from the Discord API: [res].")
return ERROR_PROC
else
var/list/A = res
var/list/pinned_messages = list()
// Begin processing the list structure delivered back to us from send_get_request.
for (var/list/B in A)
var/content = B["content"]
// We have mentions to take care of.
if (!isnull(B["mentions"]))
var/list/mentions = B["mentions"]
for (var/list/C in mentions)
content = replacetextEx(content, "<@[C["id"]]>", C["username"])
// Role mentions are up next.
if (!isnull(B["mention_roles"]))
var/list/mentions = B["mention_roles"]
for (var/C in mentions)
content = replacetextEx(content, "<@&[C]>", "@SomeRole")
pinned_messages += list(list("author" = B["author"]["username"], "content" = content))
return pinned_messages
/*
* Proc get_invite
* Retreives (or creates if none found) an invite URL to the specific channel.
*
* @param text token - the authorization token to be used for the requests.
* @return mixed - text upon success, the link to the invite; num upon failure.
*/
/datum/discord_channel/proc/get_invite(var/token)
if (invite_url)
return invite_url
var/res = send_get_request("https://discordapp.com/api/channels/[id]/invites", "Authorization: Bot [token]")
// Is a number, so an error code.
if (isnum(res))
switch (res)
if (-1)
return ERROR_PROC
if (0 to 90)
log_debug("BOREALIS: cURL error while fetching pins from the Discord API: [res].")
return ERROR_CURL
if (100 to 600)
log_debug("BOREALIS: HTTP error while fetching pins from the Discord API: [res].")
return ERROR_HTTP
else
log_debug("BOREALIS: Unknown response code while fetching pins from the Discord API: [res].")
return ERROR_PROC
else
var/list/A = res
// No length to return data, but a valid 200 return header.
// So we simply have no invites active. Make one!
if (!A || !A.len)
return create_invite(token)
var/best_age = A[1]["max_age"]
var/code = A[1]["code"]
// Find the best invite.
// Why? Because round time expires are lame, I guess.
if (best_age != 0)
for (var/i = 2, i < A.len, i++)
if (A[i]["max_age"] == 0)
code = A[i]["code"]
break
if (best_age < A[i]["max_age"])
best_age = A[i]["max_age"]
code = A[i]["code"]
// Sanity check for debug I guess.
if (!code)
log_debug("BOREALIS: Retreived an empty invite. This should not happen. Response object: [json_encode(A)]")
// Save the URL for later retreival.
invite_url = "https://discord.gg/[code]"
return invite_url
/*
* Proc create_invite
* Creates a permanent invite to a channel and returns it, under the assumption that
* there are no other invites for this channel.
*
* @param text token - the authorization token to be used for the requests.
* @return mixed - String upon success - the URL of the newly generated invite.
* Num upon failure.
*/
/datum/discord_channel/proc/create_invite(var/token)
var/data = list("max_age" = 0, "max_uses" = 0)
var/res = send_post_request("https://discordapp.com/api/channels/[id]/invites", json_encode(data), "Authorization: Bot [token]", "Content-Type: application/json")
if (res == 200)
var/list/get_req = send_get_request("https://discordapp.com/api/channels/[id]/invites", "Authorization: Bot [token]")
if (!istype(get_req) || !get_req.len)
return ERROR_PROC
// The first index should now exist. So we just use that!
invite_url = "https://discord.gg/[get_req[1]["code"]]"
return invite_url
#undef CHAN_ADMIN
#undef CHAN_CCIAA
#undef CHAN_ANNOUNCE
#undef CHAN_INVITE
#undef SEND_OK
#undef SEND_TIMEOUT
#undef ERROR_PROC
#undef ERROR_CURL
#undef ERROR_HTTP
+1 -1
View File
@@ -30,7 +30,7 @@
expansions.Cut()
return ..()
/obj/ResetVars(var/list/exclude = list())
/obj/resetVariables(var/list/exclude = list())
exclude += "expansions"
..(exclude)
//expansions = list()
+6 -7
View File
@@ -7,8 +7,8 @@
var/atom/movable/teleatom //atom to teleport
var/atom/destination //destination to teleport to
var/precision = 0 //teleport precision
var/datum/effect/effect/system/effectin //effect to show right before teleportation
var/datum/effect/effect/system/effectout //effect to show right after teleportation
var/datum/effect_system/effectin //effect to show right before teleportation
var/datum/effect_system/effectout //effect to show right after teleportation
var/soundin //soundfile to play before teleportation
var/soundout //soundfile to play after teleportation
var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation)
@@ -78,13 +78,13 @@
/datum/teleport/proc/teleportChecks()
return 1
/datum/teleport/proc/playSpecials(atom/location,datum/effect/effect/system/effect,sound)
/datum/teleport/proc/playSpecials(atom/location,datum/effect_system/effect,sound)
if(location)
if(effect)
spawn(-1)
src = null
effect.attach(location)
effect.start()
effect.location = location
effect.queue()
if(sound)
spawn(-1)
src = null
@@ -141,8 +141,7 @@
/datum/teleport/instant/science/setEffects(datum/effect/effect/system/aeffectin,datum/effect/effect/system/aeffectout)
if(!aeffectin || !aeffectout)
var/datum/effect/effect/system/spark_spread/aeffect = new
aeffect.set_up(5, 1, teleatom)
var/datum/effect_system/sparks/aeffect = getFromPool(/datum/effect_system/sparks, null, FALSE, 5, alldirs)
effectin = effectin || aeffect
effectout = effectout || aeffect
return 1
-136
View File
@@ -1,136 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * *
* /datum/recipe by rastaf0 13 apr 2011 *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* This is powerful and flexible recipe system.
* It exists not only for food.
* supports both reagents and objects as prerequisites.
* In order to use this system you have to define a deriative from /datum/recipe
* * reagents are reagents. Acid, milc, booze, etc.
* * items are objects. Fruits, tools, circuit boards.
* * result is type to create as new object
* * time is optional parameter, you shall use in in your machine,
default /datum/recipe/ procs does not rely on this parameter.
*
* Functions you need:
* /datum/recipe/proc/make(var/obj/container as obj)
* Creates result inside container,
* deletes prerequisite reagents,
* transfers reagents from prerequisite objects,
* deletes all prerequisite objects (even not needed for recipe at the moment).
*
* /proc/select_recipe(list/datum/recipe/avaiable_recipes, obj/obj as obj, exact = 1)
* Wonderful function that select suitable recipe for you.
* obj is a machine (or magik hat) with prerequisites,
* exact = 0 forces algorithm to ignore superfluous stuff.
*
*
* Functions you do not need to call directly but could:
* /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents)
* /datum/recipe/proc/check_items(var/obj/container as obj)
*
* */
/datum/recipe
var/list/reagents // example: = list("berryjuice" = 5) // do not list same reagent twice
var/list/items // example: = list(/obj/item/weapon/crowbar, /obj/item/weapon/welder) // place /foo/bar before /foo
var/list/fruit // example: = list("fruit" = 3)
var/result // example: = /obj/item/weapon/reagent_containers/food/snacks/donut/normal
var/time = 100 // 1/10 part of second
/datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents)
. = 1
for (var/r_r in reagents)
var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r)
if (!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals
if (aval_r_amnt>reagents[r_r])
. = 0
else
return -1
if ((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len)
return 0
return .
/datum/recipe/proc/check_fruit(var/obj/container)
. = 1
if(fruit && fruit.len)
var/list/checklist = list()
// You should trust Copy().
checklist = fruit.Copy()
for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container)
if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag]))
continue
checklist[G.seed.kitchen_tag]--
for(var/ktag in checklist)
if(!isnull(checklist[ktag]))
if(checklist[ktag] < 0)
. = 0
else if(checklist[ktag] > 0)
. = -1
break
return .
/datum/recipe/proc/check_items(var/obj/container as obj)
. = 1
if (items && items.len)
var/list/checklist = list()
checklist = items.Copy() // You should really trust Copy
for(var/obj/O in container)
if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown))
continue // Fruit is handled in check_fruit().
var/found = 0
for(var/i = 1; i < checklist.len+1; i++)
var/item_type = checklist[i]
if (istype(O,item_type))
checklist.Cut(i, i+1)
found = 1
break
if (!found)
. = 0
if (checklist.len)
. = -1
return .
//general version
/datum/recipe/proc/make(var/obj/container as obj)
var/obj/result_obj = new result(container)
for (var/obj/O in (container.contents-result_obj))
O.reagents.trans_to_obj(result_obj, O.reagents.total_volume)
qdel(O)
container.reagents.clear_reagents()
return result_obj
// food-related
/datum/recipe/proc/make_food(var/obj/container as obj)
if(!result)
world << "<span class='danger'>Recipe [type] is defined without a result, please bug this.</span>"
return
var/obj/result_obj = new result(container)
for (var/obj/O in (container.contents-result_obj))
if (O.reagents)
O.reagents.del_reagent("nutriment")
O.reagents.update_total()
O.reagents.trans_to_obj(result_obj, O.reagents.total_volume)
qdel(O)
container.reagents.clear_reagents()
return result_obj
/proc/select_recipe(var/list/datum/recipe/avaiable_recipes, var/obj/obj as obj, var/exact)
var/list/datum/recipe/possible_recipes = new
var/target = exact ? 0 : 1
for (var/datum/recipe/recipe in avaiable_recipes)
if((recipe.check_reagents(obj.reagents) < target) || (recipe.check_items(obj) < target) || (recipe.check_fruit(obj) < target))
continue
possible_recipes |= recipe
if (possible_recipes.len==0)
return null
else if (possible_recipes.len==1)
return possible_recipes[1]
else //okay, let's select the most complicated recipe
var/highest_count = 0
. = possible_recipes[1]
for (var/datum/recipe/recipe in possible_recipes)
var/count = ((recipe.items)?(recipe.items.len):0) + ((recipe.reagents)?(recipe.reagents.len):0) + ((recipe.fruit)?(recipe.fruit.len):0)
if (count >= highest_count)
highest_count = count
. = recipe
return .
+79
View File
@@ -0,0 +1,79 @@
/*************
* Task Datum *
*************/
/datum/scheduled_task
var/trigger_time
var/procedure
var/list/arguments
var/task_after_process
var/list/task_after_process_args
var/datum/scheduled_task/next
var/destroyed = FALSE
/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
..()
src.trigger_time = trigger_time
src.procedure = procedure
src.arguments = arguments ? arguments : list()
src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task
src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list()
task_after_process_args += src
/datum/scheduled_task/Destroy()
if (!destroyed)
kill(no_del = TRUE)
procedure = null
arguments.Cut()
task_after_process = null
task_after_process_args.Cut()
return ..()
/datum/scheduled_task/proc/pre_process()
return
/datum/scheduled_task/proc/process()
if(procedure)
call(procedure)(arglist(arguments))
/datum/scheduled_task/proc/post_process()
call(task_after_process)(arglist(task_after_process_args))
// Resets the trigger time, has no effect if the task has already triggered
/datum/scheduled_task/proc/trigger_task_in(var/trigger_in)
src.trigger_time = world.time + trigger_in
/datum/scheduled_task/proc/kill(no_del = FALSE)
if (!destroyed)
warning("scheduler: Non-destroyed task was killed!")
destroyed = TRUE
if (src in scheduler.tasks)
warning("scheduler: Task was not cleaned up correctly, rebuilding scheduler queue!")
scheduler.rebuild_queue()
if (!no_del)
qdel(src)
/datum/scheduled_task/source
var/datum/source
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
src.source = source
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
/datum/scheduled_task/source/Destroy()
source = null
return ..()
/datum/scheduled_task/source/process()
call(source, procedure)(arglist(arguments))
/datum/scheduled_task/source/proc/source_destroyed()
destroyed = TRUE
/proc/destroy_scheduled_task(var/datum/scheduled_task/st)
st.destroyed = TRUE
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
st.trigger_time = world.time + trigger_delay
scheduler.schedule(st)
+111 -15
View File
@@ -46,8 +46,9 @@
if (F["motd"])
F["motd"] >> motd
if (F["memo"])
F["memo"] >> memo_list
if (!config.use_discord_pins)
if (F["memo"])
F["memo"] >> memo_list
update_data()
@@ -63,18 +64,32 @@
motd = initial(motd)
motd_hash = ""
if (memo_list.len)
memo = ""
for (var/ckey in memo_list)
var/data = {"<p><b>[ckey]</b> wrote on [memo_list[ckey]["date"]]:<br>
[memo_list[ckey]["content"]]</p>"}
if (!config.use_discord_pins)
// The initialization of memos in case use_discord_pins == 1 is done in discord_bot.dm
// Primary reason is to avoid null references when the bot isn't created yet.
if (memo_list.len)
memo = ""
for (var/ckey in memo_list)
var/data = {"<p><b>[ckey]</b> wrote on [memo_list[ckey]["date"]]:<br>
[memo_list[ckey]["content"]]</p>"}
memo += data
memo += data
memo_hash = md5(memo)
else
memo = initial(memo)
memo_hash = ""
memo_hash = md5(memo)
else
memo = initial(memo)
memo_hash = ""
/datum/server_greeting/proc/update_pins()
var/list/temp_list = discord_bot.retreive_pins()
// A is a number in a string form
// temp_list[A] is a list of lists.
for (var/A in temp_list)
var/list/memos = temp_list[A]
var/flag = text2num(A)
memo_list += new /datum/memo_datum(memos, flag)
/*
* Helper to update the MoTD or memo contents.
@@ -94,9 +109,15 @@
motd = new_value
if ("memo_write")
if (config.use_discord_pins)
return 0
memo_list[new_value[1]] = list("date" = time2text(world.realtime, "DD-MMM-YYYY"), "content" = new_value[2])
if ("memo_delete")
if (config.use_discord_pins)
return 0
if (memo_list[new_value])
memo_list -= new_value
else
@@ -132,7 +153,7 @@
if (motd_hash && user.prefs.motd_hash != motd_hash)
outdated_info |= OUTDATED_MOTD
if (user.holder && memo_hash && user.prefs.memo_hash != memo_hash)
if (user.holder && user.prefs.memo_hash != get_memo_hash(user))
outdated_info |= OUTDATED_MEMO
if (user.prefs.notifications.len)
@@ -185,13 +206,13 @@
else
if (outdated_info & OUTDATED_MEMO)
data["update"] = 1
data["changeHash"] = memo_hash
data["changeHash"] = get_memo_hash(user)
else
data["update"] = 0
data["changeHash"] = null
data["div"] = "#memo"
data["content"] = memo
data["content"] = get_memo_content(user)
user << output(JS_SANITIZE(data), "greeting.browser:AddContent")
if (outdated_info & OUTDATED_MOTD)
@@ -216,6 +237,81 @@
if ("request_data")
send_to_javascript(C)
/*
* Gets the appropriate memo hash for the memo system in use.
* Args:
* - var/C client
* Returns:
* - string
*/
/datum/server_greeting/proc/get_memo_hash(var/client/C)
if (!C || !C.holder)
return ""
if (!config.use_discord_pins)
return memo_hash
var/joint_checksum = ""
for (var/A in memo_list)
var/datum/memo_datum/memo = A
if (C.holder.rights & memo.flag)
joint_checksum += memo.hash
return md5(joint_checksum)
/*
* Gets the appropriate memo content for the memo system in use.
* Args:
* - var/C client
* Returns:
* - string if old memo system is used (config.use_discord_pins = 0)
* - list of strings if new memo system is used
*/
/datum/server_greeting/proc/get_memo_content(var/client/C)
if (!C || !C.holder)
return ""
if (!config.use_discord_pins)
return memo
var/list/content = list()
for (var/A in memo_list)
var/datum/memo_datum/memo = A
if (C.holder.rights & memo.flag)
content += memo.contents
return content
/datum/memo_datum
var/contents
var/hash
var/flag
/datum/memo_datum/New(var/list/input = list(), var/_flag)
flag = _flag
// Yes. This is an unfortunately acceptable way of doing it.
// Why? Because you cannot use numbers as indexes in an assoc list without fucking DM.
var/static/list/flags_to_divs = list("[R_ADMIN]" = "danger",
"[R_MOD]" = "warning",
"[(R_MOD|R_ADMIN)]" = "warning",
"[R_CCIAA]" = "info",
"[R_DEV]" = "info")
if (input.len)
contents = "<div class='alert alert-[flags_to_divs["[flag]"]]'>"
for (var/i = 1, i <= input.len, i++)
contents += "<b>[input[i]["author"]]</b> wrote:<br>[nl2br(input[i]["content"])]"
if (i < input.len)
contents += "<hr></hr>"
contents += "</div>"
else
contents = ""
hash = md5(contents)
#undef OUTDATED_NOTE
#undef OUTDATED_MEMO
#undef OUTDATED_MOTD
+41
View File
@@ -1733,3 +1733,44 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = /obj/structure/closet/crate
containername = "IPC/Shell tag implanters"
group = "Security"
/datum/supply_packs/ame_ctrl
name = "Antimatter Reactor Control Unit"
contains = list(
/obj/machinery/power/am_control_unit
)
cost = 40
containertype = /obj/structure/closet/crate/secure/large
containername = "antimatter reactor control unit kit"
group = "Engineering"
access = access_ce
/datum/supply_packs/ame_section
name = "Antimatter Reactor Shielding Kit"
contains = list(
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container
)
cost = 20
containertype = /obj/structure/closet/crate/secure/phoron
containername = "antimatter reactor shielding (9x) crate"
group = "Engineering"
access = access_ce
/datum/supply_packs/ame_fuel
name = "Antimatter Reactor Fuel"
contains = list(
/obj/item/weapon/am_containment
)
cost = 20
containertype = /obj/structure/closet/crate/secure/bin
containername = "antimatter fuel container"
group = "Engineering"
access = access_engine
+6
View File
@@ -9,6 +9,12 @@
item_cost = 2
path = /obj/item/weapon/storage/toolbox/syndicate
/datum/uplink_item/item/tools/advancedpinpointer
name = "Advanced pinpointer"
item_cost = 15
path = /obj/item/weapon/pinpointer/advpinpointer
desc = "An advanced pinpointer that can find any target with DNA along with various other items."
/datum/uplink_item/item/tools/money
name = "Operations Funding"
item_cost = 2
+2 -2
View File
@@ -114,7 +114,7 @@ var/global/ManifestJSON
department = 1
if(depthead && sci.len != 1)
sci.Swap(1,sci.len)
if(real_rank in cargo_positions)
car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive)
department = 1
@@ -146,7 +146,7 @@ var/global/ManifestJSON
"bot" = bot,\
"misc" = misc\
)
ManifestJSON = list2json(PDA_Manifest)
ManifestJSON = json_encode(PDA_Manifest)
return
+12 -11
View File
@@ -173,7 +173,7 @@
throw_range = 5
w_class = 2.0
attack_verb = list("warned", "cautioned", "smashed")
/obj/item/weapon/caution/attack_self(mob/user as mob)
if(src.icon_state == "caution")
src.icon_state = "caution_blinking"
@@ -181,14 +181,15 @@
else
src.icon_state = "caution"
user << "You turn the sign off."
/obj/item/weapon/caution/AltClick()
if(src.icon_state == "caution")
src.icon_state = "caution_blinking"
usr << "You turn the sign on."
else
src.icon_state = "caution"
usr << "You turn the sign off."
if(!usr || usr.stat || usr.lying || usr.restrained() || !Adjacent(usr)) return
if(src.icon_state == "caution")
src.icon_state = "caution_blinking"
usr << "You turn the sign on."
else
src.icon_state = "caution"
usr << "You turn the sign off."
/obj/item/weapon/caution/cone
desc = "This cone is trying to warn you of something!"
@@ -197,10 +198,10 @@
item_state = "cone"
contained_sprite = 1
slot_flags = SLOT_HEAD
/obj/item/weapon/caution/cone/attack_self(mob/user as mob)
return
/obj/item/weapon/caution/cone/AltClick()
return
@@ -427,7 +428,7 @@
icon = 'icons/obj/stock_parts.dmi'
w_class = 2.0
var/rating = 1
/obj/item/weapon/stock_parts/New()
src.pixel_x = rand(-5.0, 5)
src.pixel_y = rand(-5.0, 5)
+1
View File
@@ -14,6 +14,7 @@ var/datum/antagonist/xenos/xenomorphs
faction_descriptor = "Hive"
faction_welcome = "Your will is ripped away as your humanity merges with the xenomorph overmind. You are now \
a thrall to the queen and her brood. Obey their instructions without question. Serve the hive."
faction = "xenomorph"
hard_cap = 5
hard_cap_round = 8
+1
View File
@@ -30,6 +30,7 @@
var/faction_descriptor // Description of the cause. Mandatory for faction role.
var/faction_verb // Verb added when becoming a member of the faction, if any.
var/faction_welcome // Message shown to faction members.
var/faction = "neutral" // Faction name, mostly used for simple animals.
// Spawn values (autotraitor and game mode)
var/hard_cap = 3 // Autotraitor var. Won't spawn more than this many antags.
+7 -3
View File
@@ -14,6 +14,9 @@
create_antagonist(player, move_to_spawn, do_not_announce, preserve_appearance)
if(!do_not_equip)
equip(player.current)
player.current.faction = faction
return 1
/datum/antagonist/proc/add_antagonist_mind(var/datum/mind/player, var/ignore_role, var/nonstandard_role_type, var/nonstandard_role_msg)
@@ -42,7 +45,7 @@
update_icons_added(player)
return 1
/datum/antagonist/proc/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted)
/datum/antagonist/proc/remove_antagonist(var/datum/mind/player, var/show_message = TRUE, var/implanted)
if(!istype(player))
return 0
@@ -50,14 +53,15 @@
player.current.verbs -= faction_verb
if(player in current_antagonists)
player.current << "<span class='danger'><font size = 3>You are no longer a [role_text]!</font></span>"
if (show_message)
player.current << "<span class='danger'><font size = 3>You are no longer a [role_text]!</font></span>"
current_antagonists -= player
faction_members -= player
player.special_role = null
update_icons_removed(player)
BITSET(player.current.hud_updateflag, SPECIALROLE_HUD)
if (!is_special_character(player))
if (!is_special_character(player) && !player.current.client.holder)
player.current.client.verbs -= /client/proc/aooc
return 1
+2 -1
View File
@@ -9,9 +9,10 @@
player.drop_from_inventory(thing)
if(thing.loc != player)
qdel(thing)
player.species.equip_survival_gear(player)
return 1
/datum/antagonist/proc/unequip(var/mob/living/carbon/human/player)
if(!istype(player))
return 0
return 1
return 1
@@ -3,6 +3,10 @@
set category = "Abilities"
if(!M.mind)
return
for (var/obj/item/weapon/implant/loyalty/I in M)
if (I.implanted)
src << "<span class='warning'>[M] is too loyal to the company!</span>"
return
convert_to_faction(M.mind, revs)
/mob/living/proc/convert_to_faction(var/datum/mind/player, var/datum/antagonist/faction)
+1 -1
View File
@@ -30,7 +30,7 @@ var/datum/antagonist/ert/ert
/datum/antagonist/ert/greet(var/datum/mind/player)
if(!..())
return
player.current << "The Emergency Response Team works for Asset Protection; your job is to protect NanoTrasen's assets. There is a code red alert on [station_name()], you are tasked to go and fix the problem."
player.current << "The Emergency Response Team works for Asset Protection; your job is to protect [company_name]'s assets. There is a code red alert on [station_name()], you are tasked to go and fix the problem."
player.current << "You should first gear up and discuss a plan with your team. More members may be joining, don't move out before you're ready."
/datum/antagonist/ert/equip(var/mob/living/carbon/human/player)
+23 -5
View File
@@ -14,7 +14,8 @@ var/datum/antagonist/wizard/wizards
hard_cap_round = 3
initial_spawn_req = 1
initial_spawn_target = 1
faction = "Space Wizard"
/datum/antagonist/wizard/New()
..()
@@ -85,7 +86,8 @@ var/datum/antagonist/wizard/wizards
if(wizard_mob.backbag == 5) wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/duffel/wizard(wizard_mob), slot_back)
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/storage/box(wizard_mob), slot_in_backpack)
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/teleportation_scroll(wizard_mob), slot_r_store)
wizard_mob.equip_to_slot_or_del(new /obj/item/weapon/spellbook(wizard_mob), slot_r_hand)
var/obj/item/I = new /obj/item/weapon/spellbook(get_turf(wizard_mob))
wizard_mob.put_in_hands(I)
wizard_mob.update_icons()
return 1
@@ -100,6 +102,22 @@ var/datum/antagonist/wizard/wizards
feedback_set_details("round_end_result","loss - wizard killed")
world << "<span class='danger'><font size = 3>The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!</font></span>"
/datum/antagonist/wizard/print_player_summary()
..()
for(var/p in current_antagonists)
var/datum/mind/player = p
var/text = "<b>[player.name]'s spells were:</b>"
if(!player.learned_spells || !player.learned_spells.len)
text += "<br>None!"
else
for(var/s in player.learned_spells)
var/spell/spell = s
text += "<br><b>[spell.name]</b> - "
text += "Speed: [spell.spell_levels["speed"]] Power: [spell.spell_levels["power"]]"
text += "<br>"
world << text
//To batch-remove wizard spells. Linked to mind.dm.
/mob/proc/spellremove()
for(var/spell/spell_to_remove in src.spell_list)
@@ -120,13 +138,13 @@ Made a proc so this is not repeated 14 (or more) times.*/
// Humans can wear clothes.
/mob/living/carbon/human/wearing_wiz_garb()
if(!is_wiz_garb(src.wear_suit))
if(!is_wiz_garb(src.wear_suit) && (!src.species.hud || (slot_wear_suit in src.species.hud.equip_slots)))
src << "<span class='warning'>I don't feel strong enough without my robe.</span>"
return 0
if(!is_wiz_garb(src.shoes))
if(!is_wiz_garb(src.shoes) && (!species.hud || (slot_shoes in src.species.hud.equip_slots)))
src << "<span class='warning'>I don't feel strong enough without my sandals.</span>"
return 0
if(!is_wiz_garb(src.head))
if(!is_wiz_garb(src.head) && (!species.hud || (slot_head in src.species.hud.equip_slots)))
src << "<span class='warning'>I don't feel strong enough without my hat.</span>"
return 0
return 1
+10
View File
@@ -6,6 +6,12 @@ var/datum/antagonist/cultist/cult
if(player.mind in cult.current_antagonists)
return 1
/proc/iscult(var/mob/test)
if (test.faction == "cult")
return 1
else return iscultist(test)
/datum/antagonist/cultist
id = MODE_CULTIST
role_text = "Cultist"
@@ -104,11 +110,15 @@ var/datum/antagonist/cultist/cult
player.memory = ""
if(show_message)
player.current.visible_message("<FONT size = 3>[player.current] looks like they just reverted to their old faith!</FONT>")
if(. && player.current && !istype(player.current, /mob/living/simple_animal/construct))
player.current.remove_language(LANGUAGE_CULT)
/datum/antagonist/cultist/add_antagonist(var/datum/mind/player)
. = ..()
if(.)
player << "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back."
if(player.current && !istype(player.current, /mob/living/simple_animal/construct))
player.current.add_language(LANGUAGE_CULT)
/datum/antagonist/cultist/can_become_antag(var/datum/mind/player, ignore_role = 1)
if(!..())
+10 -2
View File
@@ -28,8 +28,8 @@ var/datum/antagonist/revolutionary/revs
faction_indicator = "rev"
faction_invisible = 1
restricted_jobs = list("Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer")
protected_jobs = list("Security Officer", "Warden", "Detective")
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Personnel", "Chief Engineer", "Research Director", "Chief Medical Officer", "Captain", "Head of Security", "Internal Affairs Agent")
/datum/antagonist/revolutionary/New()
..()
@@ -46,3 +46,11 @@ var/datum/antagonist/revolutionary/revs
rev_obj.target = player.mind
rev_obj.explanation_text = "Assassinate, capture or convert [player.real_name], the [player.mind.assigned_role]."
global_objectives += rev_obj
/datum/antagonist/revolutionary/can_become_antag(var/datum/mind/player)
if(!..())
return 0
for(var/obj/item/weapon/implant/loyalty/L in player.current)
if(L && (L.imp_in == player.current))
return 0
return 1
+2 -2
View File
@@ -2,8 +2,8 @@ var/datum/antagonist/thrall/vampire_thrall = null
/datum/antagonist/thrall
id = MODE_THRALL
role_text = "thrall"
role_text_plural = "thralls"
role_text = "Thrall"
role_text_plural = "Thralls"
bantype = "vampires"
feedback_tag = "thrall_objective"
restricted_jobs = list("AI", "Cyborg", "Chaplain")
+35 -20
View File
@@ -100,7 +100,7 @@ var/list/ghostteleportlocs = list()
icon_state = "space"
requires_power = 1
always_unpowered = 1
lighting_use_dynamic = 1
dynamic_lighting = 1
power_light = 0
power_equip = 0
power_environ = 0
@@ -350,7 +350,7 @@ area/space/atmosalert()
name = "\improper Centcom"
icon_state = "centcom"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
no_light_control = 1
/area/centcom/control
@@ -389,7 +389,7 @@ area/space/atmosalert()
name = "\improper Mercenary Base"
icon_state = "syndie-ship"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
no_light_control = 1
/area/syndicate_mothership/control
@@ -444,7 +444,7 @@ area/space/atmosalert()
name = "\improper Thunderdome"
icon_state = "thunder"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
sound_env = ARENA
no_light_control = 1
@@ -468,7 +468,7 @@ area/space/atmosalert()
/area/acting
name = "\improper Centcom Acting Guild"
icon_state = "red"
lighting_use_dynamic = 0
dynamic_lighting = 0
requires_power = 0
no_light_control = 1
@@ -477,7 +477,7 @@ area/space/atmosalert()
/area/acting/stage
name = "\improper Stage"
lighting_use_dynamic = 1
dynamic_lighting = 1
icon_state = "yellow"
@@ -544,7 +544,7 @@ area/space/atmosalert()
name = "\improper Wizard's Den"
icon_state = "yellow"
requires_power = 0
lighting_use_dynamic = 0
dynamic_lighting = 0
no_light_control = 1
/area/skipjack_station
@@ -676,6 +676,13 @@ area/space/atmosalert()
flags = RAD_SHIELDED
sound_env = TUNNEL_ENCLOSED
turf_initializer = new /datum/turf_initializer/maintenance()
ambience = list(
'sound/ambience/ambimaint1.ogg',
'sound/ambience/ambimaint2.ogg',
'sound/ambience/ambimaint3.ogg',
'sound/ambience/ambimaint4.ogg',
'sound/ambience/ambimaint5.ogg'
)
/area/maintenance/aft
name = "Aft Maintenance"
@@ -1063,7 +1070,7 @@ area/space/atmosalert()
/area/holodeck
name = "\improper Holodeck"
icon_state = "Holodeck"
lighting_use_dynamic = 0
dynamic_lighting = 0
sound_env = LARGE_ENCLOSED
no_light_control = 1
@@ -1133,13 +1140,19 @@ area/space/atmosalert()
/area/engineering/
name = "\improper Engineering"
icon_state = "engineering"
ambience = list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
ambience = list(
'sound/ambience/ambisin1.ogg',
'sound/ambience/ambisin2.ogg',
'sound/ambience/ambisin3.ogg',
'sound/ambience/ambisin4.ogg'
)
/area/engineering/atmos
name = "\improper Atmospherics"
icon_state = "atmos"
sound_env = LARGE_ENCLOSED
no_light_control = 1
name = "\improper Atmospherics"
icon_state = "atmos"
sound_env = LARGE_ENCLOSED
no_light_control = 1
ambience = list('sound/ambience/ambiatm1.ogg')
/area/engineering/atmos/monitoring
name = "\improper Atmospherics Monitoring Room"
@@ -1217,7 +1230,6 @@ area/space/atmosalert()
/area/solar
requires_power = 1
always_unpowered = 1
lighting_use_dynamic = 0
base_turf = /turf/space
auxport
@@ -1580,6 +1592,7 @@ area/space/atmosalert()
/area/hydroponics
name = "\improper Hydroponics"
icon_state = "hydro"
no_light_control = TRUE
/area/hydroponics/garden
name = "\improper Garden"
@@ -1613,10 +1626,12 @@ area/space/atmosalert()
/area/rnd/xenobiology/xenoflora_storage
name = "\improper Xenoflora Storage"
icon_state = "xeno_f_store"
no_light_control = TRUE
/area/rnd/xenobiology/xenoflora
name = "\improper Xenoflora Lab"
icon_state = "xeno_f_lab"
no_light_control = TRUE
/area/rnd/storage
name = "\improper Toxins Storage"
@@ -1949,25 +1964,25 @@ area/space/atmosalert()
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/turret_protected/AIsatextFS
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/turret_protected/AIsatextAS
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/turret_protected/AIsatextAP
name = "\improper AI Sat Ext"
icon_state = "storage"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
/area/turret_protected/NewAIMain
name = "\improper AI Main New"
@@ -2143,7 +2158,7 @@ area/space/atmosalert()
name = "Beach"
icon_state = "null"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
requires_power = 0
ambience = list()
var/sound/mysound = null
@@ -2265,7 +2280,7 @@ var/list/the_station_areas = list (
name = "Keelin's private beach"
icon_state = "null"
luminosity = 1
lighting_use_dynamic = 0
dynamic_lighting = 0
requires_power = 0
var/sound/mysound = null
no_light_control = 1
+34 -14
View File
@@ -6,9 +6,10 @@
/area
var/global/global_uid = 0
var/uid
blend_mode = BLEND_MULTIPLY
/area/New()
icon_state = ""
icon_state = "white"
layer = 10
uid = ++global_uid
all_areas += src
@@ -18,7 +19,7 @@
power_equip = 0
power_environ = 0
if(lighting_use_dynamic)
if(dynamic_lighting)
luminosity = 0
else
luminosity = 1
@@ -148,21 +149,40 @@
D.open()
return
#define DO_PARTY(COLOR) animate(color = COLOR, time = 0.5 SECONDS, easing = QUAD_EASING)
/area/proc/updateicon()
if ((fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc.
if(fire && !eject && !party)
icon_state = "blue"
/*else if(atmosalm && !fire && !eject && !party)
icon_state = "bluenew"*/
else if(!fire && eject && !party)
icon_state = "red"
else if(party && !fire && !eject)
icon_state = "party"
if(fire && !eject && !party) // FIRE
color = "#ff9292"
animate(src) // stop any current animations.
animate(src, color = "#ffa5b2", time = 1 SECOND, loop = -1, easing = SINE_EASING)
animate(color = "#ff9292", time = 1 SECOND, easing = SINE_EASING)
else if(!fire && eject && !party) // EJECT
color = "#ff9292"
animate(src)
animate(src, color = "#bc8a81", time = 1 SECOND, loop = -1, easing = EASE_IN|CUBIC_EASING)
animate(color = "#ff9292", time = 0.5 SECOND, easing = EASE_OUT|CUBIC_EASING)
else if(party && !fire && !eject) // PARTY
color = "#ff728e"
animate(src)
animate(src, color = "#7272ff", time = 0.5 SECONDS, loop = -1, easing = QUAD_EASING)
DO_PARTY("#72aaff")
DO_PARTY("#ffc68e")
DO_PARTY("#72c6ff")
DO_PARTY("#ff72e2")
DO_PARTY("#72ff8e")
DO_PARTY("#ffff8e")
DO_PARTY("#ff728e")
else
icon_state = "blue-red"
color = "#ffb2b2"
animate(src)
animate(src, color = "#B3DFFF", time = 0.5 SECOND, loop = -1, easing = SINE_EASING)
animate(color = "#ffb2b2", time = 0.5 SECOND, loop = -1, easing = SINE_EASING)
else
// new lighting behaviour with obj lights
icon_state = null
animate(src, color = "#FFFFFF", time = 0.5 SECONDS, easing = QUAD_EASING) // Stop the animation.
#undef DO_PARTY
/*
@@ -353,4 +373,4 @@ var/list/mob/living/forced_ambiance_list = new
turfs += F
if (turfs.len)
return pick(turfs)
else return null
else return null
+8 -2
View File
@@ -223,7 +223,7 @@ its easier to just keep the beam vertical.
/atom/proc/set_dir(new_dir)
. = new_dir != dir
dir = new_dir
for(var/datum/light_source/L in light_sources)
/*for(var/datum/light_source/L in light_sources)
if (L.source_atom.offset_light)
L.force_update = 1
if (world.tick_usage < 80)
@@ -231,7 +231,7 @@ its easier to just keep the beam vertical.
//This makes things more responsive, but probably has a performance cost for people spinning rapidly
//Ergo, it checks tick usage first
else
L.source_atom.update_light()
L.source_atom.update_light()*/
/atom/proc/ex_act()
return
@@ -505,3 +505,9 @@ its easier to just keep the beam vertical.
else if(ismob(I))
var/mob/M = I
M.show_message( message, 2, deaf_message, 1)
/atom/proc/change_area(var/area/oldarea, var/area/newarea)
change_area_name(oldarea.name, newarea.name)
/atom/proc/change_area_name(var/oldname, var/newname)
name = replacetext(name,oldname,newname)
+18
View File
@@ -72,7 +72,9 @@
loc.Exited(src)
loc = destination
loc.Entered(src)
update_client_hook(loc)
return 1
update_client_hook(loc)
return 0
//called when src is thrown into hit_atom
@@ -274,3 +276,19 @@ var/list/accessible_z_levels = list("1" = 5, "3" = 10, "4" = 15, "6" = 60)
if(!candidates.len)
return null
return text2num(pickweight(candidates))
// Parallax stuff.
/atom/movable/proc/update_client_hook(atom/destination)
if(locate(/mob) in src)
for(var/client/C in parallax_on_clients)
if((get_turf(C.eye) == destination) && (C.mob.hud_used))
C.mob.hud_used.update_parallax_values()
/mob/update_client_hook(atom/destination)
if(locate(/mob) in src)
for(var/client/C in parallax_on_clients)
if((get_turf(C.eye) == destination) && (C.mob.hud_used))
C.mob.hud_used.update_parallax_values()
else if(client && hud_used)
hud_used.update_parallax_values()
-128
View File
@@ -1,128 +0,0 @@
/////////////////////////// DNA DATUM
/datum/dna
var/unique_enzymes = null
var/struc_enzymes = null
var/uni_identity = null
var/b_type = "A+"
var/mutantrace = null //The type of mutant race the player is if applicable (i.e. potato-man)
var/real_name //Stores the real name of the person who originally got this dna datum. Used primarely for changelings,
/datum/dna/proc/check_integrity(var/mob/living/carbon/human/character)
if(character)
if(length(uni_identity) != 39)
//Lazy.
var/temp
//Hair
var/hair = 0
if(!character.h_style)
character.h_style = "Skinhead"
var/hrange = round(4095 / hair_styles_list.len)
var/index = hair_styles_list.Find(character.h_style)
if(index)
hair = index * hrange - rand(1,hrange-1)
//Facial Hair
var/beard = 0
if(!character.f_style)
character.f_style = "Shaved"
var/f_hrange = round(4095 / facial_hair_styles_list.len)
index = facial_hair_styles_list.Find(character.f_style)
if(index)
beard = index * f_hrange - rand(1,f_hrange-1)
temp = add_zero2(num2hex((character.r_hair),1), 3)
temp += add_zero2(num2hex((character.b_hair),1), 3)
temp += add_zero2(num2hex((character.g_hair),1), 3)
temp += add_zero2(num2hex((character.r_facial),1), 3)
temp += add_zero2(num2hex((character.b_facial),1), 3)
temp += add_zero2(num2hex((character.g_facial),1), 3)
temp += add_zero2(num2hex(((character.s_tone + 220) * 16),1), 3)
temp += add_zero2(num2hex((character.r_eyes),1), 3)
temp += add_zero2(num2hex((character.g_eyes),1), 3)
temp += add_zero2(num2hex((character.b_eyes),1), 3)
var/gender
if (character.gender == MALE)
gender = add_zero2(num2hex((rand(1,(2050+BLOCKADD))),1), 3)
else
gender = add_zero2(num2hex((rand((2051+BLOCKADD),4094)),1), 3)
temp += gender
temp += add_zero2(num2hex((beard),1), 3)
temp += add_zero2(num2hex((hair),1), 3)
uni_identity = temp
if(length(struc_enzymes)!= 3*STRUCDNASIZE)
var/mutstring = ""
for(var/i = 1, i <= STRUCDNASIZE, i++)
mutstring += add_zero2(num2hex(rand(1,1024)),3)
struc_enzymes = mutstring
if(length(unique_enzymes) != 32)
unique_enzymes = md5(character.real_name)
else
if(length(uni_identity) != 39) uni_identity = "00600200A00E0110148FC01300B0095BD7FD3F4"
if(length(struc_enzymes)!= 3*STRUCDNASIZE) struc_enzymes = "43359156756131E13763334D1C369012032164D4FE4CD61544B6C03F251B6C60A42821D26BA3B0FD6"
/datum/dna/proc/ready_dna(mob/living/carbon/human/character)
var/temp
//Hair
var/hair = 0
if(!character.h_style)
character.h_style = "Bald"
var/hrange = round(4095 / hair_styles_list.len)
var/index = hair_styles_list.Find(character.h_style)
if(index)
hair = index * hrange - rand(1,hrange-1)
//Facial Hair
var/beard = 0
if(!character.f_style)
character.f_style = "Shaved"
var/f_hrange = round(4095 / facial_hair_styles_list.len)
index = facial_hair_styles_list.Find(character.f_style)
if(index)
beard = index * f_hrange - rand(1,f_hrange-1)
temp = add_zero2(num2hex((character.r_hair),1), 3)
temp += add_zero2(num2hex((character.b_hair),1), 3)
temp += add_zero2(num2hex((character.g_hair),1), 3)
temp += add_zero2(num2hex((character.r_facial),1), 3)
temp += add_zero2(num2hex((character.b_facial),1), 3)
temp += add_zero2(num2hex((character.g_facial),1), 3)
temp += add_zero2(num2hex(((character.s_tone + 220) * 16),1), 3)
temp += add_zero2(num2hex((character.r_eyes),1), 3)
temp += add_zero2(num2hex((character.g_eyes),1), 3)
temp += add_zero2(num2hex((character.b_eyes),1), 3)
var/gender
if (character.gender == MALE)
gender = add_zero2(num2hex((rand(1,(2050+BLOCKADD))),1), 3)
else
gender = add_zero2(num2hex((rand((2051+BLOCKADD),4094)),1), 3)
temp += gender
temp += add_zero2(num2hex((beard),1), 3)
temp += add_zero2(num2hex((hair),1), 3)
uni_identity = temp
var/mutstring = ""
for(var/i = 1, i <= STRUCDNASIZE, i++)
mutstring += add_zero2(num2hex(rand(1,1024)),3)
struc_enzymes = mutstring
unique_enzymes = md5(character.real_name)
reg_dna[unique_enzymes] = character.real_name
/////////////////////////// DNA DATUM
+1 -3
View File
@@ -59,9 +59,7 @@
C.prefs.be_special_role ^= MODE_MERCENARY
obj/item/weapon/antag_spawner/borg_tele/spawn_antag(client/C, turf/T)
var/datum/effect/effect/system/spark_spread/S = new /datum/effect/effect/system/spark_spread
S.set_up(4, 1, src)
S.start()
spark(T, 4, alldirs)
var/mob/living/silicon/robot/H = new /mob/living/silicon/robot/syndicate(T)
H.key = C.key
var/newname = sanitizeSafe(input(H,"Enter a name, or leave blank for the default name.", "Name change","") as text, MAX_NAME_LEN)
@@ -0,0 +1,91 @@
/obj/item/weapon/melee/arm_blade
name = "arm blade"
desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter."
icon = 'icons/obj/changeling.dmi'
icon_state = "arm_blade"
item_state = "arm_blade"
contained_sprite = 1
w_class = 4
force = 30
sharp = 1
edge = 1
anchored = 1
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
can_embed = 0
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
var/mob/living/creator
/obj/item/weapon/melee/arm_blade/New()
processing_objects |= src
/obj/item/weapon/melee/arm_blade/Destroy()
processing_objects -= src
..()
/obj/item/weapon/melee/arm_blade/dropped()
visible_message("<span class='danger'>With a sickening crunch, [user] reforms their arm blade into an arm!</span>",
"<span class='notice'>We assimilate the weapon back into our body.</span>",
"<span class='warning'>You hear organic matter ripping and tearing!</span>")
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
spawn(1) if(src) qdel(src)
/obj/item/weapon/melee/arm_blade/process()
if(!creator || loc != creator || (creator.l_hand != src && creator.r_hand != src))
// Tidy up a bit.
if(istype(loc,/mob/living))
var/mob/living/carbon/human/host = loc
if(istype(host))
for(var/obj/item/organ/external/organ in host.organs)
for(var/obj/item/O in organ.implants)
if(O == src)
organ.implants -= src
host.pinned -= src
host.embedded -= src
host.drop_from_inventory(src)
spawn(1) if(src) qdel(src)
/obj/item/weapon/shield/riot/changeling
name = "shield-like mass"
desc = "A mass of tough, boney tissue. You can still see the fingers as a twisted pattern in the shield."
icon = 'icons/obj/changeling.dmi'
icon_state = "ling_shield"
item_state = "ling_shield"
contained_sprite = 1
slot_flags = null
anchored = 1
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
can_embed = 0
var/mob/living/creator
/obj/item/weapon/shield/riot/changeling/New()
processing_objects |= src
/obj/item/weapon/shield/riot/changeling/Destroy()
processing_objects -= src
..()
/obj/item/weapon/shield/riot/changeling/dropped()
visible_message("<span class='danger'>With a sickening crunch, [user] reforms their arm blade into an arm!</span>",
"<span class='notice'>We assimilate the weapon back into our body.</span>",
"<span class='warning'>You hear organic matter ripping and tearing!</span>")
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
spawn(1) if(src) qdel(src)
/obj/item/weapon/shield/riot/changeling/process()
if(!creator || loc != creator || (creator.l_hand != src && creator.r_hand != src))
// Tidy up a bit.
if(istype(loc,/mob/living))
var/mob/living/carbon/human/host = loc
if(istype(host))
for(var/obj/item/organ/external/organ in host.organs)
for(var/obj/item/O in organ.implants)
if(O == src)
organ.implants -= src
host.pinned -= src
host.embedded -= src
host.drop_from_inventory(src)
spawn(1) if(src) qdel(src)
@@ -1,8 +1,7 @@
var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")
/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind)
var/list/absorbed_dna = list()
var/list/absorbed_species = list()
var/list/datum/absorbed_dna/absorbed_dna = list()
var/list/absorbed_languages = list()
var/absorbedcount = 0
var/chem_charges = 20
@@ -31,12 +30,24 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
geneticdamage = max(0, geneticdamage-1)
/datum/changeling/proc/GetDNA(var/dna_owner)
var/datum/dna/chosen_dna
for(var/datum/dna/DNA in absorbed_dna)
if(dna_owner == DNA.real_name)
chosen_dna = DNA
break
return chosen_dna
for(var/datum/absorbed_dna/DNA in absorbed_dna)
if(dna_owner == DNA.name)
return DNA
/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA)
var/datum/changeling/changeling = null
if(src.mind && src.mind.changeling)
changeling = src.mind.changeling
if(!changeling)
return
for(var/language in newDNA.languages)
changeling.absorbed_languages |= language
changeling_update_languages(changeling.absorbed_languages)
if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite
changeling.absorbed_dna += newDNA
//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human
/mob/proc/make_changeling()
@@ -65,14 +76,13 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(!(P in src.verbs))
src.verbs += P.verbpath
mind.changeling.absorbed_dna |= dna
for(var/language in languages)
mind.changeling.absorbed_languages |= language
var/mob/living/carbon/human/H = src
if(istype(H))
mind.changeling.absorbed_species += H.species.name
for(var/language in languages)
mind.changeling.absorbed_languages |= language
var/datum/absorbed_dna/newDNA = new(H.real_name, H.dna, H.species.name, H.languages)
absorbDNA(newDNA)
return 1
@@ -83,13 +93,6 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(P.isVerb)
verbs -= P.verbpath
//removes changeling powers but keeps those with allowduringlesserform
/mob/proc/remove_changeling_powers_monkey()
if(!mind || !mind.changeling) return
for(var/datum/power/changeling/P in mind.changeling.purchasedpowers)
if(P.isVerb && (P.allowduringlesserform == 0))
verbs -= P.verbpath
//Helper proc. Does all the checks and stuff for us to avoid copypasta
/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0)
@@ -133,46 +136,6 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
return
//Used to switch species based on the changeling datum.
/mob/proc/changeling_change_species()
set category = "Changeling"
set name = "Change Species (5)"
var/mob/living/carbon/human/H = src
if(!istype(H))
src << "<span class='warning'>We may only use this power while in humanoid form.</span>"
return
var/datum/changeling/changeling = changeling_power(5,1,0)
if(!changeling) return
if(changeling.absorbed_species.len < 2)
src << "<span class='warning'>We do not know of any other species genomes to use.</span>"
return
var/S = input("Select the target species: ", "Target Species", null) as null|anything in changeling.absorbed_species
if(!S) return
domutcheck(src, null)
changeling.chem_charges -= 5
changeling.geneticdamage = 30
src.visible_message("<span class='warning'>[src] transforms!</span>")
src.verbs -= /mob/proc/changeling_change_species
H.set_species(S,1) //Until someone moves body colour into DNA, they're going to have to use the default.
spawn(10)
src.verbs += /mob/proc/changeling_change_species
src.regenerate_icons()
changeling_update_languages(changeling.absorbed_languages)
feedback_add_details("changeling_powers","TR")
return 1
//Absorbs the victim's DNA making them uncloneable. Requires a strong grip on the victim.
//Doesn't cost anything as it's the most basic ability.
/mob/proc/changeling_absorb_dna()
@@ -196,6 +159,10 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
src << "<span class='warning'>We do not know how to parse this creature's DNA!</span>"
return
if(islesserform(T))
src << "<span class='warning'>This creature DNA is not compatible with our form!</span>"
return
if(HUSK in T.mutations)
src << "<span class='warning'>This creature's DNA is ruined beyond useability!</span>"
return
@@ -213,6 +180,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
switch(stage)
if(1)
src << "<span class='notice'>This creature is compatible. We must hold still...</span>"
src.visible_message("<span class='warning'>[src]'s skin begins to shift and squirm!</span>")
if(2)
src << "<span class='notice'>We extend a proboscis.</span>"
src.visible_message("<span class='warning'>[src] extends a proboscis!</span>")
@@ -234,9 +202,6 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
src.visible_message("<span class='danger'>[src] sucks the fluids from [T]!</span>")
T << "<span class='danger'>You have been absorbed by the changeling!</span>"
T.dna.real_name = T.real_name //Set this again, just to be sure that it's properly set.
changeling.absorbed_dna |= T.dna
if(src.nutrition < max_nutrition) src.nutrition = min((nutrition + T.nutrition), max_nutrition)
changeling.chem_charges += 10
changeling.geneticpoints += 2
@@ -247,16 +212,15 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
changeling_update_languages(changeling.absorbed_languages)
//Steal their species!
if(T.species && !(T.species.name in changeling.absorbed_species))
changeling.absorbed_species += T.species.name
var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages)
absorbDNA(newDNA)
if(T.mind && T.mind.changeling)
if(T.mind.changeling.absorbed_dna)
for(var/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot
if(dna_data in changeling.absorbed_dna)
for(var/datum/absorbed_dna/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot
if(changeling.GetDNA(dna_data.name))
continue
changeling.absorbed_dna += dna_data
absorbDNA(dna_data)
changeling.absorbedcount++
T.mind.changeling.absorbed_dna.len = 1
@@ -295,31 +259,44 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(!changeling) return
var/list/names = list()
for(var/datum/dna/DNA in changeling.absorbed_dna)
names += "[DNA.real_name]"
for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
names += "[DNA.name]"
var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
if(!S) return
var/datum/dna/chosen_dna = changeling.GetDNA(S)
var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
if(!chosen_dna)
return
changeling.chem_charges -= 5
src.visible_message("<span class='warning'>[src] transforms!</span>")
changeling.geneticdamage = 30
src.dna = chosen_dna.Clone()
src.real_name = chosen_dna.real_name
src.flavor_text = ""
src.UpdateAppearance()
domutcheck(src, null)
handle_changeling_transform(chosen_dna)
src.verbs -= /mob/proc/changeling_transform
spawn(10) src.verbs += /mob/proc/changeling_transform
spawn(10)
src.verbs += /mob/proc/changeling_transform
changeling_update_languages(changeling.absorbed_languages)
feedback_add_details("changeling_powers","TR")
return 1
/mob/proc/handle_changeling_transform(var/datum/absorbed_dna/chosen_dna)
src.visible_message("<span class='warning'>[src] transforms!</span>")
if(ishuman(src))
var/mob/living/carbon/human/H = src
var/newSpecies = chosen_dna.speciesName
H.set_species(newSpecies,1)
src.dna = chosen_dna.dna
src.real_name = chosen_dna.name
src.flavor_text = ""
domutcheck(src, null)
src.UpdateAppearance()
//Transform into a monkey.
/mob/proc/changeling_lesser_form()
@@ -340,15 +317,10 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
return
changeling.chem_charges--
H.remove_changeling_powers_monkey() //we weren't making us of allowduringlesserform before for some reason? -Ryan784
H.visible_message("<span class='warning'>[H] transforms!</span>")
changeling.geneticdamage = 30
H << "<span class='warning'>Our genes cry out!</span>"
var/list/implants = list() //Try to preserve implants.
for(var/obj/item/weapon/implant/W in H)
implants += W
H.monkeyize()
verbs += /mob/proc/changeling_lesser_transform //allow us to actually transform BACK into a human - Ryan784
H = H.monkeyize()
feedback_add_details("changeling_powers","LF")
return 1
@@ -457,25 +429,29 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
// charge the changeling chemical cost for stasis
changeling.chem_charges -= 20
// restore us to health
C.revive()
// remove our fake death flag
C.status_flags &= ~(FAKEDEATH)
// let us move again
C.update_canmove()
// re-add out changeling powers
C.make_changeling()
// sending display messages
C << "<span class='notice'>We have regenerated.</span>"
C << "<span class='notice'><font size='5'>We are ready to rise. Use the <b>Revive</b> verb when you are ready.</font></span>"
C.verbs += /mob/proc/changeling_revive
feedback_add_details("changeling_powers","FD")
return 1
/mob/proc/changeling_revive()
set category = "Changeling"
set name = "Revive"
var/mob/living/carbon/C = src
// restore us to health
C.revive()
// remove our fake death flag
C.status_flags &= ~(FAKEDEATH)
// let us move again
C.update_canmove()
// re-add out changeling powers
C.make_changeling()
// sending display messages
C << "<span class='notice'>We have regenerated.</span>"
C.verbs -= /mob/proc/changeling_revive
//Boosts the range of your next sting attack by 1
/mob/proc/changeling_boost_range()
@@ -581,7 +557,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
// HIVE MIND UPLOAD/DOWNLOAD DNA
var/list/datum/dna/hivemind_bank = list()
var/list/datum/absorbed_dna/hivemind_bank = list()
/mob/proc/changeling_hiveupload()
set category = "Changeling"
@@ -592,9 +568,14 @@ var/list/datum/dna/hivemind_bank = list()
if(!changeling) return
var/list/names = list()
for(var/datum/dna/DNA in changeling.absorbed_dna)
if(!(DNA in hivemind_bank))
names += DNA.real_name
for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
var/valid = 1
for(var/datum/absorbed_dna/DNB in hivemind_bank)
if(DNA.name == DNB.name)
valid = 0
break
if(valid)
names += DNA.name
if(names.len <= 0)
src << "<span class='notice'>The airwaves already have all of our DNA.</span>"
@@ -603,7 +584,7 @@ var/list/datum/dna/hivemind_bank = list()
var/S = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names
if(!S) return
var/datum/dna/chosen_dna = changeling.GetDNA(S)
var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
if(!chosen_dna)
return
@@ -622,9 +603,9 @@ var/list/datum/dna/hivemind_bank = list()
if(!changeling) return
var/list/names = list()
for(var/datum/dna/DNA in hivemind_bank)
if(!(DNA in changeling.absorbed_dna))
names[DNA.real_name] = DNA
for(var/datum/absorbed_dna/DNA in hivemind_bank)
if(!(changeling.GetDNA(DNA.name)))
names[DNA.name] = DNA
if(names.len <= 0)
src << "<span class='notice'>There's no new DNA to absorb from the air.</span>"
@@ -637,7 +618,7 @@ var/list/datum/dna/hivemind_bank = list()
return
changeling.chem_charges -= 20
changeling.absorbed_dna += chosen_dna
absorbDNA(chosen_dna)
src << "<span class='notice'>We absorb the DNA of [S] from the air.</span>"
feedback_add_details("changeling_powers","HD")
return 1
@@ -692,7 +673,7 @@ var/list/datum/dna/hivemind_bank = list()
return 1
//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities)
/mob/proc/changeling_sting(var/required_chems=0, var/verb_path)
/mob/proc/changeling_sting(var/required_chems=0, var/verb_path, var/stealthy = 0)
var/datum/changeling/changeling = changeling_power(required_chems)
if(!changeling) return
@@ -711,9 +692,20 @@ var/list/datum/dna/hivemind_bank = list()
src.verbs -= verb_path
spawn(10) src.verbs += verb_path
src << "<span class='notice'>We stealthily sting [T].</span>"
if(stealthy == 1)
src << "<span class='notice'>We stealthily sting [T].</span>"
T << "<span class='warning'>You feel a tiny prick.</span>"
else
src.visible_message(pick("<span class='danger'>[src]'s eyes balloon and burst out in a welter of blood, burrowing into [T]!</span>",
"<span class='danger'>[src]'s arm rapidly shifts into a giant scorpion-stinger and stabs into [T]!</span>",
"<span class='danger'>[src]'s throat lengthens and twists before vomitting a chunky red spew all over [T]!</span>",
"<span class='danger'>[src]'s tongue stretches an impossible length and stabs into [T]!</span>",
"<span class='danger'>[src] sneezes a cloud of shrieking spiders at [T]!</span>",
"<span class='danger'>[src] erupts a grotesque tail and impales [T]!</span>",
"<span class='danger'>[src]'s chin skin bulges and tears, launching a bone-dart at [T]!</span>"))
if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting
T << "<span class='warning'>You feel a tiny prick.</span>"
return
@@ -722,7 +714,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Hallucination Sting (15)"
set desc = "Causes terror in the target."
var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting)
var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting,stealthy = 1)
if(!T) return 0
spawn(rand(300,600))
if(T) T.hallucination += 400
@@ -734,7 +726,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Silence sting (10)"
set desc="Sting target"
var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting)
var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting,stealthy = 1)
if(!T) return 0
T.silent += 30
feedback_add_details("changeling_powers","SS")
@@ -745,7 +737,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Blind sting (20)"
set desc="Sting target"
var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting)
var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>Your eyes burn horrificly!</span>"
T.disabilities |= NEARSIGHTED
@@ -760,7 +752,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Deaf sting (5)"
set desc="Sting target:"
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting)
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>Your ears pop and begin ringing loudly!</span>"
T.sdisabilities |= DEAF
@@ -773,7 +765,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Paralysis sting (30)"
set desc="Sting target"
var/mob/living/carbon/T = changeling_sting(30,/mob/proc/changeling_paralysis_sting)
var/mob/living/carbon/T = changeling_sting(30,/mob/proc/changeling_paralysis_sting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>Your muscles begin to painfully tighten.</span>"
T.Weaken(20)
@@ -788,29 +780,25 @@ var/list/datum/dna/hivemind_bank = list()
var/datum/changeling/changeling = changeling_power(40)
if(!changeling) return 0
var/list/names = list()
for(var/datum/dna/DNA in changeling.absorbed_dna)
names += "[DNA.real_name]"
for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
names += "[DNA.name]"
var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
if(!S) return
var/datum/dna/chosen_dna = changeling.GetDNA(S)
var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
if(!chosen_dna)
return
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_transformation_sting)
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_transformation_sting,stealthy = 1)
if(!T) return 0
if((HUSK in T.mutations) || (!ishuman(T) && !issmall(T)))
src << "<span class='warning'>Our sting appears ineffective against its DNA.</span>"
return 0
T.visible_message("<span class='warning'>[T] transforms!</span>")
T.dna = chosen_dna.Clone()
T.real_name = chosen_dna.real_name
T.UpdateAppearance()
domutcheck(T, null)
T.handle_changeling_transform(chosen_dna)
feedback_add_details("changeling_powers","TS")
return 1
@@ -819,10 +807,9 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Unfat sting (5)"
set desc = "Sting target"
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_unfat_sting)
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_unfat_sting,stealthy = 1)
if(!T) return 0
T << "<span class='danger'>you feel a small prick as stomach churns violently and you become to feel skinnier.</span>"
T.overeatduration = 0
T.nutrition -= 100
feedback_add_details("changeling_powers","US")
return 1
@@ -832,13 +819,13 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Death Sting (40)"
set desc = "Causes spasms onto death."
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_DEATHsting)
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_DEATHsting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>You feel a small prick and your chest becomes tight.</span>"
T.silent = 10
T.Paralyse(10)
T.make_jittery(1000)
if(T.reagents) T.reagents.add_reagent("lexorin", 40)
if(T.reagents) T.reagents.add_reagent("cyanide", 5)
feedback_add_details("changeling_powers","DTHS")
return 1
@@ -853,13 +840,66 @@ var/list/datum/dna/hivemind_bank = list()
if(!changeling)
return 0
var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting)
var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting,stealthy = 1)
if(!T) return 0
T.dna.real_name = T.real_name
changeling.absorbed_dna |= T.dna
if(T.species && !(T.species.name in changeling.absorbed_species))
changeling.absorbed_species += T.species.name
var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages)
absorbDNA(newDNA)
feedback_add_details("changeling_powers","ED")
return 1
/mob/proc/armblades()
set category = "Changeling"
set name = "Form Blades"
set desc="Rupture the flesh and mend the bone of your hand into a deadly blade."
var/mob/living/M = src
if(M.l_hand && M.r_hand)
M << "<span class='danger'>Your hands are full.</span>"
return
var/obj/item/weapon/melee/arm_blade/blade = new(M)
blade.creator = M
M.put_in_hands(blade)
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
src.visible_message("<span class='danger'>A grotesque blade forms around [M]\'s arm!</span>",
"<span class='danger'>Our arm twists and mutates, transforming it into a deadly blade.</span>",
"<span class='danger'>You hear organic matter ripping and tearing!</span>")
gibs(src.loc)
/mob/proc/changeling_shield()
set category = "Changeling"
set name = "Form Shield"
set desc="Bend the flesh and bone of your hand into a grotesque shield."
var/mob/living/M = src
if(M.l_hand && M.r_hand)
M << "<span class='danger'>Your hands are full.</span>"
return
var/obj/item/weapon/shield/riot/changeling/shield = new(M)
shield.creator = M
M.put_in_hands(shield)
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
src.visible_message("<span class='warning'>The end of [M]\'s hand inflates rapidly, forming a huge shield-like mass!</span>",
"<span class='warning'>We inflate our hand into a robust shield.</span>",
"<span class='warning'>You hear organic matter ripping and tearing!</span>")
gibs(src.loc)
//dna related datum
/datum/absorbed_dna
var/name
var/datum/dna/dna
var/speciesName
var/list/languages
/datum/absorbed_dna/New(var/newName, var/newDNA, var/newSpecies, var/newLanguages)
..()
name = newName
dna = newDNA
speciesName = newSpecies
languages = newLanguages
@@ -26,12 +26,6 @@ var/list/datum/power/changeling/powerinstances = list()
genomecost = 0
verbpath = /mob/proc/changeling_transform
/datum/power/changeling/change_species
name = "Change Species"
desc = "We take on the apperance of a species that we have absorbed."
genomecost = 0
verbpath = /mob/proc/changeling_change_species
/datum/power/changeling/fakedeath
name = "Regenerative Stasis"
desc = "We become weakened to a death-like state, where we will rise again from death."
@@ -64,14 +58,14 @@ var/list/datum/power/changeling/powerinstances = list()
/datum/power/changeling/deaf_sting
name = "Deaf Sting"
desc = "We silently sting a human, completely deafening them for a short time."
desc = "We sting a human, completely deafening them for a short time."
genomecost = 1
allowduringlesserform = 1
verbpath = /mob/proc/changeling_deaf_sting
/datum/power/changeling/blind_sting
name = "Blind Sting"
desc = "We silently sting a human, completely blinding them for a short time."
desc = "We sting a human, completely blinding them for a short time."
genomecost = 2
allowduringlesserform = 1
verbpath = /mob/proc/changeling_blind_sting
@@ -108,7 +102,7 @@ var/list/datum/power/changeling/powerinstances = list()
/datum/power/changeling/paralysis_sting
name = "Paralysis Sting"
desc = "We silently sting a human, paralyzing them for a short time."
desc = "We sting a human, paralyzing them for a short time."
genomecost = 8
verbpath = /mob/proc/changeling_paralysis_sting
@@ -121,7 +115,7 @@ var/list/datum/power/changeling/powerinstances = list()
/datum/power/changeling/DeathSting
name = "Death Sting"
desc = "We silently sting a human, filling them with potent chemicals. Their rapid death is all but assured."
desc = "We sting a human, filling them with potent chemicals. Their rapid death is all but assured."
genomecost = 10
verbpath = /mob/proc/changeling_DEATHsting
@@ -184,7 +178,19 @@ var/list/datum/power/changeling/powerinstances = list()
genomecost = 7
verbpath = /mob/proc/changeling_rapidregen
//weapon and armor like powers
/datum/power/changeling/armblades
name = "Mutate Armblades"
desc = "Permits us to reshape our arms into a deadly blade."
genomecost = 2
verbpath = /mob/proc/armblades
/datum/power/changeling/shield
name = "Mutate Shield"
desc = "Permits us to bloat our hands into a robust shield."
genomecost = 3
verbpath = /mob/proc/changeling_shield
// Modularchangling, totally stolen from the new player panel. YAYY
/datum/changeling/proc/EvolutionMenu()//The new one
+541 -26
View File
@@ -17,59 +17,565 @@
desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie"
icon_state = "forge"
/*
Cult pylons can be used as arcane defensive turrets.
First of all they must be empowered with a sacrifice. Any small organic creature that can be picked up is valid
Hit the pylon with the creature - while still alive - to present it as a sacrifice, and then end its life to complete the process.
Once empowered, pylons will fire rapid, weak magical beams at non cult mobs.
As crystals, pylons are immune to energy weaponry. More than immune, they will absorb lasers and become
supercharged for some time, boosting their damage. Pylons can only be harmed by ballistics and physical weapons
Building reinforced windows around them is highly recommended.
Pylons are fairly fragile and will quickly break under direct hits.
A damaged pylon will self repair, if it contains a soul.
Pylons are largely intended as a stalling, early warning, fire support and area denial system. They are unlikely to actually
kill anyone alone but they can keep pesky civilians away for long enough to learn runes and prepare rituals.
If you can get ahold of a laser rifle to supercharge them, they can even give security some pause
Recommended tools for combatting pylons:
Heavy melee (fireaxe, baseball bat, etc)
Sniper rifles
Exosuits
Explosives
Ablative armour
*/
/obj/structure/cult/pylon
name = "Pylon"
desc = "A floating crystal that hums with an unearthly energy"
icon_state = "pylon"
description_antag = "A pylon can be upgraded into a magical defensive turret that shoots anyone opposing the cult\
</br>Upgrading a pylon requires a sacrifice. Bring it a small organic creature, like a monkey or mouse. Use the creature on the pylon, or drag and drop to present it.\
</br>Once the sacrifice is accepted, kill it to complete the process. This will gib its body and make a very visible mess. After this point the pylon is fixed to the floor and cant be moved\
</br>The pylon will fire weak beams that are harmless to the cult. In addition it can be upgraded even more by shooting it with a laser, which will give it a limited number of extra-power shots."
icon_state = "pylonbase"
var/isbroken = 0
light_range = 5
light_color = "#3e0000"
var/obj/item/wepon = null
var/pylonmode = 0
//Pylon has three states:
//0 = Idle, the pylon is an inert crystal
//1 = Awaiting sacrifice. The pylon is actively tracking a creature to be sacrificed to it
//2 = Turret. The pylon has been empowered by a sacrifice and is now a turret permanantly
var/damagetaken = 0
var/empowered = 0 //Number of empowered, higher-damage shots remaining
var/processing = 0
var/mob/living/sacrifice //Holds a reference to a mob that is a pending sacrifice
var/mob/living/sacrificer //A reference to the last mob that attempted to sacrifice something. So we can message them
//Sacrifier is also used in target handling. the pylon will not bite the hand that feeds it. Noncultist colleagues are fair game though
var/next_shot = 0 //Absolute world time when we're allowed to fire again
var/shot_delay = 5 //Minimum delay between shots, in deciseconds
var/mob/living/target
var/notarget //Number of times handle_firing has been called without finding anything to shoot at
//This is used for switching to lower-intensity processing
var/datum/language/cultcommon/lang
//An instance of the cult language datum. Used to scramble speech when speaking to noncultists
var/turf/last_target_loc
var/process_interval = 1
var/ticks
anchored = 0
//Just a subtype that starts off in turret mode. For adminbus and debugging. Maybe a future wizard spell
//Spawn them next to ERPers
/obj/structure/cult/pylon/turret
pylonmode = 2
/obj/structure/cult/pylon/turret/New()
..()
start_process()
//Another subtype which starts with infinite empower shots. For empowered adminbus
/obj/structure/cult/pylon/turret/empowered
empowered = 99999999
/obj/structure/cult/pylon/New()
..()
lang = new /datum/language/cultcommon()
update_icon()
/obj/structure/cult/pylon/examine(var/mob/user)
..()
if (damagetaken)
switch (damagetaken)
if (1 to 8)
user << "It has very faint hairline fractures."
if (8 to 20)
user << "It has several cracks across its surface."
if (20 to 30)
user << "It is chipped and deeply cracked, it may shatter with much more pressure."
if (30 to INFINITY)
user << "It is almost cleaved in two, the pylon looks like it will fall to shards under its own weight."
/obj/structure/cult/pylon/Move()
..()
last_target_loc = null
/obj/structure/cult/pylon/proc/start_process()
process_interval = 1
if (!processing)
processing_objects += src
processing = 1
//If the pylon goes a long time without shooting anything, it will consider slowing down processing
/obj/structure/cult/pylon/proc/reconsider_interval()
var/mindist = INFINITY
for (var/m in player_list)
var/mob/living/L = m
if (L.z != z)
continue
if (L.stat == DEAD)
continue
if (iscult(L))
continue
if (L == sacrificer)
continue
var/d = get_dist(src, L)
if (d < mindist)
mindist = d
mindist = min(150, mindist)
process_interval = Ceiling(mindist*0.1)
process_interval = max(1, process_interval)
//Run each process loop, this function checks if we can stop processing yet.
//Returns 0 if its time to stop. Returns 1 if we should keep going.
/obj/structure/cult/pylon/proc/check_process()
if (isbroken)
return 0
if (pylonmode == 2)
return 1
if (pylonmode == 1)
if (sacrifice)
return 1
if (empowered > 0)
return 1
return 0
/obj/structure/cult/pylon/process()
ticks++
if(!check_process())
processing_objects.Remove(src)
processing = 0
return
switch (pylonmode)
if (1)
handle_sacrifice()
if (2)
if ((ticks % process_interval) == 0)
handle_firing()
if (damagetaken && prob(50))
damagetaken = max(0, damagetaken-1) //An empowered pylon slowly self repairs
if (prob(10))
visible_message("Cracks in the [src] gradually seal as new crystalline matter grows to fill them.")
if (empowered && prob(2))
empowered = max(0, empowered - 1) //Overcharging gradually wears off over time
if (empowered <= 0)
update_icon()
//If user is a cultist, speaks message to them with a prefix
//If user is not cultist, then speaks cult-y gibberish
/obj/structure/cult/pylon/proc/speak_to(var/mob/user, var/message)
if (iscult(user) || (/datum/language/cultcommon in user.languages))
user << "A voice speaks into your mind, [span("cult","\"<I>[message]</I>\"")]"
else
user << "A voice speaks into your mind, [span("cult","\"<I>[lang.scramble(message)]</I>\"")]"
//Todo: Replace the messages here with better ones. Should display a proper message to cultists
//And nonsensical arcane gibberish to non cultists
/obj/structure/cult/pylon/proc/present_sacrifice(var/mob/living/user, var/mob/living/victim)
if (!user || !victim)
return 0
if (isbroken)
user << "The pylon lies silent."
return 0
if (pylonmode != 0)
return 0
if (!(victim.mob_size) || victim.mob_size > 6)
//toolarge sacrifice, display a message and return
speak_to(user, "This creature is too large, we only require a lesser sacrifice. A small morsel of life...")
return
if (victim.stat == DEAD)
speak_to(user, "You are too late, the spark of life is already gone from this one. It is naught but an empty husk...")
return
var/types = victim.find_type()
if ((!(types & TYPE_ORGANIC)) || ((types & TYPE_WIERD)))
//Invalid sacrifice. Display a message and return
speak_to(user, "This soulless automaton cannot satisfy our hunger. We yearn for life essence, it must have a soul.")
return
sacrifice = victim
sacrificer = user
//Sacrifice accepted, display message here
speak_to(user, "Your sacrifice has been deemed worthy, and accepted. End its life now, and liberate its soul, to seal our contract...")
sacrifice << span("danger", "You feel an invisible force grip your soul, as you're drawn inexorably towards the pylon. Every part of you screams to flee from here!")
if (istype(sacrifice.loc,/obj/item/weapon/holder))
var/obj/item/weapon/holder/H = sacrifice.loc
H.release_to_floor()
else
sacrifice.forceMove(get_turf(sacrifice))
pylonmode = 1
update_icon()
start_process()
//Runs every proc while there's a pending sacrifice. This checks whether the sacrifice was killed or escaped
/obj/structure/cult/pylon/proc/handle_sacrifice()
.=0 //Return value has 3 states.
//0 = waiting, the sacrifice is still alive
//1 = Finished, the sacrifice was killed, finalize the process and become a turret
//-1 = Escape: The sacrifice left the vicinity of the pylon, return to inert mode.
if (!sacrifice)
//If the sacrifice was deleted somehow, we cant know exactly what happened. We'll assume it escaped
.=-1
else if(get_dist(src, sacrifice) > 5)
//If the sacrifice gets more than 5 tiles away, it has escaped
.=-1
else if (sacrifice.stat == DEAD)
.=1
switch(.)
if (1)
finalize_sacrifice()
if (-1)
speak_to(sacrificer, "Fool! Your offering has escaped. Bring it back, or find us another...")
sacrifice = null
pylonmode = 0
update_icon()
if (sacrifice)
walk_to(sacrifice,0)
else
walk_towards(sacrifice,src, 10)
/obj/structure/cult/pylon/proc/finalize_sacrifice()
sacrifice.visible_message(span("danger","\The [sacrifice]'s physical form unwinds as its soul is extracted from the remains, and drawn into the pylon!"))
if (istype(sacrifice.loc,/obj/item/weapon/holder))
var/obj/item/weapon/holder/H = sacrifice.loc
H.release_to_floor()
else
sacrifice.forceMove(get_turf(sacrifice)) //Make sure its on the floor before we gib it
pylonmode = 2
sacrifice.gib()
update_icon()
//Called every process in turret mode, and also by chaining spawns
/obj/structure/cult/pylon/proc/handle_firing()
if ((world.time < next_shot) || isbroken)
return
//Not ready to fire yet
var/list/stuffcache
//Stuffcache holds a list of things found by a dview call, so we only need to do it once per proc.
if (target && (target.stat != DEAD))
if (target.loc == last_target_loc)
//To minimise expensive DVIEW calls, if the target hasnt moved since the last shot we'll assume they're still in sight
//This could cause problems in some edge cases (like lowering firelocks or shutters without moving)
//But it has a major performance gain that makes it worth it
fire_at(target)
return
else
stuffcache = mobs_in_view(10, src)
//for (var/turf/T in stuffcache)
//new /obj/effect/testtrans(T)
if ((target in stuffcache) && isInSight(src, target))
for (var/mob/m in stuffcache)
fire_at(target)
return
else
target = null
//We may have already populated stuffcache this run, dont repeat work
if (!stuffcache)
stuffcache = mobs_in_view(10, src)
target = null //Either we lost a target or lack one
//for (var/turf/T in stuffcache)
//new /obj/effect/testtrans(T)
for (var/mob/living/L in stuffcache)
if (L == sacrificer)
continue
//A pylon won't shoot the person who created it, regardless of cult status.
//This is mainly for xenoarch antags
if (L.stat == DEAD)
continue
//No point shooting at corpses
if (iscult(L))
continue
//Pylon wont shoot at cultists or constructs
if (!isInSight(src, L))
continue
target = L
break
if (target)
fire_at(target)
else
notarget++
if (notarget > 60 || process_interval > 1)
notarget = 0
reconsider_interval()
/obj/structure/cult/pylon/proc/fire_at(var/atom/target)
//Only store loc if target is on a turf. Otherwise this bugs out with people in exosuits
if (istype(target.loc, /turf))
last_target_loc = target.loc
process_interval = 1 //Instantly wake up if we found a target
var/obj/item/projectile/beam/cult/A
if (empowered > 0)
A = new /obj/item/projectile/beam/cult/heavy(loc)
empowered = max(0, empowered-1)
playsound(loc, 'sound/weapons/laserdeep.ogg', 100, 1)
if (empowered <= 0)
update_icon()
else
A = new /obj/item/projectile/beam/cult(loc)
playsound(loc, 'sound/weapons/laserdeep.ogg', 65, 1)
A.ignore = sacrificer
A.launch(target)
next_shot = world.time + shot_delay
spawn(shot_delay+1)
handle_firing()
/obj/structure/cult/pylon/attack_hand(mob/M as mob)
attackpylon(M, 5)
if (M.a_intent == "help")
M << "The pylon feels warm to the touch...."
else
attackpylon(M, 4, M)
/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage)
attackpylon(user, damage)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
//Artificiers maintain pylons
if(istype(user, /mob/living/simple_animal/construct))
var/mob/living/simple_animal/construct/C = user
if (C.can_repair)
repair(user)
return
attackpylon(user, damage, user)
/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob)
attackpylon(user, W.force)
if (istype(W, /obj/item/weapon/holder))
var/obj/item/weapon/holder/H = W
if (H.contained)
present_sacrifice(user, H.contained)
return
/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(!isbroken)
if(prob(1+ damage * 5))
user.visible_message(
"<span class='danger'>[user] smashed the pylon!</span>",
"<span class='warning'>You hit the pylon, and its crystal breaks apart!</span>",
"You hear a tinkle of crystal shards"
attackpylon(user, W.force, W)
//Mousedrop so that constructs can drag mice out of maintenance to make turrets
/obj/structure/cult/pylon/MouseDrop_T(var/atom/movable/C, mob/user)
if (istype(C, /mob/living))
present_sacrifice(user, C)
return
return ..()
/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj)
attackpylon(user, Proj.damage, Proj)
//Explosions will usually cause instant shattering, or heavy damage
//Class 3 or lower blast is sometimes survivable. 2 or higher will always shatter
/obj/structure/cult/pylon/ex_act(var/severity)
if (severity > 0)
attackpylon(null, (rand(200,400)/severity), null)
/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage, var/source)
var/ranged = 0
if (user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if (istype(source, /obj/item))
var/obj/item/I = source
if (I.damtype != BRUTE)
user << "You swing at the pylon to no effect."
return
if (istype(source, /obj/item/projectile))
if (istype(source, /obj/item/projectile/beam/cult))
return //No feedback loops
var/obj/item/projectile/proj = source
if (proj.damage_type == BURN)
if (empowered <= 0)
visible_message(
"<span class='cult'>The beam refracts inside the pylon, splitting into an indistinct violet glow. The crystal takes on a new, more ominous aura!</span>"
)
empowered += damage*0.3
//When shot with a laser, the pylon absorbs the beam, becoming empowered for a while, glowing brighter
// and firing more powerful blasts which have some armor penetration
// Using lasers to empower a defensive pylon yields more total damage than directly shooting your enemies
start_process()
update_icon()
return
else if (proj.damage_type != BRUTE)
return
ranged = 1
if (!damage)
return
damagetaken += damage
if(!isbroken)
if (user)
user.do_attack_animation(src)
playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1)
isbroken = 1
density = 0
icon_state = "pylon-broken"
set_light(0)
if(prob(damagetaken*0.5))
shatter()
else
user << "You hit the pylon!"
if (user && !ranged)
user << "You hit the pylon!"
playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1)
else
if(prob(damage * 2))
user << "You pulverize what was left of the pylon!"
if(prob(damagetaken))
if (user)
user << "You pulverize what was left of the pylon!"
qdel(src)
else
else if (user && !ranged)
user << "You hit the pylon!"
playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1)
start_process()
/obj/structure/cult/pylon/proc/shatter()
visible_message(
"<span class='danger'>The pylon shatters into shards of crystal!</span>",
"You hear a tinkle of crystal shards"
)
playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1)
isbroken = 1
if (pylonmode == 2)
//If the pylon had a soul in it then it plays a creepy evil sound as the soul is released
var/possibles = list('sound/hallucinations/far_noise.ogg',
'sound/hallucinations/growl3.ogg',
'sound/hallucinations/veryfar_noise.ogg',
'sound/hallucinations/wail.ogg',
'sound/voice/hiss5.ogg')
playsound(get_turf(src), pick(possibles), 50, 1)
pylonmode = 0 //A broken pylon loses its soul. Even if repaired it will need a new sacrifice to re-empower it
//Make sure we stop it from firing
target = null
sacrificer = null
notarget = 0
last_target_loc = null
process_interval = 1
//It will stop processing after the next check, start of the next process
empowered = 0
density = 0
update_icon()
/obj/structure/cult/pylon/proc/repair(mob/user as mob)
if(isbroken)
user << "You repair the pylon."
if(user)
user << span("notice","You weave forgotten magic, summoning the shards of the crystal and knitting them anew, until it hovers flawless once more.")
isbroken = 0
density = 1
icon_state = "pylon"
set_light(5)
else if (damagetaken > 0)
user << span("notice","You meld the crystal lattice back into integrity, sealing over the cracks until they never were.")
else
user << span("notice","The crystal lights up at your touch.")
process_interval = 1 //Wake up the crystal
notarget = 0
damagetaken = 0
update_icon()
/obj/structure/cult/pylon/update_icon()
overlays.Cut()
if (pylonmode == 2)
anchored = 1
if (empowered > 0)
overlays += "crystal_overcharge"
set_light(7, 3, l_color = "#a160bf")
else
set_light(6, 3, l_color = "#3e0000")
overlays += "crystal_turret"
else if (!isbroken)
set_light(5, 2, l_color = "#3e0000")
overlays += "crystal_idle"
if (pylonmode == 1)
anchored = 1
else
anchored = 0
else
anchored = 0
set_light(0)
//============================================
/obj/structure/cult/tome
name = "Desk"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl"
@@ -144,7 +650,7 @@
if(M.has_brain_worms())
return //Borer stuff - RR
if(iscultist(M)) return
if(iscult(M)) return
if(!ishuman(M) && !isrobot(M)) return
M.transforming = 1
@@ -174,3 +680,12 @@
new_mob.key = M.key
new_mob << "<B>Your form morphs into that of a corgi.</B>" //Because we don't have cluwnes
/obj/effect/testtrans
icon = 'icons/obj/cult.dmi'
icon_state = "tt"
/obj/effect/testtrans/New()
..()
spawn(10)
qdel(src)
+37 -5
View File
@@ -46,6 +46,7 @@ In short:
escape_list = get_area_turfs(locate(/area/hallway/secondary/exit))
convert_all_parallax()
//Separated into separate procs for profiling
AreaSet()
MiscSet()
@@ -62,23 +63,36 @@ In short:
continue
A.updateicon()
CHECK_TICK
/datum/universal_state/hell/OverlayAndAmbientSet()
spawn(0)
for(var/atom/movable/lighting_overlay/L in world)
L.update_lumcount(1, 0, 0)
set waitfor = FALSE
for(var/turf/T in turfs)
if(istype(T, /turf/space))
T.overlays += image(icon = T.icon, icon_state = "hell01")
else
if(!T.holy && prob(1) && !(T.z in config.admin_levels))
new /obj/effect/gateway/active/cult(T)
T.underlays += "hell01"
CHECK_TICK
for(var/turf/space/T in turfs)
OnTurfChange(T)
for(var/datum/lighting_corner/C in global.all_lighting_corners)
if (!C.active)
continue
C.update_lumcount(0.5, 0, 0)
CHECK_TICK
/datum/universal_state/hell/proc/MiscSet()
for(var/turf/simulated/floor/T in turfs)
if(!T.holy && prob(1))
new /obj/effect/gateway/active/cult(T)
CHECK_TICK
for (var/obj/machinery/firealarm/alm in machines)
if (!(alm.stat & BROKEN))
alm.ex_act(2)
CHECK_TICK
/datum/universal_state/hell/proc/APCSet()
for (var/obj/machinery/power/apc/APC in machines)
@@ -88,8 +102,26 @@ In short:
APC.cell.charge = 0
APC.emagged = 1
APC.queue_icon_update()
CHECK_TICK
/datum/universal_state/hell/proc/KillMobs()
for(var/mob/living/simple_animal/M in mob_list)
if(M && !M.client)
M.stat = DEAD
CHECK_TICK
// Parallax.
/datum/universal_state/hell/convert_parallax(obj/screen/plane_master/parallax_spacemaster/PS)
PS.color = list(
0,0,0,0,
0,0,0,0,
0,0,0,0,
1,0,0,1)
/datum/universal_state/hell/proc/convert_all_parallax()
for(var/client/C in clients)
var/obj/screen/plane_master/parallax_spacemaster/PS = locate() in C.screen
if(PS)
convert_parallax(PS)
CHECK_TICK
+2 -1
View File
@@ -72,6 +72,7 @@ var/global/list/narsie_list = list()
/obj/singularity/narsie/large/eat()
for (var/turf/A in orange(consume_range, src))
consume(A)
CHECK_TICK
/obj/singularity/narsie/mezzer()
for(var/mob/living/carbon/M in oviewers(8, src))
@@ -152,7 +153,7 @@ var/global/list/narsie_list = list()
T.desc = "An opening has been made on that wall, but who can say if what you seek truly lies on the other side?"
T.icon = 'icons/turf/walls.dmi'
T.icon_state = "cult-narsie"
T.opacity = 0
T.set_opacity(0)
T.density = 0
set_light(1)
+2
View File
@@ -69,3 +69,5 @@
universe = new newstate
if(on_enter)
universe.OnEnter()
/datum/universal_state/proc/convert_parallax(parallax_spacemaster)
@@ -13,11 +13,11 @@ var/global/universe_has_ended = 0
return 0
/datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T)
var/turf/space/S = T
if(istype(S))
S.color = "#0066FF"
if(T.name == "space")
T.overlays += image(icon = T.icon, icon_state = "end01")
T.underlays -= "end01"
else
S.color = initial(S.color)
T.overlays -= image(icon = T.icon, icon_state = "end01")
/datum/universal_state/supermatter_cascade/DecayTurf(var/turf/T)
if(istype(T,/turf/simulated/wall))
@@ -90,22 +90,31 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked
continue
A.updateicon()
CHECK_TICK
/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet()
spawn(0)
for(var/atom/movable/lighting_overlay/L in world)
if(L.z in config.admin_levels)
L.update_lumcount(1,1,1)
else
L.update_lumcount(0.0, 0.4, 1)
set waitfor = FALSE
for(var/turf/T in turfs)
if(istype(T, /turf/space))
T.overlays += image(icon = T.icon, icon_state = "end01")
else
if (!(T.z in config.admin_levels))
T.underlays += "end01"
CHECK_TICK
for(var/turf/space/T in turfs)
OnTurfChange(T)
for(var/datum/lighting_corner/C in global.all_lighting_corners)
if (!C.active)
continue
if (!(C.z in config.admin_levels))
C.update_lumcount(0.15, 0.15, 0.5)
CHECK_TICK
/datum/universal_state/supermatter_cascade/proc/MiscSet()
for (var/obj/machinery/firealarm/alm in machines)
if (!(alm.stat & BROKEN))
alm.ex_act(2)
CHECK_TICK
/datum/universal_state/supermatter_cascade/proc/APCSet()
for (var/obj/machinery/power/apc/APC in machines)
@@ -115,6 +124,7 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked
APC.cell.charge = 0
APC.emagged = 1
APC.queue_icon_update()
CHECK_TICK
/datum/universal_state/supermatter_cascade/proc/PlayerSet()
for(var/datum/mind/M in player_list)
@@ -125,3 +135,4 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked
flick("e_flash", M.current.flash)
clear_antag_roles(M)
CHECK_TICK

Some files were not shown because too many files have changed in this diff Show More