diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000000..1770177310d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+[*]
+indent_style = tab
+indent_size = 4
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+end_of_line = lf
+
+[*.yml]
+indent_style = space
+indent_size = 2
+
+[*.py]
+indent_style = space
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 45b7dc5a642..526be3bcaf0 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -15,7 +15,6 @@
add: Added new things
del: Removed old things
tweak: tweaked a few things
-balance: rebalanced something
fix: fixed a few things
wip: added a few works in progress
soundadd: added a new sound thingy
diff --git a/.gitignore b/.gitignore
index a4634f3fc84..74660294c3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,4 +21,18 @@ data/
*.db
stddef.dm
.atom-build.json
-*.vscode/*
\ No newline at end of file
+*.vscode/*
+!/.vscode/extensions.json
+
+# ignore DMI tool build cache
+/tools/dmitool/bin/
+/tools/dmitool/.gradle/
+/tools/dmitool/.settings/
+/tools/dmitool/.classpath
+/tools/dmitool/.project
+
+# ignore midi2piano build cache
+/tools/midi2piano/obj/*
+
+
+
diff --git a/.travis.yml b/.travis.yml
index 146be925c77..7f74746d15f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -50,7 +50,7 @@ jobs:
before_script: skip
script:
- shopt -s globstar
- - (! grep 'step_[xy]' _maps/map_files/**/*.dmm)
+ - python3 tools/travis/check_line_endings.py
- (num=$(grep -Ern '\\(red|blue|green|black|italic|bold|b|i[^mc])' code/ | wc -l); echo "$num BYOND text macros (expecting ${BYOND_MACRO_COUNT} or fewer)"; [ $num -le ${BYOND_MACRO_COUNT} ])
- md5sum -c - <<< "6dc1b6bf583f3bd4176b6df494caa5f1 *html/changelogs/example.yml"
- python tools/ss13_genchangelog.py html/changelog.html html/changelogs
@@ -69,4 +69,4 @@ jobs:
- npm install --loglevel=error
before_script:
script:
- - gulp
+ - node node_modules/gulp/bin/gulp.js --require less-loader
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000000..9ab4061ce6a
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ "recommendations": [
+ "gbasood.byond-dm-language-support",
+ "platymuus.dm-langclient",
+ "EditorConfig.EditorConfig"
+ ]
+}
diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 01f83ba0194..837f8f440d6 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -97,7 +97,7 @@ CREATE TABLE `customuseritems` (
`cuiPropAdjust` text,
`cuiJobMask` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -124,7 +124,7 @@ CREATE TABLE `death` (
`fireloss` int(11) NOT NULL,
`oxyloss` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -160,7 +160,7 @@ CREATE TABLE `admin` (
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -177,7 +177,7 @@ CREATE TABLE `admin_log` (
`adminip` varchar(18) NOT NULL,
`log` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -212,7 +212,7 @@ CREATE TABLE `ban` (
`unbanned_computerid` varchar(32) DEFAULT NULL,
`unbanned_ip` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -230,7 +230,7 @@ CREATE TABLE `feedback` (
`var_value` int(16) DEFAULT NULL,
`details` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -272,7 +272,7 @@ CREATE TABLE `player` (
`parallax` tinyint(1) DEFAULT '8',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
-) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -293,7 +293,7 @@ CREATE TABLE `poll_option` (
`descmid` varchar(32) DEFAULT NULL,
`descmax` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -314,7 +314,7 @@ CREATE TABLE `poll_question` (
`createdby_ckey` varchar(45) NULL DEFAULT NULL,
`createdby_ip` varchar(45) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -333,7 +333,7 @@ CREATE TABLE `poll_textreply` (
`replytext` text NOT NULL,
`adminrank` varchar(32) NOT NULL DEFAULT 'Player',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -353,7 +353,7 @@ CREATE TABLE `poll_vote` (
`adminrank` varchar(32) NOT NULL,
`rating` int(2) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -369,7 +369,7 @@ CREATE TABLE `privacy` (
`consent` bit(1) NOT NULL,
PRIMARY KEY (`ckey`),
UNIQUE KEY `ckey_UNIQUE` (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -391,7 +391,7 @@ CREATE TABLE `karma` (
`spenderip` text NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -407,7 +407,7 @@ CREATE TABLE `karmatotals` (
`karma` int(11) NOT NULL,
`karmaspent` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -426,7 +426,7 @@ CREATE TABLE `library` (
`ckey` varchar(45) NOT NULL,
`flagged` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -442,7 +442,7 @@ CREATE TABLE `legacy_population` (
`admincount` int(11) DEFAULT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -458,7 +458,7 @@ CREATE TABLE `whitelist` (
`job` text,
`species` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -485,7 +485,7 @@ CREATE TABLE `watch` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -507,7 +507,7 @@ CREATE TABLE `notes` (
`server` varchar(50) NOT NULL,
`crew_playtime` mediumint(8) UNSIGNED DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -524,7 +524,7 @@ CREATE TABLE `memo` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -538,7 +538,7 @@ CREATE TABLE `ipintel` (
`date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
`intel` real NOT NULL DEFAULT '0',
PRIMARY KEY (`ip`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -551,7 +551,7 @@ CREATE TABLE `vpn_whitelist` (
`ckey` varchar(32) NOT NULL,
`reason` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -564,7 +564,7 @@ CREATE TABLE `oauth_tokens` (
`ckey` varchar(32) NOT NULL,
`token` varchar(32) NOT NULL,
PRIMARY KEY (`token`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -580,7 +580,7 @@ CREATE TABLE `playtime_history` (
`time_living` SMALLINT NOT NULL,
`time_ghost` SMALLINT NOT NULL,
PRIMARY KEY (`ckey`, `date`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
@@ -594,4 +594,4 @@ CREATE TABLE `connection_log` (
`ip` varchar(32) NOT NULL,
`computerid` varchar(32) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
\ No newline at end of file
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql
index 9801c54d6f9..b43141f6784 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -97,7 +97,7 @@ CREATE TABLE `SS13_customuseritems` (
`cuiPropAdjust` text,
`cuiJobMask` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -124,7 +124,7 @@ CREATE TABLE `SS13_death` (
`fireloss` int(11) NOT NULL,
`oxyloss` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -159,7 +159,7 @@ CREATE TABLE `SS13_admin` (
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -176,7 +176,7 @@ CREATE TABLE `SS13_admin_log` (
`adminip` varchar(18) NOT NULL,
`log` text NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -211,7 +211,7 @@ CREATE TABLE `SS13_ban` (
`unbanned_computerid` varchar(32) DEFAULT NULL,
`unbanned_ip` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -229,7 +229,7 @@ CREATE TABLE `SS13_feedback` (
`var_value` int(16) DEFAULT NULL,
`details` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -271,7 +271,7 @@ CREATE TABLE `SS13_player` (
`parallax` tinyint(1) DEFAULT '8',
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
-) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -292,7 +292,7 @@ CREATE TABLE `SS13_poll_option` (
`descmid` varchar(32) DEFAULT NULL,
`descmax` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -313,7 +313,7 @@ CREATE TABLE `SS13_poll_question` (
`createdby_ckey` varchar(45) NULL DEFAULT NULL,
`createdby_ip` varchar(45) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -332,7 +332,7 @@ CREATE TABLE `SS13_poll_textreply` (
`replytext` text NOT NULL,
`adminrank` varchar(32) NOT NULL DEFAULT 'Player',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -352,7 +352,7 @@ CREATE TABLE `SS13_poll_vote` (
`adminrank` varchar(32) NOT NULL,
`rating` int(2) DEFAULT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -368,7 +368,7 @@ CREATE TABLE `ss13_privacy` (
`consent` bit(1) NOT NULL,
PRIMARY KEY (`ckey`),
UNIQUE KEY `ckey_UNIQUE` (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -390,7 +390,7 @@ CREATE TABLE `SS13_karma` (
`spenderip` text NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -406,7 +406,7 @@ CREATE TABLE `SS13_karmatotals` (
`karma` int(11) NOT NULL,
`karmaspent` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -425,7 +425,7 @@ CREATE TABLE `SS13_library` (
`ckey` varchar(45) NOT NULL,
`flagged` int(11) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -441,7 +441,7 @@ CREATE TABLE `SS13_legacy_population` (
`admincount` int(11) DEFAULT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -457,7 +457,7 @@ CREATE TABLE `SS13_whitelist` (
`job` text,
`species` text,
PRIMARY KEY (`id`)
-) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=latin1;
+) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -484,7 +484,7 @@ CREATE TABLE `SS13_watch` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -506,7 +506,7 @@ CREATE TABLE `SS13_notes` (
`server` varchar(50) NOT NULL,
`crew_playtime` mediumint(8) UNSIGNED DEFAULT '0',
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -523,7 +523,7 @@ CREATE TABLE `SS13_memo` (
`last_editor` varchar(32),
`edits` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -537,7 +537,7 @@ CREATE TABLE `SS13_ipintel` (
`date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
`intel` real NOT NULL DEFAULT '0',
PRIMARY key (`ip`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -550,7 +550,7 @@ CREATE TABLE `SS13_vpn_whitelist` (
`ckey` varchar(32) NOT NULL,
`reason` text,
PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -563,7 +563,7 @@ CREATE TABLE `SS13_oauth_tokens` (
`ckey` varchar(32) NOT NULL,
`token` varchar(32) NOT NULL,
PRIMARY KEY (`token`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -578,7 +578,7 @@ CREATE TABLE `SS13_playtime_history` (
`time_living` SMALLINT NOT NULL,
`time_ghost` SMALLINT NOT NULL,
PRIMARY KEY (`ckey`, `date`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Table structure for table `SS13_connection_log`
@@ -591,4 +591,4 @@ CREATE TABLE `SS13_connection_log` (
`ip` varchar(32) NOT NULL,
`computerid` varchar(32) NOT NULL,
PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
\ No newline at end of file
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
\ No newline at end of file
diff --git a/SQL/updates/10-11.sql b/SQL/updates/10-11.sql
new file mode 100644
index 00000000000..9564d3efaad
--- /dev/null
+++ b/SQL/updates/10-11.sql
@@ -0,0 +1,41 @@
+# Updating SQL from ver 10 to 11
+# Changes tables from latin1 to utf8mb4
+# Based on: https://github.com/BeeStation/BeeStation-Hornet/pull/1074/files
+# -Kyet
+
+# Generation (not necessary to run unless your DB has extra tables)
+SELECT CONCAT("ALTER TABLE ", TABLE_SCHEMA, '.', TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") AS ExecuteTheString
+FROM INFORMATION_SCHEMA.TABLES
+WHERE TABLE_SCHEMA="feedback"
+AND TABLE_TYPE="BASE TABLE";
+
+# Actual table conversion stuff
+ALTER TABLE feedback.admin CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.admin_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.ban CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.characters CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.connection_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.customuseritems CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.death CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.donators CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.erro_privacy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.feedback CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.ipintel CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.karma CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.karmatotals CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.legacy_population CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.library CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.memo CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.notes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.oauth_tokens CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.player CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.playtime_history CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_option CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_question CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_textreply CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.poll_vote CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.population CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.privacy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.vpn_whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.watch CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ALTER TABLE feedback.whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
\ No newline at end of file
diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm
index 8de75d7aaf1..74e179255ce 100644
--- a/_maps/map_files/cyberiad/cyberiad.dmm
+++ b/_maps/map_files/cyberiad/cyberiad.dmm
@@ -27893,7 +27893,6 @@
/area/chapel/main)
"aYA" = (
/obj/structure/table,
-/obj/item/lighter/zippo/black,
/turf/simulated/floor/plasteel{
icon_state = "vault";
dir = 5
@@ -29916,6 +29915,7 @@
network = list("SS13")
},
/obj/structure/table/wood,
+/obj/item/lighter/zippo/black,
/turf/simulated/floor/carpet/black,
/area/chapel/office)
"bcx" = (
@@ -35447,7 +35447,6 @@
/area/library)
"bmF" = (
/obj/structure/cult/archives,
-/obj/item/videocam,
/obj/machinery/light_switch{
pixel_y = -25
},
@@ -72015,7 +72014,7 @@
dir = 8;
layer = 2.9
},
-/obj/item/circuitboard/rdconsole/public{
+/obj/item/circuitboard/rdconsole{
pixel_x = -4
},
/obj/item/circuitboard/rdserver{
diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm
index 0c1b0a35426..0e3c1b78798 100644
--- a/code/ATMOSPHERICS/atmospherics.dm
+++ b/code/ATMOSPHERICS/atmospherics.dm
@@ -1,345 +1,345 @@
-/*
-Quick overview:
-
-Pipes combine to form pipelines
-Pipelines and other atmospheric objects combine to form pipe_networks
- Note: A single pipe_network represents a completely open space
-
-Pipes -> Pipelines
-Pipelines + Other Objects -> Pipe network
-*/
-GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
-/obj/machinery/atmospherics
- anchored = 1
- layer = GAS_PIPE_HIDDEN_LAYER //under wires
- resistance_flags = FIRE_PROOF
- max_integrity = 200
- plane = FLOOR_PLANE
- idle_power_usage = 0
- active_power_usage = 0
- power_channel = ENVIRON
- on_blueprints = TRUE
- var/nodealert = 0
- var/can_unwrench = 0
-
- var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber
- var/connected_to = 1 //same as above, currently not used for anything
- var/icon_connect_type = "" //"-supply" or "-scrubbers"
-
- var/initialize_directions = 0
-
- var/pipe_color
- var/obj/item/pipe/stored
- var/image/pipe_image
-
-/obj/machinery/atmospherics/New()
- if (!armor)
- armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
- ..()
-
- if(!pipe_color)
- pipe_color = color
- color = null
-
- if(!pipe_color_check(pipe_color))
- pipe_color = null
-
-/obj/machinery/atmospherics/Initialize()
- . = ..()
- SSair.atmos_machinery += src
-
-/obj/machinery/atmospherics/proc/atmos_init()
- if(can_unwrench)
- stored = new(src, make_from = src)
-
-/obj/machinery/atmospherics/Destroy()
- QDEL_NULL(stored)
- SSair.atmos_machinery -= src
- SSair.deferred_pipenet_rebuilds -= src
- for(var/mob/living/L in src) //ventcrawling is serious business
- L.remove_ventcrawl()
- L.forceMove(get_turf(src))
- QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else
- return ..()
-
-// Icons/overlays/underlays
-/obj/machinery/atmospherics/update_icon()
- var/turf/T = loc
- if(level == 2 || !T.intact)
- plane = GAME_PLANE
- else
- plane = FLOOR_PLANE
-
-/obj/machinery/atmospherics/proc/update_pipe_image()
- pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view)
- pipe_image.plane = HUD_PLANE
-
-/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0)
- if(!istype(GLOB.pipe_icon_manager))
- if(!safety) //to prevent infinite loops
- GLOB.pipe_icon_manager = new()
- check_icon_cache(1)
- return 0
-
- return 1
-
-/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node)
- //Don't use this for standard pipes
- if(!istype(node))
- return null
-
- return node.pipe_color
-
-/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type)
- if(node)
- if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
- //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node))
- underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
- else
- //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node))
- underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
- else
- //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
- underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type)
-
-/obj/machinery/atmospherics/proc/update_underlays()
- if(check_icon_cache())
- return 1
- else
- return 0
-
-// Connect types
-/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2)
- var/i
- var/list1[] = atmos1.connect_types
- var/list2[] = atmos2.connect_types
- for(i=1,i<=list1.len,i++)
- var/j
- for(j=1,j<=list2.len,j++)
- if(list1[i] == list2[j])
- var/n = list1[i]
- return n
- return 0
-
-/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2)
- var/i
- var/list1[] = atmos1.connect_types
- var/list2[] = pipe2.connect_types
- for(i=1,i<=list1.len,i++)
- var/j
- for(j=1,j<=list2.len,j++)
- if(list1[i] == list2[j])
- var/n = list1[i]
- return n
- return 0
-
-// Pipenet related functions
-/obj/machinery/atmospherics/proc/returnPipenet()
- return
-
-/obj/machinery/atmospherics/proc/returnPipenetAir()
- return
-
-/obj/machinery/atmospherics/proc/setPipenet()
- return
-
-/obj/machinery/atmospherics/proc/replacePipenet()
- return
-
-/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE)
- // Called to build a network from this node
- if(remove_deferral)
- SSair.deferred_pipenet_rebuilds -= src
-
-/obj/machinery/atmospherics/proc/defer_build_network()
- SSair.deferred_pipenet_rebuilds += src
-
-/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
- return
-
-/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P)
- if(P)
- P.other_atmosmch -= src
-
-//(De)construction
-/obj/machinery/atmospherics/attackby(obj/item/W, mob/user)
- if(can_unwrench && istype(W, /obj/item/wrench))
- var/turf/T = get_turf(src)
- if(level == 1 && isturf(T) && T.intact)
- to_chat(user, "You must remove the plating first.")
- return 1
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- add_fingerprint(user)
-
- var/unsafe_wrenching = FALSE
- var/I = int_air ? int_air.return_pressure() : 0
- var/E = env_air ? env_air.return_pressure() : 0
- var/internal_pressure = I - E
-
- playsound(src.loc, W.usesound, 50, 1)
- to_chat(user, "You begin to unfasten \the [src]...")
- if(internal_pressure > 2*ONE_ATMOSPHERE)
- to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?")
- unsafe_wrenching = TRUE //Oh dear oh dear
-
- if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src))
- user.visible_message( \
- "[user] unfastens \the [src].", \
- "You have unfastened \the [src].", \
- "You hear ratchet.")
- investigate_log("was REMOVED by [key_name(usr)]", "atmos")
-
- //You unwrenched a pipe full of pressure? let's splat you into the wall silly.
- if(unsafe_wrenching)
- unsafe_pressure_release(user,internal_pressure)
- deconstruct(TRUE)
- else
- return ..()
-
-//Called when an atmospherics object is unwrenched while having a large pressure difference
-//with it's locs air contents.
-/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures)
- if(!user)
- return
-
- if(!pressures)
- var/datum/gas_mixture/int_air = return_air()
- var/datum/gas_mixture/env_air = loc.return_air()
- pressures = int_air.return_pressure() - env_air.return_pressure()
-
- var/fuck_you_dir = get_dir(src, user)
- var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir)
- user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!")
- //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed
- user.throw_at(general_direction, pressures/10, pressures/50)
-
-/obj/machinery/atmospherics/deconstruct(disassembled = TRUE)
- if(!(flags & NODECONSTRUCT))
- if(can_unwrench)
- if(stored)
- stored.forceMove(get_turf(src))
- if(!disassembled)
- stored.obj_integrity = stored.max_integrity * 0.5
- transfer_fingerprints_to(stored)
- stored = null
- ..()
-
-/obj/machinery/atmospherics/on_construction(D, P, C)
- if(C)
- color = C
- dir = D
- initialize_directions = P
- var/turf/T = loc
- level = T.intact ? 2 : 1
- add_fingerprint(usr)
- if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets
- SSair.machinery_to_construct.Add(src)
- return
- initialize_atmos_network()
-
-/obj/machinery/atmospherics/proc/initialize_atmos_network()
- atmos_init()
- var/list/nodes = pipeline_expansion()
- for(var/obj/machinery/atmospherics/A in nodes)
- A.atmos_init()
- A.addMember(src)
- build_network()
-
-// Find a connecting /obj/machinery/atmospherics in specified direction.
-/obj/machinery/atmospherics/proc/findConnecting(var/direction)
- for(var/obj/machinery/atmospherics/target in get_step(src,direction))
- var/can_connect = check_connect_types(target, src)
- if(can_connect && (target.initialize_directions & get_dir(target,src)))
- return target
-
-// Ventcrawling
-#define VENT_SOUND_DELAY 30
-/obj/machinery/atmospherics/relaymove(mob/living/user, direction)
- direction &= initialize_directions
- if(!direction || !(direction in cardinal)) //cant go this way.
- return
-
- if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug
- return
-
- var/obj/machinery/atmospherics/target_move = findConnecting(direction)
- if(target_move)
- if(is_type_in_list(target_move, ventcrawl_machinery) && target_move.can_crawl_through())
- user.remove_ventcrawl()
- user.forceMove(target_move.loc) //handles entering and so on
- user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.")
- else if(target_move.can_crawl_through())
- if(returnPipenet() != target_move.returnPipenet())
- user.update_pipe_vision(target_move)
- user.loc = target_move
- user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement
- if(world.time - user.last_played_vent > VENT_SOUND_DELAY)
- user.last_played_vent = world.time
- playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
- else
- if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent
- user.remove_ventcrawl()
- user.forceMove(src.loc)
- user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.")
- user.canmove = 0
- spawn(1)
- user.canmove = 1
-
-/obj/machinery/atmospherics/AltClick(var/mob/living/L)
- if(is_type_in_list(src, ventcrawl_machinery))
- L.handle_ventcrawl(src)
- return
- ..()
-
-/obj/machinery/atmospherics/proc/can_crawl_through()
- return 1
-
-/obj/machinery/atmospherics/proc/change_color(var/new_color)
- //only pass valid pipe colors please ~otherwise your pipe will turn invisible
- if(!pipe_color_check(new_color))
- return
-
- pipe_color = new_color
- update_icon()
-
-// Additional icon procs
-/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction)
- var/turf/T = get_turf(src)
- if(!istype(T)) return
- if(node)
- var/node_dir = get_dir(src,node)
- if(node.icon_connect_type == "-supply")
- add_underlay_adapter(T, , node_dir, "")
- add_underlay_adapter(T, node, node_dir, "-supply")
- add_underlay_adapter(T, , node_dir, "-scrubbers")
- else if(node.icon_connect_type == "-scrubbers")
- add_underlay_adapter(T, , node_dir, "")
- add_underlay_adapter(T, , node_dir, "-supply")
- add_underlay_adapter(T, node, node_dir, "-scrubbers")
- else
- add_underlay_adapter(T, node, node_dir, "")
- add_underlay_adapter(T, , node_dir, "-supply")
- add_underlay_adapter(T, , node_dir, "-scrubbers")
- else
- add_underlay_adapter(T, , direction, "-supply")
- add_underlay_adapter(T, , direction, "-scrubbers")
- add_underlay_adapter(T, , direction, "")
-
-/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays
- if(node)
- if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
- underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
- else
- underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
- else
- underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type)
-
-/obj/machinery/atmospherics/singularity_pull(S, current_size)
- if(current_size >= STAGE_FIVE)
- deconstruct(FALSE)
- return ..()
-
-/obj/machinery/atmospherics/update_remote_sight(mob/user)
- user.sight |= (SEE_TURFS|BLIND)
- . = ..()
+/*
+Quick overview:
+
+Pipes combine to form pipelines
+Pipelines and other atmospheric objects combine to form pipe_networks
+ Note: A single pipe_network represents a completely open space
+
+Pipes -> Pipelines
+Pipelines + Other Objects -> Pipe network
+*/
+GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
+/obj/machinery/atmospherics
+ anchored = 1
+ layer = GAS_PIPE_HIDDEN_LAYER //under wires
+ resistance_flags = FIRE_PROOF
+ max_integrity = 200
+ plane = FLOOR_PLANE
+ idle_power_usage = 0
+ active_power_usage = 0
+ power_channel = ENVIRON
+ on_blueprints = TRUE
+ var/nodealert = 0
+ var/can_unwrench = 0
+
+ var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber
+ var/connected_to = 1 //same as above, currently not used for anything
+ var/icon_connect_type = "" //"-supply" or "-scrubbers"
+
+ var/initialize_directions = 0
+
+ var/pipe_color
+ var/obj/item/pipe/stored
+ var/image/pipe_image
+
+/obj/machinery/atmospherics/New()
+ if (!armor)
+ armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
+ ..()
+
+ if(!pipe_color)
+ pipe_color = color
+ color = null
+
+ if(!pipe_color_check(pipe_color))
+ pipe_color = null
+
+/obj/machinery/atmospherics/Initialize()
+ . = ..()
+ SSair.atmos_machinery += src
+
+/obj/machinery/atmospherics/proc/atmos_init()
+ if(can_unwrench)
+ stored = new(src, make_from = src)
+
+/obj/machinery/atmospherics/Destroy()
+ QDEL_NULL(stored)
+ SSair.atmos_machinery -= src
+ SSair.deferred_pipenet_rebuilds -= src
+ for(var/mob/living/L in src) //ventcrawling is serious business
+ L.remove_ventcrawl()
+ L.forceMove(get_turf(src))
+ QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else
+ return ..()
+
+// Icons/overlays/underlays
+/obj/machinery/atmospherics/update_icon()
+ var/turf/T = get_turf(loc)
+ if(!T || level == 2 || !T.intact)
+ plane = GAME_PLANE
+ else
+ plane = FLOOR_PLANE
+
+/obj/machinery/atmospherics/proc/update_pipe_image()
+ pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view)
+ pipe_image.plane = HUD_PLANE
+
+/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0)
+ if(!istype(GLOB.pipe_icon_manager))
+ if(!safety) //to prevent infinite loops
+ GLOB.pipe_icon_manager = new()
+ check_icon_cache(1)
+ return 0
+
+ return 1
+
+/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node)
+ //Don't use this for standard pipes
+ if(!istype(node))
+ return null
+
+ return node.pipe_color
+
+/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type)
+ if(node)
+ if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node))
+ underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
+ else
+ //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node))
+ underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
+ else
+ //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
+ underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type)
+
+/obj/machinery/atmospherics/proc/update_underlays()
+ if(check_icon_cache())
+ return 1
+ else
+ return 0
+
+// Connect types
+/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2)
+ var/i
+ var/list1[] = atmos1.connect_types
+ var/list2[] = atmos2.connect_types
+ for(i=1,i<=list1.len,i++)
+ var/j
+ for(j=1,j<=list2.len,j++)
+ if(list1[i] == list2[j])
+ var/n = list1[i]
+ return n
+ return 0
+
+/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2)
+ var/i
+ var/list1[] = atmos1.connect_types
+ var/list2[] = pipe2.connect_types
+ for(i=1,i<=list1.len,i++)
+ var/j
+ for(j=1,j<=list2.len,j++)
+ if(list1[i] == list2[j])
+ var/n = list1[i]
+ return n
+ return 0
+
+// Pipenet related functions
+/obj/machinery/atmospherics/proc/returnPipenet()
+ return
+
+/obj/machinery/atmospherics/proc/returnPipenetAir()
+ return
+
+/obj/machinery/atmospherics/proc/setPipenet()
+ return
+
+/obj/machinery/atmospherics/proc/replacePipenet()
+ return
+
+/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE)
+ // Called to build a network from this node
+ if(remove_deferral)
+ SSair.deferred_pipenet_rebuilds -= src
+
+/obj/machinery/atmospherics/proc/defer_build_network()
+ SSair.deferred_pipenet_rebuilds += src
+
+/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
+ return
+
+/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P)
+ if(P)
+ P.other_atmosmch -= src
+
+//(De)construction
+/obj/machinery/atmospherics/attackby(obj/item/W, mob/user)
+ if(can_unwrench && istype(W, /obj/item/wrench))
+ var/turf/T = get_turf(src)
+ if(level == 1 && isturf(T) && T.intact)
+ to_chat(user, "You must remove the plating first.")
+ return 1
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ add_fingerprint(user)
+
+ var/unsafe_wrenching = FALSE
+ var/I = int_air ? int_air.return_pressure() : 0
+ var/E = env_air ? env_air.return_pressure() : 0
+ var/internal_pressure = I - E
+
+ playsound(src.loc, W.usesound, 50, 1)
+ to_chat(user, "You begin to unfasten \the [src]...")
+ if(internal_pressure > 2*ONE_ATMOSPHERE)
+ to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?")
+ unsafe_wrenching = TRUE //Oh dear oh dear
+
+ if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src))
+ user.visible_message( \
+ "[user] unfastens \the [src].", \
+ "You have unfastened \the [src].", \
+ "You hear ratchet.")
+ investigate_log("was REMOVED by [key_name(usr)]", "atmos")
+
+ //You unwrenched a pipe full of pressure? let's splat you into the wall silly.
+ if(unsafe_wrenching)
+ unsafe_pressure_release(user,internal_pressure)
+ deconstruct(TRUE)
+ else
+ return ..()
+
+//Called when an atmospherics object is unwrenched while having a large pressure difference
+//with it's locs air contents.
+/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures)
+ if(!user)
+ return
+
+ if(!pressures)
+ var/datum/gas_mixture/int_air = return_air()
+ var/datum/gas_mixture/env_air = loc.return_air()
+ pressures = int_air.return_pressure() - env_air.return_pressure()
+
+ var/fuck_you_dir = get_dir(src, user)
+ var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir)
+ user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!")
+ //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed
+ user.throw_at(general_direction, pressures/10, pressures/50)
+
+/obj/machinery/atmospherics/deconstruct(disassembled = TRUE)
+ if(!(flags & NODECONSTRUCT))
+ if(can_unwrench)
+ if(stored)
+ stored.forceMove(get_turf(src))
+ if(!disassembled)
+ stored.obj_integrity = stored.max_integrity * 0.5
+ transfer_fingerprints_to(stored)
+ stored = null
+ ..()
+
+/obj/machinery/atmospherics/on_construction(D, P, C)
+ if(C)
+ color = C
+ dir = D
+ initialize_directions = P
+ var/turf/T = loc
+ level = T.intact ? 2 : 1
+ add_fingerprint(usr)
+ if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets
+ SSair.machinery_to_construct.Add(src)
+ return
+ initialize_atmos_network()
+
+/obj/machinery/atmospherics/proc/initialize_atmos_network()
+ atmos_init()
+ var/list/nodes = pipeline_expansion()
+ for(var/obj/machinery/atmospherics/A in nodes)
+ A.atmos_init()
+ A.addMember(src)
+ build_network()
+
+// Find a connecting /obj/machinery/atmospherics in specified direction.
+/obj/machinery/atmospherics/proc/findConnecting(var/direction)
+ for(var/obj/machinery/atmospherics/target in get_step(src,direction))
+ var/can_connect = check_connect_types(target, src)
+ if(can_connect && (target.initialize_directions & get_dir(target,src)))
+ return target
+
+// Ventcrawling
+#define VENT_SOUND_DELAY 30
+/obj/machinery/atmospherics/relaymove(mob/living/user, direction)
+ direction &= initialize_directions
+ if(!direction || !(direction in cardinal)) //cant go this way.
+ return
+
+ if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug
+ return
+
+ var/obj/machinery/atmospherics/target_move = findConnecting(direction)
+ if(target_move)
+ if(is_type_in_list(target_move, ventcrawl_machinery) && target_move.can_crawl_through())
+ user.remove_ventcrawl()
+ user.forceMove(target_move.loc) //handles entering and so on
+ user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.")
+ else if(target_move.can_crawl_through())
+ if(returnPipenet() != target_move.returnPipenet())
+ user.update_pipe_vision(target_move)
+ user.loc = target_move
+ user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement
+ if(world.time - user.last_played_vent > VENT_SOUND_DELAY)
+ user.last_played_vent = world.time
+ playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
+ else
+ if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent
+ user.remove_ventcrawl()
+ user.forceMove(src.loc)
+ user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.")
+ user.canmove = 0
+ spawn(1)
+ user.canmove = 1
+
+/obj/machinery/atmospherics/AltClick(var/mob/living/L)
+ if(is_type_in_list(src, ventcrawl_machinery))
+ L.handle_ventcrawl(src)
+ return
+ ..()
+
+/obj/machinery/atmospherics/proc/can_crawl_through()
+ return 1
+
+/obj/machinery/atmospherics/proc/change_color(var/new_color)
+ //only pass valid pipe colors please ~otherwise your pipe will turn invisible
+ if(!pipe_color_check(new_color))
+ return
+
+ pipe_color = new_color
+ update_icon()
+
+// Additional icon procs
+/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction)
+ var/turf/T = get_turf(src)
+ if(!istype(T)) return
+ if(node)
+ var/node_dir = get_dir(src,node)
+ if(node.icon_connect_type == "-supply")
+ add_underlay_adapter(T, , node_dir, "")
+ add_underlay_adapter(T, node, node_dir, "-supply")
+ add_underlay_adapter(T, , node_dir, "-scrubbers")
+ else if(node.icon_connect_type == "-scrubbers")
+ add_underlay_adapter(T, , node_dir, "")
+ add_underlay_adapter(T, , node_dir, "-supply")
+ add_underlay_adapter(T, node, node_dir, "-scrubbers")
+ else
+ add_underlay_adapter(T, node, node_dir, "")
+ add_underlay_adapter(T, , node_dir, "-supply")
+ add_underlay_adapter(T, , node_dir, "-scrubbers")
+ else
+ add_underlay_adapter(T, , direction, "-supply")
+ add_underlay_adapter(T, , direction, "-scrubbers")
+ add_underlay_adapter(T, , direction, "")
+
+/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays
+ if(node)
+ if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
+ underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
+ else
+ underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
+ else
+ underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type)
+
+/obj/machinery/atmospherics/singularity_pull(S, current_size)
+ if(current_size >= STAGE_FIVE)
+ deconstruct(FALSE)
+ return ..()
+
+/obj/machinery/atmospherics/update_remote_sight(mob/user)
+ user.sight |= (SEE_TURFS|BLIND)
+ . = ..()
diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
index 68a1033a266..0a8b9eb95a4 100644
--- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
@@ -1,155 +1,155 @@
-/obj/machinery/atmospherics/binary
- dir = SOUTH
- initialize_directions = SOUTH|NORTH
- use_power = IDLE_POWER_USE
-
- layer = GAS_PUMP_LAYER
-
- var/datum/gas_mixture/air1
- var/datum/gas_mixture/air2
-
- var/obj/machinery/atmospherics/node1
- var/obj/machinery/atmospherics/node2
-
- var/datum/pipeline/parent1
- var/datum/pipeline/parent2
-
-/obj/machinery/atmospherics/binary/New()
- ..()
- switch(dir)
- if(NORTH)
- initialize_directions = NORTH|SOUTH
- if(SOUTH)
- initialize_directions = NORTH|SOUTH
- if(EAST)
- initialize_directions = EAST|WEST
- if(WEST)
- initialize_directions = EAST|WEST
-
- air1 = new
- air2 = new
-
- air1.volume = 200
- air2.volume = 200
-
-/obj/machinery/atmospherics/binary/Destroy()
- if(node1)
- node1.disconnect(src)
- node1 = null
- nullifyPipenet(parent1)
- if(node2)
- node2.disconnect(src)
- node2 = null
- nullifyPipenet(parent2)
- return ..()
-
-/obj/machinery/atmospherics/binary/atmos_init()
- ..()
- var/node2_connect = dir
- var/node1_connect = turn(dir, 180)
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
- if(target.initialize_directions & get_dir(target,src))
- var/c = check_connect_types(target,src)
- if(c)
- target.connected_to = c
- connected_to = c
- node1 = target
- break
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
- if(target.initialize_directions & get_dir(target,src))
- var/c = check_connect_types(target,src)
- if(c)
- target.connected_to = c
- connected_to = c
- node2 = target
- break
-
- update_icon()
- update_underlays()
-
-/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE)
- if(!parent1)
- parent1 = new /datum/pipeline()
- parent1.build_pipeline(src)
-
- if(!parent2)
- parent2 = new /datum/pipeline()
- parent2.build_pipeline(src)
- ..()
-
-/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference)
- if(reference == node1)
- if(istype(node1, /obj/machinery/atmospherics/pipe))
- qdel(parent1)
- node1 = null
- else if(reference == node2)
- if(istype(node2, /obj/machinery/atmospherics/pipe))
- qdel(parent2)
- node2 = null
- update_icon()
-
-/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P)
- ..()
- if(!P)
- return
- if(P == parent1)
- parent1.other_airs -= air1
- parent1 = null
- else if(P == parent2)
- parent2.other_airs -= air2
- parent2 = null
-
-/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P)
- if(P == parent1)
- return air1
- else if(P == parent2)
- return air2
-
-/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P)
- if(P)
- if(parent1 == P)
- return list(node1)
- else if(parent2 == P)
- return list(node2)
- else
- return list(node1, node2)
-
-/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A)
- if(A == node1)
- parent1 = P
- else if(A == node2)
- parent2 = P
-
-/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A)
- if(A == node1)
- return parent1
- else if(A == node2)
- return parent2
-
-/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New)
- if(Old == parent1)
- parent1 = New
- else if(Old == parent2)
- parent2 = New
-
-/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures)
- ..()
-
- var/turf/T = get_turf(src)
- if(T)
- //Remove the gas from air1+air2 and assume it
- var/datum/gas_mixture/environment = T.return_air()
- var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
- lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
- var/shared_loss = lost/2
-
- var/datum/gas_mixture/to_release = air1.remove(shared_loss)
- to_release.merge(air2.remove(shared_loss))
- T.assume_air(to_release)
- air_update_turf(1)
-
-/obj/machinery/atmospherics/binary/process_atmos()
- ..()
- return parent1 && parent2
+/obj/machinery/atmospherics/binary
+ dir = SOUTH
+ initialize_directions = SOUTH|NORTH
+ use_power = IDLE_POWER_USE
+
+ layer = GAS_PUMP_LAYER
+
+ var/datum/gas_mixture/air1
+ var/datum/gas_mixture/air2
+
+ var/obj/machinery/atmospherics/node1
+ var/obj/machinery/atmospherics/node2
+
+ var/datum/pipeline/parent1
+ var/datum/pipeline/parent2
+
+/obj/machinery/atmospherics/binary/New()
+ ..()
+ switch(dir)
+ if(NORTH)
+ initialize_directions = NORTH|SOUTH
+ if(SOUTH)
+ initialize_directions = NORTH|SOUTH
+ if(EAST)
+ initialize_directions = EAST|WEST
+ if(WEST)
+ initialize_directions = EAST|WEST
+
+ air1 = new
+ air2 = new
+
+ air1.volume = 200
+ air2.volume = 200
+
+/obj/machinery/atmospherics/binary/Destroy()
+ if(node1)
+ node1.disconnect(src)
+ node1 = null
+ nullifyPipenet(parent1)
+ if(node2)
+ node2.disconnect(src)
+ node2 = null
+ nullifyPipenet(parent2)
+ return ..()
+
+/obj/machinery/atmospherics/binary/atmos_init()
+ ..()
+ var/node2_connect = dir
+ var/node1_connect = turn(dir, 180)
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ var/c = check_connect_types(target,src)
+ if(c)
+ target.connected_to = c
+ connected_to = c
+ node1 = target
+ break
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ var/c = check_connect_types(target,src)
+ if(c)
+ target.connected_to = c
+ connected_to = c
+ node2 = target
+ break
+
+ update_icon()
+ update_underlays()
+
+/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE)
+ if(!parent1)
+ parent1 = new /datum/pipeline()
+ parent1.build_pipeline(src)
+
+ if(!parent2)
+ parent2 = new /datum/pipeline()
+ parent2.build_pipeline(src)
+ ..()
+
+/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference)
+ if(reference == node1)
+ if(istype(node1, /obj/machinery/atmospherics/pipe))
+ qdel(parent1)
+ node1 = null
+ else if(reference == node2)
+ if(istype(node2, /obj/machinery/atmospherics/pipe))
+ qdel(parent2)
+ node2 = null
+ update_icon()
+
+/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P)
+ ..()
+ if(!P)
+ return
+ if(P == parent1)
+ parent1.other_airs -= air1
+ parent1 = null
+ else if(P == parent2)
+ parent2.other_airs -= air2
+ parent2 = null
+
+/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P)
+ if(P == parent1)
+ return air1
+ else if(P == parent2)
+ return air2
+
+/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P)
+ if(P)
+ if(parent1 == P)
+ return list(node1)
+ else if(parent2 == P)
+ return list(node2)
+ else
+ return list(node1, node2)
+
+/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A)
+ if(A == node1)
+ parent1 = P
+ else if(A == node2)
+ parent2 = P
+
+/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A)
+ if(A == node1)
+ return parent1
+ else if(A == node2)
+ return parent2
+
+/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New)
+ if(Old == parent1)
+ parent1 = New
+ else if(Old == parent2)
+ parent2 = New
+
+/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures)
+ ..()
+
+ var/turf/T = get_turf(src)
+ if(T)
+ //Remove the gas from air1+air2 and assume it
+ var/datum/gas_mixture/environment = T.return_air()
+ var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+ lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
+ var/shared_loss = lost/2
+
+ var/datum/gas_mixture/to_release = air1.remove(shared_loss)
+ to_release.merge(air2.remove(shared_loss))
+ T.assume_air(to_release)
+ air_update_turf(1)
+
+/obj/machinery/atmospherics/binary/process_atmos()
+ ..()
+ return parent1 && parent2
diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm
index e2dd952213a..b45dd8929b4 100644
--- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm
@@ -1,125 +1,125 @@
-//node1, air1, network1 correspond to input
-//node2, air2, network2 correspond to output
-/obj/machinery/atmospherics/binary/circulator
- name = "circulator/heat exchanger"
- desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side."
- icon = 'icons/obj/atmospherics/circulator.dmi'
- icon_state = "circ1-off"
-
- var/side = CIRC_LEFT
-
- var/global/const/CIRC_LEFT = WEST
- var/global/const/CIRC_RIGHT = EAST
-
- var/last_pressure_delta = 0
-
- var/obj/machinery/power/generator/generator
-
- anchored = 1
- density = 1
-
- can_unwrench = 1
- var/side_inverted = 0
-
-// Creating a custom circulator pipe subtype to be delivered through cargo
-/obj/item/pipe/circulator
- name = "circulator/heat exchanger fitting"
-
-/obj/item/pipe/circulator/New(loc)
- var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null)
- ..(loc, make_from = C)
-
-/obj/machinery/atmospherics/binary/circulator/Destroy()
- if(generator && generator.cold_circ == src)
- generator.cold_circ = null
- else if(generator && generator.hot_circ == src)
- generator.hot_circ = null
- return ..()
-
-/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air()
- var/datum/gas_mixture/inlet = get_inlet_air()
- var/datum/gas_mixture/outlet = get_outlet_air()
- var/output_starting_pressure = outlet.return_pressure()
- var/input_starting_pressure = inlet.return_pressure()
-
- if(output_starting_pressure >= input_starting_pressure - 10)
- //Need at least 10 KPa difference to overcome friction in the mechanism
- last_pressure_delta = 0
- return null
-
- //Calculate necessary moles to transfer using PV = nRT
- if(inlet.temperature > 0)
- var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2
-
- var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION)
-
- last_pressure_delta = pressure_delta
-
- //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];")
-
- //Actually transfer the gas
- var/datum/gas_mixture/removed = inlet.remove(transfer_moles)
-
- parent1.update = 1
- parent2.update = 1
-
- return removed
-
- else
- last_pressure_delta = 0
-
-/obj/machinery/atmospherics/binary/circulator/process_atmos()
- ..()
- update_icon()
-
-/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air()
- if(side_inverted==0)
- return air2
- else
- return air1
-
-/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air()
- if(side_inverted==0)
- return air1
- else
- return air2
-
-/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side()
- if(dir==SOUTH||dir==NORTH)
- if(side_inverted==0)
- return "South"
- else
- return "North"
-
-/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side()
- if(dir==SOUTH||dir==NORTH)
- if(side_inverted==0)
- return "North"
- else
- return "South"
-
-/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/W as obj, mob/user as mob, params)
- if(ismultitool(W))
- if(side_inverted == 0)
- side_inverted = 1
- else
- side_inverted = 0
- to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.")
- desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side."
- else
- return ..()
-
-/obj/machinery/atmospherics/binary/circulator/update_icon()
- ..()
-
- if(stat & (BROKEN|NOPOWER))
- icon_state = "circ[side]-p"
- else if(last_pressure_delta > 0)
- if(last_pressure_delta > ONE_ATMOSPHERE)
- icon_state = "circ[side]-run"
- else
- icon_state = "circ[side]-slow"
- else
- icon_state = "circ[side]-off"
-
- return 1
\ No newline at end of file
+//node1, air1, network1 correspond to input
+//node2, air2, network2 correspond to output
+#define CIRC_LEFT WEST
+#define CIRC_RIGHT EAST
+
+/obj/machinery/atmospherics/binary/circulator
+ name = "circulator/heat exchanger"
+ desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side."
+ icon = 'icons/obj/atmospherics/circulator.dmi'
+ icon_state = "circ1-off"
+
+ var/side = CIRC_LEFT
+
+ var/last_pressure_delta = 0
+
+ var/obj/machinery/power/generator/generator
+
+ anchored = 1
+ density = 1
+
+ can_unwrench = 1
+ var/side_inverted = 0
+
+// Creating a custom circulator pipe subtype to be delivered through cargo
+/obj/item/pipe/circulator
+ name = "circulator/heat exchanger fitting"
+
+/obj/item/pipe/circulator/New(loc)
+ var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null)
+ ..(loc, make_from = C)
+
+/obj/machinery/atmospherics/binary/circulator/Destroy()
+ if(generator && generator.cold_circ == src)
+ generator.cold_circ = null
+ else if(generator && generator.hot_circ == src)
+ generator.hot_circ = null
+ return ..()
+
+/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air()
+ var/datum/gas_mixture/inlet = get_inlet_air()
+ var/datum/gas_mixture/outlet = get_outlet_air()
+ var/output_starting_pressure = outlet.return_pressure()
+ var/input_starting_pressure = inlet.return_pressure()
+
+ if(output_starting_pressure >= input_starting_pressure - 10)
+ //Need at least 10 KPa difference to overcome friction in the mechanism
+ last_pressure_delta = 0
+ return null
+
+ //Calculate necessary moles to transfer using PV = nRT
+ if(inlet.temperature > 0)
+ var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2
+
+ var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION)
+
+ last_pressure_delta = pressure_delta
+
+ //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];")
+
+ //Actually transfer the gas
+ var/datum/gas_mixture/removed = inlet.remove(transfer_moles)
+
+ parent1.update = 1
+ parent2.update = 1
+
+ return removed
+
+ else
+ last_pressure_delta = 0
+
+/obj/machinery/atmospherics/binary/circulator/process_atmos()
+ ..()
+ update_icon()
+
+/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air()
+ if(side_inverted==0)
+ return air2
+ else
+ return air1
+
+/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air()
+ if(side_inverted==0)
+ return air1
+ else
+ return air2
+
+/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side()
+ if(dir==SOUTH||dir==NORTH)
+ if(side_inverted==0)
+ return "South"
+ else
+ return "North"
+
+/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side()
+ if(dir==SOUTH||dir==NORTH)
+ if(side_inverted==0)
+ return "North"
+ else
+ return "South"
+
+/obj/machinery/atmospherics/binary/circulator/multitool_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.use_tool(src, user, 0, volume = I.tool_volume))
+ return
+ if(!side_inverted)
+ side_inverted = TRUE
+ else
+ side_inverted = FALSE
+ to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.")
+ desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side."
+
+/obj/machinery/atmospherics/binary/circulator/update_icon()
+ ..()
+
+ if(stat & (BROKEN|NOPOWER))
+ icon_state = "circ[side]-p"
+ else if(last_pressure_delta > 0)
+ if(last_pressure_delta > ONE_ATMOSPHERE)
+ icon_state = "circ[side]-run"
+ else
+ icon_state = "circ[side]-slow"
+ else
+ icon_state = "circ[side]-off"
+
+ return 1
diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
index b9902dbb066..c0ed34ef206 100644
--- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm
@@ -1,269 +1,269 @@
-/obj/machinery/atmospherics/binary/dp_vent_pump
- icon = 'icons/atmos/vent_pump.dmi'
- icon_state = "map_dp_vent"
-
- //node2 is output port
- //node1 is input port
-
- req_one_access_txt = "24;10"
-
- name = "dual-port air vent"
- desc = "Has a valve and pump attached to it. There are two ports."
-
- can_unwrench = 1
-
- level = 1
-
- connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes
-
- var/on = 0
- var/pump_direction = 1 //0 = siphoning, 1 = releasing
-
- var/external_pressure_bound = ONE_ATMOSPHERE
- var/input_pressure_min = 0
- var/output_pressure_max = 0
-
- var/frequency = ATMOS_VENTSCRUB
- var/id_tag = null
- var/datum/radio_frequency/radio_connection
- var/advcontrol = 0//does this device listen to the AAC
-
- settagwhitelist = list("id_tag")
-
- var/pressure_checks = 1
- //1: Do not pass external_pressure_bound
- //2: Do not pass input_pressure_min
- //4: Do not pass output_pressure_max
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/New()
- ..()
- if(!id_tag)
- assign_uid()
- id_tag = num2text(uid)
- icon = null
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy()
- if(SSradio)
- SSradio.remove_object(src, frequency)
- radio_connection = null
- return ..()
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init()
- ..()
- if(frequency)
- set_frequency(frequency)
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume
- name = "large dual port air vent"
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on
- on = TRUE
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New()
- ..()
- air1.volume = 1000
- air2.volume = 1000
-
-/obj/machinery/atmospherics/binary/volume_pump/update_underlays()
- if(..())
- underlays.Cut()
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
- add_underlay(T, node1, turn(dir, -180))
- add_underlay(T, node2, dir)
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0)
- ..()
-
- if(!check_icon_cache())
- return
-
- overlays.Cut()
-
- var/vent_icon = "vent"
-
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
-
- if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe))
- vent_icon += "h"
-
- if(!powered())
- vent_icon += "off"
- else
- vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]"
-
- overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon)
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays()
- if(..())
- underlays.Cut()
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
- if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe))
- return
- else
- if(node1)
- add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type)
- else
- add_underlay(T, node1, turn(dir, -180))
- if(node2)
- add_underlay(T, node2, dir, node2.icon_connect_type)
- else
- add_underlay(T, node2, dir)
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos()
- ..()
- if(!on)
- return 0
-
- var/datum/gas_mixture/environment = loc.return_air()
- var/environment_pressure = environment.return_pressure()
-
- if(pump_direction) //input -> external
- var/pressure_delta = 10000
-
- if(pressure_checks&1)
- pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure))
- if(pressure_checks&2)
- pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min))
-
- if(pressure_delta > 0)
- if(air1.temperature > 0)
- var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
-
- loc.assume_air(removed)
-
- parent1.update = 1
- air_update_turf()
- else //external -> output
- var/pressure_delta = 10000
-
- if(pressure_checks&1)
- pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound))
- if(pressure_checks&4)
- pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure()))
-
- if(pressure_delta > 0)
- if(environment.temperature > 0)
- var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
-
- var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
-
- air2.merge(removed)
-
- parent2.update = 1
- air_update_turf()
- return 1
-
-//Radio remote control
-/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
-
- signal.data = list(
- "tag" = id_tag,
- "device" = "ADVP",
- "power" = on,
- "direction" = pump_direction?("release"):("siphon"),
- "checks" = pressure_checks,
- "input" = input_pressure_min,
- "output" = output_pressure_max,
- "external" = external_pressure_bound,
- "sigtype" = "status"
- )
- radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
-
- return 1
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol))
- return 0
- if(signal.data["power"] != null)
- on = text2num(signal.data["power"])
-
- if(signal.data["power_toggle"] != null)
- on = !on
-
- if(signal.data["direction"] != null)
- pump_direction = text2num(signal.data["direction"])
-
- if(signal.data["checks"] != null)
- pressure_checks = text2num(signal.data["checks"])
-
- if(signal.data["purge"])
- pressure_checks &= ~1
- pump_direction = 0
-
- if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none
- pressure_checks |= 1
- pump_direction = 1
-
- if(signal.data["set_input_pressure"] != null)
- input_pressure_min = between(
- 0,
- text2num(signal.data["set_input_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if(signal.data["set_output_pressure"] != null)
- output_pressure_max = between(
- 0,
- text2num(signal.data["set_output_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if(signal.data["set_external_pressure"] != null)
- external_pressure_bound = between(
- 0,
- text2num(signal.data["set_external_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if(signal.data["status"])
- spawn(2)
- broadcast_status()
- return //do not update_icon
-
- spawn(2)
- broadcast_status()
- update_icon()
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob)
- if(istype(W, /obj/item/multitool))
- update_multitool_menu(user)
- return 1
-
- return ..()
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P)
- return {"
-
- "}
-
-/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O)
- . = ..()
- if(.)
- return .
- if("toggleadvcontrol" in href_list)
- advcontrol = !advcontrol
- return TRUE
+/obj/machinery/atmospherics/binary/dp_vent_pump
+ icon = 'icons/atmos/vent_pump.dmi'
+ icon_state = "map_dp_vent"
+
+ //node2 is output port
+ //node1 is input port
+
+ req_one_access_txt = "24;10"
+
+ name = "dual-port air vent"
+ desc = "Has a valve and pump attached to it. There are two ports."
+
+ can_unwrench = 1
+
+ level = 1
+
+ connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes
+
+ var/on = 0
+ var/pump_direction = 1 //0 = siphoning, 1 = releasing
+
+ var/external_pressure_bound = ONE_ATMOSPHERE
+ var/input_pressure_min = 0
+ var/output_pressure_max = 0
+
+ var/frequency = ATMOS_VENTSCRUB
+ var/id_tag = null
+ var/datum/radio_frequency/radio_connection
+ var/advcontrol = 0//does this device listen to the AAC
+
+ settagwhitelist = list("id_tag")
+
+ var/pressure_checks = 1
+ //1: Do not pass external_pressure_bound
+ //2: Do not pass input_pressure_min
+ //4: Do not pass output_pressure_max
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/New()
+ ..()
+ if(!id_tag)
+ assign_uid()
+ id_tag = num2text(uid)
+ icon = null
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy()
+ if(SSradio)
+ SSradio.remove_object(src, frequency)
+ radio_connection = null
+ return ..()
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume
+ name = "large dual port air vent"
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on
+ on = TRUE
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New()
+ ..()
+ air1.volume = 1000
+ air2.volume = 1000
+
+/obj/machinery/atmospherics/binary/volume_pump/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, turn(dir, -180))
+ add_underlay(T, node2, dir)
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0)
+ ..()
+
+ if(!check_icon_cache())
+ return
+
+ overlays.Cut()
+
+ var/vent_icon = "vent"
+
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+
+ if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe))
+ vent_icon += "h"
+
+ if(!powered())
+ vent_icon += "off"
+ else
+ vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]"
+
+ overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon)
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe))
+ return
+ else
+ if(node1)
+ add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type)
+ else
+ add_underlay(T, node1, turn(dir, -180))
+ if(node2)
+ add_underlay(T, node2, dir, node2.icon_connect_type)
+ else
+ add_underlay(T, node2, dir)
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos()
+ ..()
+ if(!on)
+ return 0
+
+ var/datum/gas_mixture/environment = loc.return_air()
+ var/environment_pressure = environment.return_pressure()
+
+ if(pump_direction) //input -> external
+ var/pressure_delta = 10000
+
+ if(pressure_checks&1)
+ pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure))
+ if(pressure_checks&2)
+ pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min))
+
+ if(pressure_delta > 0)
+ if(air1.temperature > 0)
+ var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+
+ var/datum/gas_mixture/removed = air1.remove(transfer_moles)
+
+ loc.assume_air(removed)
+
+ parent1.update = 1
+ air_update_turf()
+ else //external -> output
+ var/pressure_delta = 10000
+
+ if(pressure_checks&1)
+ pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound))
+ if(pressure_checks&4)
+ pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure()))
+
+ if(pressure_delta > 0)
+ if(environment.temperature > 0)
+ var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)
+
+ var/datum/gas_mixture/removed = loc.remove_air(transfer_moles)
+
+ air2.merge(removed)
+
+ parent2.update = 1
+ air_update_turf()
+ return 1
+
+//Radio remote control
+/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
+
+ signal.data = list(
+ "tag" = id_tag,
+ "device" = "ADVP",
+ "power" = on,
+ "direction" = pump_direction?("release"):("siphon"),
+ "checks" = pressure_checks,
+ "input" = input_pressure_min,
+ "output" = output_pressure_max,
+ "external" = external_pressure_bound,
+ "sigtype" = "status"
+ )
+ radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
+
+ return 1
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol))
+ return 0
+ if(signal.data["power"] != null)
+ on = text2num(signal.data["power"])
+
+ if(signal.data["power_toggle"] != null)
+ on = !on
+
+ if(signal.data["direction"] != null)
+ pump_direction = text2num(signal.data["direction"])
+
+ if(signal.data["checks"] != null)
+ pressure_checks = text2num(signal.data["checks"])
+
+ if(signal.data["purge"])
+ pressure_checks &= ~1
+ pump_direction = 0
+
+ if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none
+ pressure_checks |= 1
+ pump_direction = 1
+
+ if(signal.data["set_input_pressure"] != null)
+ input_pressure_min = between(
+ 0,
+ text2num(signal.data["set_input_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(signal.data["set_output_pressure"] != null)
+ output_pressure_max = between(
+ 0,
+ text2num(signal.data["set_output_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(signal.data["set_external_pressure"] != null)
+ external_pressure_bound = between(
+ 0,
+ text2num(signal.data["set_external_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(signal.data["status"])
+ spawn(2)
+ broadcast_status()
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob)
+ if(istype(W, /obj/item/multitool))
+ update_multitool_menu(user)
+ return 1
+
+ return ..()
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P)
+ return {"
+
+ "}
+
+/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O)
+ . = ..()
+ if(.)
+ return .
+ if("toggleadvcontrol" in href_list)
+ advcontrol = !advcontrol
+ return TRUE
diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
index 3448655c953..6eeb382b3d0 100644
--- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm
@@ -1,192 +1,192 @@
-/obj/machinery/atmospherics/binary/passive_gate
- //Tries to achieve target pressure at output (like a normal pump) except
- // Uses no power but can not transfer gases from a low pressure area to a high pressure area
- icon = 'icons/atmos/passive_gate.dmi'
- icon_state = "map"
-
- name = "passive gate"
- desc = "A one-way air valve that does not require power"
-
- can_unwrench = 1
-
- var/on = 0
- var/target_pressure = ONE_ATMOSPHERE
-
- var/frequency = 0
- var/id = null
- var/datum/radio_frequency/radio_connection
-
-/obj/machinery/atmospherics/binary/passive_gate/atmos_init()
- ..()
- if(frequency)
- set_frequency(frequency)
-
-/obj/machinery/atmospherics/binary/passive_gate/Destroy()
- if(SSradio)
- SSradio.remove_object(src, frequency)
- radio_connection = null
- return ..()
-
-/obj/machinery/atmospherics/binary/passive_gate/update_icon()
- ..()
- icon_state = "[on ? "on" : "off"]"
-
-/obj/machinery/atmospherics/binary/passive_gate/update_underlays()
- if(..())
- underlays.Cut()
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
- add_underlay(T, node1, turn(dir, 180))
- add_underlay(T, node2, dir)
-
-/obj/machinery/atmospherics/binary/passive_gate/process_atmos()
- ..()
- if(!on)
- return 0
-
- var/output_starting_pressure = air2.return_pressure()
- var/input_starting_pressure = air1.return_pressure()
-
- if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
- //No need to pump gas if target is already reached or input pressure is too low
- //Need at least 10 KPa difference to overcome friction in the mechanism
- return 1
-
- //Calculate necessary moles to transfer using PV = nRT
- if((air1.total_moles() > 0) && (air1.temperature>0))
- var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
- //Can not have a pressure delta that would cause output_pressure > input_pressure
-
- var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- //Actually transfer the gas
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
- air2.merge(removed)
-
- parent1.update = 1
-
- parent2.update = 1
- return 1
-
-//Radio remote control
-/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
-
-/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
-
- signal.data = list(
- "tag" = id,
- "device" = "AGP",
- "power" = on,
- "target_output" = target_pressure,
- "sigtype" = "status"
- )
-
- radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
-
- return 1
-
-/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
-
- var/old_on = on //for logging
-
- if("power" in signal.data)
- on = text2num(signal.data["power"])
-
- if("power_toggle" in signal.data)
- on = !on
-
- if("set_output_pressure" in signal.data)
- target_pressure = between(
- 0,
- text2num(signal.data["set_output_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if(on != old_on)
- investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
-
- if("status" in signal.data)
- spawn(2)
- broadcast_status()
- return //do not update_icon
-
- spawn(2)
- broadcast_status()
- update_icon()
- return
-
-/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user)
- if(..())
- return
-
- if(!allowed(user))
- to_chat(user, "Access denied.")
- return
-
- add_fingerprint(user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
- user.set_machine(src)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state)
- ui.open()
-
-/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user)
- var/list/data = list()
- data["on"] = on
- data["pressure"] = round(target_pressure)
- data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
- return data
-
-/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list)
- if(..())
- return 1
-
- if(href_list["power"])
- on = !on
- investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
- . = TRUE
- if(href_list["pressure"])
- var/pressure = href_list["pressure"]
- if(pressure == "max")
- pressure = MAX_OUTPUT_PRESSURE
- . = TRUE
- else if(pressure == "input")
- pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
- if(!isnull(pressure))
- . = TRUE
- else if(text2num(pressure) != null)
- pressure = text2num(pressure)
- . = TRUE
- if(.)
- target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
- investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
-
- update_icon()
- SSnanoui.update_uis(src)
-
-/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params)
- if(!istype(W, /obj/item/wrench))
- return ..()
- if(on)
- to_chat(user, "You cannot unwrench this [src], turn it off first.")
- return 1
- return ..()
+/obj/machinery/atmospherics/binary/passive_gate
+ //Tries to achieve target pressure at output (like a normal pump) except
+ // Uses no power but can not transfer gases from a low pressure area to a high pressure area
+ icon = 'icons/atmos/passive_gate.dmi'
+ icon_state = "map"
+
+ name = "passive gate"
+ desc = "A one-way air valve that does not require power"
+
+ can_unwrench = 1
+
+ var/on = 0
+ var/target_pressure = ONE_ATMOSPHERE
+
+ var/frequency = 0
+ var/id = null
+ var/datum/radio_frequency/radio_connection
+
+/obj/machinery/atmospherics/binary/passive_gate/atmos_init()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/binary/passive_gate/Destroy()
+ if(SSradio)
+ SSradio.remove_object(src, frequency)
+ radio_connection = null
+ return ..()
+
+/obj/machinery/atmospherics/binary/passive_gate/update_icon()
+ ..()
+ icon_state = "[on ? "on" : "off"]"
+
+/obj/machinery/atmospherics/binary/passive_gate/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, turn(dir, 180))
+ add_underlay(T, node2, dir)
+
+/obj/machinery/atmospherics/binary/passive_gate/process_atmos()
+ ..()
+ if(!on)
+ return 0
+
+ var/output_starting_pressure = air2.return_pressure()
+ var/input_starting_pressure = air1.return_pressure()
+
+ if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10))
+ //No need to pump gas if target is already reached or input pressure is too low
+ //Need at least 10 KPa difference to overcome friction in the mechanism
+ return 1
+
+ //Calculate necessary moles to transfer using PV = nRT
+ if((air1.total_moles() > 0) && (air1.temperature>0))
+ var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2)
+ //Can not have a pressure delta that would cause output_pressure > input_pressure
+
+ var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+
+ //Actually transfer the gas
+ var/datum/gas_mixture/removed = air1.remove(transfer_moles)
+ air2.merge(removed)
+
+ parent1.update = 1
+
+ parent2.update = 1
+ return 1
+
+//Radio remote control
+/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
+
+/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
+
+ signal.data = list(
+ "tag" = id,
+ "device" = "AGP",
+ "power" = on,
+ "target_output" = target_pressure,
+ "sigtype" = "status"
+ )
+
+ radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
+
+ return 1
+
+/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
+ return 0
+
+ var/old_on = on //for logging
+
+ if("power" in signal.data)
+ on = text2num(signal.data["power"])
+
+ if("power_toggle" in signal.data)
+ on = !on
+
+ if("set_output_pressure" in signal.data)
+ target_pressure = between(
+ 0,
+ text2num(signal.data["set_output_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(on != old_on)
+ investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
+
+ if("status" in signal.data)
+ spawn(2)
+ broadcast_status()
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
+ return
+
+/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user)
+ if(..())
+ return
+
+ if(!allowed(user))
+ to_chat(user, "Access denied.")
+ return
+
+ add_fingerprint(user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
+ user.set_machine(src)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state)
+ ui.open()
+
+/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["pressure"] = round(target_pressure)
+ data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
+ return data
+
+/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list)
+ if(..())
+ return 1
+
+ if(href_list["power"])
+ on = !on
+ investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
+ . = TRUE
+ if(href_list["pressure"])
+ var/pressure = href_list["pressure"]
+ if(pressure == "max")
+ pressure = MAX_OUTPUT_PRESSURE
+ . = TRUE
+ else if(pressure == "input")
+ pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
+ if(!isnull(pressure))
+ . = TRUE
+ else if(text2num(pressure) != null)
+ pressure = text2num(pressure)
+ . = TRUE
+ if(.)
+ target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
+ investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
+
+ update_icon()
+ SSnanoui.update_uis(src)
+
+/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params)
+ if(!istype(W, /obj/item/wrench))
+ return ..()
+ if(on)
+ to_chat(user, "You cannot unwrench this [src], turn it off first.")
+ return 1
+ return ..()
diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm
index 1998e67e17d..92550bb9325 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm
@@ -1,261 +1,261 @@
-/*
-Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure.
-
-node1, air1, network1 correspond to input
-node2, air2, network2 correspond to output
-
-Thus, the two variables affect pump operation are set in New():
- air1.volume
- This is the volume of gas available to the pump that may be transfered to the output
- air2.volume
- Higher quantities of this cause more air to be perfected later
- but overall network volume is also increased as this increases...
-*/
-
-/obj/machinery/atmospherics/binary/pump
- icon = 'icons/atmos/pump.dmi'
- icon_state = "map_off"
-
- name = "gas pump"
- desc = "A pump"
-
- can_unwrench = 1
-
- var/on = 0
- var/target_pressure = ONE_ATMOSPHERE
-
- var/frequency = 0
- var/id = null
- var/datum/radio_frequency/radio_connection
-
-/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/binary/pump/AICtrlClick()
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- set_max()
- return
-
-/obj/machinery/atmospherics/binary/pump/AIAltClick()
- set_max()
- return ..()
-
-/obj/machinery/atmospherics/binary/pump/proc/toggle()
- if(powered())
- on = !on
- update_icon()
-
-/obj/machinery/atmospherics/binary/pump/proc/set_max()
- if(powered())
- target_pressure = MAX_OUTPUT_PRESSURE
- update_icon()
-
-/obj/machinery/atmospherics/binary/pump/Destroy()
- if(SSradio)
- SSradio.remove_object(src, frequency)
- radio_connection = null
- return ..()
-
-/obj/machinery/atmospherics/binary/pump/on
- icon_state = "map_on"
- on = 1
-
-/obj/machinery/atmospherics/binary/pump/update_icon()
- ..()
-
- if(!powered())
- icon_state = "off"
- else
- icon_state = "[on ? "on" : "off"]"
-
-/obj/machinery/atmospherics/binary/pump/update_underlays()
- if(..())
- underlays.Cut()
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
- add_underlay(T, node1, turn(dir, -180))
- add_underlay(T, node2, dir)
-
-/obj/machinery/atmospherics/binary/pump/process_atmos()
- ..()
- if((stat & (NOPOWER|BROKEN)) || !on)
- return 0
-
- var/output_starting_pressure = air2.return_pressure()
-
- if( (target_pressure - output_starting_pressure) < 0.01)
- //No need to pump gas if target is already reached!
- return 1
-
- //Calculate necessary moles to transfer using PV=nRT
- if((air1.total_moles() > 0) && (air1.temperature>0))
- var/pressure_delta = target_pressure - output_starting_pressure
- var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- //Actually transfer the gas
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
- air2.merge(removed)
-
- parent1.update = 1
-
- parent2.update = 1
- return 1
-
-//Radio remote control
-/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
-
-/obj/machinery/atmospherics/binary/pump/proc/broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
-
- signal.data = list(
- "tag" = id,
- "device" = "AGP",
- "power" = on,
- "target_output" = target_pressure,
- "sigtype" = "status"
- )
-
- radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
- return 1
-
-/obj/machinery/atmospherics/binary/pump/atmos_init()
- ..()
- if(frequency)
- set_frequency(frequency)
-
-/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
-
- var/old_on = on //for logging
-
- if(signal.data["power"])
- on = text2num(signal.data["power"])
-
- if(signal.data["power_toggle"])
- on = !on
-
- if(signal.data["set_output_pressure"])
- target_pressure = between(
- 0,
- text2num(signal.data["set_output_pressure"]),
- ONE_ATMOSPHERE*50
- )
-
- if(on != old_on)
- investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
-
- if(signal.data["status"])
- spawn(2)
- broadcast_status()
- return //do not update_icon
-
- spawn(2)
- broadcast_status()
- update_icon()
- return
-
-/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user)
- if(..())
- return
-
- if(!allowed(user))
- to_chat(user, "Access denied.")
- return
-
- add_fingerprint(user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
- user.set_machine(src)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state)
- ui.open()
-
-/obj/machinery/atmospherics/binary/pump/ui_data(mob/user)
- var/list/data = list()
- data["on"] = on
- data["pressure"] = round(target_pressure)
- data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
- return data
-
-/obj/machinery/atmospherics/binary/pump/Topic(href,href_list)
- if(..())
- return 1
-
- if(href_list["power"])
- on = !on
- investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
- . = TRUE
- if(href_list["pressure"])
- var/pressure = href_list["pressure"]
- if(pressure == "max")
- pressure = MAX_OUTPUT_PRESSURE
- . = TRUE
- else if(pressure == "input")
- pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
- if(!isnull(pressure))
- . = TRUE
- else if(text2num(pressure) != null)
- pressure = text2num(pressure)
- . = TRUE
- if(.)
- target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
- investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
-
- update_icon()
- SSnanoui.update_uis(src)
-
-/obj/machinery/atmospherics/binary/pump/power_change()
- var/old_stat = stat
- ..()
- if(old_stat != stat)
- update_icon()
-
-/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/pen))
- var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN)
- if(!t)
- return
- if(!in_range(src, usr) && loc != usr)
- return
- name = t
- return
- else if(!istype(W, /obj/item/wrench))
- return ..()
- if(!(stat & NOPOWER) && on)
- to_chat(user, "You cannot unwrench this [src], turn it off first.")
- return 1
- return ..()
\ No newline at end of file
+/*
+Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure.
+
+node1, air1, network1 correspond to input
+node2, air2, network2 correspond to output
+
+Thus, the two variables affect pump operation are set in New():
+ air1.volume
+ This is the volume of gas available to the pump that may be transfered to the output
+ air2.volume
+ Higher quantities of this cause more air to be perfected later
+ but overall network volume is also increased as this increases...
+*/
+
+/obj/machinery/atmospherics/binary/pump
+ icon = 'icons/atmos/pump.dmi'
+ icon_state = "map_off"
+
+ name = "gas pump"
+ desc = "A pump"
+
+ can_unwrench = 1
+
+ var/on = 0
+ var/target_pressure = ONE_ATMOSPHERE
+
+ var/frequency = 0
+ var/id = null
+ var/datum/radio_frequency/radio_connection
+
+/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/pump/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/binary/pump/AIAltClick()
+ set_max()
+ return ..()
+
+/obj/machinery/atmospherics/binary/pump/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/binary/pump/proc/set_max()
+ if(powered())
+ target_pressure = MAX_OUTPUT_PRESSURE
+ update_icon()
+
+/obj/machinery/atmospherics/binary/pump/Destroy()
+ if(SSradio)
+ SSradio.remove_object(src, frequency)
+ radio_connection = null
+ return ..()
+
+/obj/machinery/atmospherics/binary/pump/on
+ icon_state = "map_on"
+ on = 1
+
+/obj/machinery/atmospherics/binary/pump/update_icon()
+ ..()
+
+ if(!powered())
+ icon_state = "off"
+ else
+ icon_state = "[on ? "on" : "off"]"
+
+/obj/machinery/atmospherics/binary/pump/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, turn(dir, -180))
+ add_underlay(T, node2, dir)
+
+/obj/machinery/atmospherics/binary/pump/process_atmos()
+ ..()
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ return 0
+
+ var/output_starting_pressure = air2.return_pressure()
+
+ if( (target_pressure - output_starting_pressure) < 0.01)
+ //No need to pump gas if target is already reached!
+ return 1
+
+ //Calculate necessary moles to transfer using PV=nRT
+ if((air1.total_moles() > 0) && (air1.temperature>0))
+ var/pressure_delta = target_pressure - output_starting_pressure
+ var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+
+ //Actually transfer the gas
+ var/datum/gas_mixture/removed = air1.remove(transfer_moles)
+ air2.merge(removed)
+
+ parent1.update = 1
+
+ parent2.update = 1
+ return 1
+
+//Radio remote control
+/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA)
+
+/obj/machinery/atmospherics/binary/pump/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
+
+ signal.data = list(
+ "tag" = id,
+ "device" = "AGP",
+ "power" = on,
+ "target_output" = target_pressure,
+ "sigtype" = "status"
+ )
+
+ radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
+ return 1
+
+/obj/machinery/atmospherics/binary/pump/atmos_init()
+ ..()
+ if(frequency)
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
+ return 0
+
+ var/old_on = on //for logging
+
+ if(signal.data["power"])
+ on = text2num(signal.data["power"])
+
+ if(signal.data["power_toggle"])
+ on = !on
+
+ if(signal.data["set_output_pressure"])
+ target_pressure = between(
+ 0,
+ text2num(signal.data["set_output_pressure"]),
+ ONE_ATMOSPHERE*50
+ )
+
+ if(on != old_on)
+ investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
+
+ if(signal.data["status"])
+ spawn(2)
+ broadcast_status()
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
+ return
+
+/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user)
+ if(..())
+ return
+
+ if(!allowed(user))
+ to_chat(user, "Access denied.")
+ return
+
+ add_fingerprint(user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
+ user.set_machine(src)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state)
+ ui.open()
+
+/obj/machinery/atmospherics/binary/pump/ui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["pressure"] = round(target_pressure)
+ data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
+ return data
+
+/obj/machinery/atmospherics/binary/pump/Topic(href,href_list)
+ if(..())
+ return 1
+
+ if(href_list["power"])
+ on = !on
+ investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
+ . = TRUE
+ if(href_list["pressure"])
+ var/pressure = href_list["pressure"]
+ if(pressure == "max")
+ pressure = MAX_OUTPUT_PRESSURE
+ . = TRUE
+ else if(pressure == "input")
+ pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
+ if(!isnull(pressure))
+ . = TRUE
+ else if(text2num(pressure) != null)
+ pressure = text2num(pressure)
+ . = TRUE
+ if(.)
+ target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
+ investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
+
+ update_icon()
+ SSnanoui.update_uis(src)
+
+/obj/machinery/atmospherics/binary/pump/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else if(!istype(W, /obj/item/wrench))
+ return ..()
+ if(!(stat & NOPOWER) && on)
+ to_chat(user, "You cannot unwrench this [src], turn it off first.")
+ return 1
+ return ..()
diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
index 0947b2ee860..558e30d44cc 100644
--- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm
@@ -1,257 +1,257 @@
-/*
-Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure.
-
-node1, air1, network1 correspond to input
-node2, air2, network2 correspond to output
-
-Thus, the two variables affect pump operation are set in New():
- air1.volume
- This is the volume of gas available to the pump that may be transfered to the output
- air2.volume
- Higher quantities of this cause more air to be perfected later
- but overall network volume is also increased as this increases...
-*/
-
-/obj/machinery/atmospherics/binary/volume_pump
- icon = 'icons/atmos/volume_pump.dmi'
- icon_state = "map_off"
-
- name = "volumetric gas pump"
- desc = "A volumetric pump"
-
- can_unwrench = 1
-
- var/on = 0
- var/transfer_rate = 200
-
- var/frequency = 0
- var/id = null
- var/datum/radio_frequency/radio_connection
-
-/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick()
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- set_max()
- return
-
-/obj/machinery/atmospherics/binary/volume_pump/AIAltClick()
- set_max()
- return ..()
-
-/obj/machinery/atmospherics/binary/volume_pump/proc/toggle()
- if(powered())
- on = !on
- update_icon()
-
-/obj/machinery/atmospherics/binary/volume_pump/proc/set_max()
- if(powered())
- transfer_rate = MAX_TRANSFER_RATE
- update_icon()
-
-/obj/machinery/atmospherics/binary/volume_pump/Destroy()
- if(SSradio)
- SSradio.remove_object(src, frequency)
- radio_connection = null
- return ..()
-
-/obj/machinery/atmospherics/binary/volume_pump/on
- on = 1
- icon_state = "map_on"
-
-/obj/machinery/atmospherics/binary/volume_pump/atmos_init()
- ..()
- set_frequency(frequency)
-
-/obj/machinery/atmospherics/binary/volume_pump/update_icon()
- ..()
-
- if(!powered())
- icon_state = "off"
- else
- icon_state = "[on ? "on" : "off"]"
-
-/obj/machinery/atmospherics/binary/volume_pump/update_underlays()
- if(..())
- underlays.Cut()
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
- add_underlay(T, node1, turn(dir, -180))
- add_underlay(T, node2, dir)
-
-/obj/machinery/atmospherics/binary/volume_pump/process_atmos()
- ..()
- if((stat & (NOPOWER|BROKEN)) || !on)
- return 0
-
- // Pump mechanism just won't do anything if the pressure is too high/too low
- var/input_starting_pressure = air1.return_pressure()
- var/output_starting_pressure = air2.return_pressure()
-
- if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000))
- return 1
-
- var/transfer_ratio = max(1, transfer_rate/air1.volume)
-
- var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio)
-
- air2.merge(removed)
-
-
- parent1.update = 1
- parent2.update = 1
-
- return 1
-
-/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = SSradio.add_object(src, frequency)
-
-/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status()
- if(!radio_connection)
- return 0
-
- var/datum/signal/signal = new
- signal.transmission_method = 1 //radio signal
- signal.source = src
-
- signal.data = list(
- "tag" = id,
- "device" = "APV",
- "power" = on,
- "transfer_rate" = transfer_rate,
- "sigtype" = "status"
- )
- radio_connection.post_signal(src, signal)
-
- return 1
-
-/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal)
- if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
- return 0
-
- var/old_on = on //for logging
-
- if(signal.data["power"])
- on = text2num(signal.data["power"])
-
- if(signal.data["power_toggle"])
- on = !on
-
- if(signal.data["set_transfer_rate"])
- transfer_rate = between(
- 0,
- text2num(signal.data["set_transfer_rate"]),
- air1.volume
- )
-
- if(on != old_on)
- investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
-
- if(signal.data["status"])
- spawn(2)
- broadcast_status()
- return //do not update_icon
-
- spawn(2)
- broadcast_status()
- update_icon()
-
-/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user)
- if(..())
- return
-
- if(!allowed(user))
- to_chat(user, "Access denied.")
- return
-
- add_fingerprint(user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
- user.set_machine(src)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state)
- ui.open()
-
-/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user)
- var/list/data = list()
- data["on"] = on
- data["rate"] = round(transfer_rate)
- data["max_rate"] = round(MAX_TRANSFER_RATE)
- return data
-
-/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list)
- if(..())
- return 1
-
- if(href_list["power"])
- on = !on
- investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
- . = TRUE
- if(href_list["rate"])
- var/rate = href_list["rate"]
- if(rate == "max")
- rate = MAX_TRANSFER_RATE
- . = TRUE
- else if(rate == "input")
- rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null
- if(!isnull(rate))
- . = TRUE
- else if(text2num(rate) != null)
- rate = text2num(rate)
- . = TRUE
- if(.)
- transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE)
- investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos")
-
- update_icon()
- SSnanoui.update_uis(src)
-
-/obj/machinery/atmospherics/binary/volume_pump/power_change()
- var/old_stat = stat
- ..()
- if(old_stat != stat)
- update_icon()
-
-/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/pen))
- var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN)
- if(!t)
- return
- if(!in_range(src, usr) && loc != usr)
- return
- name = t
- return
- else if(!istype(W, /obj/item/wrench))
- return ..()
- if(!(stat & NOPOWER) && on)
- to_chat(user, "You cannot unwrench this [src], turn it off first.")
- return 1
- return ..()
\ No newline at end of file
+/*
+Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure.
+
+node1, air1, network1 correspond to input
+node2, air2, network2 correspond to output
+
+Thus, the two variables affect pump operation are set in New():
+ air1.volume
+ This is the volume of gas available to the pump that may be transfered to the output
+ air2.volume
+ Higher quantities of this cause more air to be perfected later
+ but overall network volume is also increased as this increases...
+*/
+
+/obj/machinery/atmospherics/binary/volume_pump
+ icon = 'icons/atmos/volume_pump.dmi'
+ icon_state = "map_off"
+
+ name = "volumetric gas pump"
+ desc = "A volumetric pump"
+
+ can_unwrench = 1
+
+ var/on = 0
+ var/transfer_rate = 200
+
+ var/frequency = 0
+ var/id = null
+ var/datum/radio_frequency/radio_connection
+
+/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/binary/volume_pump/AIAltClick()
+ set_max()
+ return ..()
+
+/obj/machinery/atmospherics/binary/volume_pump/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/binary/volume_pump/proc/set_max()
+ if(powered())
+ transfer_rate = MAX_TRANSFER_RATE
+ update_icon()
+
+/obj/machinery/atmospherics/binary/volume_pump/Destroy()
+ if(SSradio)
+ SSradio.remove_object(src, frequency)
+ radio_connection = null
+ return ..()
+
+/obj/machinery/atmospherics/binary/volume_pump/on
+ on = 1
+ icon_state = "map_on"
+
+/obj/machinery/atmospherics/binary/volume_pump/atmos_init()
+ ..()
+ set_frequency(frequency)
+
+/obj/machinery/atmospherics/binary/volume_pump/update_icon()
+ ..()
+
+ if(!powered())
+ icon_state = "off"
+ else
+ icon_state = "[on ? "on" : "off"]"
+
+/obj/machinery/atmospherics/binary/volume_pump/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+ add_underlay(T, node1, turn(dir, -180))
+ add_underlay(T, node2, dir)
+
+/obj/machinery/atmospherics/binary/volume_pump/process_atmos()
+ ..()
+ if((stat & (NOPOWER|BROKEN)) || !on)
+ return 0
+
+ // Pump mechanism just won't do anything if the pressure is too high/too low
+ var/input_starting_pressure = air1.return_pressure()
+ var/output_starting_pressure = air2.return_pressure()
+
+ if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000))
+ return 1
+
+ var/transfer_ratio = max(1, transfer_rate/air1.volume)
+
+ var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio)
+
+ air2.merge(removed)
+
+
+ parent1.update = 1
+ parent2.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = SSradio.add_object(src, frequency)
+
+/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status()
+ if(!radio_connection)
+ return 0
+
+ var/datum/signal/signal = new
+ signal.transmission_method = 1 //radio signal
+ signal.source = src
+
+ signal.data = list(
+ "tag" = id,
+ "device" = "APV",
+ "power" = on,
+ "transfer_rate" = transfer_rate,
+ "sigtype" = "status"
+ )
+ radio_connection.post_signal(src, signal)
+
+ return 1
+
+/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal)
+ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
+ return 0
+
+ var/old_on = on //for logging
+
+ if(signal.data["power"])
+ on = text2num(signal.data["power"])
+
+ if(signal.data["power_toggle"])
+ on = !on
+
+ if(signal.data["set_transfer_rate"])
+ transfer_rate = between(
+ 0,
+ text2num(signal.data["set_transfer_rate"]),
+ air1.volume
+ )
+
+ if(on != old_on)
+ investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
+
+ if(signal.data["status"])
+ spawn(2)
+ broadcast_status()
+ return //do not update_icon
+
+ spawn(2)
+ broadcast_status()
+ update_icon()
+
+/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user)
+ if(..())
+ return
+
+ if(!allowed(user))
+ to_chat(user, "Access denied.")
+ return
+
+ add_fingerprint(user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
+ user.set_machine(src)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state)
+ ui.open()
+
+/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["rate"] = round(transfer_rate)
+ data["max_rate"] = round(MAX_TRANSFER_RATE)
+ return data
+
+/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list)
+ if(..())
+ return 1
+
+ if(href_list["power"])
+ on = !on
+ investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
+ . = TRUE
+ if(href_list["rate"])
+ var/rate = href_list["rate"]
+ if(rate == "max")
+ rate = MAX_TRANSFER_RATE
+ . = TRUE
+ else if(rate == "input")
+ rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null
+ if(!isnull(rate))
+ . = TRUE
+ else if(text2num(rate) != null)
+ rate = text2num(rate)
+ . = TRUE
+ if(.)
+ transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE)
+ investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos")
+
+ update_icon()
+ SSnanoui.update_uis(src)
+
+/obj/machinery/atmospherics/binary/volume_pump/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else if(!istype(W, /obj/item/wrench))
+ return ..()
+ if(!(stat & NOPOWER) && on)
+ to_chat(user, "You cannot unwrench this [src], turn it off first.")
+ return 1
+ return ..()
diff --git a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
index a1f80916aee..d03329e24fc 100644
--- a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm
@@ -91,4 +91,4 @@
return WEST
else
return 0
-
\ No newline at end of file
+
diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
index e45a8c3956f..647167800eb 100755
--- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm
@@ -1,267 +1,267 @@
-/obj/machinery/atmospherics/trinary/filter
- icon = 'icons/atmos/filter.dmi'
- icon_state = "map"
-
- can_unwrench = 1
-
- name = "gas filter"
-
- var/target_pressure = ONE_ATMOSPHERE
-
- var/filter_type = 0
-/*
-Filter types:
--1: Nothing
- 0: Toxins: Toxins, Oxygen Agent B
- 1: Oxygen: Oxygen ONLY
- 2: Nitrogen: Nitrogen ONLY
- 3: Carbon Dioxide: Carbon Dioxide ONLY
- 4: Sleeping Agent (N2O)
-*/
-
- var/frequency = 0
- var/datum/radio_frequency/radio_connection
-
-/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/trinary/filter/AICtrlClick()
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- set_max()
- return
-
-/obj/machinery/atmospherics/trinary/filter/AIAltClick()
- set_max()
- return ..()
-
-/obj/machinery/atmospherics/trinary/filter/proc/toggle()
- if(powered())
- on = !on
- update_icon()
-
-/obj/machinery/atmospherics/trinary/filter/proc/set_max()
- if(powered())
- target_pressure = MAX_OUTPUT_PRESSURE
- update_icon()
-
-/obj/machinery/atmospherics/trinary/filter/Destroy()
- if(SSradio)
- SSradio.remove_object(src, frequency)
- radio_connection = null
- return ..()
-
-/obj/machinery/atmospherics/trinary/filter/flipped
- icon_state = "mmap"
- flipped = 1
-
-/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency)
- SSradio.remove_object(src, frequency)
- frequency = new_frequency
- if(frequency)
- radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA)
-
-/obj/machinery/atmospherics/trinary/filter/update_icon()
- ..()
-
- if(flipped)
- icon_state = "m"
- else
- icon_state = ""
-
- if(!powered())
- icon_state += "off"
- else if(node2 && node3 && node1)
- icon_state += on ? "on" : "off"
- else
- icon_state += "off"
- on = 0
-
-/obj/machinery/atmospherics/trinary/filter/update_underlays()
- if(..())
- underlays.Cut()
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
-
- add_underlay(T, node1, turn(dir, -180))
-
- if(flipped)
- add_underlay(T, node2, turn(dir, 90))
- else
- add_underlay(T, node2, turn(dir, -90))
-
- add_underlay(T, node3, dir)
-
-/obj/machinery/atmospherics/trinary/filter/power_change()
- var/old_stat = stat
- ..()
- if(old_stat != stat)
- update_icon()
-
-/obj/machinery/atmospherics/trinary/filter/process_atmos()
- ..()
- if(!on)
- return 0
-
- var/output_starting_pressure = air3.return_pressure()
-
- if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure )
- //No need to mix if target is already full!
- return 1
-
- //Calculate necessary moles to transfer using PV=nRT
-
- var/pressure_delta = target_pressure - output_starting_pressure
- var/transfer_moles
-
- if(air1.temperature > 0)
- transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- //Actually transfer the gas
-
- if(transfer_moles > 0)
- var/datum/gas_mixture/removed = air1.remove(transfer_moles)
-
- if(!removed)
- return
- var/datum/gas_mixture/filtered_out = new
- filtered_out.temperature = removed.temperature
-
- switch(filter_type)
- if(0) //removing hydrocarbons
- filtered_out.toxins = removed.toxins
- removed.toxins = 0
-
- if(removed.trace_gases.len>0)
- for(var/datum/gas/trace_gas in removed.trace_gases)
- if(istype(trace_gas, /datum/gas/oxygen_agent_b))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
-
- if(1) //removing O2
- filtered_out.oxygen = removed.oxygen
- removed.oxygen = 0
-
- if(2) //removing N2
- filtered_out.nitrogen = removed.nitrogen
- removed.nitrogen = 0
-
- if(3) //removing CO2
- filtered_out.carbon_dioxide = removed.carbon_dioxide
- removed.carbon_dioxide = 0
-
- if(4)//removing N2O
- if(removed.trace_gases.len>0)
- for(var/datum/gas/trace_gas in removed.trace_gases)
- if(istype(trace_gas, /datum/gas/sleeping_agent))
- removed.trace_gases -= trace_gas
- filtered_out.trace_gases += trace_gas
-
- else
- filtered_out = null
-
-
- air2.merge(filtered_out)
- air3.merge(removed)
-
- parent2.update = 1
-
- parent3.update = 1
-
- parent1.update = 1
-
- return 1
-
-/obj/machinery/atmospherics/trinary/filter/atmos_init()
- set_frequency(frequency)
- ..()
-
-/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user)
- if(..())
- return
-
- if(!allowed(user))
- to_chat(user, "Access denied.")
- return
-
- add_fingerprint(user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
- user.set_machine(src)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state)
- ui.open()
-
-/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user)
- var/list/data = list()
- data["on"] = on
- data["pressure"] = round(target_pressure)
- data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
- data["filter_type"] = filter_type
- return data
-
-/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE
- if(..())
- return 1
-
- if(href_list["power"])
- on = !on
- investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
- . = TRUE
- if(href_list["pressure"])
- var/pressure = href_list["pressure"]
- if(pressure == "max")
- pressure = MAX_OUTPUT_PRESSURE
- . = TRUE
- else if(pressure == "input")
- pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
- if(!isnull(pressure) && !..())
- . = TRUE
- else if(text2num(pressure) != null)
- pressure = text2num(pressure)
- . = TRUE
- if(.)
- target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
- investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
- if(href_list["filter"])
- filter_type = text2num(href_list["filter"])
- investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos")
- . = TRUE
-
- update_icon()
- SSnanoui.update_uis(src)
-
-/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/pen))
- var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN)
- if(!t)
- return
- if(!in_range(src, usr) && loc != usr)
- return
- name = t
- return
- else
- return ..()
\ No newline at end of file
+/obj/machinery/atmospherics/trinary/filter
+ icon = 'icons/atmos/filter.dmi'
+ icon_state = "map"
+
+ can_unwrench = 1
+
+ name = "gas filter"
+
+ var/target_pressure = ONE_ATMOSPHERE
+
+ var/filter_type = 0
+/*
+Filter types:
+-1: Nothing
+ 0: Toxins: Toxins, Oxygen Agent B
+ 1: Oxygen: Oxygen ONLY
+ 2: Nitrogen: Nitrogen ONLY
+ 3: Carbon Dioxide: Carbon Dioxide ONLY
+ 4: Sleeping Agent (N2O)
+*/
+
+ var/frequency = 0
+ var/datum/radio_frequency/radio_connection
+
+/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/filter/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/trinary/filter/AIAltClick()
+ set_max()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/filter/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/filter/proc/set_max()
+ if(powered())
+ target_pressure = MAX_OUTPUT_PRESSURE
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/filter/Destroy()
+ if(SSradio)
+ SSradio.remove_object(src, frequency)
+ radio_connection = null
+ return ..()
+
+/obj/machinery/atmospherics/trinary/filter/flipped
+ icon_state = "mmap"
+ flipped = 1
+
+/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency)
+ SSradio.remove_object(src, frequency)
+ frequency = new_frequency
+ if(frequency)
+ radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA)
+
+/obj/machinery/atmospherics/trinary/filter/update_icon()
+ ..()
+
+ if(flipped)
+ icon_state = "m"
+ else
+ icon_state = ""
+
+ if(!powered())
+ icon_state += "off"
+ else if(node2 && node3 && node1)
+ icon_state += on ? "on" : "off"
+ else
+ icon_state += "off"
+ on = 0
+
+/obj/machinery/atmospherics/trinary/filter/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+
+ add_underlay(T, node1, turn(dir, -180))
+
+ if(flipped)
+ add_underlay(T, node2, turn(dir, 90))
+ else
+ add_underlay(T, node2, turn(dir, -90))
+
+ add_underlay(T, node3, dir)
+
+/obj/machinery/atmospherics/trinary/filter/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/filter/process_atmos()
+ ..()
+ if(!on)
+ return 0
+
+ var/output_starting_pressure = air3.return_pressure()
+
+ if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure )
+ //No need to mix if target is already full!
+ return 1
+
+ //Calculate necessary moles to transfer using PV=nRT
+
+ var/pressure_delta = target_pressure - output_starting_pressure
+ var/transfer_moles
+
+ if(air1.temperature > 0)
+ transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+
+ //Actually transfer the gas
+
+ if(transfer_moles > 0)
+ var/datum/gas_mixture/removed = air1.remove(transfer_moles)
+
+ if(!removed)
+ return
+ var/datum/gas_mixture/filtered_out = new
+ filtered_out.temperature = removed.temperature
+
+ switch(filter_type)
+ if(0) //removing hydrocarbons
+ filtered_out.toxins = removed.toxins
+ removed.toxins = 0
+
+ if(removed.trace_gases.len>0)
+ for(var/datum/gas/trace_gas in removed.trace_gases)
+ if(istype(trace_gas, /datum/gas/oxygen_agent_b))
+ removed.trace_gases -= trace_gas
+ filtered_out.trace_gases += trace_gas
+
+ if(1) //removing O2
+ filtered_out.oxygen = removed.oxygen
+ removed.oxygen = 0
+
+ if(2) //removing N2
+ filtered_out.nitrogen = removed.nitrogen
+ removed.nitrogen = 0
+
+ if(3) //removing CO2
+ filtered_out.carbon_dioxide = removed.carbon_dioxide
+ removed.carbon_dioxide = 0
+
+ if(4)//removing N2O
+ if(removed.trace_gases.len>0)
+ for(var/datum/gas/trace_gas in removed.trace_gases)
+ if(istype(trace_gas, /datum/gas/sleeping_agent))
+ removed.trace_gases -= trace_gas
+ filtered_out.trace_gases += trace_gas
+
+ else
+ filtered_out = null
+
+
+ air2.merge(filtered_out)
+ air3.merge(removed)
+
+ parent2.update = 1
+
+ parent3.update = 1
+
+ parent1.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/trinary/filter/atmos_init()
+ set_frequency(frequency)
+ ..()
+
+/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user)
+ if(..())
+ return
+
+ if(!allowed(user))
+ to_chat(user, "Access denied.")
+ return
+
+ add_fingerprint(user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
+ user.set_machine(src)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state)
+ ui.open()
+
+/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["pressure"] = round(target_pressure)
+ data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
+ data["filter_type"] = filter_type
+ return data
+
+/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE
+ if(..())
+ return 1
+
+ if(href_list["power"])
+ on = !on
+ investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
+ . = TRUE
+ if(href_list["pressure"])
+ var/pressure = href_list["pressure"]
+ if(pressure == "max")
+ pressure = MAX_OUTPUT_PRESSURE
+ . = TRUE
+ else if(pressure == "input")
+ pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
+ if(!isnull(pressure) && !..())
+ . = TRUE
+ else if(text2num(pressure) != null)
+ pressure = text2num(pressure)
+ . = TRUE
+ if(.)
+ target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
+ investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
+ if(href_list["filter"])
+ filter_type = text2num(href_list["filter"])
+ investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos")
+ . = TRUE
+
+ update_icon()
+ SSnanoui.update_uis(src)
+
+/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else
+ return ..()
diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
index 19849582a0f..18f27fb9a65 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm
@@ -1,233 +1,233 @@
-/obj/machinery/atmospherics/trinary/mixer
- icon = 'icons/atmos/mixer.dmi'
- icon_state = "map"
-
- can_unwrench = 1
-
- name = "gas mixer"
-
- var/target_pressure = ONE_ATMOSPHERE
- var/node1_concentration = 0.5
- var/node2_concentration = 0.5
-
- //node 3 is the outlet, nodes 1 & 2 are intakes
-
-/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/trinary/mixer/AICtrlClick()
- toggle()
- return ..()
-
-/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user)
- if(!istype(user) || user.incapacitated())
- to_chat(user, "You can't do that right now!")
- return
- if(!in_range(src, user) && !issilicon(usr))
- return
- if(!ishuman(usr) && !issilicon(usr))
- return
- set_max()
- return
-
-/obj/machinery/atmospherics/trinary/mixer/AIAltClick()
- set_max()
- return ..()
-
-/obj/machinery/atmospherics/trinary/mixer/flipped
- icon_state = "mmap"
- flipped = 1
-
-/obj/machinery/atmospherics/trinary/mixer/proc/toggle()
- if(powered())
- on = !on
- update_icon()
-
-/obj/machinery/atmospherics/trinary/mixer/proc/set_max()
- if(powered())
- target_pressure = MAX_OUTPUT_PRESSURE
- update_icon()
-
-/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0)
- ..()
-
- if(flipped)
- icon_state = "m"
- else
- icon_state = ""
-
- if(!powered())
- icon_state += "off"
- else if(node2 && node3 && node1)
- icon_state += on ? "on" : "off"
- else
- icon_state += "off"
- on = 0
-
-/obj/machinery/atmospherics/trinary/mixer/update_underlays()
- if(..())
- underlays.Cut()
- var/turf/T = get_turf(src)
- if(!istype(T))
- return
-
- add_underlay(T, node1, turn(dir, -180))
-
- if(flipped)
- add_underlay(T, node2, turn(dir, 90))
- else
- add_underlay(T, node2, turn(dir, -90))
-
- add_underlay(T, node3, dir)
-
-/obj/machinery/atmospherics/trinary/mixer/power_change()
- var/old_stat = stat
- ..()
- if(old_stat != stat)
- update_icon()
-
-/obj/machinery/atmospherics/trinary/mixer/New()
- ..()
- air3.volume = 300
-
-/obj/machinery/atmospherics/trinary/mixer/process_atmos()
- ..()
- if(!on)
- return 0
-
- var/output_starting_pressure = air3.return_pressure()
-
- if(output_starting_pressure >= target_pressure)
- //No need to mix if target is already full!
- return 1
-
- //Calculate necessary moles to transfer using PV=nRT
-
- var/pressure_delta = target_pressure - output_starting_pressure
- var/transfer_moles1 = 0
- var/transfer_moles2 = 0
-
- if(air1.temperature > 0)
- transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
-
- if(air2.temperature > 0)
- transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
-
- var/air1_moles = air1.total_moles()
- var/air2_moles = air2.total_moles()
-
- if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2))
- if(!transfer_moles1 || !transfer_moles2) return
- var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2)
-
- transfer_moles1 *= ratio
- transfer_moles2 *= ratio
-
- //Actually transfer the gas
-
- if(transfer_moles1 > 0)
- var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1)
- air3.merge(removed1)
-
- if(transfer_moles2 > 0)
- var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2)
- air3.merge(removed2)
-
- if(transfer_moles1)
- parent1.update = 1
-
- if(transfer_moles2)
- parent2.update = 1
-
- parent3.update = 1
-
- return 1
-
-/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user)
- if(..())
- return
-
- if(!allowed(user))
- to_chat(user, "Access denied.")
- return
-
- add_fingerprint(user)
- ui_interact(user)
-
-/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
- user.set_machine(src)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state)
- ui.open()
-
-/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user)
- var/list/data = list()
- data["on"] = on
- data["pressure"] = round(target_pressure)
- data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
- data["node1_concentration"] = round(node1_concentration*100)
- data["node2_concentration"] = round(node2_concentration*100)
- return data
-
-/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list)
- if(..())
- return 1
-
- if(href_list["power"])
- on = !on
- investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
- . = TRUE
- if(href_list["pressure"])
- var/pressure = href_list["pressure"]
- if(pressure == "max")
- pressure = MAX_OUTPUT_PRESSURE
- . = TRUE
- else if(pressure == "input")
- pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
- if(!isnull(pressure) && !..())
- . = TRUE
- else if(text2num(pressure) != null)
- pressure = text2num(pressure)
- . = TRUE
- if(.)
- target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
- investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
- if(href_list["node1"])
- var/value = text2num(href_list["node1"])
- node1_concentration = max(0, min(1, node1_concentration + value))
- node2_concentration = max(0, min(1, node2_concentration - value))
- investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos")
- . = TRUE
- if(href_list["node2"])
- var/value = text2num(href_list["node2"])
- node2_concentration = max(0, min(1, node2_concentration + value))
- node1_concentration = max(0, min(1, node1_concentration - value))
- investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos")
- . = TRUE
-
- update_icon()
- SSnanoui.update_uis(src)
-
-/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/pen))
- var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN)
- if(!t)
- return
- if(!in_range(src, usr) && loc != usr)
- return
- name = t
- return
- else
- return ..()
\ No newline at end of file
+/obj/machinery/atmospherics/trinary/mixer
+ icon = 'icons/atmos/mixer.dmi'
+ icon_state = "map"
+
+ can_unwrench = 1
+
+ name = "gas mixer"
+
+ var/target_pressure = ONE_ATMOSPHERE
+ var/node1_concentration = 0.5
+ var/node2_concentration = 0.5
+
+ //node 3 is the outlet, nodes 1 & 2 are intakes
+
+/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/mixer/AICtrlClick()
+ toggle()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user)
+ if(!istype(user) || user.incapacitated())
+ to_chat(user, "You can't do that right now!")
+ return
+ if(!in_range(src, user) && !issilicon(usr))
+ return
+ if(!ishuman(usr) && !issilicon(usr))
+ return
+ set_max()
+ return
+
+/obj/machinery/atmospherics/trinary/mixer/AIAltClick()
+ set_max()
+ return ..()
+
+/obj/machinery/atmospherics/trinary/mixer/flipped
+ icon_state = "mmap"
+ flipped = 1
+
+/obj/machinery/atmospherics/trinary/mixer/proc/toggle()
+ if(powered())
+ on = !on
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/mixer/proc/set_max()
+ if(powered())
+ target_pressure = MAX_OUTPUT_PRESSURE
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0)
+ ..()
+
+ if(flipped)
+ icon_state = "m"
+ else
+ icon_state = ""
+
+ if(!powered())
+ icon_state += "off"
+ else if(node2 && node3 && node1)
+ icon_state += on ? "on" : "off"
+ else
+ icon_state += "off"
+ on = 0
+
+/obj/machinery/atmospherics/trinary/mixer/update_underlays()
+ if(..())
+ underlays.Cut()
+ var/turf/T = get_turf(src)
+ if(!istype(T))
+ return
+
+ add_underlay(T, node1, turn(dir, -180))
+
+ if(flipped)
+ add_underlay(T, node2, turn(dir, 90))
+ else
+ add_underlay(T, node2, turn(dir, -90))
+
+ add_underlay(T, node3, dir)
+
+/obj/machinery/atmospherics/trinary/mixer/power_change()
+ var/old_stat = stat
+ ..()
+ if(old_stat != stat)
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/mixer/New()
+ ..()
+ air3.volume = 300
+
+/obj/machinery/atmospherics/trinary/mixer/process_atmos()
+ ..()
+ if(!on)
+ return 0
+
+ var/output_starting_pressure = air3.return_pressure()
+
+ if(output_starting_pressure >= target_pressure)
+ //No need to mix if target is already full!
+ return 1
+
+ //Calculate necessary moles to transfer using PV=nRT
+
+ var/pressure_delta = target_pressure - output_starting_pressure
+ var/transfer_moles1 = 0
+ var/transfer_moles2 = 0
+
+ if(air1.temperature > 0)
+ transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+
+ if(air2.temperature > 0)
+ transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
+
+ var/air1_moles = air1.total_moles()
+ var/air2_moles = air2.total_moles()
+
+ if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2))
+ if(!transfer_moles1 || !transfer_moles2) return
+ var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2)
+
+ transfer_moles1 *= ratio
+ transfer_moles2 *= ratio
+
+ //Actually transfer the gas
+
+ if(transfer_moles1 > 0)
+ var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1)
+ air3.merge(removed1)
+
+ if(transfer_moles2 > 0)
+ var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2)
+ air3.merge(removed2)
+
+ if(transfer_moles1)
+ parent1.update = 1
+
+ if(transfer_moles2)
+ parent2.update = 1
+
+ parent3.update = 1
+
+ return 1
+
+/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user)
+ if(..())
+ return
+
+ if(!allowed(user))
+ to_chat(user, "Access denied.")
+ return
+
+ add_fingerprint(user)
+ ui_interact(user)
+
+/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state)
+ user.set_machine(src)
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state)
+ ui.open()
+
+/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["pressure"] = round(target_pressure)
+ data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
+ data["node1_concentration"] = round(node1_concentration*100)
+ data["node2_concentration"] = round(node2_concentration*100)
+ return data
+
+/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list)
+ if(..())
+ return 1
+
+ if(href_list["power"])
+ on = !on
+ investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
+ . = TRUE
+ if(href_list["pressure"])
+ var/pressure = href_list["pressure"]
+ if(pressure == "max")
+ pressure = MAX_OUTPUT_PRESSURE
+ . = TRUE
+ else if(pressure == "input")
+ pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
+ if(!isnull(pressure) && !..())
+ . = TRUE
+ else if(text2num(pressure) != null)
+ pressure = text2num(pressure)
+ . = TRUE
+ if(.)
+ target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
+ investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
+ if(href_list["node1"])
+ var/value = text2num(href_list["node1"])
+ node1_concentration = max(0, min(1, node1_concentration + value))
+ node2_concentration = max(0, min(1, node2_concentration - value))
+ investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos")
+ . = TRUE
+ if(href_list["node2"])
+ var/value = text2num(href_list["node2"])
+ node2_concentration = max(0, min(1, node2_concentration + value))
+ node1_concentration = max(0, min(1, node1_concentration - value))
+ investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos")
+ . = TRUE
+
+ update_icon()
+ SSnanoui.update_uis(src)
+
+/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/pen))
+ var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ name = t
+ return
+ else
+ return ..()
diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
index 8087db9966e..79fae0f7d88 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
@@ -1,213 +1,213 @@
-/obj/machinery/atmospherics/trinary
- dir = SOUTH
- initialize_directions = SOUTH|NORTH|WEST
- use_power = IDLE_POWER_USE
-
- var/on = 0
- layer = GAS_FILTER_LAYER
-
- var/datum/gas_mixture/air1
- var/datum/gas_mixture/air2
- var/datum/gas_mixture/air3
-
- var/obj/machinery/atmospherics/node1
- var/obj/machinery/atmospherics/node2
- var/obj/machinery/atmospherics/node3
-
- var/datum/pipeline/parent1
- var/datum/pipeline/parent2
- var/datum/pipeline/parent3
-
- var/flipped = 0
-
-/obj/machinery/atmospherics/trinary/New()
- ..()
-
- if(!flipped)
- switch(dir)
- if(NORTH)
- initialize_directions = EAST|NORTH|SOUTH
- if(SOUTH)
- initialize_directions = SOUTH|WEST|NORTH
- if(EAST)
- initialize_directions = EAST|WEST|SOUTH
- if(WEST)
- initialize_directions = WEST|NORTH|EAST
- else
- switch(dir)
- if(NORTH)
- initialize_directions = SOUTH|NORTH|WEST
- if(SOUTH)
- initialize_directions = NORTH|SOUTH|EAST
- if(EAST)
- initialize_directions = WEST|EAST|NORTH
- if(WEST)
- initialize_directions = EAST|WEST|SOUTH
-
- air1 = new
- air2 = new
- air3 = new
-
- air1.volume = 200
- air2.volume = 200
- air3.volume = 200
-
-/obj/machinery/atmospherics/trinary/Destroy()
- if(node1)
- node1.disconnect(src)
- node1 = null
- nullifyPipenet(parent1)
- if(node2)
- node2.disconnect(src)
- node2 = null
- nullifyPipenet(parent2)
- if(node3)
- node3.disconnect(src)
- node3 = null
- nullifyPipenet(parent3)
- return ..()
-
-/obj/machinery/atmospherics/trinary/atmos_init()
- ..()
- //Mixer:
- //1 and 2 is input
- //Node 3 is output
- //If we flip the mixer, 1 and 3 shall exchange positions
-
- //Filter:
- //Node 1 is input
- //Node 2 is filtered output
- //Node 3 is rest output
- //If we flip the filter, 1 and 3 shall exchange positions
-
- var/node1_connect = turn(dir, -180)
- var/node2_connect = turn(dir, -90)
- var/node3_connect = dir
-
- if(flipped)
- node2_connect = turn(node2_connect, -180)
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
- if(target.initialize_directions & get_dir(target,src))
- node1 = target
- break
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
- if(target.initialize_directions & get_dir(target,src))
- node2 = target
- break
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect))
- if(target.initialize_directions & get_dir(target,src))
- node3 = target
- break
-
- update_icon()
- update_underlays()
-
-/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE)
- if(!parent1)
- parent1 = new /datum/pipeline()
- parent1.build_pipeline(src)
-
- if(!parent2)
- parent2 = new /datum/pipeline()
- parent2.build_pipeline(src)
-
- if(!parent3)
- parent3 = new /datum/pipeline()
- parent3.build_pipeline(src)
- ..()
-
-/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference)
- if(reference == node1)
- if(istype(node1, /obj/machinery/atmospherics/pipe))
- qdel(parent1)
- node1 = null
- else if(reference == node2)
- if(istype(node2, /obj/machinery/atmospherics/pipe))
- qdel(parent2)
- node2 = null
- else if(reference == node3)
- if(istype(node3, /obj/machinery/atmospherics/pipe))
- qdel(parent3)
- node3 = null
- update_icon()
-
-/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P)
- ..()
- if(!P)
- return
- if(P == parent1)
- parent1.other_airs -= air1
- parent1 = null
- else if(P == parent2)
- parent2.other_airs -= air2
- parent2 = null
- else if(P == parent3)
- parent3.other_airs -= air3
- parent3 = null
-
-/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P)
- if(P == parent1)
- return air1
- else if(P == parent2)
- return air2
- else if(P == parent3)
- return air3
-
-/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P)
- if(P)
- if(parent1 == P)
- return list(node1)
- else if(parent2 == P)
- return list(node2)
- else if(parent3 == P)
- return list(node3)
- return list(node1, node2, node3)
-
-/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A)
- if(A == node1)
- parent1 = P
- else if(A == node2)
- parent2 = P
- else if(A == node3)
- parent3 = P
-
-/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A)
- if(A == node1)
- return parent1
- else if(A == node2)
- return parent2
- else if(A == node3)
- return parent3
-
-/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New)
- if(Old == parent1)
- parent1 = New
- else if(Old == parent2)
- parent2 = New
- else if(Old == parent3)
- parent3 = New
-
-/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures)
- ..()
-
- var/turf/T = get_turf(src)
- if(T)
- //Remove the gas from air1+air2+air3 and assume it
- var/datum/gas_mixture/environment = T.return_air()
- var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
- lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
- lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION)
- var/shared_loss = lost/3
-
- var/datum/gas_mixture/to_release = air1.remove(shared_loss)
- to_release.merge(air2.remove(shared_loss))
- to_release.merge(air3.remove(shared_loss))
- T.assume_air(to_release)
- air_update_turf(1)
-
-/obj/machinery/atmospherics/trinary/process_atmos()
- ..()
- return parent1 && parent2 && parent3
+/obj/machinery/atmospherics/trinary
+ dir = SOUTH
+ initialize_directions = SOUTH|NORTH|WEST
+ use_power = IDLE_POWER_USE
+
+ var/on = 0
+ layer = GAS_FILTER_LAYER
+
+ var/datum/gas_mixture/air1
+ var/datum/gas_mixture/air2
+ var/datum/gas_mixture/air3
+
+ var/obj/machinery/atmospherics/node1
+ var/obj/machinery/atmospherics/node2
+ var/obj/machinery/atmospherics/node3
+
+ var/datum/pipeline/parent1
+ var/datum/pipeline/parent2
+ var/datum/pipeline/parent3
+
+ var/flipped = 0
+
+/obj/machinery/atmospherics/trinary/New()
+ ..()
+
+ if(!flipped)
+ switch(dir)
+ if(NORTH)
+ initialize_directions = EAST|NORTH|SOUTH
+ if(SOUTH)
+ initialize_directions = SOUTH|WEST|NORTH
+ if(EAST)
+ initialize_directions = EAST|WEST|SOUTH
+ if(WEST)
+ initialize_directions = WEST|NORTH|EAST
+ else
+ switch(dir)
+ if(NORTH)
+ initialize_directions = SOUTH|NORTH|WEST
+ if(SOUTH)
+ initialize_directions = NORTH|SOUTH|EAST
+ if(EAST)
+ initialize_directions = WEST|EAST|NORTH
+ if(WEST)
+ initialize_directions = EAST|WEST|SOUTH
+
+ air1 = new
+ air2 = new
+ air3 = new
+
+ air1.volume = 200
+ air2.volume = 200
+ air3.volume = 200
+
+/obj/machinery/atmospherics/trinary/Destroy()
+ if(node1)
+ node1.disconnect(src)
+ node1 = null
+ nullifyPipenet(parent1)
+ if(node2)
+ node2.disconnect(src)
+ node2 = null
+ nullifyPipenet(parent2)
+ if(node3)
+ node3.disconnect(src)
+ node3 = null
+ nullifyPipenet(parent3)
+ return ..()
+
+/obj/machinery/atmospherics/trinary/atmos_init()
+ ..()
+ //Mixer:
+ //1 and 2 is input
+ //Node 3 is output
+ //If we flip the mixer, 1 and 3 shall exchange positions
+
+ //Filter:
+ //Node 1 is input
+ //Node 2 is filtered output
+ //Node 3 is rest output
+ //If we flip the filter, 1 and 3 shall exchange positions
+
+ var/node1_connect = turn(dir, -180)
+ var/node2_connect = turn(dir, -90)
+ var/node3_connect = dir
+
+ if(flipped)
+ node2_connect = turn(node2_connect, -180)
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ node1 = target
+ break
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ node2 = target
+ break
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect))
+ if(target.initialize_directions & get_dir(target,src))
+ node3 = target
+ break
+
+ update_icon()
+ update_underlays()
+
+/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE)
+ if(!parent1)
+ parent1 = new /datum/pipeline()
+ parent1.build_pipeline(src)
+
+ if(!parent2)
+ parent2 = new /datum/pipeline()
+ parent2.build_pipeline(src)
+
+ if(!parent3)
+ parent3 = new /datum/pipeline()
+ parent3.build_pipeline(src)
+ ..()
+
+/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference)
+ if(reference == node1)
+ if(istype(node1, /obj/machinery/atmospherics/pipe))
+ qdel(parent1)
+ node1 = null
+ else if(reference == node2)
+ if(istype(node2, /obj/machinery/atmospherics/pipe))
+ qdel(parent2)
+ node2 = null
+ else if(reference == node3)
+ if(istype(node3, /obj/machinery/atmospherics/pipe))
+ qdel(parent3)
+ node3 = null
+ update_icon()
+
+/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P)
+ ..()
+ if(!P)
+ return
+ if(P == parent1)
+ parent1.other_airs -= air1
+ parent1 = null
+ else if(P == parent2)
+ parent2.other_airs -= air2
+ parent2 = null
+ else if(P == parent3)
+ parent3.other_airs -= air3
+ parent3 = null
+
+/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P)
+ if(P == parent1)
+ return air1
+ else if(P == parent2)
+ return air2
+ else if(P == parent3)
+ return air3
+
+/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P)
+ if(P)
+ if(parent1 == P)
+ return list(node1)
+ else if(parent2 == P)
+ return list(node2)
+ else if(parent3 == P)
+ return list(node3)
+ return list(node1, node2, node3)
+
+/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A)
+ if(A == node1)
+ parent1 = P
+ else if(A == node2)
+ parent2 = P
+ else if(A == node3)
+ parent3 = P
+
+/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A)
+ if(A == node1)
+ return parent1
+ else if(A == node2)
+ return parent2
+ else if(A == node3)
+ return parent3
+
+/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New)
+ if(Old == parent1)
+ parent1 = New
+ else if(Old == parent2)
+ parent2 = New
+ else if(Old == parent3)
+ parent3 = New
+
+/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures)
+ ..()
+
+ var/turf/T = get_turf(src)
+ if(T)
+ //Remove the gas from air1+air2+air3 and assume it
+ var/datum/gas_mixture/environment = T.return_air()
+ var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
+ lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
+ lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION)
+ var/shared_loss = lost/3
+
+ var/datum/gas_mixture/to_release = air1.remove(shared_loss)
+ to_release.merge(air2.remove(shared_loss))
+ to_release.merge(air3.remove(shared_loss))
+ T.assume_air(to_release)
+ air_update_turf(1)
+
+/obj/machinery/atmospherics/trinary/process_atmos()
+ ..()
+ return parent1 && parent2 && parent3
diff --git a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm
index 7b3f8299339..31039a412d5 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm
@@ -177,4 +177,4 @@
switch_side()
#undef TVALVE_STATE_STRAIGHT
-#undef TVALVE_STATE_SIDE
\ No newline at end of file
+#undef TVALVE_STATE_SIDE
diff --git a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm
index a9c3b98ad74..9b06019e49e 100644
--- a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm
@@ -45,4 +45,4 @@
parent.update = 1
- return 1
\ No newline at end of file
+ return 1
diff --git a/code/ATMOSPHERICS/components/unary_devices/tank.dm b/code/ATMOSPHERICS/components/unary_devices/tank.dm
index cb82417033b..11a752b9030 100644
--- a/code/ATMOSPHERICS/components/unary_devices/tank.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/tank.dm
@@ -110,4 +110,4 @@
var/datum/gas/oxygen_agent_b/trace_gas = new
trace_gas.moles = (50 * ONE_ATMOSPHERE) * (air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- air_contents.trace_gases += trace_gas
\ No newline at end of file
+ air_contents.trace_gases += trace_gas
diff --git a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm
index 5a6e76a182b..6cd050de022 100644
--- a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm
@@ -77,4 +77,4 @@
parent.update = 1
- return 1
\ No newline at end of file
+ return 1
diff --git a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm
index 4ef7743fc8c..14c93dd0490 100644
--- a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm
@@ -102,4 +102,4 @@
/obj/machinery/atmospherics/unary/process_atmos()
..()
- return parent
\ No newline at end of file
+ return parent
diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm
index 1b920002227..afdca1d19ce 100644
--- a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm
@@ -344,27 +344,6 @@
playsound(loc, 'sound/weapons/bladeslice.ogg', 100, TRUE)
/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/weldingtool))
- var/obj/item/weldingtool/WT = W
- if(WT.remove_fuel(0,user))
- to_chat(user, "Now welding the vent.")
- if(do_after(user, 20 * WT.toolspeed, target = src))
- if(!src || !WT.isOn()) return
- playsound(src.loc, WT.usesound, 50, 1)
- if(!welded)
- user.visible_message("[user] welds the vent shut.", "You weld the vent shut.", "You hear welding.")
- welded = 1
- update_icon()
- else
- user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.")
- welded = 0
- update_icon()
- else
- to_chat(user, "The welding tool needs to be on to start this task.")
- return 1
- else
- to_chat(user, "You need more welding fuel to complete this task.")
- return 1
if(istype(W, /obj/item/screwdriver))
if(!welded)
if(open)
@@ -400,6 +379,23 @@
return ..()
+/obj/machinery/atmospherics/unary/vent_pump/welder_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.tool_use_check(user, 0))
+ return
+ WELDER_ATTEMPT_WELD_MESSAGE
+ if(I.use_tool(src, user, 20, volume = I.tool_volume))
+ if(!welded)
+ welded = TRUE
+ visible_message("[user] welds [src] shut!",\
+ "You weld [src] shut!")
+ else
+ welded = FALSE
+ visible_message("[user] unwelds [src]!",\
+ "You unweld [src]!")
+ update_icon()
+
+
/obj/machinery/atmospherics/unary/vent_pump/attack_hand()
if(!welded)
if(open)
diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm
index 5fcf7817a30..d9aff36688b 100644
--- a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm
@@ -393,27 +393,6 @@
playsound(loc, 'sound/weapons/bladeslice.ogg', 100, TRUE)
/obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/W as obj, var/mob/user as mob, params)
- if(istype(W, /obj/item/weldingtool))
- var/obj/item/weldingtool/WT = W
- if(WT.remove_fuel(0,user))
- to_chat(user, "Now welding the scrubber.")
- if(do_after(user, 20 * WT.toolspeed, target = src))
- if(!src || !WT.isOn()) return
- playsound(get_turf(src), WT.usesound, 50, 1)
- if(!welded)
- user.visible_message("[user] welds the scrubber shut.", "You weld the vent scrubber.", "You hear welding.")
- welded = 1
- update_icon()
- else
- user.visible_message("[user] unwelds the scrubber.", "You unweld the scrubber.", "You hear welding.")
- welded = 0
- update_icon()
- else
- to_chat(user, "The welding tool needs to be on to start this task.")
- return 1
- else
- to_chat(user, "You need more welding fuel to complete this task.")
- return 1
if(istype(W, /obj/item/multitool))
update_multitool_menu(user)
return 1
@@ -423,3 +402,19 @@
return 1
return ..()
+
+/obj/machinery/atmospherics/unary/vent_scrubber/welder_act(mob/user, obj/item/I)
+ . = TRUE
+ if(!I.tool_use_check(user, 0))
+ return
+ WELDER_ATTEMPT_WELD_MESSAGE
+ if(I.use_tool(src, user, 20, volume = I.tool_volume))
+ if(!welded)
+ welded = TRUE
+ visible_message("[user] welds [src] shut!",\
+ "You weld [src] shut!")
+ else
+ welded = FALSE
+ visible_message("[user] unwelds [src]!",\
+ "You unweld [src]!")
+ update_icon()
diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm
index 43082c38ded..4d849686851 100644
--- a/code/ATMOSPHERICS/datum_pipeline.dm
+++ b/code/ATMOSPHERICS/datum_pipeline.dm
@@ -1,276 +1,276 @@
-/datum/pipeline
- var/datum/gas_mixture/air
- var/list/datum/gas_mixture/other_airs = list()
-
- var/list/obj/machinery/atmospherics/pipe/members = list()
- var/list/obj/machinery/atmospherics/other_atmosmch = list()
-
- var/update = 1
-
- var/alert_pressure = 0
-
-/datum/pipeline/New()
- SSair.networks += src
-
-/datum/pipeline/Destroy()
- SSair.networks -= src
- if(air && air.volume)
- temporarily_store_air()
- for(var/obj/machinery/atmospherics/pipe/P in members)
- P.parent = null
- for(var/obj/machinery/atmospherics/A in other_atmosmch)
- A.nullifyPipenet(src)
- return ..()
-
-/datum/pipeline/process()//This use to be called called from the pipe networks
- if(update)
- update = 0
- reconcile_air()
- return
-
-var/pipenetwarnings = 10
-
-/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base)
- var/volume = 0
- if(istype(base, /obj/machinery/atmospherics/pipe))
- var/obj/machinery/atmospherics/pipe/E = base
- volume = E.volume
- alert_pressure = E.alert_pressure
- members += E
- if(E.air_temporary)
- air = E.air_temporary
- E.air_temporary = null
- else
- addMachineryMember(base)
- if(!air)
- air = new
- var/list/possible_expansions = list(base)
- while(possible_expansions.len>0)
- for(var/obj/machinery/atmospherics/borderline in possible_expansions)
-
- var/list/result = borderline.pipeline_expansion(src)
-
- if(result.len>0)
- for(var/obj/machinery/atmospherics/P in result)
- if(istype(P, /obj/machinery/atmospherics/pipe))
- var/obj/machinery/atmospherics/pipe/item = P
- if(!members.Find(item))
-
- if(item.parent)
- log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z]."))
- members += item
- possible_expansions += item
-
- volume += item.volume
- item.parent = src
-
- alert_pressure = min(alert_pressure, item.alert_pressure)
-
- if(item.air_temporary)
- air.merge(item.air_temporary)
- item.air_temporary = null
- else
- P.setPipenet(src, borderline)
- addMachineryMember(P)
-
- possible_expansions -= borderline
-
- air.volume = volume
-
-/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A)
- other_atmosmch |= A
- var/datum/gas_mixture/G = A.returnPipenetAir(src)
- other_airs |= G
-
-/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N)
- if(istype(A, /obj/machinery/atmospherics/pipe))
- var/obj/machinery/atmospherics/pipe/P = A
- P.parent = src
- var/list/adjacent = P.pipeline_expansion()
- for(var/obj/machinery/atmospherics/pipe/I in adjacent)
- if(I.parent == src)
- continue
- var/datum/pipeline/E = I.parent
- merge(E)
- if(!members.Find(P))
- members += P
- air.volume += P.volume
- else
- A.setPipenet(src, N)
- addMachineryMember(A)
-
-/datum/pipeline/proc/merge(datum/pipeline/E)
- air.volume += E.air.volume
- members.Add(E.members)
- for(var/obj/machinery/atmospherics/pipe/S in E.members)
- S.parent = src
- air.merge(E.air)
- for(var/obj/machinery/atmospherics/A in E.other_atmosmch)
- A.replacePipenet(E, src)
- other_atmosmch.Add(E.other_atmosmch)
- other_airs.Add(E.other_airs)
- E.members.Cut()
- E.other_atmosmch.Cut()
- qdel(E)
-
-/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A)
- var/datum/pipeline/P = returnPipenet(A)
- P.addMember(A, src)
-
-/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A)
- parent.addMember(A, src)
-
-/datum/pipeline/proc/temporarily_store_air()
- //Update individual gas_mixtures by volume ratio
-
- for(var/obj/machinery/atmospherics/pipe/member in members)
- member.air_temporary = new
- member.air_temporary.volume = member.volume
-
- member.air_temporary.oxygen = air.oxygen*member.volume/air.volume
- member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume
- member.air_temporary.toxins = air.toxins*member.volume/air.volume
- member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume
-
- member.air_temporary.temperature = air.temperature
-
- if(air.trace_gases.len)
- for(var/datum/gas/trace_gas in air.trace_gases)
- var/datum/gas/corresponding = new trace_gas.type()
- member.air_temporary.trace_gases += corresponding
-
- corresponding.moles = trace_gas.moles*member.volume/air.volume
-
-/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity)
- var/total_heat_capacity = air.heat_capacity()
- var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume)
-
- if(istype(target, /turf/simulated))
- var/turf/simulated/modeled_location = target
-
- if(modeled_location.blocks_air)
-
- if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0))
- var/delta_temperature = air.temperature - modeled_location.temperature
-
- var/heat = thermal_conductivity*delta_temperature* \
- (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity))
-
- air.temperature -= heat/total_heat_capacity
- modeled_location.temperature += heat/modeled_location.heat_capacity
-
- else
- var/delta_temperature = 0
- var/sharer_heat_capacity = 0
-
- delta_temperature = (air.temperature - modeled_location.air.temperature)
- sharer_heat_capacity = modeled_location.air.heat_capacity()
-
- var/self_temperature_delta = 0
- var/sharer_temperature_delta = 0
-
- if((sharer_heat_capacity>0) && (partial_heat_capacity>0))
- var/heat = thermal_conductivity*delta_temperature* \
- (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity))
-
- self_temperature_delta = -heat/total_heat_capacity
- sharer_temperature_delta = heat/sharer_heat_capacity
- else
- return 1
-
- air.temperature += self_temperature_delta
-
- modeled_location.air.temperature += sharer_temperature_delta
-
-
- else
- if((target.heat_capacity>0) && (partial_heat_capacity>0))
- var/delta_temperature = air.temperature - target.temperature
-
- var/heat = thermal_conductivity*delta_temperature* \
- (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity))
-
- air.temperature -= heat/total_heat_capacity
- update = 1
-
-/datum/pipeline/proc/reconcile_air()
- var/list/datum/gas_mixture/GL = list()
- var/list/datum/pipeline/PL = list()
- PL += src
-
- for(var/i=1;i<=PL.len;i++)
- var/datum/pipeline/P = PL[i]
- if(!P)
- return
- GL += P.air
- GL += P.other_airs
- for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch)
- if(V.open)
- PL |= V.parent1
- PL |= V.parent2
- for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch)
- if(!T.state)
- if(src != T.parent2) // otherwise dc'd side connects to both other sides!
- PL |= T.parent1
- PL |= T.parent3
- else
- if(src != T.parent3)
- PL |= T.parent1
- PL |= T.parent2
- for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch)
- if(C.connected_device)
- GL += C.portableConnectorReturnAir()
-
- var/total_volume = 0
- var/total_thermal_energy = 0
- var/total_heat_capacity = 0
- var/total_oxygen = 0
- var/total_nitrogen = 0
- var/total_toxins = 0
- var/total_carbon_dioxide = 0
- var/list/total_trace_gases = list()
-
- for(var/datum/gas_mixture/G in GL)
- total_volume += G.volume
- total_thermal_energy += G.thermal_energy()
- total_heat_capacity += G.heat_capacity()
-
- total_oxygen += G.oxygen
- total_nitrogen += G.nitrogen
- total_toxins += G.toxins
- total_carbon_dioxide += G.carbon_dioxide
-
- if(G.trace_gases.len)
- for(var/datum/gas/trace_gas in G.trace_gases)
- var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases
- if(!corresponding)
- corresponding = new trace_gas.type()
- total_trace_gases += corresponding
-
- corresponding.moles += trace_gas.moles
-
- if(total_volume > 0)
-
- //Calculate temperature
- var/temperature = 0
-
- if(total_heat_capacity > 0)
- temperature = total_thermal_energy/total_heat_capacity
-
- //Update individual gas_mixtures by volume ratio
- for(var/datum/gas_mixture/G in GL)
- G.oxygen = total_oxygen*G.volume/total_volume
- G.nitrogen = total_nitrogen*G.volume/total_volume
- G.toxins = total_toxins*G.volume/total_volume
- G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume
-
- G.temperature = temperature
-
- if(total_trace_gases.len)
- for(var/datum/gas/trace_gas in total_trace_gases)
- var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases
- if(!corresponding)
- corresponding = new trace_gas.type()
- G.trace_gases += corresponding
-
- corresponding.moles = trace_gas.moles*G.volume/total_volume
+/datum/pipeline
+ var/datum/gas_mixture/air
+ var/list/datum/gas_mixture/other_airs = list()
+
+ var/list/obj/machinery/atmospherics/pipe/members = list()
+ var/list/obj/machinery/atmospherics/other_atmosmch = list()
+
+ var/update = 1
+
+ var/alert_pressure = 0
+
+/datum/pipeline/New()
+ SSair.networks += src
+
+/datum/pipeline/Destroy()
+ SSair.networks -= src
+ if(air && air.volume)
+ temporarily_store_air()
+ for(var/obj/machinery/atmospherics/pipe/P in members)
+ P.parent = null
+ for(var/obj/machinery/atmospherics/A in other_atmosmch)
+ A.nullifyPipenet(src)
+ return ..()
+
+/datum/pipeline/process()//This use to be called called from the pipe networks
+ if(update)
+ update = 0
+ reconcile_air()
+ return
+
+var/pipenetwarnings = 10
+
+/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base)
+ var/volume = 0
+ if(istype(base, /obj/machinery/atmospherics/pipe))
+ var/obj/machinery/atmospherics/pipe/E = base
+ volume = E.volume
+ alert_pressure = E.alert_pressure
+ members += E
+ if(E.air_temporary)
+ air = E.air_temporary
+ E.air_temporary = null
+ else
+ addMachineryMember(base)
+ if(!air)
+ air = new
+ var/list/possible_expansions = list(base)
+ while(possible_expansions.len>0)
+ for(var/obj/machinery/atmospherics/borderline in possible_expansions)
+
+ var/list/result = borderline.pipeline_expansion(src)
+
+ if(result.len>0)
+ for(var/obj/machinery/atmospherics/P in result)
+ if(istype(P, /obj/machinery/atmospherics/pipe))
+ var/obj/machinery/atmospherics/pipe/item = P
+ if(!members.Find(item))
+
+ if(item.parent)
+ log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z]."))
+ members += item
+ possible_expansions += item
+
+ volume += item.volume
+ item.parent = src
+
+ alert_pressure = min(alert_pressure, item.alert_pressure)
+
+ if(item.air_temporary)
+ air.merge(item.air_temporary)
+ item.air_temporary = null
+ else
+ P.setPipenet(src, borderline)
+ addMachineryMember(P)
+
+ possible_expansions -= borderline
+
+ air.volume = volume
+
+/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A)
+ other_atmosmch |= A
+ var/datum/gas_mixture/G = A.returnPipenetAir(src)
+ other_airs |= G
+
+/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N)
+ if(istype(A, /obj/machinery/atmospherics/pipe))
+ var/obj/machinery/atmospherics/pipe/P = A
+ P.parent = src
+ var/list/adjacent = P.pipeline_expansion()
+ for(var/obj/machinery/atmospherics/pipe/I in adjacent)
+ if(I.parent == src)
+ continue
+ var/datum/pipeline/E = I.parent
+ merge(E)
+ if(!members.Find(P))
+ members += P
+ air.volume += P.volume
+ else
+ A.setPipenet(src, N)
+ addMachineryMember(A)
+
+/datum/pipeline/proc/merge(datum/pipeline/E)
+ air.volume += E.air.volume
+ members.Add(E.members)
+ for(var/obj/machinery/atmospherics/pipe/S in E.members)
+ S.parent = src
+ air.merge(E.air)
+ for(var/obj/machinery/atmospherics/A in E.other_atmosmch)
+ A.replacePipenet(E, src)
+ other_atmosmch.Add(E.other_atmosmch)
+ other_airs.Add(E.other_airs)
+ E.members.Cut()
+ E.other_atmosmch.Cut()
+ qdel(E)
+
+/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A)
+ var/datum/pipeline/P = returnPipenet(A)
+ P.addMember(A, src)
+
+/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A)
+ parent.addMember(A, src)
+
+/datum/pipeline/proc/temporarily_store_air()
+ //Update individual gas_mixtures by volume ratio
+
+ for(var/obj/machinery/atmospherics/pipe/member in members)
+ member.air_temporary = new
+ member.air_temporary.volume = member.volume
+
+ member.air_temporary.oxygen = air.oxygen*member.volume/air.volume
+ member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume
+ member.air_temporary.toxins = air.toxins*member.volume/air.volume
+ member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume
+
+ member.air_temporary.temperature = air.temperature
+
+ if(air.trace_gases.len)
+ for(var/datum/gas/trace_gas in air.trace_gases)
+ var/datum/gas/corresponding = new trace_gas.type()
+ member.air_temporary.trace_gases += corresponding
+
+ corresponding.moles = trace_gas.moles*member.volume/air.volume
+
+/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity)
+ var/total_heat_capacity = air.heat_capacity()
+ var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume)
+
+ if(istype(target, /turf/simulated))
+ var/turf/simulated/modeled_location = target
+
+ if(modeled_location.blocks_air)
+
+ if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0))
+ var/delta_temperature = air.temperature - modeled_location.temperature
+
+ var/heat = thermal_conductivity*delta_temperature* \
+ (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity))
+
+ air.temperature -= heat/total_heat_capacity
+ modeled_location.temperature += heat/modeled_location.heat_capacity
+
+ else
+ var/delta_temperature = 0
+ var/sharer_heat_capacity = 0
+
+ delta_temperature = (air.temperature - modeled_location.air.temperature)
+ sharer_heat_capacity = modeled_location.air.heat_capacity()
+
+ var/self_temperature_delta = 0
+ var/sharer_temperature_delta = 0
+
+ if((sharer_heat_capacity>0) && (partial_heat_capacity>0))
+ var/heat = thermal_conductivity*delta_temperature* \
+ (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity))
+
+ self_temperature_delta = -heat/total_heat_capacity
+ sharer_temperature_delta = heat/sharer_heat_capacity
+ else
+ return 1
+
+ air.temperature += self_temperature_delta
+
+ modeled_location.air.temperature += sharer_temperature_delta
+
+
+ else
+ if((target.heat_capacity>0) && (partial_heat_capacity>0))
+ var/delta_temperature = air.temperature - target.temperature
+
+ var/heat = thermal_conductivity*delta_temperature* \
+ (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity))
+
+ air.temperature -= heat/total_heat_capacity
+ update = 1
+
+/datum/pipeline/proc/reconcile_air()
+ var/list/datum/gas_mixture/GL = list()
+ var/list/datum/pipeline/PL = list()
+ PL += src
+
+ for(var/i=1;i<=PL.len;i++)
+ var/datum/pipeline/P = PL[i]
+ if(!P)
+ return
+ GL += P.air
+ GL += P.other_airs
+ for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch)
+ if(V.open)
+ PL |= V.parent1
+ PL |= V.parent2
+ for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch)
+ if(!T.state)
+ if(src != T.parent2) // otherwise dc'd side connects to both other sides!
+ PL |= T.parent1
+ PL |= T.parent3
+ else
+ if(src != T.parent3)
+ PL |= T.parent1
+ PL |= T.parent2
+ for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch)
+ if(C.connected_device)
+ GL += C.portableConnectorReturnAir()
+
+ var/total_volume = 0
+ var/total_thermal_energy = 0
+ var/total_heat_capacity = 0
+ var/total_oxygen = 0
+ var/total_nitrogen = 0
+ var/total_toxins = 0
+ var/total_carbon_dioxide = 0
+ var/list/total_trace_gases = list()
+
+ for(var/datum/gas_mixture/G in GL)
+ total_volume += G.volume
+ total_thermal_energy += G.thermal_energy()
+ total_heat_capacity += G.heat_capacity()
+
+ total_oxygen += G.oxygen
+ total_nitrogen += G.nitrogen
+ total_toxins += G.toxins
+ total_carbon_dioxide += G.carbon_dioxide
+
+ if(G.trace_gases.len)
+ for(var/datum/gas/trace_gas in G.trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases
+ if(!corresponding)
+ corresponding = new trace_gas.type()
+ total_trace_gases += corresponding
+
+ corresponding.moles += trace_gas.moles
+
+ if(total_volume > 0)
+
+ //Calculate temperature
+ var/temperature = 0
+
+ if(total_heat_capacity > 0)
+ temperature = total_thermal_energy/total_heat_capacity
+
+ //Update individual gas_mixtures by volume ratio
+ for(var/datum/gas_mixture/G in GL)
+ G.oxygen = total_oxygen*G.volume/total_volume
+ G.nitrogen = total_nitrogen*G.volume/total_volume
+ G.toxins = total_toxins*G.volume/total_volume
+ G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume
+
+ G.temperature = temperature
+
+ if(total_trace_gases.len)
+ for(var/datum/gas/trace_gas in total_trace_gases)
+ var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases
+ if(!corresponding)
+ corresponding = new trace_gas.type()
+ G.trace_gases += corresponding
+
+ corresponding.moles = trace_gas.moles*G.volume/total_volume
diff --git a/code/ATMOSPHERICS/pipes/cap.dm b/code/ATMOSPHERICS/pipes/cap.dm
index 8421685d44c..15b53d83e9c 100644
--- a/code/ATMOSPHERICS/pipes/cap.dm
+++ b/code/ATMOSPHERICS/pipes/cap.dm
@@ -123,4 +123,4 @@
connect_types = list(2)
layer = 2.39
icon_connect_type = "-supply"
- color = PIPE_COLOR_BLUE
\ No newline at end of file
+ color = PIPE_COLOR_BLUE
diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm
index db982639c2f..e0bd78bc2de 100644
--- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm
+++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm
@@ -68,4 +68,4 @@
color = PIPE_COLOR_GREEN
/obj/machinery/atmospherics/pipe/simple/hidden/purple
- color = PIPE_COLOR_PURPLE
\ No newline at end of file
+ color = PIPE_COLOR_PURPLE
diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm
index ce6ff78b2d0..e7e5ec55e90 100644
--- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm
+++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm
@@ -8,4 +8,4 @@
fatigue_pressure = 900*ONE_ATMOSPHERE
alert_pressure = 900*ONE_ATMOSPHERE
- level = 2
\ No newline at end of file
+ level = 2
diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm
index 1ca4da646ac..410c992c74c 100644
--- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm
+++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm
@@ -65,4 +65,4 @@
/obj/machinery/atmospherics/pipe/simple/visible/universal/update_underlays()
..()
- update_icon()
\ No newline at end of file
+ update_icon()
diff --git a/code/LINDA/LINDA_fire.dm b/code/LINDA/LINDA_fire.dm
index 9467f7a3742..2fbfb1dff76 100644
--- a/code/LINDA/LINDA_fire.dm
+++ b/code/LINDA/LINDA_fire.dm
@@ -338,4 +338,4 @@
if(prob(chance) || bypass_rng)
T.visible_message("[T] melts!")
T.burn_down()
- return affected
\ No newline at end of file
+ return affected
diff --git a/code/LINDA/LINDA_system.dm b/code/LINDA/LINDA_system.dm
index c9d168a0159..fd24dcc0c77 100644
--- a/code/LINDA/LINDA_system.dm
+++ b/code/LINDA/LINDA_system.dm
@@ -142,44 +142,33 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5)
return
T.atmos_spawn_air(text, amount)
-var/const/SPAWN_HEAT = 1
-var/const/SPAWN_20C = 2
-var/const/SPAWN_TOXINS = 4
-var/const/SPAWN_OXYGEN = 8
-var/const/SPAWN_CO2 = 16
-var/const/SPAWN_NITROGEN = 32
-
-var/const/SPAWN_N2O = 64
-
-var/const/SPAWN_AIR = 256
-
/turf/simulated/proc/atmos_spawn_air(var/flag, var/amount)
if(!text || !amount || !air)
return
var/datum/gas_mixture/G = new
- if(flag & SPAWN_20C)
+ if(flag & LINDA_SPAWN_20C)
G.temperature = T20C
- if(flag & SPAWN_HEAT)
+ if(flag & LINDA_SPAWN_HEAT)
G.temperature += 1000
- if(flag & SPAWN_TOXINS)
+ if(flag & LINDA_SPAWN_TOXINS)
G.toxins += amount
- if(flag & SPAWN_OXYGEN)
+ if(flag & LINDA_SPAWN_OXYGEN)
G.oxygen += amount
- if(flag & SPAWN_CO2)
+ if(flag & LINDA_SPAWN_CO2)
G.carbon_dioxide += amount
- if(flag & SPAWN_NITROGEN)
+ if(flag & LINDA_SPAWN_NITROGEN)
G.nitrogen += amount
- if(flag & SPAWN_N2O)
+ if(flag & LINDA_SPAWN_N2O)
var/datum/gas/sleeping_agent/T = new
T.moles += amount
G.trace_gases += T
- if(flag & SPAWN_AIR)
+ if(flag & LINDA_SPAWN_AIR)
G.oxygen += MOLES_O2STANDARD * amount
G.nitrogen += MOLES_N2STANDARD * amount
diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm
index a0a0b6b8784..b86ad2299ff 100644
--- a/code/LINDA/LINDA_turf_tile.dm
+++ b/code/LINDA/LINDA_turf_tile.dm
@@ -486,7 +486,7 @@ turf/simulated/proc/consider_superconductivity(starting)
turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space
if(temperature > T0C) //Considering 0 degC as te break even point for radiation in and out
- var/delta_temperature = (temperature_archived - 2.7) //hardcoded space temperature
+ var/delta_temperature = (temperature_archived - TCMB) //hardcoded space temperature
if((heat_capacity > 0) && (abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER))
var/heat = thermal_conductivity*delta_temperature* \
diff --git a/code/__DEFINES/_readme.dm b/code/__DEFINES/_readme.dm
index 42ad52286fe..8bf6ada6477 100644
--- a/code/__DEFINES/_readme.dm
+++ b/code/__DEFINES/_readme.dm
@@ -11,4 +11,4 @@
and most importantly,
how to undo your changes if you screw it up.
- Sayu
-*/
\ No newline at end of file
+*/
diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm
index 666971e7209..10b815e547c 100644
--- a/code/__DEFINES/_tick.dm
+++ b/code/__DEFINES/_tick.dm
@@ -10,4 +10,4 @@
#define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 )
#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
-#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
\ No newline at end of file
+#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm
new file mode 100644
index 00000000000..ea319971a1b
--- /dev/null
+++ b/code/__DEFINES/access.dm
@@ -0,0 +1,111 @@
+#define ACCESS_SECURITY 1 // Security equipment
+#define ACCESS_BRIG 2 // Brig timers and permabrig
+#define ACCESS_ARMORY 3
+#define ACCESS_FORENSICS_LOCKERS 4
+#define ACCESS_MEDICAL 5
+#define ACCESS_MORGUE 6
+#define ACCESS_TOX 7
+#define ACCESS_TOX_STORAGE 8
+#define ACCESS_GENETICS 9
+#define ACCESS_ENGINE 10
+#define ACCESS_ENGINE_EQUIP 11
+#define ACCESS_MAINT_TUNNELS 12
+#define ACCESS_EXTERNAL_AIRLOCKS 13
+#define ACCESS_EMERGENCY_STORAGE 14
+#define ACCESS_CHANGE_IDS 15
+#define ACCESS_AI_UPLOAD 16
+#define ACCESS_TELEPORTER 17
+#define ACCESS_EVA 18
+#define ACCESS_HEADS 19
+#define ACCESS_CAPTAIN 20
+#define ACCESS_ALL_PERSONAL_LOCKERS 21
+#define ACCESS_CHAPEL_OFFICE 22
+#define ACCESS_TECH_STORAGE 23
+#define ACCESS_ATMOSPHERICS 24
+#define ACCESS_BAR 25
+#define ACCESS_JANITOR 26
+#define ACCESS_CREMATORIUM 27
+#define ACCESS_KITCHEN 28
+#define ACCESS_ROBOTICS 29
+#define ACCESS_RD 30
+#define ACCESS_CARGO 31
+#define ACCESS_CONSTRUCTION 32
+#define ACCESS_CHEMISTRY 33
+#define ACCESS_CARGO_BOT 34
+#define ACCESS_HYDROPONICS 35
+#define ACCESS_MANUFACTURING 36
+#define ACCESS_LIBRARY 37
+#define ACCESS_LAWYER 38
+#define ACCESS_VIROLOGY 39
+#define ACCESS_CMO 40
+#define ACCESS_QM 41
+#define ACCESS_COURT 42
+#define ACCESS_CLOWN 43
+#define ACCESS_MIME 44
+#define ACCESS_SURGERY 45
+#define ACCESS_THEATRE 46
+#define ACCESS_RESEARCH 47
+#define ACCESS_MINING 48
+#define ACCESS_MINING_OFFICE 49 //not in use
+#define ACCESS_MAILSORTING 50
+#define ACCESS_MINT 51
+#define ACCESS_MINT_VAULT 52
+#define ACCESS_HEADS_VAULT 53
+#define ACCESS_MINING_STATION 54
+#define ACCESS_XENOBIOLOGY 55
+#define ACCESS_CE 56
+#define ACCESS_HOP 57
+#define ACCESS_HOS 58
+#define ACCESS_RC_ANNOUNCE 59 //Request console announcements
+#define ACCESS_KEYCARD_AUTH 60 //Used for events which require at least two people to confirm them
+#define ACCESS_TCOMSAT 61 // has access to the entire telecomms satellite / machinery
+#define ACCESS_GATEWAY 62
+#define ACCESS_SEC_DOORS 63 // Security front doors
+#define ACCESS_PSYCHIATRIST 64 // Psychiatrist's office
+#define ACCESS_XENOARCH 65
+#define ACCESS_PARAMEDIC 66
+#define ACCESS_BLUESHIELD 67
+#define ACCESS_SALVAGE_CAPTAIN 69 // Salvage ship captain's quarters
+#define ACCESS_MECHANIC 70
+#define ACCESS_PILOT 71
+#define ACCESS_NTREP 73
+#define ACCESS_MAGISTRATE 74
+#define ACCESS_MINISAT 75
+#define ACCESS_MINERAL_STOREROOM 76
+#define ACCESS_NETWORK 77
+
+#define ACCESS_WEAPONS 99 //Weapon authorization for secbots
+
+ //BEGIN CENTCOM ACCESS
+#define ACCESS_CENT_GENERAL 101//General facilities.
+#define ACCESS_CENT_LIVING 102//Living quarters.
+#define ACCESS_CENT_MEDICAL 103//Medical.
+#define ACCESS_CENT_SECURITY 104//Security.
+#define ACCESS_CENT_STORAGE 105//Storage areas.
+#define ACCESS_CENT_SHUTTLES 106//Shuttle docks.
+#define ACCESS_CENT_TELECOMMS 107//Telecomms.
+#define ACCESS_CENT_TELEPORTER 108//Teleporter
+#define ACCESS_CENT_SPECOPS 109//Special Ops.
+#define ACCESS_CENT_SPECOPS_COMMANDER 110//Special Ops Commander.
+#define ACCESS_CENT_BLACKOPS 111//Black Ops.
+#define ACCESS_CENT_THUNDER 112//Thunderdome.
+#define ACCESS_CENT_BRIDGE 113//Bridge.
+#define ACCESS_CENT_COMMANDER 114//Commander's Office/ID computer.
+
+//The Syndicate
+#define ACCESS_SYNDICATE 150//General Syndicate Access
+#define ACCESS_SYNDICATE_LEADER 151//Nuke Op Leader Access
+#define ACCESS_VOX 152//Vox Access
+#define ACCESS_SYNDICATE_COMMAND 153//Admin syndi officer
+
+//Trade Stations
+#define ACCESS_TRADE_SOL 160
+
+//MONEY
+#define ACCESS_CRATE_CASH 200
+
+//Awaymissions
+#define ACCESS_AWAY01 271
+
+//Ghost roles
+#define ACCESS_FREE_GOLEMS 300
diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index 65f4da2b50a..bbf394c6c0b 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -70,4 +70,4 @@
///Max amount of keypress messages per second over two seconds before client is autokicked
#define MAX_KEYPRESS_AUTOKICK 50
///Length of held key rolling buffer
-#define HELD_KEY_BUFFER_LENGTH 15
\ No newline at end of file
+#define HELD_KEY_BUFFER_LENGTH 15
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index e69de29bb2d..8b137891791 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -0,0 +1 @@
+
diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index 2f296960622..de20ccd1a4c 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -131,4 +131,4 @@
#define ATMOS_ALARM_DANGER 2
//LAVALAND
-#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland
\ No newline at end of file
+#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland
diff --git a/code/__DEFINES/bots.dm b/code/__DEFINES/bots.dm
index 75bf8a0a553..c283445d0ad 100644
--- a/code/__DEFINES/bots.dm
+++ b/code/__DEFINES/bots.dm
@@ -38,4 +38,4 @@
#define SENTIENCE_ARTIFICIAL 2
#define SENTIENCE_OTHER 3
#define SENTIENCE_MINEBOT 4
-#define SENTIENCE_BOSS 5
\ No newline at end of file
+#define SENTIENCE_BOSS 5
diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm
index 26c81f8b143..18059a60839 100644
--- a/code/__DEFINES/callbacks.dm
+++ b/code/__DEFINES/callbacks.dm
@@ -1,4 +1,4 @@
#define GLOBAL_PROC "some_magic_bullshit"
#define CALLBACK new /datum/callback
-#define INVOKE_ASYNC ImmediateInvokeAsync
\ No newline at end of file
+#define INVOKE_ASYNC ImmediateInvokeAsync
diff --git a/code/__DEFINES/clothing.dm b/code/__DEFINES/clothing.dm
index c44c65397dd..c39ce0947de 100644
--- a/code/__DEFINES/clothing.dm
+++ b/code/__DEFINES/clothing.dm
@@ -97,4 +97,4 @@
//flags for muzzle speech blocking
#define MUZZLE_MUTE_NONE 0 // Does not mute you.
#define MUZZLE_MUTE_MUFFLE 1 // Muffles everything you say "MHHPHHMMM!!!
-#define MUZZLE_MUTE_ALL 2 // Completely mutes you.
\ No newline at end of file
+#define MUZZLE_MUTE_ALL 2 // Completely mutes you.
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index df957eaa11e..c9691c195b8 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -107,6 +107,7 @@
#define COMSIG_MOVABLE_MOVED "movable_moved" //from base of atom/movable/Moved(): (/atom, dir)
#define COMSIG_MOVABLE_CROSS "movable_cross" //from base of atom/movable/Cross(): (/atom/movable)
#define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (/atom/movable)
+#define COMSIG_CROSSED_MOVABLE "crossed_movable" //when we cross over something (calling Crossed() on that atom)
#define COMSIG_MOVABLE_UNCROSSED "movable_uncrossed" //from base of atom/movable/Uncrossed(): (/atom/movable)
#define COMSIG_MOVABLE_UNCROSS "movable_uncross" //from base of atom/movable/Uncross(): (/atom/movable)
#define COMPONENT_MOVABLE_BLOCK_UNCROSS 1
@@ -287,4 +288,4 @@
#define COMSIG_XENO_SLIME_CLICK_SHIFT "xeno_slime_click_shift" //from slime ShiftClickOn(): (/mob)
#define COMSIG_XENO_TURF_CLICK_SHIFT "xeno_turf_click_shift" //from turf ShiftClickOn(): (/mob)
#define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" //from turf AltClickOn(): (/mob)
-#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob)
\ No newline at end of file
+#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob)
diff --git a/code/__DEFINES/crafting.dm b/code/__DEFINES/crafting.dm
index 0fbd76b9e21..33c80e970cf 100644
--- a/code/__DEFINES/crafting.dm
+++ b/code/__DEFINES/crafting.dm
@@ -33,4 +33,4 @@
#define RECIPE_GRILL "Grill"
#define RECIPE_CANDY "Candy"
-#define RECIPE_FAIL null
\ No newline at end of file
+#define RECIPE_FAIL null
diff --git a/code/__DEFINES/game.dm b/code/__DEFINES/game.dm
index 5c262724a14..4ad2ca587d9 100644
--- a/code/__DEFINES/game.dm
+++ b/code/__DEFINES/game.dm
@@ -2,4 +2,4 @@
#define GAME_STATE_PREGAME 1
#define GAME_STATE_SETTING_UP 2
#define GAME_STATE_PLAYING 3
-#define GAME_STATE_FINISHED 4
\ No newline at end of file
+#define GAME_STATE_FINISHED 4
diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index 308e7a5f6d9..73a812e8f5b 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -53,4 +53,4 @@
#define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone"
#define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel"
#define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva"
-#define SPECIAL_ROLE_EVENTMISC "Event Role"
\ No newline at end of file
+#define SPECIAL_ROLE_EVENTMISC "Event Role"
diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm
index c8f98c1fbeb..c465ab2d514 100644
--- a/code/__DEFINES/genetics.dm
+++ b/code/__DEFINES/genetics.dm
@@ -166,4 +166,4 @@
#define NO_GERMS "no_germs"
#define NO_DECAY "no_decay"
#define PIERCEIMMUNE "pierce_immunity"
-#define NO_HUNGER "no_hunger"
\ No newline at end of file
+#define NO_HUNGER "no_hunger"
diff --git a/code/__DEFINES/hydroponics.dm b/code/__DEFINES/hydroponics.dm
index f6693b0bcfc..8a3890d45f9 100644
--- a/code/__DEFINES/hydroponics.dm
+++ b/code/__DEFINES/hydroponics.dm
@@ -57,4 +57,4 @@
#define TRAIT_BIOLUM_COLOUR 37
#define TRAIT_IMMUTABLE 38
#define TRAIT_RARITY 39
-#define TRAIT_BATTERY_RECHARGE 40
\ No newline at end of file
+#define TRAIT_BATTERY_RECHARGE 40
diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm
new file mode 100644
index 00000000000..d72628e2cb7
--- /dev/null
+++ b/code/__DEFINES/job.dm
@@ -0,0 +1,64 @@
+///////////////////////////////
+// WARNING //
+////////////////////////////////////////////////////////////////////////
+// Do NOT touch the values associated with these defines, as they are //
+// used by the game database to keep track of job flags. Do NOT touch //
+////////////////////////////////////////////////////////////////////////
+
+#define JOBCAT_ENGSEC (1<<0)
+
+#define JOB_CAPTAIN (1<<0)
+#define JOB_HOS (1<<1)
+#define JOB_WARDEN (1<<2)
+#define JOB_DETECTIVE (1<<3)
+#define JOB_OFFICER (1<<4)
+#define JOB_CHIEF (1<<5)
+#define JOB_ENGINEER (1<<6)
+#define JOB_ATMOSTECH (1<<7)
+#define JOB_AI (1<<8)
+#define JOB_CYBORG (1<<9)
+#define JOB_CENTCOM (1<<10)
+#define JOB_SYNDICATE (1<<11)
+
+#define JOBCAT_MEDSCI (1<<1)
+
+#define JOB_RD (1<<0)
+#define JOB_SCIENTIST (1<<1)
+#define JOB_CHEMIST (1<<2)
+#define JOB_CMO (1<<3)
+#define JOB_DOCTOR (1<<4)
+#define JOB_GENETICIST (1<<5)
+#define JOB_VIROLOGIST (1<<6)
+#define JOB_PSYCHIATRIST (1<<7)
+#define JOB_ROBOTICIST (1<<8)
+#define JOB_PARAMEDIC (1<<9)
+#define JOB_CORONER (1<<10)
+
+
+#define JOBCAT_SUPPORT (1<<2)
+
+#define JOB_HOP (1<<0)
+#define JOB_BARTENDER (1<<1)
+#define JOB_BOTANIST (1<<2)
+#define JOB_CHEF (1<<3)
+#define JOB_JANITOR (1<<4)
+#define JOB_LIBRARIAN (1<<5)
+#define JOB_QUARTERMASTER (1<<6)
+#define JOB_CARGOTECH (1<<7)
+#define JOB_MINER (1<<8)
+#define JOB_LAWYER (1<<9)
+#define JOB_CHAPLAIN (1<<10)
+#define JOB_CLOWN (1<<11)
+#define JOB_MIME (1<<12)
+#define JOB_CIVILIAN (1<<13)
+
+
+#define JOBCAT_KARMA (1<<3)
+
+#define JOB_NANO (1<<0)
+#define JOB_BLUESHIELD (1<<1)
+#define JOB_BARBER (1<<3)
+#define JOB_MECHANIC (1<<4)
+#define JOB_BRIGDOC (1<<5)
+#define JOB_JUDGE (1<<6)
+#define JOB_PILOT (1<<7)
diff --git a/code/__DEFINES/js.dm b/code/__DEFINES/js.dm
new file mode 100644
index 00000000000..e1a86e664d4
--- /dev/null
+++ b/code/__DEFINES/js.dm
@@ -0,0 +1,91 @@
+//this function places received data into element with specified id.
+#define JS_BYJAX {"
+
+function replaceContent() {
+ var args = Array.prototype.slice.call(arguments);
+ var id = args\[0\];
+ var content = args\[1\];
+ var callback = null;
+ if(args\[2\]){
+ callback = args\[2\];
+ if(args\[3\]){
+ args = args.slice(3);
+ }
+ }
+ var parent = document.getElementById(id);
+ if(typeof(parent)!=='undefined' && parent!=null){
+ parent.innerHTML = content?content:'';
+ }
+ if(callback && window\[callback\]){
+ window\[callback\].apply(null,args);
+ }
+}
+"}
+
+/*
+sends data to control_id:replaceContent
+
+receiver - mob
+control_id - window id (for windows opened with browse(), it'll be "windowname.browser")
+target_element - HTML element id
+new_content - HTML content
+callback - js function that will be called after the data is sent
+callback_args - arguments for callback function
+
+Be sure to include required js functions in your page, or it'll raise an exception.
+
+And yes I know this is a proc in a defines file, but its highly relevant so it can be here
+*/
+proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null)
+ if(receiver && target_element && control_id) // && winexists(receiver, control_id))
+ var/list/argums = list(target_element, new_content)
+ if(callback)
+ argums += callback
+ if(callback_args)
+ argums += callback_args
+ argums = list2params(argums)
+/* if(callback_args)
+ argums += "&[list2params(callback_args)]"
+*/
+ receiver << output(argums,"[control_id]:replaceContent")
+ return
+
+
+// Misc JS for some dropdowns
+#define JS_DROPDOWNS {"
+function dropdowns() {
+ var divs = document.getElementsByTagName('div');
+ var headers = new Array();
+ var links = new Array();
+ for(var i=0;i=0) {
+ elem.className = elem.className.replace('visible','hidden');
+ this.className = this.className.replace('open','closed');
+ this.innerHTML = this.innerHTML.replace('-','+');
+ }
+ else {
+ elem.className = elem.className.replace('hidden','visible');
+ this.className = this.className.replace('closed','open');
+ this.innerHTML = this.innerHTML.replace('+','-');
+ }
+ return false;
+ }
+ })(links\[i\]);
+ }
+ }
+}
+"}
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index db88f46a676..ed4cb0d5940 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -110,4 +110,4 @@
#define ABOVE_HUD_LAYER 22
#define SPLASHSCREEN_LAYER 23
-#define SPLASHSCREEN_PLANE 23
\ No newline at end of file
+#define SPLASHSCREEN_PLANE 23
diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm
index 7357c5f441e..01dbc2f1c2b 100644
--- a/code/__DEFINES/lighting.dm
+++ b/code/__DEFINES/lighting.dm
@@ -89,4 +89,4 @@
#define FLASH_LIGHT_DURATION 2
#define FLASH_LIGHT_POWER 3
-#define FLASH_LIGHT_RANGE 3.8
\ No newline at end of file
+#define FLASH_LIGHT_RANGE 3.8
diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm
index 7a691c44df2..b56024bfab8 100644
--- a/code/__DEFINES/machines.dm
+++ b/code/__DEFINES/machines.dm
@@ -88,3 +88,7 @@
#define SUPERMATTER_DANGER 4 // Integrity < 75%
#define SUPERMATTER_EMERGENCY 5 // Integrity < 50%
#define SUPERMATTER_DELAMINATING 6 // Pretty obvious, Integrity < 25%
+
+// Firelock states
+#define FD_OPEN 1
+#define FD_CLOSED 2
diff --git a/code/__DEFINES/mecha.dm b/code/__DEFINES/mecha.dm
index e8955e64b50..b414f146705 100644
--- a/code/__DEFINES/mecha.dm
+++ b/code/__DEFINES/mecha.dm
@@ -19,4 +19,4 @@
#define MECHA_SECURE_BOLTS 1
#define MECHA_LOOSE_BOLTS 2
#define MECHA_OPEN_HATCH 3
-#define MECHA_UNSECURE_CELL 4
\ No newline at end of file
+#define MECHA_UNSECURE_CELL 4
diff --git a/code/__DEFINES/medal.dm b/code/__DEFINES/medal.dm
index 81dea5f373b..b47d562efee 100644
--- a/code/__DEFINES/medal.dm
+++ b/code/__DEFINES/medal.dm
@@ -21,4 +21,4 @@
#define DRAKE_SCORE "Drakes Killed"
#define LEGION_SCORE "Legion Killed"
#define SWARMER_BEACON_SCORE "Swarmer Beacons Killed"
-#define TENDRIL_CLEAR_SCORE "Tendrils Killed"
\ No newline at end of file
+#define TENDRIL_CLEAR_SCORE "Tendrils Killed"
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 6e1fa5e01fd..346664e0758 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -319,7 +319,7 @@
#define INVESTIGATE_BOMB "bombs"
// The SQL version required by this version of the code
-#define SQL_VERSION 10
+#define SQL_VERSION 11
// Vending machine stuff
#define CAT_NORMAL 1
@@ -419,4 +419,19 @@
#define ENGTYPE_TESLA "Tesla"
#define SUMMON_GUNS "guns"
-#define SUMMON_MAGIC "magic"
\ No newline at end of file
+#define SUMMON_MAGIC "magic"
+
+// Medical stuff
+#define SYMPTOM_ACTIVATION_PROB 3
+
+// Atmos stuff that fucking terrifies me
+#define LINDA_SPAWN_HEAT 1
+#define LINDA_SPAWN_20C 2
+#define LINDA_SPAWN_TOXINS 4
+#define LINDA_SPAWN_OXYGEN 8
+#define LINDA_SPAWN_CO2 16
+#define LINDA_SPAWN_NITROGEN 32
+
+#define LINDA_SPAWN_N2O 64
+
+#define LINDA_SPAWN_AIR 256
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 293d97e4fa3..e1e15a926d6 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -239,4 +239,4 @@
#define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0)
-#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return;
\ No newline at end of file
+#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return;
diff --git a/code/__DEFINES/move_force.dm b/code/__DEFINES/move_force.dm
index 750821bd898..8051a516298 100644
--- a/code/__DEFINES/move_force.dm
+++ b/code/__DEFINES/move_force.dm
@@ -16,4 +16,4 @@
#define MOVE_FORCE_NORMAL MOVE_FORCE_DEFAULT
#define MOVE_FORCE_WEAK MOVE_FORCE_DEFAULT / 2
#define MOVE_FORCE_VERY_WEAK (MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1
-#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3)
\ No newline at end of file
+#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3)
diff --git a/code/__DEFINES/pda.dm b/code/__DEFINES/pda.dm
index d22b90870b0..c5d32f03ba8 100644
--- a/code/__DEFINES/pda.dm
+++ b/code/__DEFINES/pda.dm
@@ -1,3 +1,3 @@
#define PDA_APP_UPDATE 0
#define PDA_APP_NOUPDATE 1
-#define PDA_APP_UPDATE_SLOW 2
\ No newline at end of file
+#define PDA_APP_UPDATE_SLOW 2
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 54fd59307f4..eb5fad0ff27 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -7,9 +7,10 @@
#define SOUND_BUZZ 64
#define SOUND_INSTRUMENTS 128
#define SOUND_MENTORHELP 256
-#define SOUND_DISCO 512
+#define SOUND_DISCO 512
+#define SOUND_AI_VOICE 1024
-#define SOUND_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_HEARTBEAT|SOUND_BUZZ|SOUND_INSTRUMENTS|SOUND_MENTORHELP|SOUND_DISCO)
+#define SOUND_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_HEARTBEAT|SOUND_BUZZ|SOUND_INSTRUMENTS|SOUND_MENTORHELP|SOUND_DISCO|SOUND_AI_VOICE)
#define CHAT_OOC 1
#define CHAT_DEAD 2
diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm
index 6adb876da8d..0aaee0533d4 100644
--- a/code/__DEFINES/qdel.dm
+++ b/code/__DEFINES/qdel.dm
@@ -20,4 +20,4 @@
#define QDELING(X) (X.gc_destroyed)
#define QDELETED(X) (!X || QDELING(X))
-#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
\ No newline at end of file
+#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
diff --git a/code/__DEFINES/radio.dm b/code/__DEFINES/radio.dm
index e49a9ea9b67..e0c71836c72 100644
--- a/code/__DEFINES/radio.dm
+++ b/code/__DEFINES/radio.dm
@@ -1,60 +1,60 @@
-
-#define DISPLAY_FREQ 1435 //status displays
-#define ATMOS_FIRE_FREQ 1437 //air alarms
-#define ENGINE_FREQ 1438 //engine components
-#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control
-#define ATMOS_DISTRO_FREQ 1443 //distro loop
-#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks
-#define BOT_BEACON_FREQ 1445 //bot navigation beacons
-#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets
-
-#define RSD_FREQ 1457 //radio signal device
-#define IMPL_FREQ 1451 //tracking implant
-
-#define RADIO_LOW_FREQ 1200 //minimum radio freq
-#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq
-#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq
-#define RADIO_HIGH_FREQ 1600 //maximum radio freq
-
-#define SYND_FREQ 1213
-#define SYNDTEAM_FREQ 1244
-#define DTH_FREQ 1341 //Special Operations
-#define AI_FREQ 1343
-#define ERT_FREQ 1345
-#define COMM_FREQ 1353 //Command
-#define BOT_FREQ 1447 //mulebot, secbot, ed209
-
-
-// Department channels
-#define PUB_FREQ 1459 //standard radio chat
-#define SEC_FREQ 1359 //security
-#define ENG_FREQ 1357 //engineering
-#define SCI_FREQ 1351 //science
-#define MED_FREQ 1355 //medical
-#define SUP_FREQ 1347 //cargo
-#define SRV_FREQ 1349 //service
-
-// Internal department channels
-#define MED_I_FREQ 1485
-#define SEC_I_FREQ 1475
-
-// Transmission methods
-#define TRANSMISSION_WIRE 0
-#define TRANSMISSION_RADIO 1
-
-//This filter is special because devices belonging to default also recieve signals sent to any other filter.
-#define RADIO_DEFAULT "radio_default"
-#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms
-#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms
-#define RADIO_CHAT "radio_telecoms"
-#define RADIO_ATMOSIA "radio_atmos"
-#define RADIO_NAVBEACONS "radio_navbeacon"
-#define RADIO_AIRLOCK "radio_airlock"
-#define RADIO_SECBOT "radio_secbot"
-#define RADIO_HONKBOT "radio_honkbot"
-#define RADIO_MULEBOT "radio_mulebot"
-#define RADIO_CLEANBOT "10"
-#define RADIO_FLOORBOT "11"
-#define RADIO_MEDBOT "12"
-#define RADIO_MAGNETS "radio_magnet"
-#define RADIO_LOGIC "radio_logic"
+
+#define DISPLAY_FREQ 1435 //status displays
+#define ATMOS_FIRE_FREQ 1437 //air alarms
+#define ENGINE_FREQ 1438 //engine components
+#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control
+#define ATMOS_DISTRO_FREQ 1443 //distro loop
+#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks
+#define BOT_BEACON_FREQ 1445 //bot navigation beacons
+#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets
+
+#define RSD_FREQ 1457 //radio signal device
+#define IMPL_FREQ 1451 //tracking implant
+
+#define RADIO_LOW_FREQ 1200 //minimum radio freq
+#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq
+#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq
+#define RADIO_HIGH_FREQ 1600 //maximum radio freq
+
+#define SYND_FREQ 1213
+#define SYNDTEAM_FREQ 1244
+#define DTH_FREQ 1341 //Special Operations
+#define AI_FREQ 1343
+#define ERT_FREQ 1345
+#define COMM_FREQ 1353 //Command
+#define BOT_FREQ 1447 //mulebot, secbot, ed209
+
+
+// Department channels
+#define PUB_FREQ 1459 //standard radio chat
+#define SEC_FREQ 1359 //security
+#define ENG_FREQ 1357 //engineering
+#define SCI_FREQ 1351 //science
+#define MED_FREQ 1355 //medical
+#define SUP_FREQ 1347 //cargo
+#define SRV_FREQ 1349 //service
+
+// Internal department channels
+#define MED_I_FREQ 1485
+#define SEC_I_FREQ 1475
+
+// Transmission methods
+#define TRANSMISSION_WIRE 0
+#define TRANSMISSION_RADIO 1
+
+//This filter is special because devices belonging to default also recieve signals sent to any other filter.
+#define RADIO_DEFAULT "radio_default"
+#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms
+#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms
+#define RADIO_CHAT "radio_telecoms"
+#define RADIO_ATMOSIA "radio_atmos"
+#define RADIO_NAVBEACONS "radio_navbeacon"
+#define RADIO_AIRLOCK "radio_airlock"
+#define RADIO_SECBOT "radio_secbot"
+#define RADIO_HONKBOT "radio_honkbot"
+#define RADIO_MULEBOT "radio_mulebot"
+#define RADIO_CLEANBOT "10"
+#define RADIO_FLOORBOT "11"
+#define RADIO_MEDBOT "12"
+#define RADIO_MAGNETS "radio_magnet"
+#define RADIO_LOGIC "radio_logic"
diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm
index e2dd3ee3ac7..564247455e3 100644
--- a/code/__DEFINES/reagents.dm
+++ b/code/__DEFINES/reagents.dm
@@ -16,3 +16,6 @@
// Is an open container for all intents and purposes.
#define OPENCONTAINER (REFILLABLE | DRAINABLE | TRANSPARENT)
+
+#define REAGENT_TOUCH 1
+#define REAGENT_INGEST 2
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 34ad4d69e54..2b9e2c56b4d 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -1,81 +1,81 @@
-
-
-//Values for antag preferences, event roles, etc. unified here
-
-
-
-//These are synced with the Database, if you change the values of the defines
-//then you MUST update the database!
-// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out
-// justice if someone's abusing your role
-#define ROLE_SYNDICATE "Syndicate"
-#define ROLE_TRAITOR "traitor"
-#define ROLE_OPERATIVE "operative"
-#define ROLE_CHANGELING "changeling"
-#define ROLE_WIZARD "wizard"
-#define ROLE_REV "revolutionary"
-#define ROLE_ALIEN "xenomorph"
-#define ROLE_PAI "pAI"
-#define ROLE_CULTIST "cultist"
-#define ROLE_BLOB "blob"
-#define ROLE_NINJA "space ninja"
-#define ROLE_MONKEY "monkey"
-#define ROLE_GANG "gangster"
-#define ROLE_SHADOWLING "shadowling"
-#define ROLE_ABDUCTOR "abductor"
-#define ROLE_REVENANT "revenant"
-#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another
-#define ROLE_HOG_CULTIST "hand of god: cultist"
-#define ROLE_DEVIL "devil"
-#define ROLE_RAIDER "vox raider"
-#define ROLE_TRADER "trader"
-#define ROLE_VAMPIRE "vampire"
-// Role tags for EVERYONE!
-#define ROLE_BORER "cortical borer"
-#define ROLE_DEMON "slaughter demon"
-#define ROLE_SENTIENT "sentient animal"
-#define ROLE_POSIBRAIN "positronic brain"
-#define ROLE_GUARDIAN "guardian"
-#define ROLE_MORPH "morph"
-#define ROLE_ERT "emergency response team"
-#define ROLE_NYMPH "Dionaea"
-#define ROLE_GSPIDER "giant spider"
-#define ROLE_DRONE "drone"
-#define ROLE_DEATHSQUAD "deathsquad"
-#define ROLE_EVENTMISC "eventmisc"
-#define ROLE_GHOST "ghost role"
-
-//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
-//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
-//(in game days played) to play that role
-var/global/list/special_roles = list(
- ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor
- ROLE_BLOB = /datum/game_mode/blob, // Blob
- ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling
- ROLE_BORER, // Cortical borer
- ROLE_CULTIST = /datum/game_mode/cult, // Cultist
- ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil
- ROLE_GSPIDER, // Giant spider
- ROLE_GUARDIAN, // Guardian
- ROLE_MORPH, // Morph
- ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative
- ROLE_PAI, // PAI
- ROLE_POSIBRAIN, // Positronic brain
- ROLE_REVENANT, // Revenant
- ROLE_REV = /datum/game_mode/revolution, // Revolutionary
- ROLE_SENTIENT, // Sentient animal
- ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling
- ROLE_DEMON, // Slaguther demon
- ROLE_NINJA, // Space ninja
- ROLE_TRADER, // Trader
- ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor
- ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire
- ROLE_RAIDER = /datum/game_mode/heist, // Vox raider
- ROLE_ALIEN, // Xenomorph
- ROLE_WIZARD = /datum/game_mode/wizard, // Wizard
- // UNUSED/BROKEN ANTAGS
-// ROLE_HOG_GOD = /datum/game_mode/hand_of_god,
-// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god,
-// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported
-// ROLE_GANG = /datum/game_mode/gang,
-)
+
+
+//Values for antag preferences, event roles, etc. unified here
+
+
+
+//These are synced with the Database, if you change the values of the defines
+//then you MUST update the database!
+// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out
+// justice if someone's abusing your role
+#define ROLE_SYNDICATE "Syndicate"
+#define ROLE_TRAITOR "traitor"
+#define ROLE_OPERATIVE "operative"
+#define ROLE_CHANGELING "changeling"
+#define ROLE_WIZARD "wizard"
+#define ROLE_REV "revolutionary"
+#define ROLE_ALIEN "xenomorph"
+#define ROLE_PAI "pAI"
+#define ROLE_CULTIST "cultist"
+#define ROLE_BLOB "blob"
+#define ROLE_NINJA "space ninja"
+#define ROLE_MONKEY "monkey"
+#define ROLE_GANG "gangster"
+#define ROLE_SHADOWLING "shadowling"
+#define ROLE_ABDUCTOR "abductor"
+#define ROLE_REVENANT "revenant"
+#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another
+#define ROLE_HOG_CULTIST "hand of god: cultist"
+#define ROLE_DEVIL "devil"
+#define ROLE_RAIDER "vox raider"
+#define ROLE_TRADER "trader"
+#define ROLE_VAMPIRE "vampire"
+// Role tags for EVERYONE!
+#define ROLE_BORER "cortical borer"
+#define ROLE_DEMON "slaughter demon"
+#define ROLE_SENTIENT "sentient animal"
+#define ROLE_POSIBRAIN "positronic brain"
+#define ROLE_GUARDIAN "guardian"
+#define ROLE_MORPH "morph"
+#define ROLE_ERT "emergency response team"
+#define ROLE_NYMPH "Dionaea"
+#define ROLE_GSPIDER "giant spider"
+#define ROLE_DRONE "drone"
+#define ROLE_DEATHSQUAD "deathsquad"
+#define ROLE_EVENTMISC "eventmisc"
+#define ROLE_GHOST "ghost role"
+
+//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
+//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
+//(in game days played) to play that role
+var/global/list/special_roles = list(
+ ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor
+ ROLE_BLOB = /datum/game_mode/blob, // Blob
+ ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling
+ ROLE_BORER, // Cortical borer
+ ROLE_CULTIST = /datum/game_mode/cult, // Cultist
+ ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil
+ ROLE_GSPIDER, // Giant spider
+ ROLE_GUARDIAN, // Guardian
+ ROLE_MORPH, // Morph
+ ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative
+ ROLE_PAI, // PAI
+ ROLE_POSIBRAIN, // Positronic brain
+ ROLE_REVENANT, // Revenant
+ ROLE_REV = /datum/game_mode/revolution, // Revolutionary
+ ROLE_SENTIENT, // Sentient animal
+ ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling
+ ROLE_DEMON, // Slaguther demon
+ ROLE_NINJA, // Space ninja
+ ROLE_TRADER, // Trader
+ ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor
+ ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire
+ ROLE_RAIDER = /datum/game_mode/heist, // Vox raider
+ ROLE_ALIEN, // Xenomorph
+ ROLE_WIZARD = /datum/game_mode/wizard, // Wizard
+ // UNUSED/BROKEN ANTAGS
+// ROLE_HOG_GOD = /datum/game_mode/hand_of_god,
+// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god,
+// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported
+// ROLE_GANG = /datum/game_mode/gang,
+)
diff --git a/code/__DEFINES/shuttle.dm b/code/__DEFINES/shuttle.dm
index 22af9a38670..72ed25d1518 100644
--- a/code/__DEFINES/shuttle.dm
+++ b/code/__DEFINES/shuttle.dm
@@ -13,4 +13,4 @@
#define SHUTTLE_DOCKER_LANDING_CLEAR 1
#define SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT 2
-#define SHUTTLE_DOCKER_BLOCKED 3
\ No newline at end of file
+#define SHUTTLE_DOCKER_BLOCKED 3
diff --git a/code/__DEFINES/sight.dm b/code/__DEFINES/sight.dm
index 58f557c4b75..52fc66aa814 100644
--- a/code/__DEFINES/sight.dm
+++ b/code/__DEFINES/sight.dm
@@ -38,4 +38,4 @@
#define VISOR_TINT (1<<1)
#define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses
#define VISOR_DARKNESSVIEW (1<<3)
-#define VISOR_INVISVIEW (1<<4)
\ No newline at end of file
+#define VISOR_INVISVIEW (1<<4)
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index 7e131dcaa21..b0274e9d802 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -14,4 +14,4 @@
#define SOUND_MINIMUM_PRESSURE 10
-#define FALLOFF_SOUNDS 0.5
\ No newline at end of file
+#define FALLOFF_SOUNDS 0.5
diff --git a/code/__DEFINES/station_goals.dm b/code/__DEFINES/station_goals.dm
index 7c13f4f9e67..0c19da7b5f1 100644
--- a/code/__DEFINES/station_goals.dm
+++ b/code/__DEFINES/station_goals.dm
@@ -1,2 +1,2 @@
#define BSA_SIZE_FRONT 4
-#define BSA_SIZE_BACK 6
\ No newline at end of file
+#define BSA_SIZE_BACK 6
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 6a14e6fae66..54b85d80e76 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -81,4 +81,4 @@
#define STATUS_EFFECT_CRUSHERDAMAGETRACKING /datum/status_effect/crusher_damage //tracks total kinetic crusher damage on a target
-#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module
\ No newline at end of file
+#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index c55fec2cd87..300d3a41fc0 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -144,4 +144,4 @@
A.overlays |= po;\
}\
A.flags_2 &= ~OVERLAY_QUEUED_2;\
-}
\ No newline at end of file
+}
diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm
new file mode 100644
index 00000000000..d0204e1d013
--- /dev/null
+++ b/code/__DEFINES/tools.dm
@@ -0,0 +1,46 @@
+#define TOOL_CROWBAR "crowbar"
+#define TOOL_MULTITOOL "multitool"
+#define TOOL_SCREWDRIVER "screwdriver"
+#define TOOL_WIRECUTTER "wirecutter"
+#define TOOL_WRENCH "wrench"
+#define TOOL_WELDER "welder"
+
+#define MIN_TOOL_SOUND_DELAY 20
+
+//Crowbar messages
+#define CROWBAR_ATTEMPT_PRY_CIRCUIT_MESSAGE user.visible_message("[user] begins removing the circuit board from [src]...", "You begin removing the circuit board from [src]...", "You hear prying noises.")
+#define CROWBAR_PRY_CIRCUIT_SUCCESS_MESSAGE user.visible_message("[user] pries out the circuit board from [src]!", "You pry out the circuit board from [src]!", "You hear prying noises.")
+
+//Screwdriver messages
+#define SCREWDRIVER_SCREW_MESSAGE user.visible_message("[user] tightens the screws on [src]!", "You tighten the screws on [src]!", "You hear a screwdriver.")
+#define SCREWDRIVER_UNSCREW_MESSAGE user.visible_message("[user] loosens the screws on [src]!", "You loosen the screws on [src]!", "You hear a screwdriver.")
+#define SCREWDRIVER_OPEN_PANEL_MESSAGE user.visible_message("[user] opens the panel on [src]!", "You open the panel on [src]!", "You hear a screwdriver.")
+#define SCREWDRIVER_CLOSE_PANEL_MESSAGE user.visible_message("[user] closes the panel on [src]!", "You close the panel on [src]!", "You hear a screwdriver.")
+
+//Wirecutter messages
+#define WIRECUTTER_SNIP_MESSAGE user.visible_message("[user] cuts the wires from [src]!", "You cut the wires from [src]!", "You hear snipping.")
+#define WIRECUTTER_ATTEMPT_DISMANTLE_MESSAGE user.visible_message("[user] begins cutting [src] apart... ", "You begin cutting [src] apart...", "You hear snipping.")
+#define WIRECUTTER_DISMANTLE_SUCCESS_MESSAGE user.visible_message("[user] cuts [src] apart!", "You cut [src] apart!", "You hear snipping.")
+
+//Welder messages and other stuff
+#define HEALPERWELD 15
+#define WELDER_ATTEMPT_WELD_MESSAGE user.visible_message("[user] begins welding [src]...", "You begin welding [src]...", "You hear welding.")
+#define WELDER_WELD_SUCCESS_MESSAGE to_chat(user, "You finish welding [src]!")
+#define WELDER_ATTEMPT_REPAIR_MESSAGE user.visible_message("[user] begins repairing the damage on [src]...", "You begin repairing [src]...", "You hear welding.")
+#define WELDER_REPAIR_SUCCESS_MESSAGE to_chat(user, "You repair the damage on [src]!")
+#define WELDER_ATTEMPT_SLICING_MESSAGE user.visible_message("[user] begins slicing through [src]...", "You begin slicing through [src]...", "You hear welding.")
+#define WELDER_SLICING_SUCCESS_MESSAGE to_chat(user, "You slice clean through [src]!")
+#define WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE user.visible_message("[user] begins slicing [src] free from [get_turf(src)]...", "You begin slicing [src] free from [get_turf(src)]...", "You hear welding.")
+#define WELDER_FLOOR_SLICE_SUCCESS_MESSAGE to_chat(user, "You slice [src] clear of [get_turf(src)]!")
+#define WELDER_ATTEMPT_FLOOR_WELD_MESSAGE user.visible_message("[user] begins welding [src] to [get_turf(src)]...", "You begin welding [src] to [get_turf(src)]...", "You hear welding.")
+#define WELDER_FLOOR_WELD_SUCCESS_MESSAGE to_chat(user, "You weld [src] to [get_turf(src)]!")
+
+//Wrench messages
+#define WRENCH_ANCHOR_MESSAGE user.visible_message("[user] tightens the bolts on [src]!", "You tighten the bolts on [src]!", "You hear ratcheting.")
+#define WRENCH_UNANCHOR_MESSAGE user.visible_message("[user] loosens the bolts on [src]!", "You loosen the bolts on [src]!", "You hear ratcheting.")
+#define WRENCH_UNANCHOR_WALL_MESSAGE user.visible_message("[user] unwrenches [src] from the wall!", "You unwrench [src] from the wall!", "You hear ratcheting.")
+#define WRENCH_ANCHOR_TO_WALL_MESSAGE user.visible_message("[user] affixes [src] to the wall!", "You affix [src] to the wall!", "You hear ratcheting.")
+
+//Generic tool messages that don't correspond to any particular tool
+#define TOOL_ATTEMPT_DISMANTLE_MESSAGE user.visible_message("[user] begins to disassemble [src] with [I]...", "You begin to disassemble [src] with [I]...", "You hear someone using some kind of tool.")
+#define TOOL_DISMANTLE_SUCCESS_MESSAGE user.visible_message("[user] dismantles [src]!", "You dismantle [src]!", "You hear someone using some kind of tool.")
diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm
index 16b6e475b8e..56e0958d1a4 100644
--- a/code/__DEFINES/typeids.dm
+++ b/code/__DEFINES/typeids.dm
@@ -3,4 +3,4 @@
#define TYPEID_NORMAL_LIST "f"
//helper macros
#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref) - 6) ) )
-#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST)
\ No newline at end of file
+#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST)
diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm
index 22e13f87f8a..0ac352f9ae9 100644
--- a/code/__DEFINES/vv.dm
+++ b/code/__DEFINES/vv.dm
@@ -19,4 +19,4 @@
#define VV_NULL "NULL"
#define VV_RESTORE_DEFAULT "Restore to Default"
#define VV_MARKED_DATUM "Marked Datum"
-#define VV_REGEX "Regex"
\ No newline at end of file
+#define VV_REGEX "Regex"
diff --git a/code/__HELPERS/AnimationLibrary.dm b/code/__HELPERS/AnimationLibrary.dm
index fd79fe197f2..cc496a85b63 100644
--- a/code/__HELPERS/AnimationLibrary.dm
+++ b/code/__HELPERS/AnimationLibrary.dm
@@ -187,4 +187,4 @@
var/original_y = A.pixel_y
animate(A, transform = matrix(punchstr, MATRIX_ROTATE), pixel_y = 16, time = 2, color = "#eeeeee", easing = BOUNCE_EASING)
animate(transform = matrix(-punchstr, MATRIX_ROTATE), pixel_y = original_y, time = 2, color = "#ffffff", easing = BOUNCE_EASING)
- animate(transform = null, time = 3, easing = BOUNCE_EASING)
\ No newline at end of file
+ animate(transform = null, time = 3, easing = BOUNCE_EASING)
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 8c07fe175b2..7a91c3bf9bc 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -1,5 +1,5 @@
//location of the rust-g library
-#define RUST_G "rust_g"
+#define RUST_G (world.system_type == UNIX ? "./librust_g.so" : "./rust_g.dll")
// On Linux/Unix systems the line endings are LF, on windows it's CRLF, admins that don't use notepad++
// will get logs that are one big line if the system is Linux and they are using notepad. This solves it by adding CR to every line ending
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 0db57b27eae..afb70cb81e6 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -47,4 +47,4 @@
if(A.plane != B.plane)
return A.plane - B.plane
else
- return A.layer - B.layer
\ No newline at end of file
+ return A.layer - B.layer
diff --git a/code/__HELPERS/constants.dm b/code/__HELPERS/constants.dm
index f7db3166605..b976a28e195 100644
--- a/code/__HELPERS/constants.dm
+++ b/code/__HELPERS/constants.dm
@@ -1,2 +1,2 @@
-var/global/const/TICKS_IN_DAY = 864000
-var/global/const/TICKS_IN_SECOND = 10
\ No newline at end of file
+#define TICKS_IN_DAY 864000
+#define TICKS_IN_SECOND 10
diff --git a/code/__HELPERS/experimental.dm b/code/__HELPERS/experimental.dm
index 533f67b68d9..4f13c07cbd4 100644
--- a/code/__HELPERS/experimental.dm
+++ b/code/__HELPERS/experimental.dm
@@ -48,4 +48,4 @@
if(A != myArea)
myArea = A
- . = myArea
\ No newline at end of file
+ . = myArea
diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm
index 27f3537d521..c96357299c2 100644
--- a/code/__HELPERS/files.dm
+++ b/code/__HELPERS/files.dm
@@ -1,60 +1,60 @@
-//checks if a file exists and contains text
-//returns text as a string if these conditions are met
-/proc/return_file_text(filename)
- if(fexists(filename) == 0)
- error("File not found ([filename])")
- return
-
- var/text = file2text(filename)
- if(!text)
- error("File empty ([filename])")
- return
-
- return text
-
-//Sends resource files to client cache
-/client/proc/getFiles()
- for(var/file in args)
- src << browse_rsc(file)
-
-/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm"))
- var/path = root
-
- for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).")
- return
-
- return path
-
-#define FTPDELAY 200 //200 tick delay to discourage spam
-/* This proc is a failsafe to prevent spamming of file requests.
- It is just a timer that only permits a download every [FTPDELAY] ticks.
- This can be changed by modifying FTPDELAY's value above.
-
- PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */
-/client/proc/file_spam_check()
- var/time_to_wait = fileaccess_timer - world.time
- if(time_to_wait > 0)
- to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.")
- return 1
- fileaccess_timer = world.time + FTPDELAY
- return 0
-#undef FTPDELAY
\ No newline at end of file
+//checks if a file exists and contains text
+//returns text as a string if these conditions are met
+/proc/return_file_text(filename)
+ if(fexists(filename) == 0)
+ error("File not found ([filename])")
+ return
+
+ var/text = file2text(filename)
+ if(!text)
+ error("File empty ([filename])")
+ return
+
+ return text
+
+//Sends resource files to client cache
+/client/proc/getFiles()
+ for(var/file in args)
+ src << browse_rsc(file)
+
+/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm"))
+ var/path = root
+
+ for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).")
+ return
+
+ return path
+
+#define FTPDELAY 200 //200 tick delay to discourage spam
+/* This proc is a failsafe to prevent spamming of file requests.
+ It is just a timer that only permits a download every [FTPDELAY] ticks.
+ This can be changed by modifying FTPDELAY's value above.
+
+ PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */
+/client/proc/file_spam_check()
+ var/time_to_wait = fileaccess_timer - world.time
+ if(time_to_wait > 0)
+ to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.")
+ return 1
+ fileaccess_timer = world.time + FTPDELAY
+ return 0
+#undef FTPDELAY
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 744b8b86a9a..f61b6452f22 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -1,536 +1,539 @@
-/proc/get_area(atom/A)
- if(isarea(A))
- return A
- var/turf/T = get_turf(A)
- return T ? T.loc : null
-
-/proc/get_area_name(N) //get area by its name
- for(var/area/A in world)
- if(A.name == N)
- return A
- return 0
-
-/proc/get_location_name(atom/X, format_text = FALSE)
- var/area/A = isarea(X) ? X : get_area(X)
- if(!A)
- return null
- return format_text ? format_text(A.name) : A.name
-
-/proc/get_areas_in_range(dist=0, atom/center=usr)
- if(!dist)
- var/turf/T = get_turf(center)
- return T ? list(T.loc) : list()
- if(!center)
- return list()
-
- var/list/turfs = RANGE_TURFS(dist, center)
- var/list/areas = list()
- for(var/V in turfs)
- var/turf/T = V
- areas |= T.loc
- return areas
-
-// Like view but bypasses luminosity check
-
-/proc/hear(var/range, var/atom/source)
- var/lum = source.luminosity
- source.luminosity = 6
-
- var/list/heard = view(range, source)
- source.luminosity = lum
-
- return heard
-
-/proc/circlerange(center=usr,radius=3)
-
- var/turf/centerturf = get_turf(center)
- var/list/turfs = new/list()
- var/rsq = radius * (radius+0.5)
-
- for(var/atom/T in range(radius, centerturf))
- var/dx = T.x - centerturf.x
- var/dy = T.y - centerturf.y
- if(dx*dx + dy*dy <= rsq)
- turfs += T
-
- //turfs += centerturf
- return turfs
-
-/proc/circleview(center=usr,radius=3)
-
- var/turf/centerturf = get_turf(center)
- var/list/atoms = new/list()
- var/rsq = radius * (radius+0.5)
-
- for(var/atom/A in view(radius, centerturf))
- var/dx = A.x - centerturf.x
- var/dy = A.y - centerturf.y
- if(dx*dx + dy*dy <= rsq)
- atoms += A
-
- //turfs += centerturf
- return atoms
-
-/proc/ff_cansee(atom/A, atom/B)
- var/AT = get_turf(A)
- var/BT = get_turf(B)
- if(AT == BT)
- return 1
- var/list/line = getline(A, B)
- for(var/turf/T in line)
- if(T == AT || T == BT)
- break
- if(T.density)
- return FALSE
- return TRUE
-
-/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj)
- var/dx = Loc1.x - Loc2.x
- var/dy = Loc1.y - Loc2.y
-
- var/dist = sqrt(dx**2 + dy**2)
-
- return dist
-
-/proc/circlerangeturfs(center=usr,radius=3)
-
- var/turf/centerturf = get_turf(center)
- var/list/turfs = new/list()
- var/rsq = radius * (radius+0.5)
-
- for(var/turf/T in range(radius, centerturf))
- var/dx = T.x - centerturf.x
- var/dy = T.y - centerturf.y
- if(dx*dx + dy*dy <= rsq)
- turfs += T
- return turfs
-
-/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()?
-
- var/turf/centerturf = get_turf(center)
- var/list/turfs = new/list()
- var/rsq = radius * (radius+0.5)
-
- for(var/turf/T in view(radius, centerturf))
- var/dx = T.x - centerturf.x
- var/dy = T.y - centerturf.y
- if(dx*dx + dy*dy <= rsq)
- turfs += T
- return turfs
-
-
-
-//var/debug_mob = 0
-
-// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents.
-// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects,
-// being unable to hear people due to being in a box within a bag.
-
-/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1)
-
- //debug_mob += O.contents.len
- if(!recursion_limit)
- return L
- for(var/atom/A in O.contents)
-
- if(ismob(A))
- var/mob/M = A
- if(client_check && !M.client)
- L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
- continue
- if(sight_check && !isInSight(A, O))
- continue
- L |= M
- //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
-
- else if(include_radio && istype(A, /obj/item/radio))
- if(sight_check && !isInSight(A, O))
- continue
- L |= A
-
- if(isobj(A) || ismob(A))
- L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
- return L
-
-// The old system would loop through lists for a total of 5000 per function call, in an empty server.
-// This new system will loop at around 1000 in an empty server.
-
-/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE)
- // Returns a list of mobs in range of R from source. Used in radio and say code.
-
- var/turf/T = get_turf(source)
- var/list/hear = list()
-
- if(!T)
- return hear
-
- var/list/range = hear(R, T)
-
- for(var/atom/A in range)
- if(ismob(A))
- var/mob/M = A
- if(M.client || include_clientless)
- hear += M
- //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
- else if(istype(A, /obj/item/radio))
- hear += A
-
- if(isobj(A) || ismob(A))
- hear |= recursive_mob_check(A, hear, 3, 1, 0, 1)
-
- return hear
-
-
-/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios)
-
- set background = 1
-
- . = list()
- // Returns a list of mobs who can hear any of the radios given in @radios
- var/list/speaker_coverage = list()
- for(var/obj/item/radio/R in radios)
- if(R)
- //Cyborg checks. Receiving message uses a bit of cyborg's charge.
- var/obj/item/radio/borg/BR = R
- if(istype(BR) && BR.myborg)
- var/mob/living/silicon/robot/borg = BR.myborg
- var/datum/robot_component/CO = borg.get_component("radio")
- if(!CO)
- continue //No radio component (Shouldn't happen)
- if(!borg.is_component_functioning("radio"))
- continue //No power.
-
- var/turf/speaker = get_turf(R)
- if(speaker)
- for(var/turf/T in hear(R.canhear_range,speaker))
- speaker_coverage[T] = T
-
-
- // Try to find all the players who can hear the message
- for(var/A in GLOB.player_list + GLOB.hear_radio_list)
- var/mob/M = A
- if(M)
- var/turf/ear = get_turf(M)
- if(ear)
- // Ghostship is magic: Ghosts can hear radio chatter from anywhere
- if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO)))
- . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down.
- return .
-
-/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5)
- var/turf/T
- if(X1==X2)
- if(Y1==Y2)
- return 1 //Light cannot be blocked on same tile
- else
- var/s = SIMPLE_SIGN(Y2-Y1)
- Y1+=s
- while(Y1!=Y2)
- T=locate(X1,Y1,Z)
- if(T.opacity)
- return 0
- Y1+=s
- else
- var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1))
- var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles
- var/signX = SIMPLE_SIGN(X2-X1)
- var/signY = SIMPLE_SIGN(Y2-Y1)
- if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie)
- if(dy > 0)
- return get_step(start, SOUTH)
- else
- return get_step(start, NORTH)
- else
- if(dx > 0)
- return get_step(start, WEST)
- else
- return get_step(start, EAST)
-
-/proc/try_move_adjacent(atom/movable/AM)
- var/turf/T = get_turf(AM)
- for(var/direction in cardinal)
- if(AM.Move(get_step(T, direction)))
- break
-
-/proc/get_mob_by_key(var/key)
- for(var/mob/M in GLOB.mob_list)
- if(M.ckey == lowertext(key))
- return M
- return null
-
-/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0)
- var/roletext = get_roletext(be_special_type)
- var/list/candidates = list()
- // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000))
- while(!candidates.len && afk_bracket < 6000)
- for(var/mob/dead/observer/G in GLOB.player_list)
- if(G.client != null)
- if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
- if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special))
- if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate")))
- if(override_age || player_old_enough_antag(G.client,be_special_type))
- candidates += G.client
- afk_bracket += 600 // Add a minute to the bracket, for every attempt
-
- return candidates
-
-/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0)
- var/roletext = get_roletext(be_special_type)
- var/list/candidates = list()
- // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000))
- while(!candidates.len && afk_bracket < 6000)
- for(var/mob/dead/observer/G in GLOB.player_list)
- if(G.client != null)
- if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
- if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special))
- if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate")))
- if(override_age || player_old_enough_antag(G.client,be_special_type))
- candidates += G
- afk_bracket += 600 // Add a minute to the bracket, for every attempt
-
- return candidates
-
-/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
- if(!isobj(O)) O = new /obj/screen/text()
- O.maptext = maptext
- O.maptext_height = maptext_height
- O.maptext_width = maptext_width
- O.screen_loc = screen_loc
- return O
-
-/proc/Show2Group4Delay(obj/O, list/group, delay=0)
- if(!isobj(O)) return
- if(!group) group = GLOB.clients
- for(var/client/C in group)
- C.screen += O
- if(delay)
- spawn(delay)
- for(var/client/C in group)
- C.screen -= O
-
-/proc/flick_overlay(image/I, list/show_to, duration)
- for(var/client/C in show_to)
- C.images += I
- spawn(duration)
- for(var/client/C in show_to)
- C.images -= I
-
-/proc/get_active_player_count()
- // Get active players who are playing in the round
- var/active_players = 0
- for(var/i = 1; i <= GLOB.player_list.len; i++)
- var/mob/M = GLOB.player_list[i]
- if(M && M.client)
- if(istype(M, /mob/new_player)) // exclude people in the lobby
- continue
- else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
- var/mob/dead/observer/O = M
- if(O.started_as_observer) // Exclude people who started as observers
- continue
- active_players++
- return active_players
-
-/datum/projectile_data
- var/src_x
- var/src_y
- var/time
- var/distance
- var/power_x
- var/power_y
- var/dest_x
- var/dest_y
-
-/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \
- var/power_x, var/power_y, var/dest_x, var/dest_y)
- src.src_x = src_x
- src.src_y = src_y
- src.time = time
- src.distance = distance
- src.power_x = power_x
- src.power_y = power_y
- src.dest_x = dest_x
- src.dest_y = dest_y
-
-/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power)
-
- // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle],
- // rotated at [rotation] and with the power of [power]
- // Thanks to VistaPOWA for this function
-
- var/power_x = power * cos(angle)
- var/power_y = power * sin(angle)
- var/time = 2* power_y / 10 //10 = g
-
- var/distance = time * power_x
-
- var/dest_x = src_x + distance*sin(rotation);
- var/dest_y = src_y + distance*cos(rotation);
-
- return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
-
-
-/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list)
- var/list/mobs_found[0]
- var/area/our_area = get_area(the_area)
- for(var/mob/M in moblist)
- if(client_needed && !M.client)
- continue
- if(our_area != get_area(M))
- continue
- mobs_found += M
- return mobs_found
-
-/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon)
- var/area/our_area = get_area(the_area)
- for(var/C in GLOB.living_mob_list)
- if(!istype(C, check_type))
- continue
- if(C == must_be_alone)
- continue
- if(our_area == get_area(C))
- return 0
- return 1
-
-
-/proc/GetRedPart(const/hexa)
- return hex2num(copytext(hexa, 2, 4))
-
-/proc/GetGreenPart(const/hexa)
- return hex2num(copytext(hexa, 4, 6))
-
-/proc/GetBluePart(const/hexa)
- return hex2num(copytext(hexa, 6, 8))
-
-/proc/lavaland_equipment_pressure_check(turf/T)
- . = FALSE
- if(!istype(T))
- return
- var/datum/gas_mixture/environment = T.return_air()
- if(!istype(environment))
- return
- var/pressure = environment.return_pressure()
- if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
- . = TRUE
-
-/proc/GetHexColors(const/hexa)
- return list(
- GetRedPart(hexa),
- GetGreenPart(hexa),
- GetBluePart(hexa),
- )
-
-/proc/MinutesToTicks(var/minutes as num)
- return minutes * 60 * 10
-
-/proc/SecondsToTicks(var/seconds)
- return seconds * 10
-
-proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
- var/roletext = be_special_type ? get_roletext(be_special_type) : null
- var/list/mob/dead/observer/candidates = list()
- var/time_passed = world.time
- if(!Question)
- Question = "Would you like to be a special role?"
-
- for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
- if(!G.key || !G.client)
- continue
- if(be_special_type)
- if(!(be_special_type in G.client.prefs.be_special))
- continue
- if(antag_age_check)
- if(!player_old_enough_antag(G.client, be_special_type))
- continue
- if(roletext)
- if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate"))
- continue
- if(config.use_exp_restrictions && min_hours)
- if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
- continue
- if(check_antaghud && cannotPossess(G))
- continue
- spawn(0)
- G << 'sound/misc/notice2.ogg'//Alerting them to their consideration
- if(flashwindow)
- window_flash(G.client)
- switch(alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round"))
- if("Yes")
- to_chat(G, "Choice registered: Yes.")
- if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed.
- to_chat(G, "Sorry, you were too late for the consideration!")
- G << 'sound/machines/buzz-sigh.ogg'
- return
- candidates += G
- if("No")
- to_chat(G, "Choice registered: No.")
- return
- if("Not This Round")
- to_chat(G, "Choice registered: No.")
- to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.")
- G.client.prefs.be_special -= be_special_type
- return
- else
- return
- sleep(poll_time)
-
- //Check all our candidates, to make sure they didn't log off during the 30 second wait period.
- for(var/mob/dead/observer/G in candidates)
- if(!G.key || !G.client)
- candidates.Remove(G)
-
- return candidates
-
-/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
- var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud)
- var/list/selected_ghosts = list()
- if(!willing_ghosts.len)
- return selected_ghosts
-
- var/list/candidate_ghosts = willing_ghosts.Copy()
-
- to_chat(adminusr, "Candidate Ghosts:");
- for(var/mob/dead/observer/G in candidate_ghosts)
- if(G.key && G.client)
- to_chat(adminusr, "- [G] ([G.key])");
- else
- candidate_ghosts -= G
-
- for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--)
- var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts
- candidate_ghosts -= this_ghost
- selected_ghosts += this_ghost
- return selected_ghosts
-
-/proc/window_flash(client/C)
- if(ismob(C))
- var/mob/M = C
- if(M.client)
- C = M.client
- if(!C || !C.prefs.windowflashing)
- return
- winset(C, "mainwindow", "flash=5")
+/proc/get_area(atom/A)
+ if(isarea(A))
+ return A
+ var/turf/T = get_turf(A)
+ return T ? T.loc : null
+
+/proc/get_area_name(N) //get area by its name
+ for(var/area/A in world)
+ if(A.name == N)
+ return A
+ return 0
+
+/proc/get_location_name(atom/X, format_text = FALSE)
+ var/area/A = isarea(X) ? X : get_area(X)
+ if(!A)
+ return null
+ return format_text ? format_text(A.name) : A.name
+
+/proc/get_areas_in_range(dist=0, atom/center=usr)
+ if(!dist)
+ var/turf/T = get_turf(center)
+ return T ? list(T.loc) : list()
+ if(!center)
+ return list()
+
+ var/list/turfs = RANGE_TURFS(dist, center)
+ var/list/areas = list()
+ for(var/V in turfs)
+ var/turf/T = V
+ areas |= T.loc
+ return areas
+
+// Like view but bypasses luminosity check
+
+/proc/hear(var/range, var/atom/source)
+ var/lum = source.luminosity
+ source.luminosity = 6
+
+ var/list/heard = view(range, source)
+ source.luminosity = lum
+
+ return heard
+
+/proc/circlerange(center=usr,radius=3)
+
+ var/turf/centerturf = get_turf(center)
+ var/list/turfs = new/list()
+ var/rsq = radius * (radius+0.5)
+
+ for(var/atom/T in range(radius, centerturf))
+ var/dx = T.x - centerturf.x
+ var/dy = T.y - centerturf.y
+ if(dx*dx + dy*dy <= rsq)
+ turfs += T
+
+ //turfs += centerturf
+ return turfs
+
+/proc/circleview(center=usr,radius=3)
+
+ var/turf/centerturf = get_turf(center)
+ var/list/atoms = new/list()
+ var/rsq = radius * (radius+0.5)
+
+ for(var/atom/A in view(radius, centerturf))
+ var/dx = A.x - centerturf.x
+ var/dy = A.y - centerturf.y
+ if(dx*dx + dy*dy <= rsq)
+ atoms += A
+
+ //turfs += centerturf
+ return atoms
+
+/proc/ff_cansee(atom/A, atom/B)
+ var/AT = get_turf(A)
+ var/BT = get_turf(B)
+ if(AT == BT)
+ return 1
+ var/list/line = getline(A, B)
+ for(var/turf/T in line)
+ if(T == AT || T == BT)
+ break
+ if(T.density)
+ return FALSE
+ return TRUE
+
+/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj)
+ var/dx = Loc1.x - Loc2.x
+ var/dy = Loc1.y - Loc2.y
+
+ var/dist = sqrt(dx**2 + dy**2)
+
+ return dist
+
+/proc/circlerangeturfs(center=usr,radius=3)
+
+ var/turf/centerturf = get_turf(center)
+ var/list/turfs = new/list()
+ var/rsq = radius * (radius+0.5)
+
+ for(var/turf/T in range(radius, centerturf))
+ var/dx = T.x - centerturf.x
+ var/dy = T.y - centerturf.y
+ if(dx*dx + dy*dy <= rsq)
+ turfs += T
+ return turfs
+
+/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()?
+
+ var/turf/centerturf = get_turf(center)
+ var/list/turfs = new/list()
+ var/rsq = radius * (radius+0.5)
+
+ for(var/turf/T in view(radius, centerturf))
+ var/dx = T.x - centerturf.x
+ var/dy = T.y - centerturf.y
+ if(dx*dx + dy*dy <= rsq)
+ turfs += T
+ return turfs
+
+
+
+//var/debug_mob = 0
+
+// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents.
+// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects,
+// being unable to hear people due to being in a box within a bag.
+
+/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1)
+
+ //debug_mob += O.contents.len
+ if(!recursion_limit)
+ return L
+ for(var/atom/A in O.contents)
+
+ if(ismob(A))
+ var/mob/M = A
+ if(client_check && !M.client)
+ L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
+ continue
+ if(sight_check && !isInSight(A, O))
+ continue
+ L |= M
+ //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
+
+ else if(include_radio && istype(A, /obj/item/radio))
+ if(sight_check && !isInSight(A, O))
+ continue
+ L |= A
+
+ if(isobj(A) || ismob(A))
+ L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
+ return L
+
+// The old system would loop through lists for a total of 5000 per function call, in an empty server.
+// This new system will loop at around 1000 in an empty server.
+
+/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE)
+ // Returns a list of mobs in range of R from source. Used in radio and say code.
+
+ var/turf/T = get_turf(source)
+ var/list/hear = list()
+
+ if(!T)
+ return hear
+
+ var/list/range = hear(R, T)
+
+ for(var/atom/A in range)
+ if(ismob(A))
+ var/mob/M = A
+ if(M.client || include_clientless)
+ hear += M
+ //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
+ else if(istype(A, /obj/item/radio))
+ hear += A
+
+ if(isobj(A) || ismob(A))
+ hear |= recursive_mob_check(A, hear, 3, 1, 0, 1)
+
+ return hear
+
+
+/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios)
+
+ set background = 1
+
+ . = list()
+ // Returns a list of mobs who can hear any of the radios given in @radios
+ var/list/speaker_coverage = list()
+ for(var/obj/item/radio/R in radios)
+ if(R)
+ //Cyborg checks. Receiving message uses a bit of cyborg's charge.
+ var/obj/item/radio/borg/BR = R
+ if(istype(BR) && BR.myborg)
+ var/mob/living/silicon/robot/borg = BR.myborg
+ var/datum/robot_component/CO = borg.get_component("radio")
+ if(!CO)
+ continue //No radio component (Shouldn't happen)
+ if(!borg.is_component_functioning("radio"))
+ continue //No power.
+
+ var/turf/speaker = get_turf(R)
+ if(speaker)
+ for(var/turf/T in hear(R.canhear_range,speaker))
+ speaker_coverage[T] = T
+
+
+ // Try to find all the players who can hear the message
+ for(var/A in GLOB.player_list + GLOB.hear_radio_list)
+ var/mob/M = A
+ if(M)
+ var/turf/ear = get_turf(M)
+ if(ear)
+ // Ghostship is magic: Ghosts can hear radio chatter from anywhere
+ if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO)))
+ . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down.
+ return .
+
+/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5)
+ var/turf/T
+ if(X1==X2)
+ if(Y1==Y2)
+ return 1 //Light cannot be blocked on same tile
+ else
+ var/s = SIMPLE_SIGN(Y2-Y1)
+ Y1+=s
+ while(Y1!=Y2)
+ T=locate(X1,Y1,Z)
+ if(T.opacity)
+ return 0
+ Y1+=s
+ else
+ var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1))
+ var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles
+ var/signX = SIMPLE_SIGN(X2-X1)
+ var/signY = SIMPLE_SIGN(Y2-Y1)
+ if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie)
+ if(dy > 0)
+ return get_step(start, SOUTH)
+ else
+ return get_step(start, NORTH)
+ else
+ if(dx > 0)
+ return get_step(start, WEST)
+ else
+ return get_step(start, EAST)
+
+/proc/try_move_adjacent(atom/movable/AM)
+ var/turf/T = get_turf(AM)
+ for(var/direction in cardinal)
+ if(AM.Move(get_step(T, direction)))
+ break
+
+/proc/get_mob_by_key(var/key)
+ for(var/mob/M in GLOB.mob_list)
+ if(M.ckey == lowertext(key))
+ return M
+ return null
+
+/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0)
+ var/roletext = get_roletext(be_special_type)
+ var/list/candidates = list()
+ // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000))
+ while(!candidates.len && afk_bracket < 6000)
+ for(var/mob/dead/observer/G in GLOB.player_list)
+ if(G.client != null)
+ if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
+ if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special))
+ if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate")))
+ if(override_age || player_old_enough_antag(G.client,be_special_type))
+ candidates += G.client
+ afk_bracket += 600 // Add a minute to the bracket, for every attempt
+
+ return candidates
+
+/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0)
+ var/roletext = get_roletext(be_special_type)
+ var/list/candidates = list()
+ // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000))
+ while(!candidates.len && afk_bracket < 6000)
+ for(var/mob/dead/observer/G in GLOB.player_list)
+ if(G.client != null)
+ if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
+ if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special))
+ if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate")))
+ if(override_age || player_old_enough_antag(G.client,be_special_type))
+ candidates += G
+ afk_bracket += 600 // Add a minute to the bracket, for every attempt
+
+ return candidates
+
+/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
+ if(!isobj(O)) O = new /obj/screen/text()
+ O.maptext = maptext
+ O.maptext_height = maptext_height
+ O.maptext_width = maptext_width
+ O.screen_loc = screen_loc
+ return O
+
+/proc/Show2Group4Delay(obj/O, list/group, delay=0)
+ if(!isobj(O)) return
+ if(!group) group = GLOB.clients
+ for(var/client/C in group)
+ C.screen += O
+ if(delay)
+ spawn(delay)
+ for(var/client/C in group)
+ C.screen -= O
+
+/proc/flick_overlay(image/I, list/show_to, duration)
+ for(var/client/C in show_to)
+ C.images += I
+ spawn(duration)
+ for(var/client/C in show_to)
+ C.images -= I
+
+/proc/get_active_player_count()
+ // Get active players who are playing in the round
+ var/active_players = 0
+ for(var/i = 1; i <= GLOB.player_list.len; i++)
+ var/mob/M = GLOB.player_list[i]
+ if(M && M.client)
+ if(istype(M, /mob/new_player)) // exclude people in the lobby
+ continue
+ else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
+ var/mob/dead/observer/O = M
+ if(O.started_as_observer) // Exclude people who started as observers
+ continue
+ active_players++
+ return active_players
+
+/datum/projectile_data
+ var/src_x
+ var/src_y
+ var/time
+ var/distance
+ var/power_x
+ var/power_y
+ var/dest_x
+ var/dest_y
+
+/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \
+ var/power_x, var/power_y, var/dest_x, var/dest_y)
+ src.src_x = src_x
+ src.src_y = src_y
+ src.time = time
+ src.distance = distance
+ src.power_x = power_x
+ src.power_y = power_y
+ src.dest_x = dest_x
+ src.dest_y = dest_y
+
+/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power)
+
+ // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle],
+ // rotated at [rotation] and with the power of [power]
+ // Thanks to VistaPOWA for this function
+
+ var/power_x = power * cos(angle)
+ var/power_y = power * sin(angle)
+ var/time = 2* power_y / 10 //10 = g
+
+ var/distance = time * power_x
+
+ var/dest_x = src_x + distance*sin(rotation);
+ var/dest_y = src_y + distance*cos(rotation);
+
+ return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
+
+
+/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list)
+ var/list/mobs_found[0]
+ var/area/our_area = get_area(the_area)
+ for(var/mob/M in moblist)
+ if(client_needed && !M.client)
+ continue
+ if(our_area != get_area(M))
+ continue
+ mobs_found += M
+ return mobs_found
+
+/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon)
+ var/area/our_area = get_area(the_area)
+ for(var/C in GLOB.living_mob_list)
+ if(!istype(C, check_type))
+ continue
+ if(C == must_be_alone)
+ continue
+ if(our_area == get_area(C))
+ return 0
+ return 1
+
+
+/proc/GetRedPart(const/hexa)
+ return hex2num(copytext(hexa, 2, 4))
+
+/proc/GetGreenPart(const/hexa)
+ return hex2num(copytext(hexa, 4, 6))
+
+/proc/GetBluePart(const/hexa)
+ return hex2num(copytext(hexa, 6, 8))
+
+/proc/lavaland_equipment_pressure_check(turf/T)
+ . = FALSE
+ if(!istype(T))
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+ if(!istype(environment))
+ return
+ var/pressure = environment.return_pressure()
+ if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
+ . = TRUE
+
+/proc/GetHexColors(const/hexa)
+ return list(
+ GetRedPart(hexa),
+ GetGreenPart(hexa),
+ GetBluePart(hexa),
+ )
+
+/proc/MinutesToTicks(var/minutes as num)
+ return minutes * 60 * 10
+
+/proc/SecondsToTicks(var/seconds)
+ return seconds * 10
+
+proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
+ var/roletext = be_special_type ? get_roletext(be_special_type) : null
+ var/list/mob/dead/observer/candidates = list()
+ var/time_passed = world.time
+ if(!Question)
+ Question = "Would you like to be a special role?"
+
+ for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
+ if(!G.key || !G.client)
+ continue
+ if(be_special_type)
+ if(!(be_special_type in G.client.prefs.be_special))
+ continue
+ if(antag_age_check)
+ if(!player_old_enough_antag(G.client, be_special_type))
+ continue
+ if(roletext)
+ if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate"))
+ continue
+ if(config.use_exp_restrictions && min_hours)
+ if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
+ continue
+ if(check_antaghud && cannotPossess(G))
+ continue
+ spawn(0)
+ G << 'sound/misc/notice2.ogg'//Alerting them to their consideration
+ if(flashwindow)
+ window_flash(G.client)
+ var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round")
+ if(!G?.client)
+ return
+ switch(ans)
+ if("Yes")
+ to_chat(G, "Choice registered: Yes.")
+ if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed.
+ to_chat(G, "Sorry, you were too late for the consideration!")
+ G << 'sound/machines/buzz-sigh.ogg'
+ return
+ candidates += G
+ if("No")
+ to_chat(G, "Choice registered: No.")
+ return
+ if("Not This Round")
+ to_chat(G, "Choice registered: No.")
+ to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.")
+ G.client.prefs.be_special -= be_special_type
+ return
+ else
+ return
+ sleep(poll_time)
+
+ //Check all our candidates, to make sure they didn't log off during the 30 second wait period.
+ for(var/mob/dead/observer/G in candidates)
+ if(!G.key || !G.client)
+ candidates.Remove(G)
+
+ return candidates
+
+/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
+ var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud)
+ var/list/selected_ghosts = list()
+ if(!willing_ghosts.len)
+ return selected_ghosts
+
+ var/list/candidate_ghosts = willing_ghosts.Copy()
+
+ to_chat(adminusr, "Candidate Ghosts:");
+ for(var/mob/dead/observer/G in candidate_ghosts)
+ if(G.key && G.client)
+ to_chat(adminusr, "- [G] ([G.key])");
+ else
+ candidate_ghosts -= G
+
+ for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--)
+ var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts
+ candidate_ghosts -= this_ghost
+ selected_ghosts += this_ghost
+ return selected_ghosts
+
+/proc/window_flash(client/C)
+ if(ismob(C))
+ var/mob/M = C
+ if(M.client)
+ C = M.client
+ if(!C || !C.prefs.windowflashing)
+ return
+ winset(C, "mainwindow", "flash=5")
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 2a23cc8baa0..0002bc0d488 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -1,90 +1,90 @@
-
-//////////////////////////
-/////Initial Building/////
-//////////////////////////
-
-/proc/makeDatumRefLists()
- //markings
- init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list)
- //head accessory
- init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list)
- //hair
- init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list)
- //facial hair
- init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list)
- //underwear
- init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
- //undershirt
- init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f)
- //socks
- init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f)
- //alt heads
- init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list)
-
- init_subtypes(/datum/surgery_step, GLOB.surgery_steps)
-
- for(var/path in (subtypesof(/datum/surgery)))
- GLOB.surgeries_list += new path()
-
- init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title")
- init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name")
- init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name")
-
- for(var/language_name in GLOB.all_languages)
- var/datum/language/L = GLOB.all_languages[language_name]
- if(!(L.flags & NONGLOBAL))
- GLOB.language_keys[":[lowertext(L.key)]"] = L
- GLOB.language_keys[".[lowertext(L.key)]"] = L
- GLOB.language_keys["#[lowertext(L.key)]"] = L
-
- var/rkey = 0
- for(var/spath in subtypesof(/datum/species))
- var/datum/species/S = new spath()
- S.race_key = ++rkey //Used in mob icon caching.
- GLOB.all_species[S.name] = S
-
- if(IS_WHITELISTED in S.species_traits)
- GLOB.whitelisted_species += S.name
-
- init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
-
- //Pipe list building
- init_subtypes(/datum/pipes, GLOB.construction_pipe_list)
- for(var/D in GLOB.construction_pipe_list)
- var/datum/pipes/P = D
- if(P.rpd_dispensable)
- GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy))
- return 1
-
-/* // Uncomment to debug chemical reaction list.
-/client/verb/debug_chemical_list()
-
- for(var/reaction in GLOB.chemical_reactions_list)
- . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n"
- if(islist(GLOB.chemical_reactions_list[reaction]))
- var/list/L = GLOB.chemical_reactions_list[reaction]
- for(var/t in L)
- . += " has: [t]\n"
- to_chat(world, .)
-*/
-
-
-//creates every subtype of prototype (excluding prototype) and adds it to list L.
-//if no list/L is provided, one is created.
-/proc/init_subtypes(prototype, list/L)
- if(!istype(L)) L = list()
- for(var/path in subtypesof(prototype))
- L += new path()
- return L
-
-/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar)
- if(!istype(L)) L = list()
- for(var/path in subtypesof(prototype) - pexempt)
- var/datum/D = new path()
- if(istype(D))
- var/assoc
- if(D.vars["[assocvar]"]) //has the var
- assoc = D.vars["[assocvar]"] //access value of var
- if(assoc) //value gotten
- L["[assoc]"] = D //put in association
- return L
+
+//////////////////////////
+/////Initial Building/////
+//////////////////////////
+
+/proc/makeDatumRefLists()
+ //markings
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list)
+ //head accessory
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list)
+ //hair
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list)
+ //facial hair
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list)
+ //underwear
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
+ //undershirt
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f)
+ //socks
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f)
+ //alt heads
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list)
+
+ init_subtypes(/datum/surgery_step, GLOB.surgery_steps)
+
+ for(var/path in (subtypesof(/datum/surgery)))
+ GLOB.surgeries_list += new path()
+
+ init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title")
+ init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name")
+ init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name")
+
+ for(var/language_name in GLOB.all_languages)
+ var/datum/language/L = GLOB.all_languages[language_name]
+ if(!(L.flags & NONGLOBAL))
+ GLOB.language_keys[":[lowertext(L.key)]"] = L
+ GLOB.language_keys[".[lowertext(L.key)]"] = L
+ GLOB.language_keys["#[lowertext(L.key)]"] = L
+
+ var/rkey = 0
+ for(var/spath in subtypesof(/datum/species))
+ var/datum/species/S = new spath()
+ S.race_key = ++rkey //Used in mob icon caching.
+ GLOB.all_species[S.name] = S
+
+ if(IS_WHITELISTED in S.species_traits)
+ GLOB.whitelisted_species += S.name
+
+ init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
+
+ //Pipe list building
+ init_subtypes(/datum/pipes, GLOB.construction_pipe_list)
+ for(var/D in GLOB.construction_pipe_list)
+ var/datum/pipes/P = D
+ if(P.rpd_dispensable)
+ GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy))
+ return 1
+
+/* // Uncomment to debug chemical reaction list.
+/client/verb/debug_chemical_list()
+
+ for(var/reaction in GLOB.chemical_reactions_list)
+ . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n"
+ if(islist(GLOB.chemical_reactions_list[reaction]))
+ var/list/L = GLOB.chemical_reactions_list[reaction]
+ for(var/t in L)
+ . += " has: [t]\n"
+ to_chat(world, .)
+*/
+
+
+//creates every subtype of prototype (excluding prototype) and adds it to list L.
+//if no list/L is provided, one is created.
+/proc/init_subtypes(prototype, list/L)
+ if(!istype(L)) L = list()
+ for(var/path in subtypesof(prototype))
+ L += new path()
+ return L
+
+/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar)
+ if(!istype(L)) L = list()
+ for(var/path in subtypesof(prototype) - pexempt)
+ var/datum/D = new path()
+ if(istype(D))
+ var/assoc
+ if(D.vars["[assocvar]"]) //has the var
+ assoc = D.vars["[assocvar]"] //access value of var
+ if(assoc) //value gotten
+ L["[assoc]"] = D //put in association
+ return L
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 0b37872decd..a434dd75577 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1,963 +1,963 @@
-/*
-IconProcs README
-
-A BYOND library for manipulating icons and colors
-
-by Lummox JR
-
-version 1.0
-
-The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation
-routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners.
-
-CHANGING ICONS
-
-Several new procs have been added to the /icon datum to simplify working with icons. To use them,
-remember you first need to setup an /icon var like so:
-
-var/icon/my_icon = new('iconfile.dmi')
-
-icon/ChangeOpacity(amount = 1)
- A very common operation in DM is to try to make an icon more or less transparent. Making an icon more
- transparent is usually much easier than making it less so, however. This proc basically is a frontend
- for MapColors() which can change opacity any way you like, in much the same way that SetIntensity()
- can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half.
- If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque.
-icon/GrayScale()
- Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact.
-icon/ColorTone(tone)
- Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an
- RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect.
- See also the global ColorTone() proc.
-icon/MinColors(icon)
- The icon is blended with a second icon where the minimum of each RGB pixel is the result.
- Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon.
-icon/MaxColors(icon)
- The icon is blended with a second icon where the maximum of each RGB pixel is the result.
- Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon.
-icon/Opaque(background = "#000000")
- All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify.
-icon/BecomeAlphaMask()
- You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc.
- The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white.
-icon/AddAlphaMask(mask)
- The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque,
- the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent.
- Where the mask is translucent, the current icon becomes more transparent.
-icon/UseAlphaMask(mask, mode)
- Sometimes you may want to take the alpha values from one icon and use them on a different icon.
- This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change
- so it has the same colors as before but uses the mask for opacity.
-
-COLOR MANAGEMENT AND HSV
-
-RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value.
-
- * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to
- cyan to blue to magenta and back to red.
- * The saturation of a color is how much color is in it. A color with low saturation will be more gray,
- and with no saturation at all it is a shade of gray.
- * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark,
- and no value at all is black.
-
-Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three
-hex digits because it ranges from 0 to 0x5FF.
-
- * 0 to 0xFF - red to yellow
- * 0x100 to 0x1FF - yellow to green
- * 0x200 to 0x2FF - green to cyan
- * 0x300 to 0x3FF - cyan to blue
- * 0x400 to 0x4FF - blue to magenta
- * 0x500 to 0x5FF - magenta to red
-
-Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible),
-value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff".
-
-More than one HSV color can match the same RGB color.
-
-Here are some procs you can use for color management:
-
-ReadRGB(rgb)
- Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used
- that includes alpha, the list will have a fourth item for the alpha value.
-hsv(hue, sat, val, apha)
- Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa"
- format. Alpha is not included in the result if null.
-ReadHSV(rgb)
- Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that
- includes alpha, the list will have a fourth item for the alpha value.
-RGBtoHSV(rgb)
- Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff".
-HSVtoRGB(hsv)
- Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa".
-BlendRGB(rgb1, rgb2, amount)
- Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result;
- if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
- The returned value is an RGB or RGBA color.
-BlendHSV(hsv1, hsv2, amount)
- Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB
- blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1,
- the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
- The returned value is an HSV or HSVA color.
-BlendRGBasHSV(rgb1, rgb2, amount)
- Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form.
-HueToAngle(hue)
- Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue.
-AngleToHue(hue)
- Converts an angle to a hue in the valid range.
-RotateHue(hsv, angle)
- Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360.
- (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value
- as the original, but a different hue.
-GrayScale(rgb)
- Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string.
-ColorTone(rgb, tone)
- Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of
- using strict shades of gray. The tone value is an RGB color; any alpha value is ignored.
-*/
-
-/*
-Get Flat Icon DEMO by DarkCampainger
-
-This is a test for the get flat icon proc, modified approprietly for icons and their states.
-Probably not a good idea to run this unless you want to see how the proc works in detail.
-mob
- icon = 'old_or_unused.dmi'
- icon_state = "green"
-
- Login()
- // Testing image underlays
- underlays += image(icon='old_or_unused.dmi',icon_state="red")
- underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32)
- underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32)
-
- // Testing image overlays
- overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32)
- overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32)
- overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32)
-
- // Testing icon file overlays (defaults to mob's state)
- overlays += '_flat_demoIcons2.dmi'
-
- // Testing icon_state overlays (defaults to mob's icon)
- overlays += "white"
-
- // Testing dynamic icon overlays
- var/icon/I = icon('old_or_unused.dmi', icon_state="aqua")
- I.Shift(NORTH,16,1)
- overlays+=I
-
- // Testing dynamic image overlays
- I=image(icon=I,pixel_x = -32, pixel_y = 32)
- overlays+=I
-
- // Testing object types (and layers)
- overlays+=/obj/effect/overlayTest
-
- loc = locate (10,10,1)
- verb
- Browse_Icon()
- set name = "1. Browse Icon"
- // Give it a name for the cache
- var/iconName = "[ckey(src.name)]_flattened.dmi"
- // Send the icon to src's local cache
- src<