diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index f3a8cee18dd9..e1728a2f5c2d 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -56,6 +56,8 @@ actual development.
* While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality *before* you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes.
+* By ticking or leaving ticked the option "Allow edits and access to secrets by maintainers", either when making a PR or at any time thereafter, you give permission for repository maintainers to push changes to your branch without explicit permission. Repository maintainers will avoid doing this unless necessary, and generally should only use it to apply a merge upstream/master, rebuild TGUI, deconflict maps, or other minor changes required shortly before a PR is to be merged. More extensive changes such as force-pushes to your branch require explicit permission from the PR author each time such a change needs to be made.
+
#### Using The Changelog
* The tags able to be used in the changelog are: `add/soundadd/imageadd`, `del/sounddel/imagedel`, `tweak`, `fix`, `wip`, `spellcheck`, and `experiment`.
@@ -547,22 +549,24 @@ in the SQL/updates folder.
* Failure to run Map Merge on a map after editing greatly increases the risk of the map's key dictionary becoming corrupted by future edits after running map merge. Resolving the corruption issue involves rebuilding the map's key dictionary;
* StrongDMM
- * When using StrongDMM, the following options should be enabled to avoid file bloat. They can be found under `File > Preferences` in SDMM2.
- * Sanitize Variables - Removes variables that are declared on the map, but are the same as default. (For example: A standard floor turf that has `dir = 2` declared on the map will have that variable deleted as it is redundant.)
- * Save format - Either `Initial` or `TGM`, never `DM`.
+ * [We strongly encourage use of StrongDMM version 2 or greater, available here.](https://github.com/SpaiR/StrongDMM/releases)
+ * When using StrongDMM, the following options must be enabled. They can be found under `File > Preferences`.
+ * Sanitize Variables - Removes variables that are declared on the map, but are the same as initial. (For example: A standard floor turf that has `dir = 2` declared on the map will have that variable deleted as it is redundant.)
+ * Save format - `TGM`.
+ * Nudge mode - pixel_x/pixel_y
* Variable Editing (Var-edits)
- * While var-editing an item within the editor is perfectly fine, it is preferred that when you are changing the base behavior of an item (how it functions) that you make a new subtype of that item within the code, especially if you plan to use the item in multiple locations on the same map, or across multiple maps. This makes it easier to make corrections as needed to all instances of the item at one time as opposed to having to find each instance of it and change them all individually.
- * Subtypes only intended to be used on away mission or ruin maps should be contained within an .dm file with a name corresponding to that map within `code\modules\awaymissions` or `code\modules\ruins` respectively. This is so in the event that the map is removed, that subtype will be removed at the same time as well to minimize leftover/unused data within the repo.
- * Please attempt to clean out any dirty variables that may be contained within items you alter through var-editing. For example, due to how DM functions, changing the `pixel_x` variable from 23 to 0 will leave a dirty record in the map's code of `pixel_x = 0`. Likewise this can happen when changing an item's icon to something else and then back. This can lead to some issues where an item's icon has changed within the code, but becomes broken on the map due to it still attempting to use the old entry.
- * Areas should not be var-edited on a map to change it's name or attributes. All areas of a single type and it's altered instances are considered the same area within the code, and editing their variables on a map can lead to issues with powernets and event subsystems which are difficult to debug.
+ * While var-editing an item within the editor is fine, it is preferred that when you are changing the base behavior of an item (how it functions) that you make a new subtype of that item within the code, especially if you plan to use the item in multiple locations on the same map, or across multiple maps. This makes it easier to make corrections as needed to all instances of the item at one time, as opposed to having to find each instance of it and change them all individually.
+ * Subtypes only intended to be used on ruin maps should be contained within an .dm file with a name corresponding to that map within `code\modules\ruins`. This is so in the event that the map is removed, that subtype will be removed at the same time as well to minimize leftover/unused data within the repo.
+ * When not using StrongDMM (which handles the following automatically) please attempt to clean out any dirty variables that may be contained within items you alter through var-editing. For example changing the `pixel_x` variable from 23 to 0 will leave a dirty record in the map's code of `pixel_x = 0`.
+ * Areas should **never** be var-edited on a map. All areas of a single type, altered instance or not, are considered the same area within the code, and editing their variables on a map can lead to issues with powernets and event subsystems which are difficult to debug.
* Unless they require custom placement, when placing the following items use the relevant "[direction] bump" instance, as it has predefined pixel offsets and directions that are standardised: APC, Air alarm, Fire alarm, station intercom, newscaster, extinguisher cabient, light switches.
-* If you are making non-minor edits to an area or room, (non-minor being anything more than moving a few objects or fixing small bugs) then you should ensure the entire area/room meets these standards.
+* If you are making non-minor edits to an area or room, (non-minor being anything more than moving a few objects or fixing small bugs) then you should ensure the entire area/room is updated to meet these standards.
* When making a change to an area or room, follow these guidelines:
* Unless absolutely necessary, do not run pipes (including disposals) under wall turfs.
- * NEVER run cables under wall turfs.
+ * **NEVER** run cables under wall turfs.
* Keep floor turf variations to a minimum. Generally, more than 3 floor turf types in one room is bad design.
* Run air pipes together where possible. The first example below is to be avoided, the second is optimal:
@@ -580,21 +584,28 @@ in the SQL/updates folder.
* A good example would be the template [Department name] - [Area], so Brig - Cell 1, or Medbay - Treatment Center. Consistency is key to good camera naming.
* Fire alarms should not be placed next to expected heat sources.
* Use the following "on" subtype of vents and scrubbers as opposed to var-editing: `/obj/machinery/atmospherics/unary/vent_scrubber/on` and `/obj/machinery/atmospherics/unary/vent_pump/on`
- * Head of staff officers should contain a requests console.
- * Firelocks should be used at area boundaries over doors and windows. Firelocks can also be used to break up hallways at reasonable intervals.
- * Double firelocks are to be avoided unless absolutely necessary.
- * Maintenance access doors should not have firelocks placed over them.
+ * Head of staff offices should contain a requests console.
+ * Electrochromic windows (`/obj/structure/window/reinforced/polarized`) and doors/windoors (using the `/obj/effect/mapping_helpers/airlock/polarized` helper) are preferred over shutters as the method of restricting view to a room through windows. Shutters are sill appropriate in industrial/hazardous areas of the station (engine rooms, HoP line, science test chamber, etc.).
+ * Electrochromic window/windoor/door sets require a unique ID var, and a window tint button (`/obj/machinery/button/windowtint`) with a matching ID var. The default `range` of the button is 7 tiles but can be amended with a var edit.
+ * Tiny fans (`/obj/structure/fans/tiny`) can be used to block airflow into problematic areas, but are not a substitute for proper door and firelock combinations. They are useful under blast doors that lead to space when opened.
+ * Firelocks should be used at area boundaries over doors and windoors, but not windows. Firelocks can also be used to break up hallways at reasonable intervals.
+ * Double firelocks are not permitted.
+ * Maintenance access doors should never have firelocks placed over them.
* Windows to secure areas or external areas should be reinforced. Windows in engine areas should be reinforced plasma glass.
* Windows in high security areas, such as the brig, bridge, and head of staff offices, should be electrified by placing a wire node under the window.
* Lights are to be used sparingly, they draw a significant amount of power.
- * Ensure door and windoor access is correctly set, these are handled by the variables `req_access_txt` and `req_one_access_txt`. Public doors should have both of these values as `"0"`. For a list of access values, see [`code\__DEFINES\access.dm`](code/__DEFINES/access.dm).
- * Always use numerical values encased in quotes for these variables. Multiple access values can be defined by separating them with a `;`, for example: `"28;31"` for kitchen AND cargo access.
- * req_access_txt requires ALL LISTED ACCESSES to open the door, while req_one_access_txt lets anyone with ONE OF THE LISTED ACCESSES open the door.
+ * Ensure door and windoor access is correctly set, this is now done by using access helpers.
+ * Multiple accesses can be added to a door by placing multiple access helpers on the same tile. Be sure to pay attention so as to avoid mixing up `all` and `any` subtypes.
+ * Old doors that use var edited access should be updated to use the correct access helper, and the var edit on the door should be cleaned.
+ * See [`code\modules\mapping\access_helpers.dm`](../code/modules/mapping/access_helpers.dm) for a list of all access helpers.
+ * Subtypes of `/obj/effect/mapping_helpers/airlock/access/any` lets anyone with ONE OF THE LISTED ACCESSES open the door.
+ * Subtypes of `/obj/effect/mapping_helpers/airlock/access/all` requires ALL ACCESSES present to open the door.
+
* Departments should be connected to maintenance through a back or side door. This lets players escape and allows antags to break in.
* If this is not possible, departments should have extra entry and exit points.
* Engine areas, or areas with a high probability of receiving explosions, should use reinforced flooring if appropriate.
* External areas, or areas where depressurisation is expected and normal, should use airless turf variants to prevent additional atmospherics load.
- * Edits in mapping tools should generally be possible to replicate in-game. For this reason, avoid stacking multiple structures on the same tile (i.e. placing a light and an APC on the same wall.)
+ * Edits in mapping tools should almost always be possible to replicate in-game. For this reason, avoid stacking multiple structures on the same tile (i.e. placing a light and an APC on the same wall.)
### Other Notes
@@ -619,7 +630,7 @@ Like all languages, Dream Maker has its quirks, some of them are beneficial to u
HOWEVER, if either `some_value` or `i` changes within the body of the for (underneath the `for(...)` header) or if you are looping over a list AND changing the length of the list then you can NOT use this type of for-loop!
-### `for(var/A in list)` VS `for(var/i in 1 to list.len)`
+### `for(var/A in list)` VS `for(var/i in 1 to length(list))`
The former is faster than the latter, as shown by the following profile results: [https://file.house/zy7H.png](https://file.house/zy7H.png)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index f3c555b2e83c..c853b70ade8d 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,35 +1,38 @@
-
-
+
+
-## What Does This PR Do
-
-
-
+## Что этот PR делает
-## Why It's Good For The Game
-
+
+
+
-## Images of changes
-
+## Почему это хорошо для игры
-## Testing
-
+
+
+## Изображения изменений
+
+
+## Тестирование
+
## Changelog
+
:cl:
-add: Added new things
-del: Removed old things
-tweak: Tweaked a few things
-fix: Fixed a few things
-wip: Added a few works in progress
-soundadd: Added a new sound thingy
-sounddel: Removed an old sound thingy
-imageadd: Added some icons and images
-imagedel: Deleted some icons and images
-spellcheck: Fixed a few typos
-experiment: Added an experimental thingy
+add: Что-то добавил
+del: Что-то удалил
+tweak: Поменял что-то по мелочи
+fix: Что-то починил
+wip: Какие-либо наработки в процессе
+soundadd: Добавил новый звук
+sounddel: Удалил старый звук
+imageadd: Добавил новую картинку
+imagedel: Удалил старую картинку
+spellcheck: Исправил опечатку
+experiment: Добавил эксперементальную функцию
/:cl:
-
-
-
+
+
+
diff --git a/.github/workflows/check_changelog.yml b/.github/workflows/check_changelog.yml
new file mode 100644
index 000000000000..f88966e9b9b9
--- /dev/null
+++ b/.github/workflows/check_changelog.yml
@@ -0,0 +1,32 @@
+name: Changelog validation
+
+permissions:
+ contents: read
+ pull-requests: write
+ issues: write
+
+on:
+ pull_request_target:
+ types: [opened, reopened, edited, labeled, unlabeled]
+
+jobs:
+ CheckCL:
+ runs-on: ubuntu-latest
+ if: github.repository == 'ss220club/Paradise-SS220' && github.base_ref == 'master' && github.event.pull_request.draft == false
+ steps:
+ - name: Downloading scripts
+ run: |
+ wget https://raw.githubusercontent.com/ss220club/Paradise-SS220/master/tools/changelog/check_changelog.py
+ wget https://raw.githubusercontent.com/ss220club/Paradise-SS220/master/tools/changelog/tags.yml
+ - name: Installing Python
+ uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1
+ with:
+ python-version: '3.x'
+ - name: Installing deps
+ run: |
+ python -m pip install --upgrade pip
+ pip install ruamel.yaml PyGithub
+ - name: Changelog validation
+ env:
+ BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
+ run: python check_changelog.py
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0bcbe4d51638..ea5caef5a324 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,6 +35,7 @@ jobs:
python tools/ci/check_line_endings.py
python tools/ci/check_file_names.py
python tools/ci/unticked_files.py ${GITHUB_WORKSPACE}
+ python tools/ci/illegal_dme_files.py ${GITHUB_WORKSPACE}
python -m tools.maplint.source --github
~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1
- name: Annotate Lints
@@ -107,7 +108,7 @@ jobs:
sudo apt install libssl1.1:i386
ldd librust_g.so
- name: Start Redis
- uses: supercharge/redis-github-action@1.5.0
+ uses: supercharge/redis-github-action@1.6.0
with:
redis-version: 6
- name: Compile & Run Unit Tests
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 000000000000..fa646aa88560
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,24 @@
+name: Deploy
+
+concurrency:
+ group: deploy
+
+on:
+ workflow_dispatch:
+ push:
+ branches: ['master', 'devtest']
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Trigger update
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.GAME1_HOST }}
+ username: ${{ secrets.GAME1_USERNAME }}
+ key: ${{ secrets.GAME1_SSH_KEY }}
+ script: |
+ cd /opt/ss13/paradise/
+ ./upgrade.sh
diff --git a/.github/workflows/generate_autodoc.yml b/.github/workflows/generate_autodoc.yml
index 7e9fbc03abeb..da435bff4689 100644
--- a/.github/workflows/generate_autodoc.yml
+++ b/.github/workflows/generate_autodoc.yml
@@ -8,7 +8,7 @@ on:
jobs:
generate_docs:
name: 'Generate Documentation'
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-20.04
steps:
- name: 'Update Branch'
uses: actions/checkout@v3
diff --git a/.github/workflows/label_merge_conflicts.yml b/.github/workflows/label_merge_conflicts.yml
index d2a56e2ef3d4..76217480b4fe 100644
--- a/.github/workflows/label_merge_conflicts.yml
+++ b/.github/workflows/label_merge_conflicts.yml
@@ -1,13 +1,19 @@
name: 'Merge Conflict Detection'
+
on:
push:
branches:
- master
+ pull_request_target:
+ types: [ready_for_review, opened, synchronize, reopened]
jobs:
triage:
- runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ runs-on: ubuntu-22.04
steps:
- - uses: mschilde/auto-label-merge-conflicts@master
+ - uses: eps1lon/actions-label-merge-conflict@v2.1.0
with:
- CONFLICT_LABEL_NAME: 'Merge Conflict'
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ dirtyLabel: 'Merge Conflict'
+ repoToken: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 8940b2c85721..a87737d38e28 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,3 +57,4 @@ __pycache__/
dmm-tools.exe
OpenDream
paradise.json
+sound/tts_cache/*
diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 75d81c9eb03c..9147a355fb43 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -77,6 +77,7 @@ CREATE TABLE `characters` (
`hair_gradient_colour` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '#000000',
`hair_gradient_alpha` tinyint(3) UNSIGNED NOT NULL DEFAULT '255',
`custom_emotes` LONGTEXT COLLATE 'utf8mb4_unicode_ci' DEFAULT NULL,
+ `tts_seed` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=125467 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -606,3 +607,51 @@ CREATE TABLE `tickets` (
CONSTRAINT `all_responses` CHECK (json_valid(`all_responses`)),
CONSTRAINT `awho` CHECK (json_valid(`awho`))
) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
+
+--
+-- Table structure for table `json_datum_saves`
+--
+DROP TABLE IF EXISTS `json_datum_saves`;
+CREATE TABLE `json_datum_saves` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `ckey` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci',
+ `slotname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_general_ci',
+ `slotjson` LONGTEXT NOT NULL COLLATE 'utf8mb4_general_ci',
+ `created` DATETIME NOT NULL DEFAULT current_timestamp(),
+ `updated` DATETIME NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+ PRIMARY KEY (`id`) USING BTREE,
+ UNIQUE INDEX `ckey_unique` (`ckey`, `slotname`) USING BTREE,
+ INDEX `ckey` (`ckey`) USING BTREE
+) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB;
+
+--
+-- Table structure for table `ckey_whitelist`
+--
+
+DROP TABLE IF EXISTS `ckey_whitelist`;
+CREATE TABLE `ckey_whitelist`
+(
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `date` DATETIME DEFAULT now() NOT NULL,
+ `ckey` VARCHAR(32) NOT NULL,
+ `adminwho` VARCHAR(32) NOT NULL,
+ `port` INT(5) UNSIGNED NOT NULL,
+ `date_start` DATETIME DEFAULT now() NOT NULL,
+ `date_end` DATETIME NULL,
+ `is_valid` BOOLEAN DEFAULT true NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `admin_wl`
+--
+
+CREATE TABLE `admin_wl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `admin_rank` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Administrator',
+ `level` int(2) NOT NULL DEFAULT '0',
+ `flags` int(16) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ KEY `ckey` (`ckey`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/SQL/updates/49-50.sql b/SQL/updates/49-50.sql
new file mode 100644
index 000000000000..228827bc4ee5
--- /dev/null
+++ b/SQL/updates/49-50.sql
@@ -0,0 +1,13 @@
+# Updating SQL from 49 to 50 -AffectedArc07
+# Add new JSON datum saves table
+CREATE TABLE `json_datum_saves` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `ckey` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci',
+ `slotname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_general_ci',
+ `slotjson` LONGTEXT NOT NULL COLLATE 'utf8mb4_general_ci',
+ `created` DATETIME NOT NULL DEFAULT current_timestamp(),
+ `updated` DATETIME NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+ PRIMARY KEY (`id`) USING BTREE,
+ UNIQUE INDEX `ckey_unique` (`ckey`, `slotname`) USING BTREE,
+ INDEX `ckey` (`ckey`) USING BTREE
+) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB;
diff --git a/SQL/updates220/49-49.220.1.sql b/SQL/updates220/49-49.220.1.sql
new file mode 100644
index 000000000000..af91b999b28a
--- /dev/null
+++ b/SQL/updates220/49-49.220.1.sql
@@ -0,0 +1,4 @@
+# Updating DB from 49 to 49.220.1
+# Adds characters.tts_seed ~furior
+
+ALTER TABLE `characters` ADD `tts_seed` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `custom_emotes`;
diff --git a/SQL/updates220/49.220.1-49.220.2.sql b/SQL/updates220/49.220.1-49.220.2.sql
new file mode 100644
index 000000000000..0e6866daf9d3
--- /dev/null
+++ b/SQL/updates220/49.220.1-49.220.2.sql
@@ -0,0 +1,12 @@
+CREATE TABLE `ckey_whitelist`
+(
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `date` DATETIME DEFAULT now() NOT NULL,
+ `ckey` VARCHAR(32) NOT NULL,
+ `adminwho` VARCHAR(32) NOT NULL,
+ `port` INT(5) UNSIGNED NOT NULL,
+ `date_start` DATETIME DEFAULT now() NOT NULL,
+ `date_end` DATETIME NULL,
+ `is_valid` BOOLEAN DEFAULT true NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/SQL/updates220/49.220.2-49.220.3.sql b/SQL/updates220/49.220.2-49.220.3.sql
new file mode 100644
index 000000000000..036938b993c8
--- /dev/null
+++ b/SQL/updates220/49.220.2-49.220.3.sql
@@ -0,0 +1,9 @@
+CREATE TABLE `admin_wl` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `admin_rank` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Administrator',
+ `level` int(2) NOT NULL DEFAULT '0',
+ `flags` int(16) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`),
+ KEY `ckey` (`ckey`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/SQL/updates220/50.220.3-50.220.4.sql b/SQL/updates220/50.220.3-50.220.4.sql
new file mode 100644
index 000000000000..70dc8d2d51c6
--- /dev/null
+++ b/SQL/updates220/50.220.3-50.220.4.sql
@@ -0,0 +1,9 @@
+CREATE TABLE IF NOT EXISTS `discord_links` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `ckey` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `discord_id` bigint(20) DEFAULT NULL,
+ `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+ `one_time_token` varchar(100) NOT NULL,
+ `valid` tinyint(1) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
diff --git a/_maps/base_map.dm b/_maps/base_map.dm
index 2c44a73bb675..18d8f64065bf 100644
--- a/_maps/base_map.dm
+++ b/_maps/base_map.dm
@@ -1,4 +1,5 @@
-#include "map_files\generic\centcomm.dmm"
+#include "map_files220\generic\centcomm.dmm" // SS220 EDIT - ORIGINAL: #include "map_files\generic\centcomm.dmm"
+#include "map_files220\generic\Admin_Zone.dmm" // SS220 ADDITION
#define CC_TRANSITION_CONFIG DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC))
#ifdef CIMAP
#include "ci_map_testing.dm"
diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm
index ecf68595812f..2a555451def7 100644
--- a/_maps/map_files/Delta/delta.dmm
+++ b/_maps/map_files/Delta/delta.dmm
@@ -287,7 +287,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
"aec" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 8
},
@@ -462,7 +462,7 @@
/turf/simulated/floor/plating,
/area/maintenance/auxsolarstarboard)
"afj" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/northeast,
/obj/item/radio/intercom{
name = "east bump";
@@ -471,7 +471,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
"afk" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 9;
icon_state = "brown"
@@ -860,7 +860,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
"ahV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/mineral/titanium/blue,
/area/shuttle/arrival/station)
"ahW" = (
@@ -894,7 +894,7 @@
/obj/machinery/light{
dir = 1
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
@@ -981,7 +981,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
"ajy" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
@@ -1650,7 +1650,7 @@
},
/area/hallway/primary/central)
"anz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 4
},
@@ -1955,7 +1955,7 @@
},
/area/hallway/secondary/entry)
"aow" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "neutralcorner"
},
@@ -1973,7 +1973,7 @@
},
/area/maintenance/fore2)
"aoA" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "neutralcorner"
@@ -2086,7 +2086,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/security/vacantoffice)
"aoY" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
name = "north bump";
pixel_y = 24
@@ -2151,7 +2151,7 @@
},
/area/security/customs)
"apg" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
name = "north bump";
pixel_y = 24
@@ -2545,7 +2545,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/hallway/secondary/entry)
"aqc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 4
},
@@ -2696,7 +2696,7 @@
/turf/simulated/floor/plasteel/dark,
/area/maintenance/electrical_shop)
"aqw" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/maintenance/electrical_shop)
"aqx" = (
@@ -2714,7 +2714,7 @@
/turf/simulated/floor/wood,
/area/maintenance/electrical_shop)
"aqz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/maintenance/electrical_shop)
"aqA" = (
@@ -2745,7 +2745,7 @@
/turf/simulated/floor/wood,
/area/maintenance/electrical_shop)
"aqD" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 1
},
@@ -2984,7 +2984,7 @@
/turf/simulated/floor/plating,
/area/maintenance/fore2)
"arh" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel/dark,
/area/maintenance/fore2)
@@ -2998,7 +2998,7 @@
},
/area/maintenance/fore2)
"arj" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/poster/contraband/random{
pixel_y = 32
},
@@ -3136,7 +3136,7 @@
/turf/simulated/floor/wood,
/area/security/vacantoffice)
"arF" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/security/vacantoffice)
"arG" = (
@@ -3621,7 +3621,7 @@
},
/area/security/checkpoint2)
"asI" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plating,
/area/maintenance/fore2)
"asJ" = (
@@ -4033,7 +4033,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/hallway/secondary/entry)
"atD" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster{
dir = 1;
name = "south bump";
@@ -4176,7 +4176,7 @@
/turf/simulated/floor/plasteel/dark,
/area/maintenance/fore2)
"atS" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/maintenance/fore2)
"atU" = (
@@ -4352,7 +4352,7 @@
/turf/simulated/floor/wood,
/area/maintenance/electrical_shop)
"auo" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/power/apc{
name = "south bump";
pixel_y = -24
@@ -4419,7 +4419,7 @@
},
/area/maintenance/fore2)
"auw" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 8
},
@@ -4612,7 +4612,7 @@
},
/area/hallway/secondary/entry)
"avc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "neutralcorner"
@@ -4660,7 +4660,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/fore)
"avg" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "neutralcorner"
@@ -6054,7 +6054,7 @@
},
/area/engine/controlroom)
"axW" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/nosmoking_2{
pixel_x = 32
},
@@ -6289,7 +6289,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/fore2)
"ayF" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/turf/simulated/floor/plasteel,
/area/engine/controlroom)
@@ -7702,7 +7702,7 @@
/turf/space,
/area/space/nearstation)
"aBW" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 1;
d2 = 2;
@@ -8212,7 +8212,7 @@
/turf/simulated/floor/plasteel,
/area/hydroponics/abandoned_garden)
"aDl" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 1
},
@@ -8248,7 +8248,7 @@
/turf/simulated/floor/plasteel,
/area/hydroponics/abandoned_garden)
"aDq" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/status_display{
pixel_y = 32
},
@@ -9786,7 +9786,7 @@
/turf/simulated/floor/plasteel,
/area/engine/controlroom)
"aHd" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel,
/area/hydroponics/abandoned_garden)
@@ -10468,7 +10468,7 @@
/turf/simulated/floor/plasteel/dark,
/area/maintenance/incinerator)
"aIB" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/southwest,
/turf/simulated/floor/plasteel,
/area/engine/controlroom)
@@ -10868,7 +10868,7 @@
},
/area/security/permabrig)
"aJD" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/southeast,
/turf/simulated/floor/plasteel,
/area/engine/controlroom)
@@ -10879,7 +10879,7 @@
/turf/simulated/floor/plating,
/area/quartermaster/storage)
"aJF" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "south bump";
pixel_y = -28
@@ -12289,7 +12289,7 @@
},
/area/quartermaster/storage)
"aNl" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "brown"
@@ -12746,7 +12746,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/maintenance/gambling_den)
"aOq" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/grimy,
/area/maintenance/gambling_den)
"aOr" = (
@@ -12756,7 +12756,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/maintenance/gambling_den)
"aOs" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plating,
/area/maintenance/gambling_den)
"aOt" = (
@@ -12775,7 +12775,7 @@
},
/area/maintenance/gambling_den)
"aOu" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/maintenance/gambling_den)
"aOv" = (
@@ -12836,7 +12836,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/status_display{
pixel_y = 32
},
@@ -13005,7 +13005,7 @@
name = "east bump";
pixel_x = 24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "red"
@@ -13273,7 +13273,7 @@
/turf/simulated/floor/plasteel/dark,
/area/atmos)
"aPI" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/atmos)
"aPJ" = (
@@ -13379,7 +13379,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/grimy,
/area/crew_quarters/theatre)
"aQb" = (
@@ -13863,7 +13863,7 @@
},
/area/atmos)
"aQZ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "vault"
@@ -14184,7 +14184,7 @@
},
/area/quartermaster/sorting)
"aRO" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/alarm{
dir = 8;
name = "east bump";
@@ -14361,7 +14361,7 @@
/area/quartermaster/storage)
"aSr" = (
/obj/machinery/light,
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/ai_status_display{
pixel_y = -32
},
@@ -15586,7 +15586,7 @@
/obj/machinery/light{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/extinguisher_cabinet{
name = "east bump";
pixel_x = 30
@@ -15641,7 +15641,7 @@
},
/area/quartermaster/storage)
"aVb" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 1
},
@@ -16861,7 +16861,7 @@
/area/hallway/primary/fore)
"aXB" = (
/obj/structure/table/wood,
-/obj/item/twohanded/staff/broom,
+/obj/item/staff/broom,
/obj/item/clothing/head/witchwig,
/obj/machinery/newscaster{
dir = 4;
@@ -17643,7 +17643,7 @@
},
/area/atmos)
"aZn" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 1;
name = "custom placement";
@@ -17750,7 +17750,7 @@
/area/crew_quarters/heads/hos)
"aZz" = (
/obj/machinery/light,
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster{
dir = 1;
name = "south bump";
@@ -18413,7 +18413,7 @@
/turf/simulated/floor/plasteel,
/area/crew_quarters/kitchen)
"baR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster{
dir = 4;
name = "west bump";
@@ -18502,7 +18502,7 @@
},
/area/quartermaster/office)
"baY" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "brown"
},
@@ -19828,7 +19828,7 @@
},
/area/hallway/primary/fore)
"bdW" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "brown"
@@ -20005,7 +20005,7 @@
/area/maintenance/fsmaint)
"beA" = (
/obj/effect/decal/warning_stripes/northeastcorner,
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -20422,7 +20422,7 @@
},
/area/quartermaster/miningdock)
"bfC" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel,
/area/quartermaster/miningdock)
@@ -20895,7 +20895,7 @@
/turf/simulated/wall,
/area/crew_quarters/chief)
"bgK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_scrubber/on,
/turf/simulated/floor/plasteel{
dir = 8;
@@ -20919,7 +20919,7 @@
/turf/simulated/floor/plasteel/dark,
/area/turret_protected/ai)
"bgO" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_pump/on,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel{
@@ -21112,7 +21112,7 @@
/area/atmos)
"bhv" = (
/obj/effect/decal/warning_stripes/yellow/hollow,
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel,
/area/hydroponics)
"bhw" = (
@@ -21660,7 +21660,7 @@
d2 = 4;
icon_state = "1-4"
},
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 2;
id = "Cell 2";
name = "Cell 2";
@@ -22226,7 +22226,7 @@
},
/area/quartermaster/miningdock)
"bjK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel{
dir = 10;
@@ -22885,7 +22885,7 @@
/turf/simulated/floor/plasteel/dark,
/area/aisat)
"blw" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light,
/turf/simulated/floor/plasteel{
icon_state = "brown"
@@ -23325,7 +23325,7 @@
/obj/structure/window/reinforced{
dir = 8
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/aisat)
"bmq" = (
@@ -23343,7 +23343,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/aisat)
"bms" = (
@@ -23529,7 +23529,7 @@
/turf/simulated/floor/plasteel,
/area/atmos)
"bmL" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/visible/purple{
dir = 9
},
@@ -24133,7 +24133,7 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 6
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "neutralcorner"
@@ -24429,7 +24429,7 @@
name = "east bump";
pixel_x = 28
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "darkred"
@@ -24968,7 +24968,7 @@
},
/area/hallway/primary/central)
"bpU" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "yellowcorner"
@@ -25550,7 +25550,7 @@
},
/area/security/prison/cell_block)
"brl" = (
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 2;
id = "Cell 3";
name = "Cell 3";
@@ -26740,7 +26740,7 @@
name = "south bump";
pixel_y = -24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 8;
name = "west bump";
@@ -26923,7 +26923,7 @@
/turf/simulated/floor/plasteel,
/area/atmos)
"buA" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small,
/obj/machinery/atmospherics/pipe/simple/visible/yellow{
dir = 4
@@ -27372,7 +27372,7 @@
},
/area/maintenance/starboard2)
"bvC" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/nosmoking_2{
pixel_y = 32
},
@@ -28169,7 +28169,7 @@
},
/area/atmos)
"bxc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "caution"
@@ -28481,7 +28481,7 @@
},
/area/hallway/primary/starboard)
"bxK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_scrubber/on,
/obj/machinery/camera{
c_tag = "Primary Security Hallway North-East";
@@ -28538,7 +28538,7 @@
},
/area/security/checkpoint)
"bxS" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 5;
icon_state = "darkred"
@@ -28623,7 +28623,7 @@
/turf/simulated/floor/greengrid,
/area/turret_protected/ai)
"byg" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/turret_protected/ai)
"byh" = (
@@ -29210,7 +29210,7 @@
},
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 1;
id = "Cell 5";
name = "Cell 5";
@@ -29520,7 +29520,7 @@
/turf/simulated/floor/plasteel,
/area/storage/tech)
"bAi" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel,
/area/storage/tech)
@@ -29601,7 +29601,7 @@
},
/area/engine/hardsuitstorage)
"bAr" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 1
},
@@ -29614,7 +29614,7 @@
},
/area/bridge)
"bAt" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/bridge)
"bAw" = (
@@ -29745,7 +29745,7 @@
icon_state = "4-8"
},
/obj/effect/decal/warning_stripes/yellow,
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -29775,7 +29775,7 @@
d2 = 8;
icon_state = "2-8"
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -29965,7 +29965,7 @@
},
/area/engine/break_room)
"bBF" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/power/apc{
dir = 1;
name = "north bump";
@@ -30370,7 +30370,7 @@
/turf/simulated/floor/plasteel/dark,
/area/bridge)
"bCn" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -30598,7 +30598,7 @@
pixel_y = 24;
req_access_txt = "19"
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 4;
d2 = 8;
@@ -31252,7 +31252,7 @@
},
/area/hallway/primary/central)
"bDE" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/status_display{
pixel_x = -32
},
@@ -31304,7 +31304,7 @@
},
/area/storage/tech)
"bDI" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/ai_status_display{
pixel_x = 32
},
@@ -31542,7 +31542,7 @@
},
/area/bridge)
"bEd" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "darkblue"
@@ -31587,7 +31587,7 @@
/turf/simulated/floor/carpet,
/area/bridge)
"bEj" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "darkblue"
@@ -31795,7 +31795,7 @@
},
/area/engine/gravitygenerator)
"bEH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "whitebluecorner"
@@ -31871,7 +31871,7 @@
},
/area/engine/break_room)
"bET" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 8;
name = "west bump";
@@ -31884,7 +31884,7 @@
},
/area/engine/break_room)
"bEU" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 4;
name = "east bump";
@@ -32135,7 +32135,7 @@
},
/area/storage/primary)
"bFr" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/turf/simulated/floor/plasteel,
/area/storage/primary)
@@ -32348,7 +32348,7 @@
},
/area/bridge)
"bFR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small,
/turf/simulated/floor/plasteel/dark,
/area/bridge)
@@ -33537,7 +33537,7 @@
},
/area/security/brig)
"bHX" = (
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 8;
id = "Cell 1";
name = "Cell 1";
@@ -33594,7 +33594,7 @@
/turf/simulated/floor/plating,
/area/security/warden)
"bIg" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "darkred"
@@ -33682,7 +33682,7 @@
/turf/simulated/floor/plasteel,
/area/engine/gravitygenerator)
"bIq" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/crew_quarters/chief)
"bIr" = (
@@ -33713,7 +33713,7 @@
/turf/simulated/floor/plasteel/dark,
/area/crew_quarters/chief)
"bIv" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
name = "north bump";
pixel_y = 24
@@ -33941,7 +33941,7 @@
/turf/simulated/floor/wood,
/area/bridge/meeting_room)
"bJa" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -34069,7 +34069,7 @@
/turf/simulated/floor/wood,
/area/crew_quarters/captain)
"bJr" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/crew_quarters/captain)
"bJs" = (
@@ -34167,7 +34167,7 @@
},
/area/security/detectives_office)
"bJC" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/power/apc{
dir = 8;
name = "west bump";
@@ -34425,7 +34425,7 @@
/turf/simulated/floor/plasteel/dark,
/area/engine/break_room)
"bKc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 4;
name = "east bump";
@@ -34443,7 +34443,7 @@
},
/area/engine/break_room)
"bKd" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 8
},
@@ -34615,7 +34615,7 @@
/turf/simulated/wall,
/area/engine/break_room)
"bKy" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/ai_status_display{
pixel_x = -32
},
@@ -34633,7 +34633,7 @@
/turf/simulated/floor/plasteel,
/area/storage/primary)
"bKB" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/status_display{
pixel_x = 32
},
@@ -35023,7 +35023,7 @@
/turf/simulated/wall,
/area/security/prison/cell_block)
"bLC" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 8
},
@@ -35076,7 +35076,7 @@
},
/area/security/prison/cell_block)
"bLG" = (
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 2;
id = "Cell 4";
name = "Cell 4";
@@ -35438,7 +35438,7 @@
},
/area/engine/break_room)
"bMw" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 6;
icon_state = "yellow"
@@ -35452,7 +35452,7 @@
name = "east bump";
pixel_x = 28
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 5;
icon_state = "yellow"
@@ -36055,7 +36055,7 @@
/turf/simulated/floor/plasteel/dark,
/area/engine/break_room)
"bNY" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "vault"
@@ -36244,7 +36244,7 @@
/turf/simulated/floor/plasteel/dark,
/area/storage/tech)
"bOr" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 1;
name = "south bump";
@@ -36265,7 +36265,7 @@
/turf/simulated/floor/plasteel,
/area/storage/tech)
"bOt" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light_switch{
dir = 1;
name = "south bump";
@@ -36320,7 +36320,7 @@
/turf/simulated/floor/plasteel,
/area/storage/primary)
"bOz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel,
/area/storage/primary)
"bOA" = (
@@ -36619,7 +36619,7 @@
/turf/simulated/floor/plasteel/dark,
/area/security/detectives_office)
"bPk" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 1;
name = "south bump";
@@ -36821,7 +36821,7 @@
},
/area/turret_protected/aisat)
"bPJ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -36835,7 +36835,7 @@
},
/area/turret_protected/aisat)
"bPN" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "vault"
@@ -37108,7 +37108,7 @@
},
/area/security/brig)
"bQp" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 8
},
@@ -37245,7 +37245,7 @@
/turf/simulated/floor/wood,
/area/bridge/meeting_room)
"bQG" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light,
/turf/simulated/floor/wood,
/area/bridge/meeting_room)
@@ -38459,7 +38459,7 @@
},
/area/hallway/primary/starboard)
"bTm" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/power/apc{
dir = 8;
name = "west bump";
@@ -38981,7 +38981,7 @@
/turf/simulated/floor/plasteel/dark,
/area/engine/break_room)
"bUf" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/alarm{
dir = 8;
name = "east bump";
@@ -39029,7 +39029,7 @@
/turf/simulated/floor/plating,
/area/crew_quarters/chief)
"bUn" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 8;
name = "custom placement";
@@ -39321,14 +39321,14 @@
/turf/simulated/floor/wood,
/area/crew_quarters/heads/hop)
"bUR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/keycard_auth{
pixel_y = 24
},
/turf/simulated/floor/wood,
/area/crew_quarters/heads/hop)
"bUT" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/crew_quarters/heads/hop)
"bUU" = (
@@ -39782,7 +39782,7 @@
},
/area/turret_protected/aisat)
"bVN" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 4;
name = "east bump";
@@ -40122,7 +40122,7 @@
name = "south bump";
pixel_y = -24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 6;
icon_state = "yellow"
@@ -40574,7 +40574,7 @@
/turf/space,
/area/space/nearstation)
"bXH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light,
/turf/simulated/floor/plasteel{
icon_state = "dark"
@@ -40599,7 +40599,7 @@
},
/area/turret_protected/aisat)
"bXK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light,
/obj/machinery/alarm{
dir = 1;
@@ -40658,7 +40658,7 @@
},
/area/turret_protected/aisat)
"bXP" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/extinguisher_cabinet{
name = "east bump";
pixel_x = 30
@@ -40731,7 +40731,7 @@
/turf/simulated/floor/plasteel,
/area/engine/engineering)
"bYe" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "cautioncorner"
@@ -41607,7 +41607,7 @@
/turf/simulated/floor/carpet/black,
/area/crew_quarters/captain/bedroom)
"cat" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster/security_unit{
dir = 8;
name = "east bump";
@@ -41642,7 +41642,7 @@
/turf/simulated/floor/plasteel/dark,
/area/crew_quarters/courtroom)
"cay" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/crew_quarters/courtroom)
"caz" = (
@@ -41658,7 +41658,7 @@
},
/area/hallway/primary/starboard)
"caA" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "neutral"
@@ -41828,7 +41828,7 @@
/turf/simulated/floor/plasteel/dark,
/area/crew_quarters/heads/hop)
"caV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -42016,7 +42016,7 @@
},
/area/engine/engineering)
"cbu" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/camera{
c_tag = "Engine Room North";
network = list("Engineering","SS13")
@@ -42234,7 +42234,7 @@
/turf/simulated/floor/plasteel,
/area/engine/engineering)
"cbS" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 4;
d2 = 8;
@@ -42995,7 +42995,7 @@
/turf/simulated/floor/plating,
/area/maintenance/port)
"cdx" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 8
},
@@ -43015,7 +43015,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/library)
"cdA" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 4
},
@@ -43192,7 +43192,7 @@
name = "south bump";
pixel_y = -24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/camera{
c_tag = "Captain's Quarters";
dir = 1
@@ -43737,7 +43737,7 @@
/turf/simulated/floor/wood,
/area/library)
"cfs" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/library)
"cft" = (
@@ -44047,7 +44047,7 @@
},
/area/magistrateoffice)
"cgk" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "cult"
},
@@ -44675,7 +44675,7 @@
},
/area/tcommsat/chamber)
"chX" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "dark"
},
@@ -46517,7 +46517,7 @@
},
/area/hallway/primary/central)
"cmf" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "west bump";
pixel_x = -28
@@ -46987,7 +46987,7 @@
/turf/simulated/floor/wood,
/area/crew_quarters/heads/hop)
"cnt" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light_switch{
dir = 1;
name = "south bump";
@@ -47098,7 +47098,7 @@
/turf/space,
/area/space/nearstation)
"cnJ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "neutral"
},
@@ -47773,7 +47773,7 @@
/turf/simulated/wall/r_wall,
/area/engine/engineering)
"cpr" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable/yellow{
d1 = 1;
d2 = 4;
@@ -48624,7 +48624,7 @@
/turf/simulated/floor/plasteel,
/area/engine/equipmentstorage)
"crk" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/camera{
c_tag = "Library South";
dir = 1
@@ -48644,7 +48644,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/library)
"crm" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light,
/obj/machinery/light_switch{
dir = 1;
@@ -48657,7 +48657,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/library)
"cro" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/grimy,
/area/library)
"crp" = (
@@ -49127,7 +49127,7 @@
/obj/structure/window/reinforced{
dir = 8
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/aisat)
"csl" = (
@@ -49135,7 +49135,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/aisat)
"csm" = (
@@ -49223,7 +49223,7 @@
/turf/simulated/floor/plating,
/area/engine/engineering)
"csw" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/warning_stripes/yellow/hollow,
/obj/machinery/firealarm{
@@ -49713,7 +49713,7 @@
/turf/simulated/floor/plasteel,
/area/engine/engineering)
"ctH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/library)
"ctI" = (
@@ -49943,7 +49943,7 @@
},
/area/hallway/primary/central/south)
"cuf" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 4;
d2 = 8;
@@ -50073,7 +50073,7 @@
},
/area/hallway/primary/central)
"cuv" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 10
},
@@ -50359,7 +50359,7 @@
},
/area/engine/engineering)
"cuX" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light_switch{
name = "north bump";
pixel_y = 24
@@ -50579,7 +50579,7 @@
/turf/simulated/floor/plasteel/dark,
/area/ai_monitored/storage/eva)
"cvz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -50852,7 +50852,7 @@
/turf/simulated/floor/plasteel/white,
/area/medical/medbay)
"cwq" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel/white,
/area/maintenance/port2)
@@ -50907,7 +50907,7 @@
/turf/simulated/floor/plating,
/area/engine/hardsuitstorage)
"cwy" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/electricshock{
pixel_y = 32
},
@@ -51534,7 +51534,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/port)
"cxV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/extinguisher_cabinet{
name = "west bump";
pixel_x = -30
@@ -51596,7 +51596,7 @@
/turf/simulated/floor/plasteel/dark,
/area/library)
"cyb" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster{
dir = 8;
name = "east bump";
@@ -51819,7 +51819,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/port2)
"cyF" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel,
/area/crew_quarters/locker/locker_toilet)
"cyH" = (
@@ -52086,7 +52086,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/port)
"czo" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "north bump";
pixel_y = 28
@@ -52861,7 +52861,7 @@
/turf/simulated/floor/plasteel/dark,
/area/library)
"cAU" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "south bump";
pixel_y = -28
@@ -52959,7 +52959,7 @@
},
/area/hallway/primary/central)
"cBi" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -53477,7 +53477,7 @@
},
/area/engine/hardsuitstorage)
"cCv" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 1;
d2 = 8;
@@ -53579,7 +53579,7 @@
/turf/simulated/floor/carpet,
/area/assembly/showroom)
"cCN" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 1;
name = "south bump";
@@ -53610,7 +53610,7 @@
/turf/simulated/floor/carpet,
/area/assembly/showroom)
"cCR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light,
/turf/simulated/floor/carpet,
/area/assembly/showroom)
@@ -53908,12 +53908,12 @@
/turf/simulated/floor/plasteel,
/area/assembly/chargebay)
"cDJ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/southeast,
/turf/simulated/floor/plasteel,
/area/engine/engineering)
"cDK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/electricshock{
pixel_y = -32
},
@@ -53957,7 +53957,7 @@
/turf/simulated/floor/plating,
/area/maintenance/port)
"cDQ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plating,
/area/maintenance/port)
"cDR" = (
@@ -54006,7 +54006,7 @@
},
/area/maintenance/port)
"cEb" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "south bump";
pixel_y = -28
@@ -54084,7 +54084,7 @@
/turf/simulated/floor/plasteel/dark,
/area/ai_monitored/storage/eva)
"cEs" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/ai_monitored/storage/eva)
"cEt" = (
@@ -55447,7 +55447,7 @@
},
/area/crew_quarters/fitness)
"cGT" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 5
},
@@ -55858,7 +55858,7 @@
},
/area/crew_quarters/fitness)
"cId" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "neutral"
},
@@ -56532,7 +56532,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/electrical)
"cJW" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/turf/simulated/floor/plasteel,
/area/maintenance/electrical)
@@ -56699,12 +56699,12 @@
},
/area/medical/research)
"cKy" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/disposalpipe/segment,
/turf/simulated/floor/plasteel/dark,
/area/medical/cmo)
"cKz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "bluecorner"
},
@@ -57173,7 +57173,7 @@
},
/area/maintenance/electrical)
"cLF" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plating,
/area/maintenance/electrical)
"cLG" = (
@@ -57365,7 +57365,7 @@
/turf/simulated/floor/plasteel/white,
/area/medical/research)
"cMn" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "whitepurplecorner"
@@ -57401,7 +57401,7 @@
},
/area/medical/reception)
"cMt" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "whitebluecorner"
@@ -57816,7 +57816,7 @@
/turf/simulated/floor/plating,
/area/toxins/xenobiology)
"cND" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "whitepurplecorner"
@@ -58002,7 +58002,7 @@
/turf/simulated/floor/plasteel/white,
/area/medical/reception)
"cNV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
},
@@ -58315,7 +58315,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/electrical)
"cOz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/status_display{
pixel_x = -32
},
@@ -59154,7 +59154,7 @@
},
/area/toxins/xenobiology)
"cQO" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -59223,7 +59223,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/primary/aft)
"cRd" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "whitepurplecorner"
@@ -59243,7 +59243,7 @@
/turf/simulated/floor/plating,
/area/maintenance/starboardsolar)
"cRg" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "whitepurplecorner"
},
@@ -59254,7 +59254,7 @@
},
/area/hallway/primary/aft)
"cRj" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "whitebluecorner"
@@ -59279,7 +59279,7 @@
},
/area/medical/reception)
"cRm" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "whitebluecorner"
},
@@ -59336,7 +59336,7 @@
name = "east bump";
pixel_x = 28
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/medical/psych)
"cRt" = (
@@ -59843,7 +59843,7 @@
},
/area/medical/medbay)
"cSB" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/disposalpipe/segment,
/turf/simulated/floor/plasteel{
dir = 8;
@@ -60289,7 +60289,7 @@
/turf/simulated/floor/plasteel/white,
/area/toxins/xenobiology)
"cTy" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light,
/turf/simulated/floor/plasteel{
icon_state = "whitepurple"
@@ -61152,7 +61152,7 @@
},
/area/toxins/lab)
"cVu" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/status_display{
pixel_y = 32
},
@@ -61345,7 +61345,7 @@
},
/area/medical/exam_room)
"cVV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/defibrillator_mount/loaded{
pixel_y = 30
},
@@ -61800,7 +61800,7 @@
},
/area/toxins/xenobiology)
"cWV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/turf/simulated/floor/plasteel{
icon_state = "whitepurple"
@@ -62076,7 +62076,7 @@
/turf/simulated/floor/plating,
/area/maintenance/starboard)
"cXO" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "neutralcorner"
},
@@ -62781,7 +62781,7 @@
/turf/simulated/floor/plasteel/freezer,
/area/medical/medbay)
"cZM" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/alarm{
dir = 4;
name = "west bump";
@@ -62906,7 +62906,7 @@
},
/area/medical/medbreak)
"cZY" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "whitebluecorner"
@@ -62939,12 +62939,12 @@
},
/area/maintenance/starboard)
"dae" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/cleanable/cobweb,
/turf/simulated/floor/plating,
/area/maintenance/abandonedbar)
"daf" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/maintenance/abandonedbar)
"dag" = (
@@ -63244,7 +63244,7 @@
/turf/simulated/floor/plasteel/white,
/area/toxins/lab)
"daM" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "whitepurplecorner"
},
@@ -63291,7 +63291,7 @@
},
/area/medical/surgery)
"daS" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plating,
/area/medical/surgery)
"daT" = (
@@ -63580,7 +63580,7 @@
/turf/simulated/floor/plasteel/dark,
/area/medical/medbay)
"dbG" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel/dark,
/area/maintenance/abandonedbar)
@@ -63610,7 +63610,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/apmaint)
"dbJ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/poster/contraband/random{
pixel_x = 32
},
@@ -63663,7 +63663,7 @@
},
/area/toxins/lab)
"dbV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/disposalpipe/segment,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel{
@@ -64225,7 +64225,7 @@
/turf/simulated/floor/plasteel/dark,
/area/maintenance/abandonedbar)
"dcY" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plating,
/area/maintenance/port2)
"dcZ" = (
@@ -64241,7 +64241,7 @@
/turf/simulated/floor/plasteel/white,
/area/maintenance/port2)
"ddc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 1
},
@@ -64487,13 +64487,13 @@
/turf/simulated/floor/plasteel/white,
/area/maintenance/port2)
"ddJ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "purplefull"
},
/area/hallway/primary/aft)
"ddK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "orangefull"
},
@@ -65666,7 +65666,7 @@
},
/area/medical/genetics_cloning)
"dgL" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "whitepurplecorner"
@@ -65739,7 +65739,7 @@
},
/area/maintenance/apmaint)
"dgS" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "whitebluefull"
},
@@ -67026,7 +67026,7 @@
/turf/simulated/floor/plasteel/white,
/area/maintenance/port2)
"dky" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel/white,
@@ -67294,7 +67294,7 @@
},
/area/medical/medbay)
"dli" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "whitepurplecorner"
@@ -67509,7 +67509,7 @@
name = "west bump";
pixel_x = -24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable,
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 4
@@ -67690,7 +67690,7 @@
dir = 4;
network = list("Medical","SS13")
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -67837,7 +67837,7 @@
/turf/simulated/floor/wood,
/area/maintenance/abandonedbar)
"dmR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light_switch{
dir = 8;
name = "east bump";
@@ -67913,7 +67913,7 @@
},
/area/maintenance/port)
"dnd" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/camera{
c_tag = "Brig - Hallway South-East";
dir = 4;
@@ -68139,7 +68139,7 @@
/turf/simulated/floor/plasteel,
/area/medical/genetics)
"dnO" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "whiteblue"
@@ -69146,7 +69146,7 @@
/turf/simulated/floor/plating,
/area/crew_quarters/hor)
"dqi" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 8;
name = "west bump";
@@ -69362,7 +69362,7 @@
/turf/simulated/floor/plating,
/area/medical/cmo)
"dqH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/medical/cmo)
"dqI" = (
@@ -69564,7 +69564,7 @@
/turf/simulated/floor/plating,
/area/maintenance/port)
"drp" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/ntrep)
"drs" = (
@@ -69743,7 +69743,7 @@
/turf/simulated/floor/plasteel/white,
/area/medical/genetics)
"drR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "whitepurplecorner"
},
@@ -69860,7 +69860,7 @@
},
/area/medical/cmo)
"dsh" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/power/apc{
dir = 4;
name = "east bump";
@@ -70176,7 +70176,7 @@
/turf/simulated/floor/plasteel,
/area/medical/genetics)
"dsW" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/alarm{
dir = 1;
name = "south bump";
@@ -70259,7 +70259,7 @@
},
/area/medical/medbay)
"dte" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/alarm{
dir = 1;
name = "south bump";
@@ -71061,7 +71061,7 @@
/turf/simulated/floor/plating,
/area/maintenance/port2)
"duR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/poster/random{
pixel_y = -32
},
@@ -71736,7 +71736,7 @@
/turf/simulated/floor/wood,
/area/crew_quarters/theatre)
"dwW" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood{
broken = 1;
icon_state = "wood-broken"
@@ -72159,7 +72159,7 @@
/turf/simulated/floor/plasteel/dark,
/area/medical/medbay)
"dyl" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 8
},
@@ -72599,7 +72599,7 @@
name = "south bump";
pixel_y = -24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/camera{
c_tag = "Morgue West";
dir = 1;
@@ -73021,7 +73021,7 @@
/turf/simulated/floor/plasteel/airless,
/area/toxins/test_area)
"dBH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel,
/area/hallway/primary/aft)
@@ -73318,7 +73318,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/aft)
"dCh" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "whitegreencorner"
@@ -73338,7 +73338,7 @@
},
/area/medical/medbay)
"dCj" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "whitegreencorner"
@@ -73824,11 +73824,11 @@
/turf/simulated/floor/plating,
/area/crew_quarters/theatre)
"dDJ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/crew_quarters/theatre)
"dDK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/alarm{
dir = 1;
name = "south bump";
@@ -74051,7 +74051,7 @@
name = "north bump";
pixel_y = 24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster{
dir = 8;
name = "east bump";
@@ -74060,7 +74060,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/primary/aft)
"dFc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small,
/turf/simulated/floor/plasteel{
dir = 8;
@@ -74339,7 +74339,7 @@
/turf/simulated/floor/plating,
/area/maintenance/starboardsolar)
"dGt" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 4
},
@@ -74349,7 +74349,7 @@
},
/area/medical/virology)
"dGu" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 1;
d2 = 2;
@@ -74367,7 +74367,7 @@
},
/area/medical/virology)
"dGv" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 8
},
@@ -74418,7 +74418,7 @@
},
/area/medical/virology)
"dGA" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 4
},
@@ -74809,7 +74809,7 @@
/obj/machinery/light{
dir = 8
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel,
/area/hallway/primary/aft)
"dHQ" = (
@@ -75280,7 +75280,7 @@
/turf/simulated/floor/plasteel/dark,
/area/chapel/office)
"dJw" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/chapel/office)
"dJx" = (
@@ -75348,7 +75348,7 @@
},
/area/hallway/secondary/exit)
"dJH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel,
/area/hallway/secondary/exit)
@@ -75483,7 +75483,7 @@
/turf/simulated/floor/plasteel/dark,
/area/chapel/office)
"dKe" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 4
},
@@ -75521,7 +75521,7 @@
/turf/simulated/floor/plasteel/dark,
/area/chapel/main)
"dKi" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/dark,
/area/chapel/main)
"dKj" = (
@@ -75535,7 +75535,7 @@
/turf/simulated/floor/plasteel/dark,
/area/chapel/main)
"dKk" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "north bump";
pixel_y = 28
@@ -76226,7 +76226,7 @@
},
/area/chapel/main)
"dMn" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "chapel"
@@ -76558,7 +76558,7 @@
/turf/simulated/floor/grass/no_creep,
/area/hallway/secondary/exit)
"dNa" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
@@ -76833,7 +76833,7 @@
/obj/machinery/light{
dir = 1
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/turf/simulated/floor/plasteel{
dir = 4;
@@ -77087,7 +77087,7 @@
},
/area/hallway/secondary/exit)
"dOv" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 8
},
@@ -77361,7 +77361,7 @@
},
/area/medical/virology)
"dPj" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 4
},
@@ -78065,7 +78065,7 @@
/turf/simulated/floor/plating,
/area/maintenance/apmaint)
"dQP" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -78132,7 +78132,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/chapel/main)
"dRa" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 4
},
@@ -78363,7 +78363,7 @@
/turf/simulated/floor/plasteel/dark,
/area/chapel/main)
"dRA" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light_switch{
dir = 8;
name = "east bump";
@@ -78781,7 +78781,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/chapel/office)
"dSN" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light_switch{
dir = 8;
name = "east bump";
@@ -78954,7 +78954,7 @@
/turf/simulated/floor/plasteel/grimy,
/area/chapel/office)
"dTp" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/camera{
c_tag = "Chaplain's Quarters"
},
@@ -79042,7 +79042,7 @@
},
/area/hallway/secondary/exit)
"dTC" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/obj/effect/decal/warning_stripes/yellow,
@@ -79206,7 +79206,7 @@
/turf/simulated/floor/carpet,
/area/chapel/office)
"dUi" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 1;
d2 = 2;
@@ -79245,7 +79245,7 @@
/turf/simulated/floor/plasteel,
/area/maintenance/apmaint)
"dUm" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 5
},
@@ -79592,7 +79592,7 @@
},
/area/maintenance/portsolar)
"dVo" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 1;
d2 = 2;
@@ -79751,7 +79751,7 @@
/turf/simulated/floor/plasteel/white,
/area/toxins/xenobiology)
"dWm" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/turf/simulated/floor/plasteel/white,
/area/toxins/xenobiology)
@@ -79914,7 +79914,7 @@
},
/area/hallway/primary/central)
"dWS" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/turf/simulated/floor/plasteel{
icon_state = "whitegreen"
@@ -80219,7 +80219,7 @@
},
/area/medical/surgery1)
"dXx" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/camera{
c_tag = "Chapel West";
dir = 4;
@@ -80602,13 +80602,13 @@
/turf/simulated/floor/plasteel/white,
/area/medical/virology)
"dYG" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel/white,
/area/maintenance/port2)
"dYH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/sign/poster/random{
pixel_y = -32
},
@@ -80853,7 +80853,7 @@
/turf/simulated/floor/plating,
/area/crew_quarters/arcade)
"eas" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 4
},
@@ -81141,7 +81141,7 @@
},
/area/crew_quarters/fitness)
"esa" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/power/apc{
name = "south bump";
pixel_y = -24
@@ -81425,7 +81425,7 @@
},
/area/crew_quarters/fitness)
"eLI" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "neutralfull"
},
@@ -82418,7 +82418,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/primary/aft)
"fOy" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/obj/item/radio/intercom{
name = "south bump";
@@ -82896,7 +82896,7 @@
/turf/simulated/floor/plating,
/area/maintenance/apmaint)
"gwZ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "east bump";
pixel_x = 28
@@ -85025,7 +85025,7 @@
/turf/simulated/floor/plasteel,
/area/engine/engineering)
"iUc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "red"
@@ -86257,7 +86257,7 @@
/turf/space,
/area/space)
"koC" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 4
},
@@ -86849,7 +86849,7 @@
/turf/simulated/floor/plating,
/area/maintenance/fsmaint)
"lmR" = (
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 1;
id = "Cell 6";
name = "Cell 6";
@@ -86898,7 +86898,7 @@
/turf/simulated/wall,
/area/maintenance/fsmaint)
"los" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "whitebluecorner"
},
@@ -87200,7 +87200,7 @@
/obj/machinery/light{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 6;
icon_state = "darkred"
@@ -87740,7 +87740,7 @@
/obj/structure/window/reinforced{
dir = 8
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "grimy"
},
@@ -90090,7 +90090,7 @@
name = "west bump";
pixel_x = -24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "barber"
},
@@ -90491,7 +90491,7 @@
/turf/simulated/floor/plasteel,
/area/toxins/storage)
"puH" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/item/radio/intercom{
name = "east bump";
pixel_x = 28
@@ -90689,7 +90689,7 @@
pixel_x = 6;
pixel_y = -4
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
pixel_y = 7
},
/obj/structure/railing/corner{
@@ -91838,7 +91838,7 @@
/turf/simulated/floor/plasteel/dark,
/area/maintenance/fore2)
"qWx" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster{
name = "north bump";
pixel_y = 28
@@ -91859,7 +91859,7 @@
},
/area/security/execution)
"qYC" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable{
d1 = 4;
d2 = 8;
@@ -92185,7 +92185,7 @@
/turf/simulated/floor/plating,
/area/maintenance/port)
"rlI" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plating,
/area/maintenance/abandonedbar)
"rlQ" = (
@@ -92342,7 +92342,7 @@
},
/area/toxins/explab)
"rsX" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/maintenance/library)
"rtG" = (
@@ -93077,7 +93077,7 @@
},
/area/security/evidence)
"spS" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 10
},
@@ -94362,7 +94362,7 @@
},
/area/maintenance/port)
"tJc" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/warning_stripes/yellow/hollow,
/obj/item/radio/intercom{
name = "north bump";
@@ -97453,7 +97453,7 @@
pixel_x = -6;
pixel_y = -4
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
pixel_y = 7
},
/obj/structure/railing/corner{
@@ -97610,7 +97610,7 @@
},
/area/security/processing)
"xIJ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 8;
icon_state = "darkred"
@@ -97744,7 +97744,7 @@
network = list("Experimentator");
pixel_y = 32
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel/white,
/area/toxins/explab)
"xPJ" = (
diff --git a/_maps/map_files/EventStructures/splashscreen_empty.dmm b/_maps/map_files/EventStructures/splashscreen_empty.dmm
new file mode 100644
index 000000000000..0e28607bac5e
--- /dev/null
+++ b/_maps/map_files/EventStructures/splashscreen_empty.dmm
@@ -0,0 +1,338 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/indestructible/riveted,
+/area/start)
+"e" = (
+/obj/effect/landmark/newplayer_start,
+/turf/simulated/floor/plating,
+/area/start)
+"D" = (
+/turf/simulated/wall/r_wall,
+/area/start)
+"E" = (
+/turf/simulated/floor/plating,
+/area/start)
+
+(1,1,1) = {"
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+"}
+(2,1,1) = {"
+D
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+D
+"}
+(3,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(4,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(5,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(6,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(7,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(8,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(9,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(10,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+e
+E
+E
+E
+E
+E
+E
+a
+"}
+(11,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(12,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(13,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(14,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(15,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(16,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(17,1,1) = {"
+a
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+a
+"}
+(18,1,1) = {"
+D
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+E
+D
+"}
+(19,1,1) = {"
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+D
+"}
diff --git a/_maps/map_files/EventStructures/splashscreen_sandbox.dmm b/_maps/map_files/EventStructures/splashscreen_sandbox.dmm
new file mode 100644
index 000000000000..c4ceae28d7f2
--- /dev/null
+++ b/_maps/map_files/EventStructures/splashscreen_sandbox.dmm
@@ -0,0 +1,804 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/obj/item/stack/cable_coil,
+/turf/simulated/floor/grass,
+/area/start)
+"c" = (
+/obj/item/storage/fancy/crayons,
+/obj/item/storage/fancy/crayons,
+/obj/item/storage/fancy/crayons,
+/turf/simulated/floor/grass,
+/area/start)
+"d" = (
+/obj/item/stack/ore/iron{
+ amount = 50
+ },
+/obj/item/stack/ore/iron{
+ amount = 50
+ },
+/obj/item/stack/ore/iron{
+ amount = 50
+ },
+/obj/item/stack/ore/iron{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"e" = (
+/obj/item/paper_bin,
+/obj/item/paper_bin,
+/obj/item/paper_bin,
+/turf/simulated/floor/grass,
+/area/start)
+"f" = (
+/obj/item/tank/internals/emergency_oxygen/double/vox,
+/obj/item/tank/internals/emergency_oxygen/double/vox,
+/obj/item/tank/internals/emergency_oxygen/double/vox,
+/obj/item/tank/internals/emergency_oxygen/double/vox,
+/obj/item/tank/internals/emergency_oxygen/double/vox,
+/turf/simulated/floor/grass,
+/area/start)
+"g" = (
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/obj/item/stack/tile/brass/fifty,
+/turf/simulated/floor/grass,
+/area/start)
+"h" = (
+/obj/item/stack/ore/tranquillite{
+ amount = 50
+ },
+/obj/item/stack/ore/tranquillite{
+ amount = 50
+ },
+/obj/item/stack/ore/tranquillite{
+ amount = 50
+ },
+/obj/item/stack/ore/tranquillite{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"i" = (
+/obj/item/clothing/mask/breath,
+/obj/item/clothing/mask/breath,
+/obj/item/clothing/mask/breath,
+/obj/item/clothing/mask/breath,
+/obj/item/clothing/mask/breath,
+/turf/simulated/floor/grass,
+/area/start)
+"j" = (
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/obj/item/storage/bag/tray/cookies_tray,
+/turf/simulated/floor/grass,
+/area/start)
+"k" = (
+/obj/item/stack/marker_beacon/ten,
+/obj/item/stack/marker_beacon/ten,
+/obj/item/stack/marker_beacon/ten,
+/turf/simulated/floor/grass,
+/area/start)
+"l" = (
+/obj/item/stack/sheet/mineral/sandstone/fifty,
+/obj/item/stack/sheet/mineral/sandstone/fifty,
+/obj/item/stack/sheet/mineral/sandstone/fifty,
+/obj/item/stack/sheet/mineral/sandstone/fifty,
+/obj/item/stack/sheet/mineral/sandstone/fifty,
+/turf/simulated/floor/grass,
+/area/start)
+"m" = (
+/obj/effect/spawner/window/reinforced/plasma,
+/turf/simulated/floor/plasteel{
+ icon_state = "dark"
+ },
+/area/start)
+"n" = (
+/turf/simulated/floor/grass,
+/area/start)
+"o" = (
+/obj/item/stack/ore/silver{
+ amount = 50
+ },
+/obj/item/stack/ore/silver{
+ amount = 50
+ },
+/obj/item/stack/ore/silver{
+ amount = 50
+ },
+/obj/item/stack/ore/silver{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"p" = (
+/obj/item/stack/ore/diamond{
+ amount = 50
+ },
+/obj/item/stack/ore/diamond{
+ amount = 50
+ },
+/obj/item/stack/ore/diamond{
+ amount = 50
+ },
+/obj/item/stack/ore/diamond{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"q" = (
+/obj/machinery/mineral/ore_redemption/upgraded,
+/turf/simulated/floor/grass,
+/area/start)
+"r" = (
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/obj/item/storage/belt,
+/turf/simulated/floor/grass,
+/area/start)
+"s" = (
+/obj/item/stack/ore/plasma{
+ amount = 50
+ },
+/obj/item/stack/ore/plasma{
+ amount = 50
+ },
+/obj/item/stack/ore/plasma{
+ amount = 50
+ },
+/obj/item/stack/ore/plasma{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"t" = (
+/obj/item/storage/box/bodybags,
+/obj/item/storage/box/bodybags,
+/obj/item/storage/box/bodybags,
+/turf/simulated/floor/grass,
+/area/start)
+"u" = (
+/obj/item/soap/syndie,
+/obj/item/soap/syndie,
+/obj/item/soap/syndie,
+/obj/item/soap/syndie,
+/obj/item/soap/syndie,
+/turf/simulated/floor/grass,
+/area/start)
+"v" = (
+/obj/item/shovel,
+/obj/item/shovel,
+/obj/item/shovel,
+/obj/item/shovel,
+/obj/item/shovel,
+/turf/simulated/floor/grass,
+/area/start)
+"w" = (
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/obj/item/storage/backpack/satcheldeluxe,
+/turf/simulated/floor/grass,
+/area/start)
+"x" = (
+/obj/machinery/recycler,
+/turf/simulated/floor/grass,
+/area/start)
+"z" = (
+/obj/structure/respawner,
+/turf/simulated/floor/grass,
+/area/start)
+"A" = (
+/obj/item/storage/bag/trash/bluespace,
+/obj/item/storage/bag/trash/bluespace,
+/obj/item/storage/bag/trash/bluespace,
+/turf/simulated/floor/grass,
+/area/start)
+"B" = (
+/obj/item/stack/ore/uranium{
+ amount = 50
+ },
+/obj/item/stack/ore/uranium{
+ amount = 50
+ },
+/obj/item/stack/ore/uranium{
+ amount = 50
+ },
+/obj/item/stack/ore/uranium{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"C" = (
+/obj/item/stack/sheet/plastic,
+/obj/item/stack/sheet/plastic,
+/obj/item/stack/sheet/plastic,
+/turf/simulated/floor/grass,
+/area/start)
+"D" = (
+/mob/living/simple_animal/bot/medbot,
+/turf/simulated/floor/grass,
+/area/start)
+"E" = (
+/obj/item/clothing/under/plasmaman/assistant,
+/obj/item/clothing/under/plasmaman/assistant,
+/obj/item/clothing/under/plasmaman/assistant,
+/obj/item/clothing/under/plasmaman/assistant,
+/obj/item/clothing/under/plasmaman/assistant,
+/turf/simulated/floor/grass,
+/area/start)
+"F" = (
+/obj/item/stack/ore/glass{
+ amount = 50
+ },
+/obj/item/stack/ore/glass{
+ amount = 50
+ },
+/obj/item/stack/ore/glass{
+ amount = 50
+ },
+/obj/item/stack/ore/glass{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"G" = (
+/obj/item/stack/sheet/mineral/abductor/fifty,
+/obj/item/stack/sheet/mineral/abductor/fifty,
+/obj/item/stack/sheet/mineral/abductor/fifty,
+/obj/item/stack/sheet/mineral/abductor/fifty,
+/obj/item/stack/sheet/mineral/abductor/fifty,
+/turf/simulated/floor/grass,
+/area/start)
+"H" = (
+/obj/item/storage/bag/ore,
+/obj/item/storage/bag/ore,
+/obj/item/storage/bag/ore,
+/turf/simulated/floor/grass,
+/area/start)
+"J" = (
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/obj/item/stack/sheet/wood,
+/turf/simulated/floor/grass,
+/area/start)
+"K" = (
+/obj/item/stack/ore/titanium{
+ amount = 50
+ },
+/obj/item/stack/ore/titanium{
+ amount = 50
+ },
+/obj/item/stack/ore/titanium{
+ amount = 50
+ },
+/obj/item/stack/ore/titanium{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"L" = (
+/obj/item/stack/rods/fifty,
+/obj/item/stack/rods/fifty,
+/obj/item/stack/rods/fifty,
+/obj/item/stack/rods/fifty,
+/obj/item/stack/rods/fifty,
+/turf/simulated/floor/grass,
+/area/start)
+"M" = (
+/obj/item/tank/internals/plasmaman/full,
+/obj/item/tank/internals/plasmaman/full,
+/obj/item/tank/internals/plasmaman/full,
+/obj/item/tank/internals/plasmaman/full,
+/obj/item/tank/internals/plasmaman/full,
+/turf/simulated/floor/grass,
+/area/start)
+"N" = (
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/obj/item/stack/tile/carpet,
+/turf/simulated/floor/grass,
+/area/start)
+"O" = (
+/obj/item/stack/tape_roll,
+/obj/item/stack/tape_roll,
+/obj/item/stack/tape_roll,
+/turf/simulated/floor/grass,
+/area/start)
+"P" = (
+/obj/item/stack/ore/gold{
+ amount = 50
+ },
+/obj/item/stack/ore/gold{
+ amount = 50
+ },
+/obj/item/stack/ore/gold{
+ amount = 50
+ },
+/obj/item/stack/ore/gold{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"Q" = (
+/obj/item/stack/nanopaste,
+/obj/item/stack/nanopaste,
+/obj/item/stack/nanopaste,
+/obj/item/stack/nanopaste,
+/obj/item/stack/nanopaste,
+/turf/simulated/floor/grass,
+/area/start)
+"R" = (
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/obj/item/storage/toolbox/syndicate,
+/turf/simulated/floor/grass,
+/area/start)
+"S" = (
+/obj/item/clothing/mask/breath/vox,
+/obj/item/clothing/mask/breath/vox,
+/obj/item/clothing/mask/breath/vox,
+/obj/item/clothing/mask/breath/vox,
+/obj/item/clothing/mask/breath/vox,
+/turf/simulated/floor/grass,
+/area/start)
+"T" = (
+/obj/item/stack/ore/bananium{
+ amount = 50
+ },
+/obj/item/stack/ore/bananium{
+ amount = 50
+ },
+/obj/item/stack/ore/bananium{
+ amount = 50
+ },
+/obj/item/stack/ore/bananium{
+ amount = 50
+ },
+/turf/simulated/floor/grass,
+/area/start)
+"U" = (
+/obj/item/mop/advanced,
+/obj/item/mop/advanced,
+/obj/item/mop/advanced,
+/turf/simulated/floor/grass,
+/area/start)
+"V" = (
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/obj/item/pen/multi,
+/turf/simulated/floor/grass,
+/area/start)
+"X" = (
+/obj/effect/landmark/newplayer_start,
+/turf/simulated/floor/grass,
+/area/start)
+"Y" = (
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/obj/item/clothing/under/color/black,
+/turf/simulated/floor/grass,
+/area/start)
+"Z" = (
+/obj/item/clothing/head/helmet/space/plasmaman/assistant,
+/obj/item/clothing/head/helmet/space/plasmaman/assistant,
+/obj/item/clothing/head/helmet/space/plasmaman/assistant,
+/obj/item/clothing/head/helmet/space/plasmaman/assistant,
+/obj/item/clothing/head/helmet/space/plasmaman/assistant,
+/turf/simulated/floor/grass,
+/area/start)
+
+(1,1,1) = {"
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+"}
+(2,1,1) = {"
+m
+z
+f
+S
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(3,1,1) = {"
+m
+Z
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(4,1,1) = {"
+m
+E
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(5,1,1) = {"
+m
+M
+i
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(6,1,1) = {"
+m
+n
+n
+n
+w
+J
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(7,1,1) = {"
+m
+s
+n
+R
+Y
+L
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(8,1,1) = {"
+m
+B
+K
+r
+v
+G
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(9,1,1) = {"
+m
+h
+T
+a
+O
+l
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(10,1,1) = {"
+m
+n
+q
+k
+e
+C
+D
+X
+n
+n
+n
+n
+n
+n
+m
+"}
+(11,1,1) = {"
+m
+p
+P
+V
+c
+g
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(12,1,1) = {"
+m
+F
+d
+A
+Q
+N
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(13,1,1) = {"
+m
+o
+n
+U
+t
+H
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(14,1,1) = {"
+m
+n
+n
+n
+u
+j
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(15,1,1) = {"
+m
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(16,1,1) = {"
+m
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(17,1,1) = {"
+m
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+x
+m
+"}
+(18,1,1) = {"
+m
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+m
+"}
+(19,1,1) = {"
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+m
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_catwalk_chasm.dmm b/_maps/map_files/EventStructures/thunderdome_catwalk_chasm.dmm
new file mode 100644
index 000000000000..4d7a90034447
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_catwalk_chasm.dmm
@@ -0,0 +1,143 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"E" = (
+/turf/simulated/floor/chasm/straight_down/lava_land_surface/normal_air,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/catwalk,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(6,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(7,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(8,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(9,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(10,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(11,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_catwalk_chasm_fake.dmm b/_maps/map_files/EventStructures/thunderdome_catwalk_chasm_fake.dmm
new file mode 100644
index 000000000000..355edebc9a5f
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_catwalk_chasm_fake.dmm
@@ -0,0 +1,143 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"E" = (
+/turf/simulated/floor/chasm/straight_down,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/catwalk,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(6,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(7,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(8,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(9,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(10,1,1) = {"
+R
+E
+E
+E
+E
+E
+R
+"}
+(11,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_default.dmm b/_maps/map_files/EventStructures/thunderdome_default.dmm
new file mode 100644
index 000000000000..fbb4852b3a2c
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_default.dmm
@@ -0,0 +1,159 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"m" = (
+/obj/effect/turf_decal/delivery,
+/turf/simulated/floor/plasteel,
+/area/tdome/arena)
+"n" = (
+/obj/machinery/flasher{
+ id = "tdomeflash";
+ name = "Thunderdome Flash"
+ },
+/turf/simulated/floor/plasteel,
+/area/tdome/arena)
+"o" = (
+/obj/machinery/igniter/on,
+/turf/simulated/floor/plasteel,
+/area/tdome/arena)
+"Q" = (
+/turf/simulated/floor/plasteel,
+/area/tdome/arena)
+"R" = (
+/obj/effect/turf_decal/bot,
+/turf/simulated/floor/plasteel,
+/area/tdome/arena)
+
+(1,1,1) = {"
+o
+Q
+Q
+Q
+Q
+Q
+o
+"}
+(2,1,1) = {"
+m
+Q
+Q
+Q
+Q
+Q
+m
+"}
+(3,1,1) = {"
+R
+m
+Q
+Q
+Q
+m
+R
+"}
+(4,1,1) = {"
+Q
+R
+Q
+Q
+Q
+R
+Q
+"}
+(5,1,1) = {"
+Q
+Q
+Q
+Q
+Q
+Q
+Q
+"}
+(6,1,1) = {"
+Q
+Q
+Q
+Q
+Q
+Q
+Q
+"}
+(7,1,1) = {"
+Q
+Q
+Q
+Q
+Q
+Q
+Q
+"}
+(8,1,1) = {"
+Q
+Q
+Q
+n
+Q
+Q
+Q
+"}
+(9,1,1) = {"
+Q
+Q
+Q
+Q
+Q
+Q
+Q
+"}
+(10,1,1) = {"
+Q
+Q
+Q
+Q
+Q
+Q
+Q
+"}
+(11,1,1) = {"
+Q
+Q
+Q
+Q
+Q
+Q
+Q
+"}
+(12,1,1) = {"
+Q
+R
+Q
+Q
+Q
+R
+Q
+"}
+(13,1,1) = {"
+R
+m
+Q
+Q
+Q
+m
+R
+"}
+(14,1,1) = {"
+m
+Q
+Q
+Q
+Q
+Q
+m
+"}
+(15,1,1) = {"
+o
+Q
+Q
+Q
+Q
+Q
+o
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_empty.dmm b/_maps/map_files/EventStructures/thunderdome_empty.dmm
new file mode 100644
index 000000000000..ed1162d065ec
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_empty.dmm
@@ -0,0 +1,140 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"R" = (
+/turf/simulated/floor/plasteel,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(6,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(7,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(8,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(9,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(10,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(11,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_empty_reinforced.dmm b/_maps/map_files/EventStructures/thunderdome_empty_reinforced.dmm
new file mode 100644
index 000000000000..1694a0e1914e
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_empty_reinforced.dmm
@@ -0,0 +1,140 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"R" = (
+/turf/simulated/floor/engine/air,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(6,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(7,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(8,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(9,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(10,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(11,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_hiero_default.dmm b/_maps/map_files/EventStructures/thunderdome_hiero_default.dmm
new file mode 100644
index 000000000000..1a7372105812
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_hiero_default.dmm
@@ -0,0 +1,146 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"F" = (
+/turf/simulated/wall/indestructible/hierophant,
+/area/tdome/arena)
+"I" = (
+/turf/simulated/floor/indestructible/hierophant/two,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/indestructible/hierophant,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+F
+F
+F
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(6,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(7,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(8,1,1) = {"
+F
+F
+I
+I
+I
+F
+F
+"}
+(9,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(10,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(11,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+F
+F
+F
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_hiero_pandora.dmm b/_maps/map_files/EventStructures/thunderdome_hiero_pandora.dmm
new file mode 100644
index 000000000000..9c02028c33b7
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_hiero_pandora.dmm
@@ -0,0 +1,150 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"h" = (
+/mob/living/simple_animal/hostile/asteroid/elite/pandora,
+/turf/simulated/floor/indestructible/hierophant/two,
+/area/tdome/arena)
+"F" = (
+/turf/simulated/wall/indestructible/hierophant,
+/area/tdome/arena)
+"I" = (
+/turf/simulated/floor/indestructible/hierophant/two,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/indestructible/hierophant,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+F
+F
+F
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(6,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(7,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(8,1,1) = {"
+F
+F
+I
+h
+I
+F
+F
+"}
+(9,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(10,1,1) = {"
+R
+I
+I
+I
+I
+I
+R
+"}
+(11,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+I
+I
+I
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+F
+F
+F
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_lavaland_holes.dmm b/_maps/map_files/EventStructures/thunderdome_lavaland_holes.dmm
new file mode 100644
index 000000000000..7334989eb880
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_lavaland_holes.dmm
@@ -0,0 +1,144 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"m" = (
+/obj/structure/nest/lavaland,
+/turf/simulated/floor/plating/asteroid/basalt,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/plating/asteroid/basalt,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+R
+R
+m
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(6,1,1) = {"
+R
+m
+R
+R
+R
+R
+R
+"}
+(7,1,1) = {"
+R
+R
+R
+R
+R
+R
+m
+"}
+(8,1,1) = {"
+R
+R
+R
+m
+R
+R
+R
+"}
+(9,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(10,1,1) = {"
+m
+R
+R
+R
+R
+R
+R
+"}
+(11,1,1) = {"
+R
+R
+R
+R
+R
+m
+R
+"}
+(12,1,1) = {"
+R
+R
+m
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_lavaland_maze.dmm b/_maps/map_files/EventStructures/thunderdome_lavaland_maze.dmm
new file mode 100644
index 000000000000..a3f1c3c1569f
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_lavaland_maze.dmm
@@ -0,0 +1,159 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"b" = (
+/obj/structure/flora/ash/tall_shroom,
+/turf/simulated/floor/plating/asteroid/basalt,
+/area/tdome/arena)
+"g" = (
+/obj/structure/flora/ash/cacti,
+/turf/simulated/floor/plating/asteroid/basalt,
+/area/tdome/arena)
+"m" = (
+/obj/structure/flora/ash/leaf_shroom,
+/turf/simulated/floor/plating/asteroid/basalt,
+/area/tdome/arena)
+"F" = (
+/obj/structure/flora/ash/cap_shroom,
+/turf/simulated/floor/plating/asteroid/basalt,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/plating/asteroid/basalt,
+/area/tdome/arena)
+"W" = (
+/turf/simulated/mineral/volcanic/lava_land_surface,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+F
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+W
+W
+R
+W
+b
+W
+W
+"}
+(3,1,1) = {"
+F
+R
+R
+W
+R
+R
+R
+"}
+(4,1,1) = {"
+R
+W
+R
+W
+R
+W
+R
+"}
+(5,1,1) = {"
+W
+W
+b
+R
+g
+R
+R
+"}
+(6,1,1) = {"
+R
+R
+R
+W
+W
+W
+R
+"}
+(7,1,1) = {"
+W
+R
+W
+W
+R
+R
+m
+"}
+(8,1,1) = {"
+g
+R
+R
+m
+R
+W
+W
+"}
+(9,1,1) = {"
+W
+W
+W
+W
+R
+R
+R
+"}
+(10,1,1) = {"
+R
+R
+R
+R
+R
+W
+R
+"}
+(11,1,1) = {"
+R
+W
+g
+W
+W
+W
+F
+"}
+(12,1,1) = {"
+R
+W
+R
+W
+R
+W
+R
+"}
+(13,1,1) = {"
+m
+R
+R
+W
+R
+R
+R
+"}
+(14,1,1) = {"
+W
+W
+R
+W
+W
+m
+W
+"}
+(15,1,1) = {"
+R
+R
+R
+F
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_reset.dmm b/_maps/map_files/EventStructures/thunderdome_reset.dmm
new file mode 100644
index 000000000000..8dee93b00a85
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_reset.dmm
@@ -0,0 +1,140 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"R" = (
+/turf/simulated/floor/chasm/straight_down/lava_land_surface/normal_air,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(4,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(5,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(6,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(7,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(8,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(9,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(10,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(11,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_snow_forest.dmm b/_maps/map_files/EventStructures/thunderdome_snow_forest.dmm
new file mode 100644
index 000000000000..202df2f2062c
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_snow_forest.dmm
@@ -0,0 +1,156 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"j" = (
+/obj/structure/flora/grass/brown,
+/turf/simulated/floor/plating/snow,
+/area/tdome/arena)
+"K" = (
+/obj/structure/flora/tree/pine,
+/turf/simulated/floor/plating/snow,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/plating/snow,
+/area/tdome/arena)
+"S" = (
+/obj/structure/flora/grass/green,
+/turf/simulated/floor/plating/snow,
+/area/tdome/arena)
+"Y" = (
+/obj/structure/flora/grass/both,
+/turf/simulated/floor/plating/snow,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+R
+R
+S
+R
+R
+R
+"}
+(3,1,1) = {"
+R
+K
+R
+R
+R
+K
+R
+"}
+(4,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(5,1,1) = {"
+R
+j
+R
+R
+R
+Y
+R
+"}
+(6,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(7,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(8,1,1) = {"
+R
+R
+R
+K
+R
+R
+R
+"}
+(9,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(10,1,1) = {"
+R
+Y
+R
+R
+S
+R
+R
+"}
+(11,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(12,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+K
+R
+R
+R
+K
+R
+"}
+(14,1,1) = {"
+R
+R
+R
+j
+R
+R
+R
+"}
+(15,1,1) = {"
+R
+R
+R
+R
+R
+R
+R
+"}
diff --git a/_maps/map_files/EventStructures/thunderdome_touchgrass.dmm b/_maps/map_files/EventStructures/thunderdome_touchgrass.dmm
new file mode 100644
index 000000000000..6494c41e1d0b
--- /dev/null
+++ b/_maps/map_files/EventStructures/thunderdome_touchgrass.dmm
@@ -0,0 +1,156 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"b" = (
+/obj/structure/flora/ausbushes/lavendergrass,
+/turf/simulated/floor/grass,
+/area/tdome/arena)
+"A" = (
+/obj/structure/flora/ausbushes/sparsegrass,
+/turf/simulated/floor/grass,
+/area/tdome/arena)
+"C" = (
+/obj/structure/flora/ausbushes/fullgrass,
+/turf/simulated/floor/grass,
+/area/tdome/arena)
+"R" = (
+/turf/simulated/floor/grass,
+/area/tdome/arena)
+"U" = (
+/obj/structure/flora/ausbushes/grassybush,
+/turf/simulated/floor/grass,
+/area/tdome/arena)
+
+(1,1,1) = {"
+R
+R
+A
+U
+R
+R
+R
+"}
+(2,1,1) = {"
+R
+C
+R
+R
+R
+C
+R
+"}
+(3,1,1) = {"
+A
+b
+R
+A
+R
+R
+C
+"}
+(4,1,1) = {"
+R
+U
+R
+R
+U
+R
+b
+"}
+(5,1,1) = {"
+C
+R
+R
+C
+R
+R
+R
+"}
+(6,1,1) = {"
+R
+R
+R
+R
+R
+b
+R
+"}
+(7,1,1) = {"
+R
+R
+U
+A
+R
+C
+U
+"}
+(8,1,1) = {"
+R
+C
+R
+R
+R
+R
+R
+"}
+(9,1,1) = {"
+b
+R
+R
+R
+A
+R
+R
+"}
+(10,1,1) = {"
+U
+R
+U
+b
+R
+U
+R
+"}
+(11,1,1) = {"
+R
+A
+R
+R
+R
+R
+C
+"}
+(12,1,1) = {"
+b
+R
+U
+R
+R
+R
+R
+"}
+(13,1,1) = {"
+R
+C
+R
+R
+C
+R
+b
+"}
+(14,1,1) = {"
+R
+R
+A
+R
+R
+A
+R
+"}
+(15,1,1) = {"
+A
+R
+R
+b
+R
+R
+R
+"}
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 511cd87e9f01..0e93b323f7f2 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -971,7 +971,7 @@
/turf/simulated/wall,
/area/crew_quarters/arcade)
"ajb" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-20"
},
/obj/machinery/computer/mob_battle_terminal/red{
@@ -3620,7 +3620,7 @@
},
/area/crew_quarters/recreation)
"auV" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "bluefull"
},
@@ -3974,7 +3974,7 @@
/turf/simulated/floor/plating,
/area/maintenance/fpmaint)
"awb" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-22"
},
/turf/simulated/floor/plasteel,
@@ -4271,7 +4271,7 @@
/turf/simulated/floor/plating,
/area/maintenance/fpmaint)
"awZ" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
name = "north bump";
pixel_y = 24
@@ -4480,7 +4480,7 @@
c_tag = "Detective's Office";
dir = 8
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-24"
},
/turf/simulated/floor/carpet,
@@ -7252,7 +7252,7 @@
"aHH" = (
/obj/effect/decal/cleanable/cobweb,
/obj/effect/turf_decal/bot,
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-20"
},
/turf/simulated/floor/plasteel,
@@ -8988,7 +8988,7 @@
name = "west bump";
pixel_x = -24
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-25"
},
/turf/simulated/floor/plasteel,
@@ -10436,7 +10436,7 @@
/obj/structure/sign/kiddieplaque{
pixel_y = 32
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
pixel_y = 10
},
/obj/structure/table/wood/fancy/green,
@@ -12624,7 +12624,7 @@
name = "east bump";
pixel_x = 27
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-11"
},
/turf/simulated/floor/wood,
@@ -13279,7 +13279,7 @@
name = "west bump";
pixel_x = -27
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 1;
icon_state = "neutralcorner"
@@ -14413,7 +14413,7 @@
name = "north bump";
pixel_y = 24
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "applebush"
},
/turf/simulated/floor/plasteel{
@@ -14606,7 +14606,7 @@
/turf/simulated/floor/plating,
/area/security/checkpoint2)
"bbM" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-18"
},
/obj/structure/sign/electricshock{
@@ -15892,7 +15892,7 @@
},
/area/engine/chiefs_office)
"beP" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/turf_decal/stripes/line{
dir = 10
},
@@ -15919,7 +15919,7 @@
name = "east bump";
pixel_x = 27
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-16"
},
/obj/item/radio/intercom{
@@ -16040,7 +16040,7 @@
/obj/effect/turf_decal/stripes/line{
dir = 10
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
level = 4.1
},
/turf/simulated/floor/plasteel,
@@ -20720,7 +20720,7 @@
pixel_x = -32;
pixel_y = -32
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_scrubber/on{
dir = 4
},
@@ -23064,7 +23064,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/primary/central)
"bwp" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
level = 4.1
},
/turf/simulated/floor/wood,
@@ -24415,7 +24415,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
"bzA" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-18"
},
/obj/effect/turf_decal/stripes/line{
@@ -24425,7 +24425,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
"bzB" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-10"
},
/turf/simulated/floor/plasteel{
@@ -25063,7 +25063,7 @@
},
/area/hallway/primary/port)
"bBr" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-20"
},
/obj/effect/turf_decal/stripes/line{
@@ -27049,7 +27049,7 @@
/turf/simulated/floor/plasteel,
/area/bridge/hall)
"bHx" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-22"
},
/turf/simulated/floor/wood,
@@ -27136,7 +27136,7 @@
/turf/simulated/floor/carpet,
/area/crew_quarters/captain)
"bHI" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
level = 4.1
},
/obj/machinery/light,
@@ -27360,7 +27360,7 @@
},
/area/tcommsat/computer)
"bIA" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-21"
},
/turf/simulated/floor/plasteel{
@@ -27376,7 +27376,7 @@
},
/area/tcommsat/computer)
"bID" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-10"
},
/obj/structure/disposalpipe/segment,
@@ -30865,7 +30865,7 @@
icon_state = "map-right-MS";
pixel_y = -32
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 9
},
@@ -31753,7 +31753,7 @@
/obj/effect/turf_decal/stripes/line{
dir = 6
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-16"
},
/obj/machinery/light{
@@ -32357,7 +32357,7 @@
name = "west bump";
pixel_x = -28
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-10"
},
/turf/simulated/floor/plasteel{
@@ -32391,7 +32391,7 @@
/area/quartermaster/office)
"bXF" = (
/obj/effect/turf_decal/stripes/corner,
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-10"
},
/obj/machinery/door_control{
@@ -34701,7 +34701,7 @@
name = "east bump";
pixel_x = 24
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-10"
},
/turf/simulated/floor/plasteel{
@@ -34975,7 +34975,7 @@
name = "west bump";
pixel_x = -27
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "applebush"
},
/turf/simulated/floor/plasteel{
@@ -39992,7 +39992,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/primary/aft)
"cvJ" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-20"
},
/turf/simulated/floor/plasteel{
@@ -40344,7 +40344,7 @@
name = "west bump";
pixel_x = -24
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-11"
},
/obj/machinery/light{
@@ -40605,7 +40605,7 @@
},
/area/medical/medbay)
"cxL" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-10"
},
/turf/simulated/floor/plasteel{
@@ -44525,7 +44525,7 @@
},
/area/medical/genetics_cloning)
"cKA" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-18"
},
/turf/simulated/floor/plasteel{
@@ -44758,7 +44758,7 @@
/turf/simulated/floor/grass,
/area/maintenance/aft2)
"cLm" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-18"
},
/obj/machinery/light/small{
@@ -45274,7 +45274,7 @@
/turf/simulated/floor/plating,
/area/maintenance/medmaint)
"cNy" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 1
},
@@ -45556,7 +45556,7 @@
/turf/simulated/floor/plating,
/area/maintenance/aft2)
"cOk" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-18"
},
/obj/machinery/light/small{
@@ -46881,7 +46881,7 @@
/obj/machinery/light{
dir = 8
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-24"
},
/obj/effect/turf_decal/stripes/line{
@@ -47050,7 +47050,7 @@
name = "east bump";
pixel_x = 27
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-14"
},
/obj/effect/turf_decal/stripes/line{
@@ -47107,7 +47107,7 @@
/turf/simulated/floor/plating,
/area/ntrep)
"cUq" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 5
},
@@ -50324,7 +50324,7 @@
/turf/simulated/floor/engine,
/area/toxins/explab_chamber)
"drv" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
level = 4.1
},
/obj/structure/disposalpipe/segment{
@@ -50480,7 +50480,7 @@
},
/area/engine/engineering)
"dym" = (
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 8;
id = "Cell 1";
name = "Cell 1";
@@ -52777,7 +52777,7 @@
name = "Emergency NanoMed";
pixel_y = -28
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
icon_state = "whiteblue"
},
@@ -53699,7 +53699,7 @@
/turf/simulated/floor/plasteel,
/area/security/brig)
"fdz" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-11"
},
/turf/simulated/floor/plasteel,
@@ -53818,7 +53818,7 @@
},
/area/atmos)
"fhC" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-22"
},
/turf/simulated/floor/plasteel{
@@ -55558,7 +55558,7 @@
/obj/machinery/light{
dir = 8
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel,
/area/hallway/secondary/exit)
"fVB" = (
@@ -55806,7 +55806,7 @@
},
/area/crew_quarters/courtroom)
"gbz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/firealarm{
dir = 8;
name = "custom placement";
@@ -55952,7 +55952,7 @@
/turf/simulated/floor/plating,
/area/engine/engineering)
"geC" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-24"
},
/obj/machinery/camera{
@@ -56516,7 +56516,7 @@
/turf/simulated/floor/plasteel,
/area/security/prison/cell_block/A)
"grL" = (
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
dir = 8;
id = "Cell 3";
name = "Cell 3";
@@ -56669,7 +56669,7 @@
/turf/simulated/floor/plasteel,
/area/hallway/secondary/entry)
"gzt" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-16"
},
/obj/structure/cable/yellow{
@@ -56687,7 +56687,7 @@
/obj/structure/window/reinforced{
dir = 8
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-10"
},
/turf/simulated/floor/plasteel{
@@ -56780,7 +56780,7 @@
},
/area/security/permabrig)
"gCX" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel{
dir = 4;
icon_state = "whitebluecorner"
@@ -58046,7 +58046,7 @@
/area/medical/cmo)
"hjr" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-22"
},
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
@@ -61998,7 +61998,7 @@
},
/area/engine/controlroom)
"iTF" = (
-/obj/machinery/door/window/reinforced/reversed{
+/obj/machinery/door/window/brigdoor{
dir = 4;
id = "Cell 2";
name = "Cell 2";
@@ -62037,7 +62037,7 @@
},
/area/security/execution)
"iTN" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light{
dir = 1
},
@@ -64451,7 +64451,7 @@
},
/area/medical/research)
"kiq" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-21"
},
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
@@ -65884,7 +65884,7 @@
/turf/simulated/floor/plating/airless,
/area/space/nearstation)
"kQx" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-11"
},
/turf/simulated/floor/wood,
@@ -66514,7 +66514,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "applebush"
},
/obj/effect/turf_decal/stripes/line{
@@ -67289,7 +67289,7 @@
},
/area/security/main)
"lvB" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable/yellow{
d1 = 4;
d2 = 8;
@@ -74316,7 +74316,7 @@
d2 = 8;
icon_state = "2-8"
},
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
id = "Cell 5";
name = "Cell 5";
req_access_txt = "2";
@@ -75266,7 +75266,7 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-21"
},
/obj/effect/turf_decal/stripes/line{
@@ -76251,7 +76251,7 @@
dir = 2;
icon_state = "pipe-c"
},
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-21"
},
/turf/simulated/floor/plasteel,
@@ -77991,7 +77991,7 @@
},
/area/assembly/robotics)
"qFz" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/wood,
/area/maintenance/apmaint)
"qFI" = (
@@ -80321,7 +80321,7 @@
/obj/structure/sign/poster/random{
pixel_y = 32
},
-/obj/item/twohanded/staff/broom,
+/obj/item/staff/broom,
/obj/item/wrench,
/obj/machinery/alarm{
dir = 8;
@@ -83866,7 +83866,7 @@
},
/area/hallway/primary/central)
"tyW" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "applebush"
},
/obj/effect/turf_decal/stripes/corner{
@@ -84353,7 +84353,7 @@
},
/area/security/armoury)
"tKL" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-24"
},
/obj/machinery/light_switch{
@@ -85815,7 +85815,7 @@
/turf/simulated/floor/plating,
/area/maintenance/xenobio_south)
"uBK" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/light/small{
dir = 4
},
@@ -87224,7 +87224,7 @@
},
/area/medical/medbay)
"viR" = (
-/obj/machinery/door/window/reinforced/normal{
+/obj/machinery/door/window/brigdoor{
id = "Cell 4";
name = "Cell 4";
req_access_txt = "2";
@@ -89195,7 +89195,7 @@
/turf/simulated/floor/engine,
/area/engine/engineering)
"wkL" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/structure/cable/yellow{
d1 = 2;
d2 = 4;
@@ -91541,7 +91541,7 @@
},
/area/hallway/secondary/exit)
"xtP" = (
-/obj/item/twohanded/required/kirbyplants{
+/obj/item/kirbyplants{
icon_state = "plant-21"
},
/obj/structure/sign/botany{
@@ -93066,7 +93066,7 @@
/turf/simulated/floor/plasteel,
/area/security/permabrig)
"yeR" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/machinery/newscaster{
name = "north bump";
pixel_y = 28
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_1.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_1.dmm
new file mode 100644
index 000000000000..2d2a3b57b752
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_1.dmm
@@ -0,0 +1,46 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/clockwork,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/clockwork,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+a
+b
+a
+c
+"}
+(2,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(3,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(4,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(5,1,1) = {"
+c
+a
+b
+a
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_2.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_2.dmm
new file mode 100644
index 000000000000..7996a2aa6d40
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_2.dmm
@@ -0,0 +1,46 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/cult,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/engine/cult,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+a
+b
+a
+c
+"}
+(2,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(3,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(4,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(5,1,1) = {"
+c
+a
+b
+a
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_3.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_3.dmm
new file mode 100644
index 000000000000..2d3360cc5800
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_3.dmm
@@ -0,0 +1,73 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/cult,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/obj/structure/stone_tile/surrounding,
+/obj/structure/stone_tile/center,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"g" = (
+/obj/structure/stone_tile/slab/cracked,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"h" = (
+/obj/structure/stone_tile/slab,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"m" = (
+/obj/structure/stone_tile/block,
+/obj/structure/stone_tile/block/cracked{
+ dir = 1
+ },
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"o" = (
+/obj/structure/stone_tile/block{
+ dir = 1
+ },
+/obj/structure/stone_tile/burnt,
+/obj/structure/stone_tile/surrounding_tile{
+ dir = 8
+ },
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+a
+h
+a
+c
+"}
+(2,1,1) = {"
+c
+c
+m
+c
+c
+"}
+(3,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(4,1,1) = {"
+c
+c
+o
+c
+c
+"}
+(5,1,1) = {"
+c
+a
+g
+a
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_4.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_4.dmm
new file mode 100644
index 000000000000..272dfcd1246b
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_4.dmm
@@ -0,0 +1,49 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/indestructible/hierophant,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/indestructible/hierophant,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"o" = (
+/turf/simulated/floor/indestructible/hierophant/two,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+a
+b
+a
+c
+"}
+(2,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(3,1,1) = {"
+c
+c
+o
+c
+c
+"}
+(4,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(5,1,1) = {"
+c
+a
+b
+a
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_5.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_5.dmm
new file mode 100644
index 000000000000..29b681cae821
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_5.dmm
@@ -0,0 +1,56 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/mineral/wood/nonmetal,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/wood,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"v" = (
+/turf/simulated/floor/wood{
+ icon_state = "wood-broken3"
+ },
+/area/lavaland/surface/outdoors/unexplored/danger)
+"C" = (
+/turf/simulated/floor/wood{
+ icon_state = "wood-broken7"
+ },
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+a
+C
+a
+c
+"}
+(2,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(3,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(4,1,1) = {"
+c
+c
+v
+c
+c
+"}
+(5,1,1) = {"
+c
+a
+b
+a
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_6.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_6.dmm
new file mode 100644
index 000000000000..2cfe9bd74464
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_horizontal_6.dmm
@@ -0,0 +1,49 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/obj/structure/lattice/catwalk/mining,
+/obj/structure/marker_beacon/dock_marker/collision,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/obj/structure/lattice/catwalk/mining,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+a
+b
+a
+c
+"}
+(2,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(3,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(4,1,1) = {"
+c
+c
+b
+c
+c
+"}
+(5,1,1) = {"
+c
+a
+b
+a
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_1.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_1.dmm
new file mode 100644
index 000000000000..473e4df56c66
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_1.dmm
@@ -0,0 +1,46 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/clockwork,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/clockwork,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+c
+c
+c
+c
+"}
+(2,1,1) = {"
+a
+c
+c
+c
+a
+"}
+(3,1,1) = {"
+b
+b
+b
+b
+b
+"}
+(4,1,1) = {"
+a
+c
+c
+c
+a
+"}
+(5,1,1) = {"
+c
+c
+c
+c
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_2.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_2.dmm
new file mode 100644
index 000000000000..44eb0610f50d
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_2.dmm
@@ -0,0 +1,46 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/cult,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/engine/cult,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+c
+c
+c
+c
+"}
+(2,1,1) = {"
+a
+c
+c
+c
+a
+"}
+(3,1,1) = {"
+b
+b
+b
+b
+b
+"}
+(4,1,1) = {"
+a
+c
+c
+c
+a
+"}
+(5,1,1) = {"
+c
+c
+c
+c
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_3.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_3.dmm
new file mode 100644
index 000000000000..8fbfe6ba32e6
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_3.dmm
@@ -0,0 +1,83 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/wall/cult,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/obj/structure/stone_tile/slab,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"w" = (
+/obj/structure/stone_tile/center/cracked,
+/obj/structure/stone_tile/surrounding/cracked,
+/obj/structure/stone_tile/slab/cracked{
+ dir = 10
+ },
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"I" = (
+/obj/structure/stone_tile,
+/obj/structure/stone_tile{
+ dir = 4
+ },
+/obj/structure/stone_tile{
+ dir = 8
+ },
+/obj/structure/stone_tile/cracked{
+ dir = 1
+ },
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"M" = (
+/obj/structure/stone_tile/slab/cracked{
+ dir = 5
+ },
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"R" = (
+/obj/structure/stone_tile/block/cracked{
+ dir = 8
+ },
+/obj/structure/stone_tile/block{
+ dir = 4
+ },
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+c
+c
+c
+c
+"}
+(2,1,1) = {"
+a
+c
+c
+c
+a
+"}
+(3,1,1) = {"
+b
+I
+w
+R
+M
+"}
+(4,1,1) = {"
+a
+c
+c
+c
+a
+"}
+(5,1,1) = {"
+c
+c
+c
+c
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_4.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_4.dmm
new file mode 100644
index 000000000000..7c187f4aeecc
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_4.dmm
@@ -0,0 +1,49 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/floor/indestructible/hierophant/two,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/indestructible/hierophant,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"y" = (
+/turf/simulated/wall/indestructible/hierophant,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+c
+c
+c
+c
+"}
+(2,1,1) = {"
+y
+c
+c
+c
+y
+"}
+(3,1,1) = {"
+a
+a
+b
+a
+a
+"}
+(4,1,1) = {"
+y
+c
+c
+c
+y
+"}
+(5,1,1) = {"
+c
+c
+c
+c
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_5.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_5.dmm
new file mode 100644
index 000000000000..15f612927e4c
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_5.dmm
@@ -0,0 +1,62 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/simulated/floor/wood,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/turf/simulated/floor/wood{
+ broken = 1;
+ icon_state = "wood-broken"
+ },
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"q" = (
+/turf/simulated/floor/wood{
+ icon_state = "wood-broken3"
+ },
+/area/lavaland/surface/outdoors/unexplored/danger)
+"y" = (
+/turf/simulated/wall/mineral/wood,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"S" = (
+/turf/simulated/floor/wood{
+ icon_state = "wood-broken7"
+ },
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+c
+c
+c
+c
+"}
+(2,1,1) = {"
+y
+c
+c
+c
+y
+"}
+(3,1,1) = {"
+q
+a
+b
+a
+S
+"}
+(4,1,1) = {"
+y
+c
+c
+c
+y
+"}
+(5,1,1) = {"
+c
+c
+c
+c
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_6.dmm b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_6.dmm
new file mode 100644
index 000000000000..ae47effe5fc8
--- /dev/null
+++ b/_maps/map_files/RandomRuins/LavaRuins/chasm_bridges/lavaland_bridge_vertical_6.dmm
@@ -0,0 +1,49 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/obj/structure/lattice/catwalk/mining,
+/obj/structure/marker_beacon/dock_marker,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"b" = (
+/obj/structure/lattice/catwalk/mining,
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+"c" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors/unexplored/danger)
+
+(1,1,1) = {"
+c
+c
+c
+c
+c
+"}
+(2,1,1) = {"
+b
+c
+c
+c
+b
+"}
+(3,1,1) = {"
+b
+a
+b
+a
+b
+"}
+(4,1,1) = {"
+b
+c
+c
+c
+b
+"}
+(5,1,1) = {"
+c
+c
+c
+c
+c
+"}
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm
index 102d87cb8c13..13db53afa1a5 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm
@@ -259,7 +259,7 @@
/turf/simulated/floor/plating/ice/smooth,
/area/ruin/powered/snow_biodome)
"cv" = (
-/obj/item/twohanded/required/chainsaw,
+/obj/item/chainsaw,
/turf/simulated/floor/plating/asteroid/snow/atmosphere,
/area/ruin/powered/snow_biodome)
"dS" = (
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm
index f81700c9e0c8..0f05d5677026 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_animal_hospital.dmm
@@ -536,7 +536,7 @@
/turf/simulated/floor/plasteel,
/area/ruin/powered/animal_hospital)
"bt" = (
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/turf/simulated/floor/plasteel,
/area/ruin/powered/animal_hospital)
"bu" = (
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
index 8120a34652b8..43f7a8697539 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
@@ -703,7 +703,7 @@
/area/lavaland/surface/outdoors)
"bP" = (
/obj/structure/stone_tile/block,
-/obj/item/twohanded/spear,
+/obj/item/spear,
/obj/effect/mapping_helpers/no_lava,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
@@ -875,7 +875,7 @@
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"ck" = (
-/obj/item/twohanded/spear,
+/obj/item/spear,
/obj/structure/stone_tile{
dir = 4
},
@@ -889,7 +889,7 @@
/obj/structure/stone_tile/cracked{
dir = 8
},
-/obj/item/twohanded/spear,
+/obj/item/spear,
/obj/effect/mapping_helpers/no_lava,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
@@ -993,7 +993,7 @@
/obj/structure/stone_tile/cracked{
dir = 1
},
-/obj/item/twohanded/spear,
+/obj/item/spear,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/ash_walkers)
"cA" = (
@@ -1036,7 +1036,7 @@
dir = 1
},
/obj/structure/table/wood,
-/obj/item/twohanded/spear,
+/obj/item/spear,
/obj/item/storage/belt/utility,
/turf/simulated/floor/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
@@ -1079,7 +1079,7 @@
dir = 4
},
/obj/structure/table/wood,
-/obj/item/twohanded/spear,
+/obj/item/spear,
/obj/item/scythe,
/turf/simulated/floor/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
@@ -1092,7 +1092,7 @@
dir = 8
},
/obj/structure/table/wood,
-/obj/item/twohanded/spear,
+/obj/item/spear,
/turf/simulated/floor/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
"cN" = (
@@ -1107,7 +1107,7 @@
dir = 4
},
/obj/structure/table/wood,
-/obj/item/twohanded/spear,
+/obj/item/spear,
/obj/item/clothing/head/helmet/roman/legionaire,
/turf/simulated/floor/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk1.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk1.dmm
index 5629bb19b49c..cb963246b394 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk1.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_blooddrunk1.dmm
@@ -93,7 +93,7 @@
/obj/structure/stone_tile{
dir = 1
},
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"r" = (
/obj/structure/stone_tile/block{
@@ -106,7 +106,7 @@
/obj/structure/stone_tile/cracked{
dir = 4
},
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"t" = (
/obj/structure/stone_tile/surrounding/cracked{
@@ -115,14 +115,14 @@
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner{
dir = 1
},
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"u" = (
/obj/structure/stone_tile{
dir = 1
},
/obj/structure/stone_tile/block/cracked,
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"v" = (
/obj/structure/stone_tile{
@@ -136,7 +136,7 @@
/obj/structure/stone_tile/block/cracked{
dir = 8
},
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"x" = (
/obj/structure/stone_tile{
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm
index 052ac940448b..eb87e9f8e66a 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_cultaltar.dmm
@@ -10,7 +10,7 @@
},
/area/ruin/unpowered/misc_lavaruin)
"c" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/ruin/unpowered/misc_lavaruin)
"d" = (
/turf/simulated/wall/cult,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_dead_ratvar.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_dead_ratvar.dmm
index 66a8ba0e158e..6db766c602db 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_dead_ratvar.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_dead_ratvar.dmm
@@ -51,7 +51,7 @@
},
/area/lavaland/surface/outdoors/unexplored)
"l" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors/unexplored)
"m" = (
/obj/item/clockwork/alloy_shards/medium,
@@ -75,7 +75,7 @@
/area/lavaland/surface/outdoors/unexplored)
"q" = (
/obj/structure/lattice/catwalk/clockwork,
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors/unexplored)
"r" = (
/obj/structure/lattice/clockwork,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_envy.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_envy.dmm
index 2628c8c4f24f..8f328c20132b 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_envy.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_envy.dmm
@@ -6,7 +6,7 @@
/turf/simulated/mineral/volcanic/lava_land_surface,
/area/lavaland/surface/outdoors)
"c" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"d" = (
/obj/effect/spawner/random_spawners/wall_rusted_always,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_gluttony.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_gluttony.dmm
index c6636911f72d..3684e0027ce5 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_gluttony.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_gluttony.dmm
@@ -6,7 +6,7 @@
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"c" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"d" = (
/obj/effect/baseturf_helper/lava_land/surface,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_greed.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_greed.dmm
index 14ef5982d634..7e1bc2f11e06 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_greed.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_greed.dmm
@@ -6,7 +6,7 @@
/turf/simulated/mineral/volcanic/lava_land_surface,
/area/lavaland/surface/outdoors)
"c" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"d" = (
/obj/effect/baseturf_helper/lava_land/surface,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_monster_nest.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_monster_nest.dmm
index d1b00e4775ab..51dcf1d6225f 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_monster_nest.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_monster_nest.dmm
@@ -8,7 +8,7 @@
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"m" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"r" = (
/obj/effect/mapping_helpers/no_lava,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pride.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pride.dmm
index 314a7285bf08..d308b8fcdaaf 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pride.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_pride.dmm
@@ -6,7 +6,7 @@
/turf/simulated/mineral/volcanic/lava_land_surface,
/area/lavaland/surface/outdoors)
"c" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"d" = (
/obj/effect/baseturf_helper/lava_land/surface,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm
index a3dd45e28451..f2cdee32a34f 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_sloth.dmm
@@ -3,7 +3,7 @@
/turf/simulated/wall/indestructible/riveted,
/area/ruin/unpowered/misc_lavaruin)
"b" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/ruin/unpowered/misc_lavaruin)
"c" = (
/obj/item/paper/fluff/stations/lavaland/sloth/note,
diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_xeno_nest.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_xeno_nest.dmm
index b0323d3ed428..f0f246a4aa7c 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_xeno_nest.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_xeno_nest.dmm
@@ -124,7 +124,7 @@
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
"G" = (
-/turf/simulated/floor/plating/lava/smooth/lava_land_surface,
+/turf/simulated/floor/plating/lava/smooth/mapping_lava,
/area/lavaland/surface/outdoors)
"H" = (
/obj/structure/alien/weeds,
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
index ac3d3bfc1319..89b5f5b1386a 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
@@ -372,7 +372,7 @@
icon_state = "1-2"
},
/obj/structure/rack,
-/obj/item/clothing/suit/space/hardsuit/medical,
+/obj/item/mod/control/pre_equipped/medical,
/obj/item/tank/internals/emergency_oxygen/double,
/turf/simulated/floor/plasteel{
icon_state = "dark"
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm b/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm
index 782e15a30898..b7b7e3bc1845 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm
@@ -143,7 +143,7 @@
/turf/simulated/floor/plasteel,
/area/ruin/space/unpowered)
"sd" = (
-/obj/item/twohanded/required/kirbyplants/dead,
+/obj/item/kirbyplants/dead,
/turf/simulated/floor/plasteel,
/area/ruin/space/unpowered)
"vg" = (
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/dj.dmm b/_maps/map_files/RandomRuins/SpaceRuins/dj.dmm
index b1b02348e97f..c633cc96f1d4 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/dj.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/dj.dmm
@@ -419,7 +419,7 @@
name = "north bump";
pixel_y = 24
},
-/obj/item/twohanded/required/kirbyplants,
+/obj/item/kirbyplants,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plasteel{
icon_state = "bar"
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm
index c917ccf4c9be..62bb34219c95 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm
@@ -1,171 +1,311 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
-"a" = (
-/turf/template_noop,
-/area/template_noop)
-"c" = (
-/turf/simulated/mineral,
-/area/ruin/space/powered)
-"d" = (
-/turf/simulated/floor/plating/asteroid/airless,
-/area/ruin/space/powered)
-"f" = (
+"at" = (
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/wood{
+ icon_state = "wood-broken5"
+ },
+/area/ruin/space/syndicate_listening_station)
+"aW" = (
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
/turf/simulated/wall/r_wall,
-/area/ruin/space/powered)
-"g" = (
+/area/ruin/space/syndicate_listening_station)
+"bU" = (
+/obj/machinery/light/small,
+/obj/item/storage/toolbox/syndicate,
+/obj/structure/closet/syndicate,
+/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
-/area/ruin/space/powered)
-"h" = (
-/obj/machinery/power/smes/magical{
- desc = "A high-capacity superconducting magnetic energy storage (SMES) unit.";
- name = "power storage unit"
- },
+/area/ruin/space/syndicate_listening_station)
+"du" = (
+/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
-/area/ruin/space/powered)
-"i" = (
-/obj/machinery/door/airlock,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"j" = (
-/turf/simulated/wall,
-/area/ruin/space/powered)
-"k" = (
-/obj/structure/table,
-/obj/item/paper/monitorkey,
-/obj/item/clothing/glasses/regular,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"l" = (
+/area/ruin/space/syndicate_listening_station)
+"eg" = (
+/obj/machinery/door/airlock/hatch/syndicate{
+ name = "Cabin"
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/wood,
+/area/ruin/space/syndicate_listening_station)
+"hp" = (
+/obj/structure/disposalpipe/trunk{
+ dir = 4
+ },
+/obj/machinery/disposal,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"hr" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
+ },
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"iB" = (
+/obj/item/taperecorder,
/obj/structure/table,
-/obj/item/radio/intercom/pirate{
- name = "listening post intercom"
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"iL" = (
+/obj/structure/disposaloutlet{
+ dir = 4
},
-/obj/machinery/light/small{
- dir = 1
+/obj/structure/disposalpipe/trunk{
+ dir = 8
},
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"m" = (
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"n" = (
-/obj/machinery/door/airlock/external,
-/turf/simulated/floor/plating,
-/area/ruin/space/powered)
-"o" = (
-/obj/machinery/light/small{
- dir = 1
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"kE" = (
+/obj/machinery/door/airlock/external{
+ id_tag = "sst_away";
+ req_access_txt = "150"
+ },
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
},
+/obj/structure/fans/tiny,
+/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
-/area/ruin/space/powered)
-"p" = (
-/obj/machinery/computer/message_monitor,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"q" = (
-/obj/structure/chair{
- dir = 4
+/area/ruin/space/syndicate_listening_station)
+"kJ" = (
+/turf/simulated/wall/r_wall,
+/area/ruin/space/syndicate_listening_station)
+"mC" = (
+/obj/machinery/power/solar,
+/obj/structure/cable,
+/turf/simulated/floor/plasteel/airless{
+ icon_state = "solarpanel"
},
-/mob/living/simple_animal/hostile/syndicate{
- desc = "A weary looking syndicate operative.";
- environment_smash = 0
+/area/ruin/space/syndicate_listening_station/asteroid)
+"nC" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
},
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"r" = (
-/obj/structure/table,
-/obj/item/paper{
- info = "Nothing of interest to report.";
- name = "november report"
+/obj/machinery/light{
+ dir = 8
},
-/obj/item/pen,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"s" = (
/obj/structure/table,
-/obj/item/radio/intercom/pirate{
- name = "listening post intercom"
- },
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"t" = (
-/obj/structure/rack,
-/obj/item/tank/jetpack/carbondioxide,
-/obj/item/tank/internals/air,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"u" = (
-/obj/machinery/tcomms/relay/ruskie{
- network_id = "LISTENINGOUTPOST-RELAY"
+/obj/item/storage/box/donkpockets{
+ pixel_x = 3;
+ pixel_y = 3
},
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"w" = (
-/obj/machinery/light/small,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"x" = (
-/obj/structure/rack,
-/obj/item/clothing/suit/space/syndicate,
-/obj/item/clothing/mask/gas,
-/obj/item/clothing/head/helmet/space/syndicate,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"z" = (
-/obj/structure/disposalpipe/segment{
- dir = 4;
- icon_state = "pipe-c"
+/obj/item/storage/box/donkpockets{
+ pixel_x = -2;
+ pixel_y = 6
},
-/turf/simulated/mineral,
-/area/ruin/space/powered)
-"A" = (
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"oE" = (
+/obj/machinery/atmospherics/portable/scrubber,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"pV" = (
+/obj/machinery/power/solar,
+/obj/structure/cable{
+ d2 = 2;
+ icon_state = "0-2"
+ },
+/turf/simulated/floor/plasteel/airless{
+ icon_state = "solarpanel"
+ },
+/area/ruin/space/syndicate_listening_station/asteroid)
+"rK" = (
/obj/structure/disposalpipe/segment{
dir = 4
},
-/turf/simulated/mineral,
-/area/ruin/space/powered)
-"B" = (
-/obj/structure/disposaloutlet{
- dir = 4
+/turf/simulated/mineral/ancient,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"rS" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
},
-/obj/structure/disposalpipe/trunk{
- dir = 8
+/obj/structure/falsewall/rock_ancient,
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"sf" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
+ },
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 4;
+ icon_state = "2-4"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4";
+ tag = "90Curve"
+ },
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"sq" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
+ },
+/obj/structure/falsewall/rock_ancient,
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"tk" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
},
/turf/simulated/floor/plating/airless,
-/area/ruin/space/powered)
-"C" = (
-/obj/structure/bed,
-/obj/item/bedsheet/brown,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"D" = (
+/area/ruin/space/syndicate_listening_station/asteroid)
+"tF" = (
+/obj/machinery/power/smes/upgraded{
+ charge = 5e+006;
+ input_level = 20000;
+ output_level = 100000
+ },
+/obj/structure/cable{
+ d2 = 2;
+ icon_state = "0-2";
+ pixel_y = 1
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"tG" = (
/obj/structure/table,
-/obj/item/flashlight/lamp/green,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"E" = (
-/obj/machinery/light/small{
+/obj/item/paper{
+ info = "Mission Details: You have been assigned to a newly constructed listening post constructed within an asteroid in Nanotrasen space to monitor their plasma mining operations. Accurate intel is crucial to the success of our operatives onboard, do not fail us.";
+ name = "mission briefing"
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"un" = (
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"uv" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4"
+ },
+/obj/structure/table,
+/obj/machinery/kitchen_machine/microwave/upgraded,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"vd" = (
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/wood,
+/area/ruin/space/syndicate_listening_station)
+"vP" = (
+/obj/structure/cable,
+/obj/machinery/power/solar_control/autostart{
dir = 1
},
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"F" = (
-/obj/machinery/economy/vending/snack,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"G" = (
-/obj/structure/disposalpipe/segment,
-/turf/simulated/mineral,
-/area/ruin/space/powered)
-"H" = (
-/obj/machinery/economy/vending/cola,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"I" = (
-/obj/structure/closet,
-/obj/item/clothing/gloves/boxing,
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"J" = (
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"wj" = (
+/obj/machinery/door/airlock/hatch/syndicate{
+ name = "Telecommunications"
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"yn" = (
+/turf/simulated/mineral/ancient,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"yv" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"zb" = (
+/obj/machinery/door/airlock/external{
+ id_tag = "sst_away";
+ req_access_txt = "150"
+ },
+/obj/structure/fans/tiny,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"zs" = (
+/obj/machinery/shower{
+ pixel_y = 20
+ },
+/obj/item/soap/syndie,
+/turf/simulated/floor/mineral/silver,
+/area/ruin/space/syndicate_listening_station)
+"Ab" = (
+/obj/structure/closet/syndicate,
+/obj/item/clothing/glasses/regular,
+/obj/item/storage/firstaid,
+/obj/item/storage/firstaid,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"AT" = (
+/turf/simulated/floor/plating/asteroid/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"Bt" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"Bu" = (
+/obj/machinery/tcomms/relay/ruskie{
+ network_id = "SYNDIE-LPOST-RELAY"
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"BD" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"Cw" = (
/obj/structure/filingcabinet,
/obj/item/paper{
info = "A good start to the operation: intercepted Nanotrasen military communications. A convoy is scheduled to transfer nuclear warheads to a new military base. This is as good a chance as any to get our hands on some heavy weaponry, I suggest we take it.";
@@ -199,1753 +339,1979 @@
info = "1 x Stechtkin pistol - $600
1 x silencer - $200
shipping charge - $4360
total - $5160";
name = "receipt"
},
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"K" = (
+/obj/item/tape,
+/obj/item/tape,
+/obj/item/tape,
+/obj/item/tape,
+/obj/item/tape,
+/obj/item/tape,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"CF" = (
+/obj/machinery/light{
+ dir = 4
+ },
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/economy/vending/cigarette/free,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"CG" = (
+/obj/structure/closet/secure_closet/personal/cabinet,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/wood,
+/area/ruin/space/syndicate_listening_station)
+"CI" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
+ },
+/obj/machinery/light/small{
+ dir = 1
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"DT" = (
+/obj/machinery/light{
+ dir = 8
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"Ej" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"Em" = (
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/machinery/power/terminal,
+/obj/effect/spawner/random_spawners/cobweb_right_rare,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"FW" = (
/obj/structure/table,
-/obj/item/paper{
- info = "Mission Details: You have been assigned to a newly constructed listening post constructed within an asteroid in Nanotrasen space to monitor their plasma mining operations. Accurate intel is crucial to the success of our operatives onboard, do not fail us.";
- name = "mission briefing"
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"Go" = (
+/mob/living/simple_animal/hostile/syndicate{
+ desc = "A weary looking Syndicate operative.";
+ environment_smash = 0
},
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"L" = (
-/obj/machinery/disposal,
-/obj/structure/disposalpipe/trunk{
- dir = 4
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"Gy" = (
+/obj/structure/falsewall/rock_ancient,
+/turf/simulated/floor/plating/asteroid/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"GQ" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
},
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"M" = (
-/obj/structure/disposalpipe/segment{
+/obj/machinery/atmospherics/portable/canister/air,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"Hg" = (
+/obj/structure/closet/syndicate,
+/obj/item/clothing/suit/space/syndicate/black/red,
+/obj/item/clothing/head/helmet/space/syndicate/black/red,
+/obj/item/clothing/mask/gas/syndicate,
+/obj/item/tank/internals/oxygen,
+/obj/item/tank/jetpack/oxygen,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"HZ" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 8;
+ icon_state = "1-8"
+ },
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"Km" = (
+/obj/machinery/light/small{
+ dir = 1
+ },
+/obj/structure/dresser,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/wood,
+/area/ruin/space/syndicate_listening_station)
+"Mv" = (
+/obj/item/bedsheet/syndie,
+/obj/structure/bed/pod,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/wood,
+/area/ruin/space/syndicate_listening_station)
+"MF" = (
+/obj/structure/cable,
+/obj/machinery/power/tracker,
+/turf/simulated/floor/plasteel/airless{
+ icon_state = "solarpanel"
+ },
+/area/ruin/space/syndicate_listening_station/asteroid)
+"MO" = (
+/obj/machinery/light{
dir = 4
},
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"Nd" = (
+/turf/simulated/floor/mineral/silver,
+/area/ruin/space/syndicate_listening_station)
+"Ow" = (
+/obj/machinery/door/airlock/external{
+ id_tag = "sst_away";
+ req_access_txt = "150"
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"OO" = (
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 4;
+ icon_state = "2-4"
+ },
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"Pc" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"PH" = (
+/obj/item/gps/ruin{
+ gpstag = "Encrypted Signal"
+ },
/turf/simulated/wall/r_wall,
-/area/ruin/space/powered)
-"N" = (
-/obj/structure/disposalpipe/segment{
- dir = 8;
- icon_state = "pipe-c"
- },
-/turf/simulated/mineral,
-/area/ruin/space/powered)
-"O" = (
-/obj/machinery/door/airlock{
- name = "Toilet"
- },
-/turf/simulated/floor/plasteel,
-/area/ruin/space/powered)
-"P" = (
-/obj/machinery/light/small{
- dir = 8
+/area/ruin/space/syndicate_listening_station)
+"PP" = (
+/obj/machinery/light/small,
+/turf/simulated/floor/mineral/silver,
+/area/ruin/space/syndicate_listening_station)
+"RN" = (
+/obj/machinery/door/airlock/silver{
+ name = "Bathroom"
},
-/turf/simulated/floor/plasteel{
- icon_state = "freezerfloor"
+/turf/simulated/floor/mineral/silver,
+/area/ruin/space/syndicate_listening_station)
+"Sq" = (
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
},
-/area/ruin/space/powered)
-"Q" = (
-/obj/machinery/shower{
- dir = 8
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"TS" = (
+/obj/machinery/door/airlock/external{
+ id_tag = "sst_away";
+ req_access_txt = "150"
},
-/turf/simulated/floor/plasteel{
- icon_state = "freezerfloor"
+/obj/structure/cable{
+ d1 = 4;
+ d2 = 8;
+ icon_state = "4-8";
+ tag = ""
},
-/area/ruin/space/powered)
-"R" = (
-/turf/simulated/floor/plasteel{
- icon_state = "freezerfloor"
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"TZ" = (
+/obj/structure/table,
+/obj/item/paper{
+ info = "Nothing of interest to report.";
+ name = "november report"
},
-/area/ruin/space/powered)
-"S" = (
+/obj/item/pen,
+/obj/item/tape,
+/obj/item/radio/intercom{
+ freerange = 1;
+ pixel_y = -24;
+ name = "intercom"
+ },
+/obj/item/paper_bin,
+/obj/item/pen,
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"UM" = (
+/obj/machinery/door/airlock/hatch/syndicate{
+ name = "Engineering"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2";
+ tag = ""
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"Vn" = (
+/obj/machinery/computer/message_monitor{
+ dir = 1
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"WN" = (
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 4;
+ icon_state = "2-4"
+ },
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 4;
+ icon_state = "1-4";
+ tag = "90Curve"
+ },
+/turf/simulated/floor/plating/airless,
+/area/ruin/space/syndicate_listening_station/asteroid)
+"Xm" = (
/obj/structure/toilet{
dir = 8
},
-/turf/simulated/floor/plasteel{
- icon_state = "freezerfloor"
+/turf/simulated/floor/mineral/silver,
+/area/ruin/space/syndicate_listening_station)
+"Xz" = (
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 4;
+ icon_state = "2-4"
},
-/area/ruin/space/powered)
-"Y" = (
-/obj/item/gps/ruin,
-/turf/simulated/wall/r_wall,
-/area/ruin/space/powered)
+/obj/structure/cable{
+ d1 = 2;
+ d2 = 8;
+ icon_state = "2-8";
+ tag = ""
+ },
+/obj/effect/decal/cleanable/dirt,
+/turf/simulated/floor/plating,
+/area/ruin/space/syndicate_listening_station)
+"Ya" = (
+/obj/machinery/power/apc/off_station{
+ dir = 4;
+ pixel_x = 24;
+ req_access = list(150)
+ },
+/obj/structure/cable{
+ d2 = 8;
+ icon_state = "0-8"
+ },
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/economy/vending/snack/free,
+/turf/simulated/floor/plasteel/dark,
+/area/ruin/space/syndicate_listening_station)
+"Zw" = (
+/turf/template_noop,
+/area/template_noop)
(1,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+pV
+WN
+mC
+AT
+pV
+WN
+mC
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(2,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+AT
+pV
+sf
+mC
+AT
+pV
+sf
+mC
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(3,1,1) = {"
-a
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-d
-d
-d
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-a
-c
-c
-a
-c
-a
-a
-a
-a
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+AT
+pV
+sf
+mC
+AT
+pV
+sf
+mC
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+Zw
+yn
+yn
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
"}
(4,1,1) = {"
-a
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-d
-d
-d
-d
-d
-d
-c
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-a
-c
-c
-c
-a
-c
+Zw
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+pV
+sf
+mC
+AT
+pV
+sf
+mC
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+yn
+Zw
+yn
+yn
+yn
+Zw
+yn
"}
(5,1,1) = {"
-a
-a
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-d
-d
-d
-d
-c
-d
-c
-c
-a
-a
-a
-a
-a
-a
-a
-c
-c
-a
-a
-c
-c
-c
-c
-a
+Zw
+Zw
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+yn
+AT
+hr
+AT
+AT
+AT
+hr
+AT
+AT
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+Zw
+Zw
+yn
+yn
+yn
+yn
+Zw
"}
(6,1,1) = {"
-a
-a
-c
-c
-c
-c
-c
-a
-a
-a
-a
-a
-c
-c
-c
-d
-d
-d
-d
-c
-c
-c
-c
-d
-a
-a
-a
-c
-a
-a
-a
-c
-c
-a
-c
-c
-c
-a
-c
-a
+Zw
+Zw
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+OO
+tk
+BD
+tk
+tk
+tk
+BD
+tk
+tk
+tk
+MF
+AT
+yn
+Zw
+Zw
+Zw
+yn
+yn
+Zw
+yn
+yn
+yn
+Zw
+yn
+Zw
"}
(7,1,1) = {"
-a
-a
-c
-c
-c
-c
-c
-a
-a
-a
-a
-d
-c
-c
-d
-d
-d
-d
-d
-c
-c
-c
-d
-d
-c
-a
-a
-c
-c
-a
-a
-a
-c
-c
-c
-c
-a
-a
-a
-a
+Zw
+Zw
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+yn
+sq
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+yn
+AT
+AT
+yn
+yn
+Zw
+Zw
+Zw
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
"}
(8,1,1) = {"
-a
-a
-a
-c
-c
-c
-c
-c
-a
-a
-d
-d
-c
-d
-d
-d
-d
-d
-d
-d
-c
-d
-d
-c
-c
-c
-c
-c
-c
-a
-a
-a
-a
-c
-c
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+yn
+yn
+yn
+yn
+OO
+HZ
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(9,1,1) = {"
-a
-a
-a
-a
-c
-a
-a
-c
-a
-c
-d
-d
-d
-d
-d
-d
-c
-c
-c
-d
-d
-d
-d
-c
-c
-c
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+yn
+Zw
+Zw
+yn
+Zw
+yn
+yn
+OO
+rS
+tk
+HZ
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(10,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-d
-d
-d
-d
-d
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-a
-c
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+hr
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(11,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-d
-d
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+kE
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(12,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+Ej
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(13,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+kJ
+TS
+kJ
+kJ
+kJ
+yn
+yn
+kJ
+kJ
+kJ
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(14,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-f
-f
-f
-f
-f
-c
-c
-c
-c
-c
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+kJ
+Xz
+Bt
+vP
+kJ
+yn
+yn
+kJ
+CG
+vd
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(15,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-c
-f
-C
-m
-I
-f
-c
-c
-c
-c
-c
-c
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+kJ
+GQ
+du
+bU
+kJ
+yn
+yn
+kJ
+Km
+at
+kJ
+kJ
+kJ
+kJ
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(16,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-c
-c
-c
-c
-f
-f
-f
-f
-f
-D
-m
-J
-f
-c
-c
-c
-c
-c
-c
-c
-c
-d
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+kJ
+GQ
+du
+oE
+kJ
+yn
+yn
+kJ
+Mv
+vd
+kJ
+Cw
+DT
+iB
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(17,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-c
-c
-f
-f
-p
-Y
-u
-f
-j
-i
-j
-f
-c
-c
-c
-c
-c
-c
-c
-d
-d
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+CI
+du
+du
+kJ
+kJ
+kJ
+kJ
+kJ
+eg
+kJ
+un
+un
+TZ
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(18,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-c
-c
-f
-k
-q
-m
-m
-i
-m
-m
-K
-f
-f
-f
-f
-c
-c
-c
-d
-d
-d
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+Em
+tF
+Pc
+UM
+yv
+nC
+uv
+FW
+un
+wj
+un
+Go
+Vn
+kJ
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(19,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-f
-f
-f
-l
-r
-s
-w
-j
-E
-m
-m
-O
-P
-R
-f
-c
-c
-c
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+kJ
+kJ
+kJ
+kJ
+un
+un
+Sq
+un
+un
+kJ
+un
+un
+tG
+kJ
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(20,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-f
-g
-i
-m
-m
-m
-m
-j
-m
-m
-L
-j
-Q
-S
-f
-c
-c
-d
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+zs
+PP
+PH
+un
+un
+Sq
+un
+un
+kJ
+Ab
+MO
+Bu
+kJ
+yn
+yn
+yn
+yn
+AT
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(21,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-f
-h
-j
-m
-m
-t
-x
-j
-F
-H
-M
-f
-f
-f
-f
-c
-c
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+Xm
+Nd
+RN
+un
+un
+Sq
+un
+un
+kJ
+kJ
+kJ
+kJ
+kJ
+yn
+yn
+yn
+yn
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(22,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-f
-f
-f
-n
-n
-f
-f
-f
-f
-f
-M
-c
-c
-c
-c
-c
-d
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+kJ
+kJ
+kJ
+kJ
+un
+Hg
+Ya
+CF
+hp
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(23,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-c
-c
-f
-o
-g
-f
-c
-c
-c
-c
-A
-c
-c
-c
-c
-c
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+yn
+kJ
+Ow
+kJ
+kJ
+kJ
+aW
+kJ
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(24,1,1) = {"
-a
-a
-a
-a
-c
-c
-c
-a
-a
-a
-c
-c
-c
-c
-f
-g
-g
-f
-c
-z
-G
-G
-N
-c
-c
-c
-c
-d
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+Zw
+Zw
+Zw
+yn
+yn
+yn
+yn
+kJ
+du
+kJ
+yn
+yn
+rK
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(25,1,1) = {"
-a
-a
-a
-a
-a
-c
-c
-a
-a
-a
-a
-c
-c
-c
-f
-n
-n
-f
-c
-A
-c
-c
-c
-c
-c
-c
-c
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+kJ
+zb
+kJ
+yn
+yn
+rK
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(26,1,1) = {"
-a
-a
-a
-c
-a
-c
-c
-c
-a
-a
-a
-c
-c
-c
-a
-a
-a
-c
-c
-A
-c
-c
-c
-c
-c
-c
-c
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+yn
+Zw
+yn
+yn
+yn
+Zw
+Zw
+Zw
+yn
+yn
+yn
+AT
+AT
+AT
+yn
+yn
+rK
+yn
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(27,1,1) = {"
-a
-a
-a
-a
-a
-a
-c
-c
-a
-a
-a
-a
-c
-c
-a
-a
-a
-c
-c
-B
-c
-c
-c
-c
-c
-c
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+AT
+AT
+yn
+yn
+yn
+iL
+yn
+yn
+yn
+yn
+yn
+yn
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(28,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-c
-a
-a
-c
-c
-a
-c
-c
-c
-a
-c
-d
-d
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+Gy
+yn
+yn
+yn
+Zw
+yn
+yn
+yn
+Zw
+yn
+AT
+AT
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(29,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-c
-a
-a
-a
-c
-a
-c
-c
-a
-a
-c
-d
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+Zw
+Zw
+Zw
+yn
+Zw
+yn
+yn
+Zw
+Zw
+yn
+AT
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(30,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(31,1,1) = {"
-a
-a
-a
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(32,1,1) = {"
-a
-a
-c
-c
-c
-a
-a
-a
-a
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(33,1,1) = {"
-a
-a
-c
-c
-c
-c
-a
-a
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+yn
+yn
+yn
+yn
+Zw
+Zw
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(34,1,1) = {"
-a
-a
-a
-c
-c
-c
-a
-a
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+yn
+yn
+yn
+Zw
+Zw
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(35,1,1) = {"
-a
-a
-c
-a
-a
-a
-a
-a
-a
-c
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(36,1,1) = {"
-a
-a
-c
-a
-a
-a
-a
-a
-a
-a
-c
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(37,1,1) = {"
-a
-a
-a
-a
-a
-a
-c
-a
-a
-a
-a
-c
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+yn
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(38,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(39,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
(40,1,1) = {"
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
-a
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
+Zw
"}
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm b/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm
index 4925559542a6..276b3d8a977b 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/moonoutpost19.dmm
@@ -2022,7 +2022,7 @@
status = 2
},
/obj/item/paper{
- info = "
Something is blocking the way!")
- movedelay = world.time + movespeed
+ return
+ var/turf/possible_1 = get_step(src, turn(direction, 45))
+ var/turf/possible_2 = get_step(src, turn(direction, -45))
+ if(can_move(possible_1))
+ forceMove(possible_1)
+ return
+ if(can_move(possible_2))
+ forceMove(possible_2)
+ return
+ to_chat(user, "Something is blocking the way!")
/obj/effect/dummy/spell_jaunt/proc/can_move(turf/T)
if(T.flags & NOJAUNT)
diff --git a/code/datums/spells/lichdom.dm b/code/datums/spells/lichdom.dm
index 5327f2d18d44..69a5045b79cf 100644
--- a/code/datums/spells/lichdom.dm
+++ b/code/datums/spells/lichdom.dm
@@ -70,7 +70,7 @@
lich.real_name = M.mind.name
M.mind.transfer_to(lich)
lich.set_species(/datum/species/skeleton/lich) // Wizard variant
- to_chat(lich, "Your bones clatter and shutter as they're pulled back into this world!")
+ to_chat(lich, "Your bones clatter and shudder as they're pulled back into this world!")
cooldown_handler.recharge_duration += 1 MINUTES
var/mob/old_body = current_body
var/turf/body_turf = get_turf(old_body)
diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm
index b8475adc12e1..05880f7c2d03 100644
--- a/code/datums/spells/summonitem.dm
+++ b/code/datums/spells/summonitem.dm
@@ -87,6 +87,10 @@
if(is_type_in_typecache(item_to_retrieve.loc, blacklisted_summons))
break
item_to_retrieve = item_to_retrieve.loc
+ if(istype(item_to_retrieve, /obj/item/storage/backpack/modstorage))
+ var/obj/item/storage/backpack/modstorage/bag = item_to_retrieve
+ if(bag.source && bag.source.mod)
+ item_to_retrieve = bag.source.mod //Grab the modsuit.
infinite_recursion += 1
diff --git a/code/datums/spells/wizard_spells.dm b/code/datums/spells/wizard_spells.dm
index 61e5b013eaf8..77a8ccfd9b98 100644
--- a/code/datums/spells/wizard_spells.dm
+++ b/code/datums/spells/wizard_spells.dm
@@ -225,11 +225,13 @@
invocation_type = "shout"
cooldown_min = 100
summon_amt = 1
+ delay = 0
action_icon_state = "time"
summon_type = list(/obj/effect/timestop/wizard)
aoe_range = 0
+
/obj/effect/proc_holder/spell/aoe/conjure/carp
name = "Summon Carp"
desc = "This spell conjures a simple carp."
diff --git a/code/datums/statclick.dm b/code/datums/statclick.dm
index 130c90ab3940..4ee7a85fda79 100644
--- a/code/datums/statclick.dm
+++ b/code/datums/statclick.dm
@@ -18,7 +18,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick)
var/class
/obj/effect/statclick/debug/Click()
- if(!is_admin(usr) || !target)
+ if(!check_rights(R_DEBUG|R_VIEWRUNTIMES) || !target)
return
if(!class)
if(istype(target, /datum/controller/subsystem))
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 946188aec14a..4e57df1548ee 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -265,67 +265,99 @@
H.remove_hud_from(owner)
/datum/status_effect/hippocraticOath/tick()
+ // Death transforms you into a snake after a short grace period
if(owner.stat == DEAD)
if(deathTick < 4)
- deathTick += 1
- else
- owner.visible_message("[owner]'s soul is absorbed into the rod, relieving the previous snake of its duty.")
- var/mob/living/simple_animal/hostile/retaliate/poison/snake/healSnake = new(owner.loc)
- var/list/chems = list("bicaridine", "perfluorodecalin", "kelotane")
- healSnake.poison_type = pick(chems)
- healSnake.name = "Asclepius's Snake"
- healSnake.real_name = "Asclepius's Snake"
- healSnake.desc = "A mystical snake previously trapped upon the Rod of Asclepius, now freed of its burden. Unlike the average snake, its bites contain chemicals with minor healing properties."
- new /obj/effect/decal/cleanable/ash(owner.loc)
- new /obj/item/rod_of_asclepius(owner.loc)
- qdel(owner)
- else
- if(ishuman(owner))
- var/mob/living/carbon/human/itemUser = owner
- //Because a servant of medicines stops at nothing to help others, lets keep them on their toes and give them an additional boost.
- if(itemUser.health < itemUser.maxHealth)
- new /obj/effect/temp_visual/heal(get_turf(itemUser), COLOR_HEALING_GREEN)
- itemUser.adjustBruteLoss(-1.5)
- itemUser.adjustFireLoss(-1.5)
- itemUser.adjustToxLoss(-1.5)
- itemUser.adjustOxyLoss(-1.5)
- itemUser.adjustStaminaLoss(-1.5)
- itemUser.adjustBrainLoss(-1.5)
- itemUser.adjustCloneLoss(-0.5) //Becasue apparently clone damage is the bastion of all health
- if(heal_points < max_heal_points)
- heal_points = min(heal_points += 3, max_heal_points)
- //Heal all those around you, unbiased
- for(var/mob/living/L in view(7, owner))
- if(heal_points <= 0)
- break
- if(L.health < L.maxHealth)
- new /obj/effect/temp_visual/heal(get_turf(L), COLOR_HEALING_GREEN)
- if(iscarbon(L))
- L.adjustBruteLoss(-3.5)
- L.adjustFireLoss(-3.5)
- L.adjustToxLoss(-3.5)
- L.adjustOxyLoss(-3.5)
- L.adjustStaminaLoss(-3.5)
- L.adjustBrainLoss(-3.5)
- L.adjustCloneLoss(-1) //Becasue apparently clone damage is the bastion of all health
- heal_points--
- if(ishuman(L))
- var/mob/living/carbon/human/H = L
- for(var/obj/item/organ/external/E in H.bodyparts)
- if(prob(10))
- E.mend_fracture()
- E.fix_internal_bleeding()
- E.fix_burn_wound(update_health = FALSE)
- heal_points--
- else if(issilicon(L))
- L.adjustBruteLoss(-3.5)
- L.adjustFireLoss(-3.5)
+ deathTick++
+ return
+
+ owner.visible_message("[owner]'s soul is absorbed into the rod, relieving the previous snake of its duty.")
+ var/mob/living/simple_animal/hostile/retaliate/poison/snake/healSnake = new(owner.loc)
+ var/list/chems = list("bicaridine", "perfluorodecalin", "kelotane")
+ healSnake.poison_type = pick(chems)
+ healSnake.name = "Asclepius's Snake"
+ healSnake.real_name = "Asclepius's Snake"
+ healSnake.desc = "A mystical snake previously trapped upon the Rod of Asclepius, now freed of its burden. Unlike the average snake, its bites contain chemicals with minor healing properties."
+ new /obj/effect/decal/cleanable/ash(owner.loc)
+ new /obj/item/rod_of_asclepius(owner.loc)
+ qdel(owner)
+ return
+
+ // A servant of medicines stops at nothing to help others, let's give them an additional boost
+ if(ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ if(H.getBruteLoss() || H.getFireLoss() || H.getOxyLoss() || H.getToxLoss() || H.getBrainLoss() || H.getStaminaLoss() || H.getCloneLoss()) // Avoid counting burn wounds
+ H.adjustBruteLoss(-1.5, robotic = TRUE)
+ H.adjustFireLoss(-1.5, robotic = TRUE)
+ H.adjustOxyLoss(-1.5)
+ H.adjustToxLoss(-1.5)
+ H.adjustBrainLoss(-1.5)
+ H.adjustStaminaLoss(-1.5)
+ H.adjustCloneLoss(-0.5)
+ new /obj/effect/temp_visual/heal(get_turf(H), COLOR_HEALING_GREEN)
+
+ // Regenerate points passively
+ if(heal_points < max_heal_points)
+ heal_points = min(heal_points + 3, max_heal_points)
+
+ // The main course: heal everyone around you with the points we just gained!
+ for(var/mob/living/L in view(7, owner))
+ heal(L)
+ if(!heal_points)
+ break
+
+/datum/status_effect/hippocraticOath/proc/heal(mob/living/L)
+ var/starting_points = heal_points
+ var/force_particle = FALSE
+
+ if(ishuman(L))
+ heal_human(L)
+ else if(iscarbon(L))
+ if(L.health < L.maxHealth || L.getStaminaLoss()) // Carbons have no burn wounds to worry about nor brain damage to heal
+ L.adjustBruteLoss(-3.5)
+ L.adjustFireLoss(-3.5)
+ L.adjustOxyLoss(-3.5)
+ L.adjustToxLoss(-3.5)
+ L.adjustStaminaLoss(-3.5)
+ L.adjustCloneLoss(-1)
+ heal_points--
+ else if(issilicon(L))
+ if(L.health < L.maxHealth)
+ L.adjustBruteLoss(-3.5)
+ L.adjustFireLoss(-3.5)
+ heal_points--
+ else if(isanimal(L))
+ var/mob/living/simple_animal/SM = L
+ if(SM.health < SM.maxHealth)
+ SM.adjustHealth(-3.5)
+ force_particle = TRUE
+ if(prob(50)) // Animals are simpler
heal_points--
- else if(isanimal(L))
- var/mob/living/simple_animal/SM = L
- SM.adjustHealth(-3.5)
- if(prob(50))
- heal_points -- // Animals are simpler
+
+ if(starting_points < heal_points || force_particle)
+ new /obj/effect/temp_visual/heal(get_turf(L), COLOR_HEALING_GREEN)
+
+/datum/status_effect/hippocraticOath/proc/heal_human(mob/living/carbon/human/H)
+ if(H.getBruteLoss() || H.getFireLoss() || H.getOxyLoss() || H.getToxLoss() || H.getBrainLoss() || H.getStaminaLoss() || H.getCloneLoss()) // Avoid counting burn wounds
+ H.adjustBruteLoss(-3.5, robotic = TRUE)
+ H.adjustFireLoss(-3.5, robotic = TRUE)
+ H.adjustOxyLoss(-3.5)
+ H.adjustToxLoss(-3.5)
+ H.adjustBrainLoss(-3.5)
+ H.adjustStaminaLoss(-3.5)
+ H.adjustCloneLoss(-1)
+ heal_points--
+ if(!heal_points)
+ return
+
+ for(var/obj/item/organ/external/E in H.bodyparts)
+ if(prob(10) && (E.status & (ORGAN_BROKEN | ORGAN_INT_BLEEDING | ORGAN_BURNT)) && !E.is_robotic())
+ E.mend_fracture()
+ E.fix_internal_bleeding()
+ E.fix_burn_wound()
+ heal_points--
+ if(!heal_points)
+ return
/obj/screen/alert/status_effect/regenerative_core
name = "Reinforcing Tendrils"
@@ -448,7 +480,6 @@
to_chat(owner, "We collapse in exhaustion.")
owner.Weaken(6 SECONDS)
owner.emote("gasp")
- cling.genetic_damage += stacks
cling = null
/datum/status_effect/panacea
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 1f8b01d7ccb0..01dd5d8f3de9 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -49,9 +49,9 @@
status_type = STATUS_EFFECT_REPLACE
alert_type = null
var/mutable_appearance/marked_underlay
- var/obj/item/twohanded/kinetic_crusher/hammer_synced
+ var/obj/item/kinetic_crusher/hammer_synced
-/datum/status_effect/crusher_mark/on_creation(mob/living/new_owner, obj/item/twohanded/kinetic_crusher/new_hammer_synced)
+/datum/status_effect/crusher_mark/on_creation(mob/living/new_owner, obj/item/kinetic_crusher/new_hammer_synced)
. = ..()
if(.)
hammer_synced = new_hammer_synced
@@ -203,12 +203,12 @@
return
if(teleports < 6)
to_chat(M, "You feel a bit sick!")
- M.vomit(lost_nutrition = 15, blood = 0, stun = 0, distance = 0, message = 1)
+ M.vomit(lost_nutrition = 15, blood = 0, should_confuse = FALSE, distance = 0, message = 1)
M.Weaken(2 SECONDS)
else
to_chat(M, "You feel really sick!")
M.adjustBruteLoss(rand(0, teleports * 2))
- M.vomit(lost_nutrition = 30, blood = 0, stun = 0, distance = 0, message = 1)
+ M.vomit(lost_nutrition = 30, blood = 0, should_confuse = FALSE, distance = 0, message = 1)
M.Weaken(6 SECONDS)
/datum/status_effect/pacifism
@@ -294,10 +294,6 @@
/datum/status_effect/cling_tentacle/on_remove()
REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, "[id]")
-/datum/status_effect/cling_tentacle/batterer
- id = "cling_tentacle_batterer"
- alert_type = null
- duration = 7 SECONDS
// start of `living` level status procs.
/**
@@ -899,6 +895,11 @@
list("Your skin feels loose.", "You feel very strange.", "You feel a stabbing pain in your head!", "Your stomach churns."),
list("Your entire body vibrates.")
)
+ fake_emote = list(
+ list(),
+ list(),
+ list()
+ )
else
fake_msg = list(
list("Your chest hurts.", "Your stomach violently rumbles!", "You feel a cold sweat form."),
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index 0930a6f66a7b..37e7b52c53ee 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -62,9 +62,11 @@
var/sound_effect = 'sound/weapons/slap.ogg'
/// So we don't leave folks with god-mode
-/datum/status_effect/high_five/proc/wiz_cleanup(mob/user, mob/highfived)
+/datum/status_effect/high_five/proc/wiz_cleanup(mob/living/carbon/user, mob/living/carbon/highfived)
user.status_flags &= ~GODMODE
highfived.status_flags &= ~GODMODE
+ user.remove_status_effect(type)
+ highfived.remove_status_effect(type)
/datum/status_effect/high_five/on_apply()
if(!iscarbon(owner))
@@ -73,6 +75,7 @@
var/mob/living/carbon/user = owner
var/is_wiz = iswizard(user)
+ var/both_wiz = FALSE
for(var/mob/living/carbon/C in orange(1, user))
if(!C.has_status_effect(type) || C == user)
continue
@@ -82,18 +85,18 @@
C.status_flags |= GODMODE
explosion(get_turf(user), 5, 2, 1, 3, cause = id)
// explosions have a spawn so this makes sure that we don't get gibbed
- addtimer(CALLBACK(src, PROC_REF(wiz_cleanup), user, C), 1)
+ addtimer(CALLBACK(src, PROC_REF(wiz_cleanup), user, C), 0.3 SECONDS) //I want to be sure this lasts long enough, with lag.
add_attack_logs(user, C, "caused a wizard [id] explosion")
- user.remove_status_effect(type)
- C.remove_status_effect(type)
-
+ both_wiz = TRUE
user.do_attack_animation(C, no_effect = TRUE)
C.do_attack_animation(user, no_effect = TRUE)
- user.visible_message("[user.name] and [C.name] [success]")
playsound(user, sound_effect, 80)
- user.remove_status_effect(type)
- C.remove_status_effect(type)
- return FALSE
+ if(!both_wiz)
+ user.visible_message("[user.name] and [C.name] [success]")
+ user.remove_status_effect(type)
+ C.remove_status_effect(type)
+ return FALSE
+ return TRUE // DO NOT AUTOREMOVE
owner.custom_emote(EMOTE_VISIBLE, request)
owner.create_point_bubble_from_path(item_path, FALSE)
@@ -144,6 +147,10 @@
id = "charging"
alert_type = null
+/datum/status_effect/impact_immune
+ id = "impact_immune"
+ alert_type = null
+
#define LWAP_LOCK_CAP 10
/datum/status_effect/lwap_scope
diff --git a/code/datums/uplink_items/uplink_general.dm b/code/datums/uplink_items/uplink_general.dm
index 8e9697dd7635..6acc801f2b0f 100644
--- a/code/datums/uplink_items/uplink_general.dm
+++ b/code/datums/uplink_items/uplink_general.dm
@@ -24,7 +24,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
uplink_items[I.category] = list()
uplink_items[I.category] += I
- if(I.limited_stock < 0 && !I.cant_discount && I.item && I.cost > 1)
+ if(I.limited_stock < 0 && !I.cant_discount && I.item && I.cost > 5)
sales_items += I
for(var/datum/uplink_item/I in last)
@@ -40,18 +40,18 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
A.limited_stock = 1
I.refundable = FALSE
A.refundable = FALSE
- if(A.cost >= 20)
- discount *= 0.5 // If the item costs 20TC or more, it's only 25% off.
+ if(A.cost >= 100)
+ discount *= 0.5 // If the item costs 100TC or more, it's only 25% off.
A.cost = max(round(A.cost * (1-discount)),1)
A.category = "Discounted Gear"
A.name += " ([round(((initial(A.cost)-A.cost)/initial(A.cost))*100)]% off!)"
A.job = null // If you get a job specific item selected, actually lets you buy it in the discount section
+ A.species = null //same as above for species speific items
A.reference = "DIS[newreference]"
A.desc += " Limit of [A.limited_stock] per uplink. Normally costs [initial(A.cost)] TC."
A.surplus = 0 // stops the surplus crate potentially giving out a bit too much
A.item = I.item
newreference++
-
if(!uplink_items[A.category])
uplink_items[A.category] = list()
@@ -74,6 +74,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
var/list/uplinktypes = list() // Empty list means it is in all the uplink types. Otherwise place the uplink type here.
var/list/excludefrom = list() // Empty list does nothing. Place the name of uplink type you don't want this item to be available in here.
var/list/job = null
+ /// This makes an item on the uplink only show up to the specified species
+ var/list/species = null
var/surplus = 100 //Chance of being included in the surplus crate (when pick() selects it)
var/cant_discount = FALSE
var/limited_stock = -1 // Can you only buy so many? -1 allows for infinite purchases
@@ -167,14 +169,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "SPI"
desc = "A box containing a small, easily concealable handgun and two eight-round magazines chambered in 10mm auto rounds. Compatible with suppressors."
item = /obj/item/storage/box/syndie_kit/stechkin
- cost = 4
+ cost = 20
/datum/uplink_item/dangerous/revolver
name = "Syndicate .357 Revolver"
reference = "SR"
desc = "A brutally simple syndicate revolver that fires .357 Magnum cartridges and has 7 chambers. Comes with a spare speed loader."
item = /obj/item/storage/box/syndie_kit/revolver
- cost = 13
+ cost = 65
surplus = 50
/datum/uplink_item/dangerous/rapid
@@ -182,14 +184,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "These gloves let the user help, shove, grab, and punch people very fast. Does not improve weapon attack speed. Can be combined with martial arts for a deadly weapon."
reference = "RPGD"
item = /obj/item/clothing/gloves/fingerless/rapid
- cost = 8
+ cost = 40
/datum/uplink_item/dangerous/sword
name = "Energy Sword"
desc = "The energy sword is an edged weapon with a blade of pure energy. The sword is small enough to be pocketed when inactive. Activating it produces a loud, distinctive noise."
reference = "ES"
item = /obj/item/melee/energy/sword/saber
- cost = 8
+ cost = 40
/datum/uplink_item/dangerous/powerfist
name = "Power Fist"
@@ -199,28 +201,28 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
deal extra damage and hit targets further. Use a screwdriver to take out any attached tanks."
reference = "PF"
item = /obj/item/melee/powerfist
- cost = 10
+ cost = 50
/datum/uplink_item/dangerous/chainsaw
name = "Chainsaw"
desc = "A high powered chainsaw for cutting up ...you know...."
reference = "CH"
- item = /obj/item/twohanded/chainsaw
- cost = 13
+ item = /obj/item/butcher_chainsaw
+ cost = 65
/datum/uplink_item/dangerous/universal_gun_kit
name = "Universal Self Assembling Gun Kit"
desc = "A universal gun kit, that can be combined with any weapon kit to make a functioning RND gun of your own. Uses built in allen keys to self assemble, just combine the kits by hitting them together."
reference = "IKEA"
item = /obj/item/weaponcrafting/gunkit/universal_gun_kit
- cost = 8
+ cost = 25
/datum/uplink_item/dangerous/batterer
name = "Mind Batterer"
desc = "A dangerous syndicate device focused on crowd control and escapes. Causes brain damage, confusion, and other nasty effects to those surrounding the user. Has 5 charges."
reference = "BTR"
item = /obj/item/batterer
- cost = 5
+ cost = 25
// Ammunition
@@ -233,35 +235,35 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "An additional 8-round 10mm magazine for use in the syndicate pistol, loaded with rounds that are cheap but around half as effective as .357"
reference = "10MM"
item = /obj/item/ammo_box/magazine/m10mm
- cost = 1
+ cost = 5
/datum/uplink_item/ammo/pistolap
name = "Stechkin - 10mm Armour Piercing Magazine"
desc = "An additional 8-round 10mm magazine for use in the syndicate pistol, loaded with rounds that are less effective at injuring the target but penetrate protective gear."
reference = "10MMAP"
item = /obj/item/ammo_box/magazine/m10mm/ap
- cost = 2
+ cost = 10
/datum/uplink_item/ammo/pistolfire
name = "Stechkin - 10mm Incendiary Magazine"
desc = "An additional 8-round 10mm magazine for use in the syndicate pistol, loaded with incendiary rounds which ignite the target."
reference = "10MMFIRE"
item = /obj/item/ammo_box/magazine/m10mm/fire
- cost = 2
+ cost = 10
/datum/uplink_item/ammo/pistolhp
name = "Stechkin - 10mm Hollow Point Magazine"
desc = "An additional 8-round 10mm magazine for use in the syndicate pistol, loaded with rounds which are more damaging but ineffective against armour."
reference = "10MMHP"
item = /obj/item/ammo_box/magazine/m10mm/hp
- cost = 2
+ cost = 10
/datum/uplink_item/ammo/revolver
name = ".357 Revolver - Speedloader"
desc = "A speed loader that contains seven additional .357 Magnum rounds for the syndicate revolver. For when you really need a lot of things dead."
reference = "357"
item = /obj/item/ammo_box/a357
- cost = 3
+ cost = 15
// STEALTHY WEAPONS
@@ -272,11 +274,9 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
name = "Fiber Wire Garrote"
desc = "A length of fiber wire between two wooden handles, perfect for the discrete assassin. This weapon, when used on a target from behind \
will instantly put them in your grasp and silence them, as well as causing rapid suffocation. Does not work on those who do not need to breathe."
+ item = /obj/item/garrote
reference = "GAR"
- item = /obj/item/twohanded/garrote
- cost = 6
-
-
+ cost = 30
/datum/uplink_item/stealthy_weapons/cameraflash
name = "Camera Flash"
@@ -285,7 +285,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
Useful for stunning borgs and individuals without eye protection or blinding a crowd for a get away."
reference = "CF"
item = /obj/item/flash/cameraflash
- cost = 1
+ cost = 5
/datum/uplink_item/stealthy_weapons/throwingweapons
name = "Box of Throwing Weapons"
@@ -293,21 +293,21 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
throwing weapons. The bolas can knock a target down and the shurikens will embed into limbs."
reference = "STK"
item = /obj/item/storage/box/syndie_kit/throwing_weapons
- cost = 3
+ cost = 15
/datum/uplink_item/stealthy_weapons/edagger
name = "Energy Dagger"
desc = "A dagger made of energy that looks and functions as a pen when off."
reference = "EDP"
item = /obj/item/pen/edagger
- cost = 2
+ cost = 10
/datum/uplink_item/stealthy_weapons/foampistol
name = "Toy Gun (with Stun Darts)"
desc = "An innocent looking toy pistol designed to fire foam darts. Comes loaded with riot grade darts, to incapacitate a target."
reference = "FSPI"
item = /obj/item/gun/projectile/automatic/toy/pistol/riot
- cost = 3
+ cost = 15
surplus = 10
/datum/uplink_item/stealthy_weapons/false_briefcase
@@ -315,14 +315,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A modified briefcase capable of storing and firing a gun under a false bottom. Use a screwdriver to pry away the false bottom and make modifications. Distinguishable upon close examination due to the added weight."
reference = "FBBC"
item = /obj/item/storage/briefcase/false_bottomed
- cost = 3
+ cost = 15
/datum/uplink_item/stealthy_weapons/soap
name = "Syndicate Soap"
desc = "A sinister-looking surfactant used to clean blood stains to hide murders and prevent DNA analysis. You can also drop it underfoot to slip people."
reference = "SOAP"
item = /obj/item/soap/syndie
- cost = 1
+ cost = 5
surplus = 50
/datum/uplink_item/stealthy_weapons/RSG
@@ -330,21 +330,21 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A syndicate rapid syringe gun able to fill and fire syringes automatically from an internal reagent reservoir. Comes pre-loaded with 7 empty syringes, and has a maximum capacity of 14 syringes and 300u of reagents."
reference = "RSG"
item = /obj/item/gun/syringe/rapidsyringe/preloaded/half
- cost = 8
+ cost = 60
/datum/uplink_item/stealthy_weapons/poisonbottle
name = "Poison Bottle"
desc = "The Syndicate will ship a bottle containing 40 units of a randomly selected poison. The poison can range from highly irritating to incredibly lethal."
reference = "TPB"
item = /obj/item/reagent_containers/glass/bottle/traitor
- cost = 2
+ cost = 10
/datum/uplink_item/stealthy_weapons/silencer
name = "Universal Suppressor"
desc = "Fitted for use on any small caliber weapon with a threaded barrel, this suppressor will silence the shots of the weapon for increased stealth and superior ambushing capability."
reference = "US"
item = /obj/item/suppressor
- cost = 1
+ cost = 5
surplus = 10
/datum/uplink_item/stealthy_weapons/dehy_carp
@@ -352,7 +352,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "Just add water to make your very own hostile to everything space carp. It looks just like a plushie. The first person to squeeze it will be registered as its owner, who it will not attack. If no owner is registered, it'll just attack everyone."
reference = "DSC"
item = /obj/item/toy/plushie/carpplushie/dehy_carp
- cost = 1
+ cost = 5
// GRENADES AND EXPLOSIVES
@@ -365,42 +365,42 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "C-4 is plastic explosive of the common variety Composition C. Reliably destroys the object it's placed on, assuming it isn't bomb resistant. Does not stick to crewmembers. Will only destroy station floors if placed directly on it. It has a modifiable timer with a minimum setting of 10 seconds."
reference = "C4"
item = /obj/item/grenade/plastic/c4
- cost = 1
+ cost = 5
/datum/uplink_item/explosives/plastic_explosives_pack
name = "Pack of 5 C-4 Explosives"
desc = "A package containing 5 C-4 Explosives at a discounted price. For when you need that little bit extra for your sabotaging needs."
reference = "C4P"
item = /obj/item/storage/box/syndie_kit/c4
- cost = 4
+ cost = 20
/datum/uplink_item/explosives/syndicate_minibomb
name = "Syndicate Minibomb"
desc = "The minibomb is a grenade with a five-second fuse."
reference = "SMB"
item = /obj/item/grenade/syndieminibomb
- cost = 6
+ cost = 30
/datum/uplink_item/explosives/frag_grenade
name = "Fragmentation Grenade"
desc = "A frag grenade. Upon detonation, releases shrapnel that can embed in nearby victims."
reference = "FG"
item = /obj/item/grenade/frag
- cost = 2
+ cost = 10
/datum/uplink_item/explosives/frag_grenade_pack
name = "Pack of 5 Fragmentation Grenades"
desc = "A box of 5 frag grenades. Upon detonation, releases shrapnel that can embed in nearby victims. And it seems you'll have a LOT of victims."
reference = "FGP"
item = /obj/item/storage/box/syndie_kit/frag_grenades
- cost = 8
+ cost = 40
/datum/uplink_item/explosives/pizza_bomb
name = "Pizza Bomb"
desc = "A pizza box with a bomb taped inside of it. The timer needs to be set by opening the box; afterwards, opening the box again will trigger the detonation."
reference = "PB"
item = /obj/item/pizzabox/pizza_bomb
- cost = 6
+ cost = 30
surplus = 80
/datum/uplink_item/explosives/atmosn2ogrenades
@@ -408,7 +408,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A box of two (2) grenades that spread knockout gas over a large area. Equip internals before using one of these."
reference = "ANG"
item = /obj/item/storage/box/syndie_kit/atmosn2ogrenades
- cost = 8
+ cost = 40
/datum/uplink_item/explosives/emp
name = "EMP Grenades and bio-chip implanter Kit"
@@ -416,7 +416,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
security's energy weapons, and silicon lifeforms when you're in a tight spot."
reference = "EMPK"
item = /obj/item/storage/box/syndie_kit/emp
- cost = 2
+ cost = 10
// STEALTHY TOOLS
@@ -429,7 +429,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
it can also be used in a washing machine to forge clothing."
reference = "CHST"
item = /obj/item/stamp/chameleon
- cost = 1
+ cost = 5
surplus = 35
/datum/uplink_item/stealthy_tools/chameleonflag
@@ -437,7 +437,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A flag that can be disguised as any other known flag. There is a hidden spot in the pole to boobytrap the flag with a grenade or minibomb, which will detonate some time after the flag is set on fire."
reference = "CHFLAG"
item = /obj/item/flag/chameleon
- cost = 1
+ cost = 5
surplus = 35
/datum/uplink_item/stealthy_tools/chamsechud
@@ -445,42 +445,42 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A stolen Nanotrasen Security HUD with Syndicate chameleon technology implemented into it. Similarly to a chameleon jumpsuit, the HUD can be morphed into various other eyewear, while retaining the HUD qualities when worn."
reference = "CHHUD"
item = /obj/item/clothing/glasses/hud/security/chameleon
- cost = 2
+ cost = 10
/datum/uplink_item/stealthy_tools/thermal
name = "Thermal Chameleon Glasses"
desc = "These glasses are thermals with Syndicate chameleon technology built into them. They allow you to see organisms through walls by capturing the upper portion of the infra-red light spectrum, emitted as heat and light by objects. Hotter objects, such as warm bodies, cybernetic organisms and artificial intelligence cores emit more of this light than cooler objects like walls and airlocks."
reference = "THIG"
item = /obj/item/clothing/glasses/chameleon/thermal
- cost = 6
+ cost = 30
/datum/uplink_item/stealthy_tools/agent_card
name = "Agent ID Card"
desc = "Agent cards prevent artificial intelligences from tracking the wearer, and can copy access from other identification cards. The access is cumulative, so scanning one card does not erase the access gained from another."
reference = "AIDC"
item = /obj/item/card/id/syndicate
- cost = 2
+ cost = 10
/datum/uplink_item/stealthy_tools/chameleon_proj
name = "Chameleon-Projector"
desc = "Projects an image across a user, disguising them as an object scanned with it, as long as they don't move the projector from their hand. The disguised user cannot run and projectiles pass over them."
reference = "CP"
item = /obj/item/chameleon
- cost = 5
+ cost = 25
/datum/uplink_item/stealthy_tools/chameleon_counter
name = "Chameleon Counterfeiter"
desc = "This device disguises itself as any object scanned by it. The disguise is not a perfect replica and can be noticed when examined by an observer."
reference = "CC"
item = /obj/item/chameleon_counterfeiter
- cost = 2
+ cost = 10
/datum/uplink_item/stealthy_tools/camera_bug
name = "Camera Bug"
desc = "Enables you to view all cameras on the network to track a target. Also has 5 sticky hidden cameras, allowing you remote view of any object you can stick a camera on."
reference = "CB"
item = /obj/item/storage/box/syndie_kit/camera_bug
- cost = 1
+ cost = 5
surplus = 90
/datum/uplink_item/stealthy_tools/dnascrambler
@@ -488,14 +488,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A syringe with one injection that randomizes appearance and name upon use. A cheaper but less versatile alternative to an agent card and voice changer."
reference = "DNAS"
item = /obj/item/dnascrambler
- cost = 2
+ cost = 10
/datum/uplink_item/stealthy_tools/smugglersatchel
name = "Smuggler's Satchel"
desc = "This satchel is thin enough to be hidden in the gap between plating and tiling, great for stashing your stolen goods. Comes with a crowbar and a floor tile inside."
reference = "SMSA"
item = /obj/item/storage/backpack/satchel_flat
- cost = 2
+ cost = 10
surplus = 30
/datum/uplink_item/stealthy_tools/emplight
@@ -504,7 +504,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
Useful for disrupting headsets, cameras, and borgs during stealth operations."
reference = "EMPL"
item = /obj/item/flashlight/emp
- cost = 4
+ cost = 20
surplus = 30
/datum/uplink_item/stealthy_tools/cutouts
@@ -513,7 +513,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
spraycan for changing their appearances."
reference = "ADCC"
item = /obj/item/storage/box/syndie_kit/cutouts
- cost = 1
+ cost = 5
surplus = 20
/datum/uplink_item/stealthy_tools/safecracking
@@ -521,7 +521,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "Everything you need to quietly open a mechanical combination safe."
reference = "SCK"
item = /obj/item/storage/box/syndie_kit/safecracking
- cost = 1
+ cost = 5
// DEVICE AND TOOLS
@@ -534,35 +534,35 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "The cryptographic sequencer, also known as an emag, is a small card that unlocks hidden functions in electronic devices, subverts intended functions and characteristically breaks security mechanisms."
reference = "EMAG"
item = /obj/item/card/emag
- cost = 6
+ cost = 30
/datum/uplink_item/device_tools/access_tuner
name = "Access Tuner"
desc = "The access tuner is a small device that can interface with airlocks from range. It takes a few seconds to connect and can change the bolt state, open the door, or toggle emergency access."
reference = "HACK"
item = /obj/item/door_remote/omni/access_tuner
- cost = 6
+ cost = 30
/datum/uplink_item/device_tools/toolbox
name = "Fully Loaded Toolbox"
desc = "The syndicate toolbox is a suspicious black and red. Aside from tools, it comes with insulated gloves and a multitool."
reference = "FLTB"
item = /obj/item/storage/toolbox/syndicate
- cost = 1
+ cost = 5
/datum/uplink_item/device_tools/surgerybag
name = "Syndicate Surgery Duffelbag"
desc = "The Syndicate surgery duffelbag comes with a full set of surgery tools, a straightjacket and a muzzle. The bag itself is also made of very light materials and won't slow you down while it is equipped."
reference = "SSDB"
item = /obj/item/storage/backpack/duffel/syndie/med/surgery
- cost = 2
+ cost = 10
/datum/uplink_item/device_tools/bonerepair
name = "Prototype Nanite Autoinjector Kit"
desc = "Stolen prototype full body repair nanites. Contains one prototype nanite autoinjector and guide."
reference = "NCAI"
item = /obj/item/storage/box/syndie_kit/bonerepair
- cost = 2
+ cost = 10
/datum/uplink_item/device_tools/syndicate_teleporter
name = "Experimental Syndicate Teleporter"
@@ -573,14 +573,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
Comes with free chameleon mesons, to help you stay stylish while seeing through walls."
reference = "TELE"
item = /obj/item/storage/box/syndie_kit/teleporter
- cost = 8
+ cost = 40
//Space Suits and Hardsuits
/datum/uplink_item/suits
- category = "Space Suits and Hardsuits"
- surplus = 40
+ category = "Space Suits and MODsuits"
+ surplus = 10 //I am setting this to 10 as there are a bunch of modsuit parts in here that should be weighted to 10. Suits and modsuits adjusted below.
/datum/uplink_item/suits/space_suit
name = "Syndicate Space Suit"
@@ -589,16 +589,68 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
sightings, however. "
reference = "SS"
item = /obj/item/storage/box/syndie_kit/space
- cost = 4
+ cost = 20
+
+/datum/uplink_item/suits/thermal
+ name = "MODsuit Thermal Visor Module"
+ desc = "A visor for a MODsuit. Lets you see living beings through walls. Also provides night vision."
+ reference = "MSTV"
+ item = /obj/item/mod/module/visor/thermal
+ cost = 15 // Don't forget, you need to get a modsuit to go with this
+ surplus = 10 //You don't need more than
+
+/datum/uplink_item/suits/night
+ name = "MODsuit Night Visor Module"
+ desc = "A visor for a MODsuit. Lets you see clearer in the dark."
+ reference = "MSNV"
+ item = /obj/item/mod/module/visor/night
+ cost = 5 // It's night vision, rnd pumps out those goggles for anyone man.
+ surplus = 10 //You don't need more than one
+
+/datum/uplink_item/suits/plate_compression
+ name = "MODsuit Plate Compression Module"
+ desc = "A MODsuit module that lets the suit compress into a smaller size. Not compatible with storage modules, \
+ you will have to take that module out first."
+ reference = "MSPC"
+ item = /obj/item/mod/module/plate_compression
+ cost = 10
+/datum/uplink_item/suits/noslip
+ name = "MODsuit Anti-Slip Module"
+ desc = "A MODsuit module preventing the user from slipping on water. Already installed in the uplink modsuits."
+ reference = "MSNS"
+ item = /obj/item/mod/module/noslip
+ cost = 5
+/datum/uplink_item/suits/springlock_module
+ name = "Heavily Modified Springlock MODsuit Module"
+ desc = "A module that spans the entire size of the MOD unit, sitting under the outer shell. \
+ This mechanical exoskeleton pushes out of the way when the user enters and it helps in booting \
+ up, but was taken out of modern suits because of the springlock's tendency to \"snap\" back \
+ into place when exposed to humidity. You know what it's like to have an entire exoskeleton enter you? \
+ This version of the module has been modified to allow for near instant activation of the MODsuit. \
+ Useful for quickly getting your MODsuit on/off, or for taking care of a target via a tragic accident. \
+ It is hidden as a DNA lock module. It will block retraction for 10 seconds by default to allow you to follow \
+ up with smoke, but you can multitool the module to disable that."
+ reference = "FNAF"
+ item = /obj/item/mod/module/springlock/bite_of_87
+ cost = 5
+ surplus = 10
+
+/datum/uplink_item/suits/hidden_holster
+ name = "Hidden Holster Module"
+ desc = "A holster module disguised to look like a tether module. Requires a modsuit to put it in of course. Gun not included."
+ reference = "HHM"
+ item = /obj/item/mod/module/holster/hidden
+ cost = 5
+ surplus = 10
/datum/uplink_item/device_tools/binary
name = "Binary Translator Key"
desc = "A key, that when inserted into a radio headset, allows you to listen to and talk with artificial intelligences and cybernetic organisms in binary. To talk on the binary channel, type :+ before your radio message."
reference = "BITK"
item = /obj/item/encryptionkey/binary
- cost = 5
+ cost = 25
surplus = 75
/datum/uplink_item/device_tools/cipherkey
@@ -606,7 +658,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A key, that when inserted into a radio headset, allows you to listen to all station department channels as well as talk on an encrypted Syndicate channel."
reference = "SEK"
item = /obj/item/encryptionkey/syndicate
- cost = 2 //Nowhere near as useful as the Binary Key!
+ cost = 10 //Nowhere near as useful as the Binary Key!
surplus = 75
/datum/uplink_item/device_tools/hacked_module
@@ -614,14 +666,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. Be careful with their wording, as artificial intelligences may look for loopholes to exploit."
reference = "HAI"
item = /obj/item/aiModule/syndicate
- cost = 12
+ cost = 60
/datum/uplink_item/device_tools/powersink
name = "Power Sink"
desc = "When screwed to wiring attached to an electric grid, then activated, this large device places excessive load on the grid, causing a stationwide blackout. The sink cannot be carried because of its excessive size. Ordering this sends you a small beacon that will teleport the power sink to your location on activation."
reference = "PS"
item = /obj/item/radio/beacon/syndicate/power_sink
- cost = 10
+ cost = 50
/datum/uplink_item/device_tools/singularity_beacon
name = "Power Beacon"
@@ -631,7 +683,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
sends you a small beacon that will teleport the larger beacon to your location upon activation."
reference = "SNGB"
item = /obj/item/radio/beacon/syndicate
- cost = 6
+ cost = 30
surplus = 0
hijack_only = TRUE //This is an item only useful for a hijack traitor, as such, it should only be available in those scenarios.
cant_discount = TRUE
@@ -641,21 +693,21 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A pinpointer that tracks any specified coordinates, DNA string, high value item or the nuclear authentication disk."
reference = "ADVP"
item = /obj/item/pinpointer/advpinpointer
- cost = 4
+ cost = 20
/datum/uplink_item/device_tools/ai_detector
name = "Artificial Intelligence Detector" // changed name in case newfriends thought it detected disguised ai's
desc = "A functional multitool that turns red when it detects an artificial intelligence watching it or its holder. Knowing when an artificial intelligence is watching you is useful for knowing when to maintain cover."
reference = "AID"
item = /obj/item/multitool/ai_detect
- cost = 1
+ cost = 5
/datum/uplink_item/device_tools/jammer
name = "Radio Jammer"
desc = "When turned on this device will scramble any outgoing radio communications near you, making them hard to understand."
reference = "RJ"
item = /obj/item/jammer
- cost = 4
+ cost = 20
// IMPLANTS
@@ -668,35 +720,35 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A bio-chip injected into the body and later activated manually to break out of any restraints or grabs. Can be activated up to 4 times."
reference = "FI"
item = /obj/item/implanter/freedom
- cost = 5
+ cost = 25
/datum/uplink_item/implants/protofreedom
name = "Prototype Freedom Bio-chip"
desc = "A prototype bio-chip injected into the body and later activated manually to break out of any restraints or grabs. Can only be activated a singular time."
reference = "PFI"
item = /obj/item/implanter/freedom/prototype
- cost = 2
+ cost = 10
/datum/uplink_item/implants/storage
name = "Storage Bio-chip"
desc = "A bio-chip injected into the body, and later activated at the user's will. It will open a small subspace pocket capable of storing two items."
reference = "ESI"
item = /obj/item/implanter/storage
- cost = 8
+ cost = 40
/datum/uplink_item/implants/mindslave
name = "Mindslave Bio-chip"
desc = "A box containing a bio-chip implanter filled with a mindslave bio-chip that when injected into another person makes them loyal to you and your cause, unless of course they're already implanted by someone else. Loyalty ends if the implant is no longer in their system."
reference = "MI"
item = /obj/item/implanter/traitor
- cost = 10
+ cost = 50
/datum/uplink_item/implants/adrenal
name = "Adrenal Bio-chip"
desc = "A bio-chip injected into the body, and later activated manually to inject a chemical cocktail, which has a mild healing effect along with removing and reducing the time of all stuns and increasing movement speed. Can be activated up to 3 times."
reference = "AI"
item = /obj/item/implanter/adrenalin
- cost = 8
+ cost = 40
/datum/uplink_item/implants/stealthimplant
name = "Stealth Bio-chip"
@@ -704,7 +756,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
On activation, it will conceal you inside a chameleon cardboard box that is only revealed once someone bumps into it."
reference = "SI"
item = /obj/item/implanter/stealth
- cost = 8
+ cost = 40
// POINTLESS BADASSERY
@@ -717,7 +769,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "Strong flavor, dense smoke, infused with omnizine."
reference = "SYSM"
item = /obj/item/storage/fancy/cigarettes/cigpack_syndicate
- cost = 2
+ cost = 7
/datum/uplink_item/badass/syndiecash
name = "Syndicate Briefcase Full of Cash"
@@ -725,14 +777,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
The briefcase also feels a little heavier to hold; it has been manufactured to pack a little bit more of a punch if your client needs some convincing."
reference = "CASH"
item = /obj/item/storage/secure/briefcase/syndie
- cost = 1
+ cost = 5
/datum/uplink_item/badass/balloon
name = "For showing that you are The Boss"
desc = "A useless red balloon with the syndicate logo on it, which can blow the deepest of covers."
reference = "BABA"
item = /obj/item/toy/syndicateballoon
- cost = 20
+ cost = 100
cant_discount = TRUE
/datum/uplink_item/badass/bomber
@@ -740,7 +792,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "An awesome jacket to help you style on Nanotrasen with. The lining is made of a thin polymer to provide a small amount of armor. Does not provide any extra storage space."
reference = "JCKT"
item = /obj/item/clothing/suit/jacket/syndicatebomber
- cost = 1
+ cost = 3
+
+/datum/uplink_item/badass/tpsuit
+ name = "Syndicate Two-Piece Suit"
+ desc = "A snappy two-piece suit that any self-respecting Syndicate agent should wear. Perfect for professionals trying to go undetected, but moderately armored with experimental nanoweave in case things do get loud. Comes with two cashmere-lined pockets for maximum style and comfort."
+ reference = "SUIT"
+ item = /obj/item/clothing/suit/storage/lawyer/blackjacket/armored
+ cost = 5
/datum/uplink_item/bundles_TC
category = "Bundles and Telecrystals"
@@ -767,3 +826,17 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "RTCT"
item = /obj/item/stack/telecrystal/twenty
cost = 20
+
+/datum/uplink_item/bundles_TC/telecrystal/fifty
+ name = "50 Raw Telecrystals"
+ desc = "Fifty telecrystals in their rawest and purest form; can be utilized on active uplinks to increase their telecrystal count."
+ reference = "RTCB"
+ item = /obj/item/stack/telecrystal/fifty
+ cost = 50
+
+/datum/uplink_item/bundles_TC/telecrystal/hundred
+ name = "100 Raw Telecrystals"
+ desc = "One-hundred telecrystals in their rawest and purest form; can be utilized on active uplinks to increase their telecrystal count."
+ reference = "RTCH"
+ item = /obj/item/stack/telecrystal/hundred
+ cost = 100
diff --git a/code/datums/uplink_items/uplink_nuclear.dm b/code/datums/uplink_items/uplink_nuclear.dm
index eb0021a58420..4cb59dfef933 100644
--- a/code/datums/uplink_items/uplink_nuclear.dm
+++ b/code/datums/uplink_items/uplink_nuclear.dm
@@ -7,7 +7,7 @@
reference = "APS"
desc = "The automatic machine pistol version of the FK-69 'Stechkin' chambered in 10mm Auto with a detachable 20-round box magazine. Perfect for dual wielding or as backup."
item = /obj/item/gun/projectile/automatic/pistol/APS
- cost = 8
+ cost = 40
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/dangerous/smg
@@ -15,7 +15,7 @@
reference = "SMG"
desc = "A fully-loaded Scarborough Arms bullpup submachine gun that fires .45 rounds with a 20-round magazine and is compatible with suppressors."
item = /obj/item/gun/projectile/automatic/c20r
- cost = 14
+ cost = 70
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 40
@@ -24,7 +24,7 @@
desc = "A fully-loaded three-round burst carbine that uses 30-round 5.56mm magazines with a togglable underslung 40mm grenade launcher."
reference = "AR"
item = /obj/item/gun/projectile/automatic/m90
- cost = 18
+ cost = 90
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 50
@@ -33,7 +33,7 @@
desc = "A fully-loaded Aussec Armory belt-fed machine gun. This deadly weapon has a massive 50-round magazine of devastating 7.62x51mm ammunition."
reference = "LMG"
item = /obj/item/gun/projectile/automatic/l6_saw
- cost = 40
+ cost = 200
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
@@ -42,7 +42,7 @@
desc = "Ranged fury, Syndicate style. guaranteed to cause shock and awe or your TC back!"
reference = "SSR"
item = /obj/item/gun/projectile/automatic/sniper_rifle/syndicate
- cost = 16
+ cost = 80
surplus = 25
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -51,7 +51,7 @@
desc = "Not many things can survive a direct hit from this. (Ammunition sold separately, keep away from children.)"
reference = "RL"
item = /obj/item/gun/rocketlauncher
- cost = 8
+ cost = 40
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/dangerous/flamethrower
@@ -59,16 +59,16 @@
desc = "A flamethrower, fuelled by a portion of highly flammable bio-toxins stolen previously from Nanotrasen stations. Make a statement by roasting the filth in their own greed. Use with caution."
reference = "FT"
item = /obj/item/flamethrower/full/tank
- cost = 1
+ cost = 5
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 40
/datum/uplink_item/dangerous/combat_defib
- name = "Combat defibrillator"
- desc = "A lifesaving device turned dangerous weapon. Click on someone with the paddles on harm intent to instantly stop their heart. Can be used as a regular defib as well."
+ name = "Combat Defibrillator Module"
+ desc = "A lifesaving device turned dangerous weapon. Click on someone with the paddles on harm intent to instantly stop their heart. Can be used as a regular defib as well. Installs in a modsuit."
reference = "CD"
- item = /obj/item/defibrillator/compact/combat/loaded
- cost = 12
+ item = /obj/item/mod/module/defibrillator/combat
+ cost = 60
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/dangerous/foamsmg
@@ -76,7 +76,7 @@
desc = "A fully-loaded Donksoft bullpup submachine gun that fires riot grade rounds with a 20-round magazine."
reference = "FSMG"
item = /obj/item/gun/projectile/automatic/c20r/toy
- cost = 5
+ cost = 25
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
@@ -85,7 +85,7 @@
desc = "A fully-loaded Donksoft belt-fed machine gun. This weapon has a massive 50-round magazine of devastating riot grade darts, that can briefly incapacitate someone in just one volley."
reference = "FLMG"
item = /obj/item/gun/projectile/automatic/l6_saw/toy
- cost = 10
+ cost = 50
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
@@ -102,14 +102,14 @@
This model lacks a method of space propulsion, and therefore it is advised to repair the mothership's teleporter if you wish to make use of it."
reference = "GE"
item = /obj/mecha/combat/gygax/dark/loaded
- cost = 90
+ cost = 450
/datum/uplink_item/support/mauler
name = "Mauler Exosuit"
desc = "A massive and incredibly deadly Syndicate exosuit. Features long-range targeting, thrust vectoring, and deployable smoke."
reference = "ME"
item = /obj/mecha/combat/marauder/mauler/loaded
- cost = 140
+ cost = 700
/datum/uplink_item/support/reinforcement
name = "Reinforcement"
@@ -118,7 +118,7 @@
reference = "REINF"
item = /obj/item/antag_spawner/nuke_ops
refund_path = /obj/item/antag_spawner/nuke_ops
- cost = 20
+ cost = 100
refundable = TRUE
cant_discount = TRUE
@@ -129,7 +129,7 @@
reference = "SAC"
item = /obj/item/antag_spawner/nuke_ops/borg_tele/assault
refund_path = /obj/item/antag_spawner/nuke_ops/borg_tele/assault
- cost = 65
+ cost = 325
/datum/uplink_item/support/reinforcement/medical_borg
name = "Syndicate Medical Cyborg"
@@ -139,7 +139,7 @@
reference = "SMC"
item = /obj/item/antag_spawner/nuke_ops/borg_tele/medical
refund_path = /obj/item/antag_spawner/nuke_ops/borg_tele/medical
- cost = 35
+ cost = 175
/datum/uplink_item/support/reinforcement/saboteur_borg
name = "Syndicate Saboteur Cyborg"
@@ -148,7 +148,7 @@
reference = "SSC"
item = /obj/item/antag_spawner/nuke_ops/borg_tele/saboteur
refund_path = /obj/item/antag_spawner/nuke_ops/borg_tele/saboteur
- cost = 25
+ cost = 125
// AMMUNITION
@@ -157,7 +157,7 @@
desc = "An additional 20-round 10mm magazine for use in the Stechkin APS machine pistol, loaded with rounds that are cheap but around half as effective as .357"
reference = "10MMAPS"
item = /obj/item/ammo_box/magazine/apsm10mm
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/apsap
@@ -165,7 +165,7 @@
desc = "An additional 20-round 10mm magazine for use in the Stechkin APS machine pistol, loaded with rounds that are less effective at injuring the target but penetrate protective gear."
reference = "10MMAPSAP"
item = /obj/item/ammo_box/magazine/apsm10mm/ap
- cost = 3
+ cost = 15
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/apsfire
@@ -173,7 +173,7 @@
desc = "An additional 20-round 10mm magazine for use in the Stechkin APS machine pistol, loaded with incendiary rounds which ignite the target."
reference = "10MMAPSFIRE"
item = /obj/item/ammo_box/magazine/apsm10mm/fire
- cost = 3
+ cost = 15
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/apshp
@@ -181,7 +181,7 @@
desc = "An additional 20-round 10mm magazine for use in the Stechkin APS machine pistol, loaded with rounds which are more damaging but ineffective against armour."
reference = "10MMAPSHP"
item = /obj/item/ammo_box/magazine/apsm10mm/hp
- cost = 4
+ cost = 20
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/bullslug
@@ -189,7 +189,7 @@
desc = "An additional 8-round slug magazine for use in the Bulldog shotgun. Now 8 times less likely to shoot your pals."
reference = "12BSG"
item = /obj/item/ammo_box/magazine/m12g
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/bullbuck
@@ -197,7 +197,7 @@
desc = "An additional 8-round buckshot magazine for use in the Bulldog shotgun. Front towards enemy."
reference = "12BS"
item = /obj/item/ammo_box/magazine/m12g/buckshot
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/bullmeteor
@@ -205,7 +205,7 @@
desc = "An alternative 8-round meteorslug magazine for use in the Bulldog shotgun. Great for blasting airlocks off their frames and knocking down enemies."
reference = "12MS"
item = /obj/item/ammo_box/magazine/m12g/meteor
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/bulldragon
@@ -213,7 +213,7 @@
desc = "An alternative 8-round dragon's breath magazine for use in the Bulldog shotgun. I'm a fire starter, twisted fire starter!"
reference = "12DB"
item = /obj/item/ammo_box/magazine/m12g/dragon
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/bulldog_ammobag
@@ -221,7 +221,7 @@
desc = "A duffel bag filled with enough 12g ammo to supply an entire team, at a discounted price."
reference = "12ADB"
item = /obj/item/storage/backpack/duffel/syndie/shotgun
- cost = 12 // normally 18
+ cost = 60 // normally 90
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/bulldog_XLmagsbag
@@ -229,7 +229,7 @@
desc = "A duffel bag containing three 16 round drum magazines(Slug, Buckshot, Dragon's Breath)."
reference = "12XLDB"
item = /obj/item/storage/backpack/duffel/syndie/shotgunXLmags
- cost = 12 // normally 18
+ cost = 60 // normally 90
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/smg
@@ -237,7 +237,7 @@
desc = "An additional 20-round .45 magazine for use in the C-20r submachine gun. These bullets pack a lot of punch that can knock most targets down, but do limited overall damage."
reference = "45"
item = /obj/item/ammo_box/magazine/smgm45
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/smg_ammobag
@@ -245,7 +245,7 @@
desc = "A duffel bag filled with enough .45 ammo to supply an entire team, at a discounted price."
reference = "45ADB"
item = /obj/item/storage/backpack/duffel/syndie/smg
- cost = 14 // normally 20
+ cost = 70 // normally 100
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/carbine
@@ -253,7 +253,7 @@
desc = "An additional 30-round 5.56 magazine for use in the M-90gl carbine. These bullets don't have the punch to knock most targets down, but dish out higher overall damage."
reference = "556"
item = /obj/item/ammo_box/magazine/m556
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/a40mm
@@ -261,7 +261,7 @@
desc = "A box of 4 additional 40mm HE grenades for use the C-90gl's underbarrel grenade launcher. Your teammates will thank you to not shoot these down small hallways."
reference = "40MM"
item = /obj/item/ammo_box/a40mm
- cost = 4
+ cost = 20
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/rocket
@@ -269,7 +269,7 @@
desc = "An extra shell for your RPG. Make sure your bestie isn't standing in front of you."
reference = "HE"
item = /obj/item/ammo_casing/rocket
- cost = 6
+ cost = 30
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/machinegun
@@ -277,12 +277,12 @@
desc = "A 50-round magazine of 5.56x45mm ammunition for use in the L6 SAW machine gun. By the time you need to use this, you'll already be on a pile of corpses."
reference = "762"
item = /obj/item/ammo_box/magazine/mm556x45
- cost = 12
+ cost = 60
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
/datum/uplink_item/ammo/sniper
- cost = 4
+ cost = 20
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/sniper/basic
@@ -297,14 +297,14 @@
Able to heavily damage objects, and delimb people."
reference = "50A"
item = /obj/item/ammo_box/magazine/sniper_rounds/antimatter
- cost = 5
+ cost = 25
/datum/uplink_item/ammo/sniper/soporific
name = "Sniper - .50 Soporific Magazine"
desc = "A 3-round magazine of soporific ammo designed for use with .50 sniper rifles. Put your enemies to sleep today!"
reference = "50S"
item = /obj/item/ammo_box/magazine/sniper_rounds/soporific
- cost = 3
+ cost = 15
/datum/uplink_item/ammo/sniper/haemorrhage
name = "Sniper - .50 Haemorrhage Magazine"
@@ -319,14 +319,14 @@
Can pierce walls and multiple enemies."
reference = "50P"
item = /obj/item/ammo_box/magazine/sniper_rounds/penetrator
- cost = 5
+ cost = 25
/datum/uplink_item/ammo/bioterror
name = "Box of Bioterror Syringes"
desc = "A box full of preloaded syringes, containing various chemicals that seize up the victim's motor and broca system , making it impossible for them to move or speak while in their system."
reference = "BTS"
item = /obj/item/storage/box/syndie_kit/bioterror
- cost = 5
+ cost = 25
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/ammo/toydarts
@@ -334,20 +334,12 @@
desc = "A box of 40 Donksoft foam riot darts, for reloading any compatible foam dart gun. Don't forget to share!"
reference = "FOAM"
item = /obj/item/ammo_box/foambox/riot
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
// STEALTHY WEAPONS
-/datum/uplink_item/stealthy_weapons/combat_plus
- name = "Combat Gloves Plus"
- desc = "Combat gloves with installed nanochips that teach you Krav Maga when worn, great as a cheap backup weapon. Warning, the nanochips will override any other fighting styles such as CQC."
- reference = "CGP"
- item = /obj/item/clothing/gloves/color/black/krav_maga/combat
- cost = 5
- uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
-
// EXPLOSIVES
/datum/uplink_item/explosives/c4bag
@@ -355,7 +347,7 @@
desc = "Because sometimes quantity is quality. Contains 10 C-4 plastic explosives."
reference = "C4B"
item = /obj/item/storage/backpack/duffel/syndie/c4
- cost = 8 //20% discount!
+ cost = 40 //20% discount!
cant_discount = TRUE
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -364,7 +356,7 @@
desc = "X-4 is a shaped charge designed to be safe to the user while causing maximum damage to the occupants of the room beach breached. It has a modifiable timer with a minimum setting of 10 seconds."
reference = "X4"
item = /obj/item/grenade/plastic/c4/x4
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/explosives/x4bag
@@ -374,7 +366,7 @@
For when you want a controlled explosion that leaves a wider, deeper, hole."
reference = "X4B"
item = /obj/item/storage/backpack/duffel/syndie/x4
- cost = 4
+ cost = 20
cant_discount = TRUE
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -383,7 +375,7 @@
desc = "A belt containing 26 lethally dangerous and destructive grenades."
reference = "GRB"
item = /obj/item/storage/belt/grenade/full
- cost = 30
+ cost = 150
surplus = 0
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -392,7 +384,7 @@
desc = "A unique grenade that deploys a swarm of viscerators upon activation, which will chase down and shred any non-operatives in the area."
reference = "VDG"
item = /obj/item/grenade/spawnergrenade/manhacks
- cost = 5
+ cost = 25
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 35
@@ -403,7 +395,7 @@
desc = "All the tools you need to play the best prank Nanotrasen has ever seen. Includes a voice changer mask, magnetic clown shoes, and standard clown outfit, tools, and backpack."
reference = "HBIK"
item = /obj/item/storage/backpack/clown/syndie
- cost = 6
+ cost = 30
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
@@ -414,7 +406,7 @@
desc = "A diamond tipped thermal drill with magnetic clamps for the purpose of quickly drilling hardened objects. Comes with built in security detection and nanite system, to keep you up if security comes a-knocking."
reference = "DDRL"
item = /obj/item/thermal_drill/diamond_drill/syndicate
- cost = 1
+ cost = 5
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/device_tools/medkit
@@ -423,7 +415,7 @@
and other medical supplies helpful for a medical field operative."
reference = "SCMK"
item = /obj/item/storage/firstaid/tactical
- cost = 4
+ cost = 20
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/device_tools/vtec
@@ -431,7 +423,7 @@
desc = "Increases the movement speed of a Cyborg. Install into any Borg, Syndicate or subverted"
reference = "VTEC"
item = /obj/item/borg/upgrade/vtec
- cost = 6
+ cost = 30
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/device_tools/magboots
@@ -440,7 +432,7 @@
These reverse-engineered knockoffs of Nanotrasen's 'Advanced Magboots' slow you down in simulated-gravity environments much like the standard issue variety."
reference = "BRMB"
item = /obj/item/clothing/shoes/magboots/syndie
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/device_tools/syndicate_detonator
@@ -449,7 +441,7 @@
Useful for when speed matters or you wish to synchronize multiple bomb blasts. Be sure to stand clear of the blast radius before using the detonator."
reference = "SD"
item = /obj/item/syndicatedetonator
- cost = 1
+ cost = 5
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/device_tools/teleporter
@@ -457,7 +449,7 @@
desc = "A printed circuit board that completes the teleporter onboard the mothership. Advise you test fire the teleporter before entering it, as malfunctions can occur."
item = /obj/item/circuitboard/teleporter
reference = "TP"
- cost = 20
+ cost = 100
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
@@ -466,7 +458,7 @@
desc = "Use to select the landing zone of your assault pod."
item = /obj/item/assault_pod
reference = "APT"
- cost = 25
+ cost = 125
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
@@ -475,7 +467,7 @@
desc = "An incredibly useful personal shield projector, capable of reflecting energy projectiles, but it cannot block other attacks. Pair with an Energy Sword for a killer combination."
item = /obj/item/shield/energy
reference = "ESD"
- cost = 16
+ cost = 80
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 20
@@ -484,7 +476,7 @@
desc = "A box of 5 dropwall shield generators, which can be used to make temporary directional shields that block projectiles, thrown objects, and reduce explosions. Configure the direction before throwing."
item = /obj/item/storage/box/syndie_kit/dropwall
reference = "DWG"
- cost = 10
+ cost = 50
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/device_tools/medgun
@@ -492,16 +484,16 @@
desc = "Medical Beam Gun, useful in prolonged firefights. DO NOT CROSS THE BEAMS. Crossing beams with another medbeam or attaching two beams to one target will have explosive consequences."
item = /obj/item/gun/medbeam
reference = "MBG"
- cost = 15
+ cost = 75
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
// SPACE SUITS
/datum/uplink_item/suits/hardsuit/elite
- name = "Elite Syndicate Hardsuit"
- desc = "An advanced hardsuit with superior armor and mobility to the standard Syndicate Hardsuit."
- item = /obj/item/clothing/suit/space/hardsuit/syndi/elite
- cost = 8
+ name = "Elite Syndicate MODsuit"
+ desc = "An advanced MODsuit with superior armor and mobility to the standard Syndicate MODsuit."
+ item = /obj/item/mod/control/pre_equipped/elite
+ cost = 40
reference = "ESHS"
excludefrom = list()
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -510,13 +502,21 @@
name = "Shielded Hardsuit"
desc = "An advanced hardsuit with built in energy shielding. The shields will rapidly recharge when not under fire."
item = /obj/item/clothing/suit/space/hardsuit/shielded/syndi
- cost = 30
+ cost = 150
reference = "SHS"
excludefrom = list()
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
// IMPLANTS
+/datum/uplink_item/implants/krav_implant
+ name = "Krav Maga Implant"
+ desc = "A biochip that teaches you Krav Maga when implanted, great as a cheap backup weapon. Warning: the biochip will override any other fighting styles such as CQC while active."
+ reference = "KMI"
+ item = /obj/item/implanter/krav_maga
+ cost = 25
+ uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+
/datum/uplink_item/implants/uplink/nuclear
name = "Nuclear Uplink Bio-chip"
reference = "UIN"
@@ -530,7 +530,7 @@
This will permanently destroy your body, however."
reference = "MBI"
item = /obj/item/implanter/explosive
- cost = 2
+ cost = 10
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/implants/macrobomb
@@ -538,7 +538,7 @@
desc = "A bio-chip injected into the body, and later activated either manually or automatically upon death. Upon death, releases a massive explosion that will wipe out everything nearby."
reference = "HAB"
item = /obj/item/implanter/explosive_macro
- cost = 20
+ cost = 100
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -554,28 +554,28 @@
desc = "These cybernetic eyes will give you thermal vision. Comes with an autosurgeon."
reference = "CIT"
item = /obj/item/autosurgeon/organ/syndicate/thermal_eyes
- cost = 8
+ cost = 40
/datum/uplink_item/cyber_implants/xray
name = "X-Ray Vision Implant"
desc = "These cybernetic eyes will give you X-ray vision. Comes with an autosurgeon."
reference = "CIX"
item = /obj/item/autosurgeon/organ/syndicate/xray_eyes
- cost = 10
+ cost = 50
/datum/uplink_item/cyber_implants/antistun
name = "Hardened CNS Rebooter Implant"
desc = "This implant will help you get back up on your feet faster after being fatigued. It is immune to EMP attacks. Comes with an autosurgeon."
reference = "CIAS"
item = /obj/item/autosurgeon/organ/syndicate/anti_stam
- cost = 12
+ cost = 60
/datum/uplink_item/cyber_implants/reviver
name = "Hardened Reviver Implant"
desc = "This implant will attempt to revive and heal you if you lose consciousness. It is immune to EMP attacks. Comes with an autosurgeon."
reference = "CIR"
item = /obj/item/autosurgeon/organ/syndicate/reviver
- cost = 8
+ cost = 40
// BUNDLES
@@ -585,7 +585,7 @@
Bulldog shotgun, two 12g buckshot drums, and a pair of Thermal imaging goggles."
reference = "BULB"
item = /obj/item/storage/backpack/duffel/syndie/bulldogbundle
- cost = 9 // normally 12
+ cost = 45 // normally 60
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/bundles_TC/c20r
@@ -593,7 +593,7 @@
desc = "Old Faithful: The classic C-20r, bundled with three magazines and a (surplus) suppressor at discount price."
reference = "C20B"
item = /obj/item/storage/backpack/duffel/syndie/c20rbundle
- cost = 18 // normally 21
+ cost = 90 // normally 105
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/bundles_TC/cyber_implants
@@ -601,7 +601,7 @@
desc = "A random selection of cybernetic implants. Guaranteed 5 high quality implants. Comes with an autosurgeon."
reference = "CIB"
item = /obj/item/storage/box/cyber_implants
- cost = 40
+ cost = 200
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/bundles_TC/medical
@@ -610,7 +610,7 @@
a medical beam gun and a pair of syndicate magboots."
reference = "MEDB"
item = /obj/item/storage/backpack/duffel/syndie/med/medicalbundle
- cost = 16 // normally 21
+ cost = 80 // normally 105
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/bundles_TC/sniper
@@ -620,15 +620,7 @@
We'll throw in a free red tie if you order NOW."
reference = "SNPB"
item = /obj/item/storage/briefcase/sniperbundle
- cost = 18 // normally 23
- uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
-
-/datum/uplink_item/bundles_TC/telecrystal/fifty
- name = "50 Raw Telecrystals"
- desc = "Fifty telecrystals in their rawest and purest form. You know you want that Mauler."
- reference = "RTCB"
- item = /obj/item/stack/telecrystal/fifty
- cost = 50
+ cost = 90 // normally 115
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -638,12 +630,12 @@
/datum/uplink_item/stealthy_weapons/cqc/nuke
reference = "NCQC"
- cost = 8
+ cost = 40
excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
/datum/uplink_item/explosives/syndicate_bomb/nuke
reference = "NSB"
- cost = 11
+ cost = 55
excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
surplus = 0
cant_discount = TRUE
@@ -651,7 +643,7 @@
/datum/uplink_item/explosives/emp_bomb/nuke
reference = "NSBEMP"
- cost = 10
+ cost = 50
excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
surplus = 0
cant_discount = TRUE
@@ -659,19 +651,19 @@
/datum/uplink_item/explosives/atmosfiregrenades/nuke
reference = "NAPG"
hijack_only = FALSE
- cost = 12
+ cost = 60
excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
surplus = 0
cant_discount = TRUE
/datum/uplink_item/stealthy_tools/chameleon/nuke
reference = "NCHAM"
- cost = 6
+ cost = 30
excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
/datum/uplink_item/stealthy_tools/syndigaloshes/nuke
reference = "NNSSS"
- cost = 4
+ cost = 20
excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
/datum/uplink_item/explosives/detomatix/nuclear
@@ -691,7 +683,7 @@
item = /obj/item/ammo_box/magazine/m12g/confetti
reference = "12CS"
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
- cost = 1
+ cost = 5
/datum/uplink_item/badass/confetti_party_pack
name = "Nuclear party pack"
@@ -699,4 +691,4 @@
item = /obj/item/storage/backpack/duffel/syndie/party
reference = "SPP"
uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
- cost = 10
+ cost = 50
diff --git a/code/datums/uplink_items/uplink_traitor.dm b/code/datums/uplink_items/uplink_traitor.dm
index 0074cc88fab0..3c6a02fc79c0 100644
--- a/code/datums/uplink_items/uplink_traitor.dm
+++ b/code/datums/uplink_items/uplink_traitor.dm
@@ -13,7 +13,7 @@
desc = "A grenade that explodes into HONK! brand banana peels that are genetically modified to be extra slippery and extrude caustic acid when stepped on."
reference = "BG"
item = /obj/item/grenade/clown_grenade
- cost = 3
+ cost = 15
job = list("Clown")
/datum/uplink_item/jobspecific/clownslippers
@@ -21,7 +21,7 @@
desc = "A pair of modified clown shoes fitted with a built-in propulsion system that allows the user to perform a short slip below anyone. Turning on the waddle dampeners removes the slowdown on the shoes."
reference = "CAS"
item = /obj/item/clothing/shoes/clown_shoes/slippers
- cost = 3
+ cost = 15
surplus = 75
job = list("Clown")
@@ -30,7 +30,7 @@
desc = "The jestographic sequencer, also known as a cmag, is a small card that inverts the access on any door it's used on. Perfect for locking command out of their own departments. Honk!"
reference = "CMG"
item = /obj/item/card/cmag
- cost = 4
+ cost = 20
surplus = 75
job = list("Clown")
@@ -39,15 +39,24 @@
desc = "A revolver that will fire backwards and kill whoever attempts to use it. Perfect for those pesky vigilante or just a good laugh."
reference = "CTR"
item = /obj/item/storage/box/syndie_kit/fake_revolver
- cost = 1
+ cost = 5
job = list("Clown")
+
+/datum/uplink_item/jobspecific/trick_grenade
+ name = "Trick Grenade"
+ desc = "Syndicate Minibomb with glue ejectors that will stick it to the user's hands on activation."
+ reference = "CGN"
+ item = /obj/item/storage/box/syndie_kit/fake_minibomb
+ cost = 5
+ job = list("Clown")
+
//mime
/datum/uplink_item/jobspecific/caneshotgun
name = "Cane Shotgun and Assassination Shells"
desc = "A specialised, one shell shotgun with a built-in cloaking device to mimic a cane. The shotgun is capable of hiding it's contents and the pin alongside being suppressed. Comes boxed with 6 specialised shrapnel rounds laced with a silencing toxin and 1 preloaded in the shotgun's chamber."
reference = "MCS"
item = /obj/item/storage/box/syndie_kit/caneshotgun
- cost = 8
+ cost = 40
job = list("Mime")
/datum/uplink_item/jobspecific/mimery
@@ -55,7 +64,7 @@
desc = "Contains two manuals to teach you advanced Mime skills. You will be able to shoot lethal bullets that silence out of your fingers, and create large walls that can block an entire hallway!"
reference = "AM"
item = /obj/item/storage/box/syndie_kit/mimery
- cost = 10
+ cost = 50
job = list("Mime")
/datum/uplink_item/jobspecific/pressure_mod
@@ -63,7 +72,7 @@
desc = "A modification kit which allows Kinetic Accelerators to do greatly increased damage while indoors. Occupies 35% mod capacity."
reference = "KPM"
item = /obj/item/borg/upgrade/modkit/indoors
- cost = 5 //you need two for full damage, so total of 10 for maximum damage
+ cost = 25 //you need two for full damage, so total of 50 for maximum damage
job = list("Shaft Miner")
//Chef
@@ -72,7 +81,7 @@
desc = "A custom sauce made from the highly poisonous fly amanita mushrooms. Anyone who ingests it will take variable toxin damage depending on how long it has been in their system, with a higher dosage taking longer to metabolize."
reference = "CESS"
item = /obj/item/reagent_containers/food/condiment/syndisauce
- cost = 2
+ cost = 10
job = list("Chef")
/datum/uplink_item/jobspecific/meatcleaver
@@ -80,7 +89,7 @@
desc = "A mean looking meat cleaver that does damage comparable to an Energy Sword but with the added benefit of chopping your victim into hunks of meat after they've died."
reference = "MC"
item = /obj/item/kitchen/knife/butcher/meatcleaver
- cost = 8
+ cost = 40
job = list("Chef")
/datum/uplink_item/jobspecific/syndidonk
@@ -88,7 +97,7 @@
desc = "A box of highly specialized Donk pockets with a number of regenerative and stimulating chemicals inside of them; the box comes equipped with a self-heating mechanism."
reference = "SDP"
item = /obj/item/storage/box/syndidonkpockets
- cost = 2
+ cost = 10
job = list("Chef")
//Chaplain
@@ -98,7 +107,7 @@
desc = "A box containing a missionary staff, missionary robes, and bible. The robes and staff can be linked to allow you to convert victims at range for a short time to do your bidding. The bible is for bible stuff."
reference = "MK"
item = /obj/item/storage/box/syndie_kit/missionary_set
- cost = 15
+ cost = 75
job = list("Chaplain")
/datum/uplink_item/jobspecific/artistic_toolbox
@@ -109,7 +118,7 @@
To activate His Grace, simply unlatch Him."
reference = "HG"
item = /obj/item/his_grace
- cost = 20
+ cost = 100
job = list("Chaplain")
surplus = 0 //No lucky chances from the crate; if you get this, this is ALL you're getting
hijack_only = TRUE //This is a murderbone weapon, as such, it should only be available in those scenarios.
@@ -121,7 +130,7 @@
desc = "An Anti-Personnel proximity mine cleverly disguised as a wet floor caution sign that is triggered by running past it, activate it to start the 15 second timer and activate again to disarm."
reference = "PM"
item = /obj/item/caution/proximity_sign
- cost = 2
+ cost = 10
job = list("Janitor")
/datum/uplink_item/jobspecific/titaniumbroom
@@ -129,8 +138,8 @@
desc = "A push broom with a reinforced handle and a metal wire brush, perfect for giving yourself more work by beating up assistants. \
When wielded, you will reflect projectiles, and hitting people will have different effects based on your intent."
reference = "TPBR"
- item = /obj/item/twohanded/push_broom/traitor
- cost = 12
+ item = /obj/item/push_broom/traitor
+ cost = 60
job = list("Janitor")
surplus = 0 //no reflect memes
@@ -141,7 +150,7 @@
desc = "A modified hypospray disguised as a functional pipette. The pipette can infect victims with viruses upon injection."
reference = "VI"
item = /obj/item/reagent_containers/dropper/precision/viral_injector
- cost = 3
+ cost = 15
job = list("Virologist")
/datum/uplink_item/jobspecific/cat_grenade
@@ -149,7 +158,7 @@
desc = "The feral cat delivery grenade contains 5 dehydrated feral cats in a similar manner to dehydrated monkeys, which, upon detonation, will be rehydrated by a small reservoir of water contained within the grenade. These cats will then attack anything in sight."
item = /obj/item/grenade/spawnergrenade/feral_cats
reference = "CCLG"
- cost = 3
+ cost = 15
job = list("Psychiatrist")//why? Becuase its funny that a person in charge of your mental wellbeing has a cat granade..
//Assistant
@@ -159,7 +168,7 @@
desc = "A pair of sleek gloves to aid in pickpocketing. While wearing these, you can loot your target without them knowing. Pickpocketing puts the item directly into your hand."
reference = "PPG"
item = /obj/item/clothing/gloves/color/black/thief
- cost = 6
+ cost = 30
job = list("Assistant")
//Bartender
@@ -169,7 +178,7 @@
desc = "A box containing 6 shotgun shells that simulate the effects of extreme drunkenness on the target, more effective for each type of alcohol in the target's system."
reference = "BSS"
item = /obj/item/storage/box/syndie_kit/boolets
- cost = 2
+ cost = 10
job = list("Bartender")
//Barber
@@ -179,7 +188,7 @@
desc = "A pair of scissors that are anything but what their name implies; can easily cut right into someone's throat."
reference = "CTS"
item = /obj/item/scissors/safety
- cost = 3
+ cost = 15
job = list("Barber")
//Botanist
@@ -189,7 +198,7 @@
desc = "A seemingly innocent briefcase full of not-so-innocent Syndicate-bred bees. Inject the case with blood to train the bees to ignore the donor(s), WARNING: exotic blood types such as slime jelly do not work. It also wirelessly taps into station intercomms to broadcast a message of TERROR."
reference = "BEE"
item = /obj/item/bee_briefcase
- cost = 10
+ cost = 50
job = list("Botanist")
//Engineer
@@ -201,7 +210,7 @@
Activated by alt-clicking, or pressing the middle mouse button. Disarm intent will deal stamina damage and cause jittering, while harm intent will deal damage based on the power of the cable you're standing on."
reference = "PG"
item = /obj/item/clothing/gloves/color/yellow/power
- cost = 10
+ cost = 50
job = list("Station Engineer", "Chief Engineer")
//RD
@@ -211,7 +220,7 @@
desc = "An extremely high-tech energy gun that utilizes bluespace technology to teleport away living targets. Select the target beacon on the telegun itself; projectiles will send targets to the beacon locked onto. Can only send targets to beacons in-sector unless they are emagged!"
reference = "TG"
item = /obj/item/gun/energy/telegun
- cost = 10
+ cost = 50
job = list("Research Director")
//Roboticist
@@ -220,7 +229,7 @@
desc = "A syndicate developed man-machine-interface which will mindslave any brain inserted into it, for as long as it's in. Cyborgs made with this MMI will be permanently slaved to you but otherwise function normally."
reference = "SMMI"
item = /obj/item/mmi/syndie
- cost = 2
+ cost = 10
job = list("Roboticist")
surplus = 0
@@ -231,7 +240,7 @@
desc = "A seemingly innocent die, those who are not afraid to roll for attack will find it's effects quite explosive. Has a four second timer."
reference = "ETW"
item = /obj/item/dice/d20/e20
- cost = 3
+ cost = 15
job = list("Librarian")
//Botanist
@@ -240,7 +249,7 @@
desc = "Part of the notorious Ambrosia family, this species is nearly indistinguishable from Ambrosia Vulgaris- but its' branches contain a revolting toxin. Eight units are enough to drive victims insane."
reference = "BRO"
item = /obj/item/seeds/ambrosia/cruciatus
- cost = 1
+ cost = 5
job = list("Botanist")
//Atmos Tech
@@ -249,15 +258,15 @@
desc = "A highly flexible jumpsuit that will help you navigate the ventilation loops of the station internally. Comes with pockets and ID slot, but can't be used without stripping off most gear, including backpack, belt, helmet, and exosuit. Free hands are also necessary to crawl around inside."
reference = "AIRJ"
item = /obj/item/clothing/under/rank/engineering/atmospheric_technician/contortionist
- cost = 6
+ cost = 30
job = list("Life Support Specialist")
/datum/uplink_item/jobspecific/energizedfireaxe
name = "Energized Fire Axe"
desc = "A fire axe with a massive energy charge built into it. Upon striking someone while charged it will throw them backwards while stunning them briefly, but will take some time to charge up again. It is also much sharper than a regular axe and can pierce light armor."
reference = "EFA"
- item = /obj/item/twohanded/fireaxe/energized
- cost = 8
+ item = /obj/item/fireaxe/energized
+ cost = 40
job = list("Life Support Specialist")
//Stimulants
@@ -267,7 +276,7 @@
desc = "A highly illegal compound contained within a compact auto-injector; when injected it makes the user extremely resistant to incapacitation and greatly enhances the body's ability to repair itself."
reference = "ST"
item = /obj/item/reagent_containers/hypospray/autoinjector/stimulants
- cost = 8
+ cost = 40
job = list("Scientist", "Research Director", "Geneticist", "Chief Medical Officer", "Medical Doctor", "Psychiatrist", "Chemist", "Paramedic", "Coroner", "Virologist")
// Genetics
@@ -278,7 +287,7 @@
Side-affects may include hypertrichosis, violent outbursts, and an unending affinity for bananas."
reference = "MAG"
item = /obj/item/implanter/gorilla_rampage
- cost = 5
+ cost = 25
job = list("Research Director", "Geneticist")
// Paper contact poison pen
@@ -288,11 +297,62 @@
desc = "Cutting edge of deadly writing implements technology, this gadget will infuse any piece of paper with various delayed poisons based on the selected color. Black ink is normal ink, red ink is a highly lethal poison, green ink causes radiation, blue ink will periodically shock the victim, and yellow ink will paralyze. The included gloves will protect you from your own poisons."
reference = "PP"
item = /obj/item/storage/box/syndie_kit/poisoner
- cost = 2
+ cost = 10
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
job = list("Head of Personnel", "Quartermaster", "Cargo Technician", "Librarian", "Coroner", "Psychiatrist", "Virologist")
+//--------------------------//
+// Species Restricted Gear //
+//-------------------------//
+
+/datum/uplink_item/species_restricted
+ category = "Species Specific Gear"
+ cant_discount = TRUE
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) // Stops the job specific category appearing for nukies
+
+//skrell
+/datum/uplink_item/species_restricted/lovepen
+ name = "Aggression Supression Pen"
+ desc = "A syringe disguised as a functional pen which is filled with a potent aggression supressing chemical. The pen holds four doses of the mixture and it cannot be refilled."
+ reference = "LP"
+ item = /obj/item/pen/sleepy/love
+ cost = 20
+ species = list("Skrell")
+
+//Vox
+/datum/uplink_item/species_restricted/spikethrower
+ name = "Skipjack Spikethrower"
+ desc = "An energy based weapon that launches high velocity plasma spikes. These spikes hit with enough force to knock the target down and leave a nasty wound."
+ reference = "STG"
+ item = /obj/item/gun/energy/spikethrower
+ cost = 60
+ species = list("Vox")
+ surplus = 0
+
+//IPC:
+//Positonic supercharge implant: stims, 3 uses, IPC adrenals
+/datum/uplink_item/species_restricted/supercharge_implant
+ name = "Synthetic Supercharge Bio-chip"
+ desc = "A bio-chip injected into the body, and later activated manually to inject a chemical cocktail, which has the effect of removing and reducing the time of all stuns and increasing movement speed. Can be activated up to 3 times."
+ reference = "SSI"
+ item = /obj/item/implanter/supercharge
+ cost = 40
+ species = list("Machine")
+ surplus = 0
+
+
+//plasmeme
+/datum/uplink_item/species_restricted/fireproofing_nanites
+ name = "Fireproofing Nanite Injector"
+ desc = "A swarm of nanomachines that absorb excess amounts of heat, allowing the user to become practically fireproof."
+ reference = "FPN"
+ item = /obj/item/fireproofing_injector
+ cost = 25
+ species = list("Plasmaman")
+ surplus = 0
+
+
// -------------------------------------
// ITEMS BLACKLISTED FROM NUCLEAR AGENTS
// -------------------------------------
@@ -302,7 +362,7 @@
desc = "A miniature energy crossbow that is small enough both to fit into a pocket and to slip into a backpack unnoticed by observers. Fires bolts tipped with toxin, a poisonous substance that is the product of a living organism. Knocks enemies down for a short period of time. Recharges automatically."
reference = "EC"
item = /obj/item/gun/energy/kinetic_accelerator/crossbow
- cost = 12
+ cost = 60
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 50
@@ -313,7 +373,7 @@
The holoparasites are unable to incoporate themselves to changeling and vampire agents."
item = /obj/item/storage/box/syndie_kit/guardian
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
- cost = 12
+ cost = 60
refund_path = /obj/item/guardiancreator/tech/choose
refundable = TRUE
cant_discount = TRUE
@@ -325,7 +385,7 @@
Unable to be understood by vampire and changeling agents."
reference = "SCS"
item = /obj/item/sleeping_carp_scroll
- cost = 13
+ cost = 65
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
cant_discount = TRUE
@@ -334,7 +394,7 @@
desc = "A robust seven-slot belt made for carrying a broad variety of weapons, ammunition and explosives. It's modelled after the standard NT toolbelt so as to avoid suspicion while wearing it."
reference = "SBM"
item = /obj/item/storage/belt/military/traitor
- cost = 2
+ cost = 10
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/stealthy_tools/frame
@@ -346,7 +406,7 @@
reference = "FRAME"
item = /obj/item/cartridge/frame
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
- cost = 4
+ cost = 20
/datum/uplink_item/stealthy_tools/voice_modulator
name = "Chameleon Voice Modulator Mask"
@@ -354,7 +414,7 @@
While the mask is active, your voice will sound unrecognizable to others"
reference = "CVMM"
item = /obj/item/clothing/mask/gas/voice_modulator/chameleon
- cost = 1
+ cost = 5
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/stealthy_weapons/sleepy_pen
@@ -362,7 +422,7 @@
desc = "A syringe disguised as a functional pen. It's filled with a potent anaesthetic. \ The pen holds two doses of the mixture. The pen can be refilled."
reference = "SP"
item = /obj/item/pen/sleepy
- cost = 8
+ cost = 40
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/stealthy_weapons/dart_pistol
@@ -370,7 +430,7 @@
desc = "A miniaturized version of a normal syringe gun. It is very quiet when fired and can fit into any space a small item can. Comes with 3 syringes: a knockout poison, a silencing agent and a deadly neurotoxin."
reference = "DART"
item = /obj/item/storage/box/syndie_kit/dart_gun
- cost = 4
+ cost = 20
surplus = 50
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -379,7 +439,7 @@
desc = "Experimental gloves with installed nanochips that teach you Krav Maga when worn, great as a cheap backup weapon. Warning, the nanochips will override any other fighting styles such as CQC. Do not look as fly as the Warden's"
reference = "CGM"
item = /obj/item/clothing/gloves/color/black/krav_maga
- cost = 10
+ cost = 50
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/device_tools/thermal_drill // Nukies get Diamond Tipped Thermal Safe Drill instead
@@ -387,19 +447,30 @@
desc = "A tungsten carbide thermal drill with magnetic clamps for the purpose of drilling hardened objects. Comes with built in security detection and nanite system, to keep you up if security comes a-knocking."
reference = "DRL"
item = /obj/item/thermal_drill/syndicate
- cost = 1
+ cost = 5
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
-/datum/uplink_item/suits/hardsuit // Nukies get elite/shielded hardsuit instead
- name = "Syndicate Hardsuit"
- desc = "The feared suit of a syndicate nuclear agent. Features armor and a combat mode \
+/datum/uplink_item/suits/modsuit
+ name = "Syndicate MODsuit"
+ desc = "The feared MODsuit of a syndicate nuclear agent. Features armor and a eva mode \
for faster movement on station. Toggling the suit in and out of \
combat mode will allow you all the mobility of a loose fitting uniform without sacrificing armoring. \
- Additionally the suit is collapsible, making it small enough to fit within a backpack. Comes packaged with internals. \
+ Comes packaged with internals. \
Nanotrasen crew who spot these suits are known to panic."
reference = "BRHS"
- item = /obj/item/storage/box/syndie_kit/hardsuit
- cost = 6
+ item = /obj/item/storage/box/syndie_kit/modsuit
+ cost = 30
+ surplus = 60 //I have upped the chance of modsuits from 40, as I do feel they are much more worthwhile with the base modsuit no longer being 8 tc, and the high armor values of the elite.
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+
+/datum/uplink_item/suits/modsuit_elite
+ name = "Syndicate Elite MODsuit"
+ desc = "An advanced MODsuit with superior armor to the standard Syndicate MODsuit. \
+ Nanotrasen crew who spot these suits are known to *really* panic."
+ reference = "MSE"
+ item = /obj/item/storage/box/syndie_kit/modsuit/elite
+ cost = 45 //45 to start, no holopara / ebow.
+ surplus = 60
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/implants/uplink // Nukies get Nuclear Uplink Bio-chip instead
@@ -407,7 +478,7 @@
desc = "A bio-chip injected into the body, and later activated manually to open an uplink with 10 telecrystals. The ability for an agent to open an uplink after their possessions have been stripped from them makes this implant excellent for escaping confinement."
reference = "UI"
item = /obj/item/implanter/uplink
- cost = 14
+ cost = 70
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
cant_discount = TRUE
@@ -418,7 +489,7 @@
You can also play card games with them."
reference = "SPC"
item = /obj/item/deck/cards/syndicate
- cost = 1
+ cost = 2
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 40
@@ -434,7 +505,7 @@
name = "Syndicate Contractor Kit"
desc = "A bundle granting you the privilege of taking on kidnapping contracts for credit and TC payouts that can add up to more than its initial cost."
reference = "SCOK"
- cost = 20
+ cost = 100
item = /obj/item/storage/box/syndie_kit/contractor
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
@@ -464,27 +535,27 @@
/datum/uplink_item/bundles_TC/badass
name = "Syndicate Bundle"
- desc = "Syndicate Bundles are specialised groups of items that arrive in a plain box. These items are collectively worth more than 20 telecrystals, but you do not know which specialisation you will receive."
+ desc = "Syndicate Bundles are specialised groups of items that arrive in a plain box. These items are collectively worth more than 100 telecrystals, but you do not know which specialisation you will receive."
reference = "SYB"
item = /obj/item/storage/box/syndie_kit/bundle
- cost = 20
+ cost = 100
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/bundles_TC/surplus_crate
name = "Syndicate Surplus Crate"
- desc = "A crate containing 50 telecrystals worth of random syndicate leftovers."
+ desc = "A crate containing 250 telecrystals worth of random syndicate leftovers."
reference = "SYSC"
- cost = 20
+ cost = 100
item = /obj/item/storage/box/syndie_kit/bundle
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
- var/crate_value = 50
+ var/crate_value = 250
/datum/uplink_item/bundles_TC/surplus_crate/super
name = "Syndicate Super Surplus Crate"
- desc = "A crate containing 125 telecrystals worth of random syndicate leftovers."
+ desc = "A crate containing 625 telecrystals worth of random syndicate leftovers."
reference = "SYSS"
- cost = 40
- crate_value = 125
+ cost = 200
+ crate_value = 625
/datum/uplink_item/bundles_TC/surplus_crate/spawn_item(turf/loc, obj/item/uplink/U)
var/obj/structure/closet/crate/C = new(loc)
@@ -529,7 +600,7 @@
Does not restrict weapon usage, and can be used alongside Gloves of the North Star."
reference = "CQC"
item = /obj/item/CQC_manual
- cost = 10
+ cost = 50
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/explosives/syndicate_bomb
@@ -538,7 +609,7 @@
You can wrench the bomb down to prevent removal. The crew may attempt to defuse the bomb."
reference = "SB"
item = /obj/item/radio/beacon/syndicate/bomb
- cost = 8
+ cost = 40
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
cant_discount = TRUE
@@ -550,7 +621,7 @@
You can wrench the bomb down to prevent removal. The crew may attempt to defuse the bomb. Will pulse 3 times."
reference = "SBEMP"
item = /obj/item/radio/beacon/syndicate/bomb/emp
- cost = 8
+ cost = 40
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
cant_discount = TRUE
@@ -561,7 +632,7 @@
reference = "APG"
item = /obj/item/storage/box/syndie_kit/atmosfiregrenades
hijack_only = TRUE
- cost = 10
+ cost = 50
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
surplus = 0
cant_discount = TRUE
@@ -572,7 +643,7 @@
Due to budget cuts, the shoes don't provide protection against slipping. The set comes with a complementary chameleon stamp."
reference = "CHAM"
item = /obj/item/storage/box/syndie_kit/chameleon
- cost = 4
+ cost = 20
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/stealthy_tools/syndigaloshes
@@ -581,7 +652,7 @@
They do not work on heavily lubricated surfaces."
reference = "NSSS"
item = /obj/item/clothing/shoes/chameleon/noslip
- cost = 2
+ cost = 10
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
/datum/uplink_item/explosives/detomatix
@@ -589,5 +660,5 @@
desc = "When inserted into a personal digital assistant, this cartridge gives you five opportunities to detonate PDAs of crewmembers who have their message feature enabled. The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer. It has a chance to detonate your PDA."
reference = "DEPC"
item = /obj/item/cartridge/syndicate
- cost = 6
+ cost = 30
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm
index 988aa0f37a8a..acabe00ccaf6 100644
--- a/code/datums/wires/explosive.dm
+++ b/code/datums/wires/explosive.dm
@@ -25,7 +25,7 @@
..()
/datum/wires/explosive/gibtonite
- holder_type = /obj/item/twohanded/required/gibtonite
+ holder_type = /obj/item/gibtonite
/datum/wires/explosive/gibtonite/interactable(mob/user)
return TRUE
@@ -34,5 +34,5 @@
return
/datum/wires/explosive/gibtonite/explode()
- var/obj/item/twohanded/required/gibtonite/P = holder
+ var/obj/item/gibtonite/P = holder
P.GibtoniteReaction(null, 2)
diff --git a/code/datums/wires/mod_wires.dm b/code/datums/wires/mod_wires.dm
new file mode 100644
index 000000000000..d63c1162eda1
--- /dev/null
+++ b/code/datums/wires/mod_wires.dm
@@ -0,0 +1,63 @@
+/datum/wires/mod
+ holder_type = /obj/item/mod/control
+ randomize = TRUE //Every modsuit is personalised
+ wire_count = 6 // 4 actual, 2 duds
+ proper_name = "MOD control unit"
+ window_x = 345
+ window_y = 90
+
+/datum/wires/mod/New(atom/holder)
+ wires = list(WIRE_HACK, WIRE_DISABLE, WIRE_ELECTRIFY, WIRE_INTERFACE)
+ ..()
+
+/datum/wires/mod/interactable(mob/user)
+ if(!..())
+ return FALSE
+ var/obj/item/mod/control/mod = holder
+ if(mod.seconds_electrified && mod.shock(user))
+ return FALSE
+ return mod.open
+
+/datum/wires/mod/get_status()
+ var/obj/item/mod/control/mod = holder
+ var/list/status = list()
+ status += "The orange light is [mod.seconds_electrified ? "on" : "off"]."
+ status += "The red light is [mod.malfunctioning ? "off" : "blinking"]."
+ status += "The green light is [mod.locked ? "on" : "off"]."
+ status += "The yellow light is [mod.interface_break ? "off" : "on"]."
+ return status
+
+/datum/wires/mod/on_pulse(wire)
+ var/obj/item/mod/control/mod = holder
+ switch(wire)
+ if(WIRE_HACK)
+ mod.locked = !mod.locked
+ if(WIRE_DISABLE)
+ mod.malfunctioning = TRUE
+ if(WIRE_ELECTRIFY)
+ mod.seconds_electrified = 30
+ if(WIRE_INTERFACE)
+ mod.interface_break = !mod.interface_break
+
+/datum/wires/mod/on_cut(wire, mend)
+ var/obj/item/mod/control/mod = holder
+ switch(wire)
+ if(WIRE_HACK)
+ if(!mend)
+ mod.req_access = list()
+ if(WIRE_DISABLE)
+ mod.malfunctioning = !mend
+ if(WIRE_ELECTRIFY)
+ if(mend)
+ mod.seconds_electrified = 0
+ else
+ mod.seconds_electrified = -1
+ if(WIRE_INTERFACE)
+ mod.interface_break = !mend
+
+/datum/wires/mod/ui_act(action, params)
+ var/obj/item/mod/control/mod = holder
+ if(!issilicon(usr) && mod.seconds_electrified && mod.shock(usr))
+ return FALSE
+ return ..()
+
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index 7a813f12325c..896be0f2c908 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -480,3 +480,15 @@
/datum/wires/proc/is_attached(color)
if(assemblies[color])
return TRUE
+
+/// Use this proc if you want wires to be pulsed on EMP
+/datum/wires/proc/emp_pulse()
+ var/list/possible_wires = shuffle(wires)
+ var/remaining_pulses = 3
+
+ for(var/wire in possible_wires)
+ if(prob(33))
+ pulse(wire)
+ remaining_pulses--
+ if(!remaining_pulses)
+ break
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index fe309999a478..50f2eb36ac1a 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -138,21 +138,15 @@
return powernet
/area/proc/reg_in_areas_in_z()
- if(contents.len)
- var/list/areas_in_z = GLOB.space_manager.areas_in_z
- var/z
- for(var/i in 1 to contents.len)
- var/atom/thing = contents[i]
- if(!thing)
- continue
- z = thing.z
- break
- if(!z)
- WARNING("No z found for [src]")
- return
- if(!areas_in_z["[z]"])
- areas_in_z["[z]"] = list()
- areas_in_z["[z]"] += src
+ if(!length(contents)) // if its nullspaced or something, I guess
+ return
+ if(!z)
+ WARNING("No z found for [src]")
+ return
+ var/list/areas_in_z = GLOB.space_manager.areas_in_z
+ if(!areas_in_z["[z]"])
+ areas_in_z["[z]"] = list()
+ areas_in_z["[z]"] += src
/area/proc/get_cameras()
var/list/cameras = list()
diff --git a/code/game/area/areas/ruins/space_areas.dm b/code/game/area/areas/ruins/space_areas.dm
index d96a40c4de31..a58edec0be56 100644
--- a/code/game/area/areas/ruins/space_areas.dm
+++ b/code/game/area/areas/ruins/space_areas.dm
@@ -149,3 +149,18 @@
name = "Suspicious Asteroid"
icon_state = "dark"
requires_power = FALSE
+
+/area/ruin/space/wreck_cargoship
+ name = "Faint Signal"
+ icon_state = "yellow"
+
+// Syndicate Listening Station
+
+/area/ruin/space/syndicate_listening_station
+ name = "Listening Post"
+ icon_state = "red"
+
+/area/ruin/space/syndicate_listening_station/asteroid
+ name = "Listening Post Asteroid"
+ icon_state = "dark"
+ requires_power = FALSE
diff --git a/code/game/dna/mutations/mutation_powers.dm b/code/game/dna/mutations/mutation_powers.dm
index 2376043ede74..e2b9231b1a90 100644
--- a/code/game/dna/mutations/mutation_powers.dm
+++ b/code/game/dna/mutations/mutation_powers.dm
@@ -33,26 +33,6 @@
H.adjustBruteLoss(-1, FALSE)
H.adjustFireLoss(-1)
-/datum/mutation/increaserun
- name = "Super Speed"
- activation_messages = list("You feel swift and unencumbered.")
- deactivation_messages = list("You feel slow.")
- instability = GENE_INSTABILITY_MINOR
- traits_to_add = list(TRAIT_IGNORESLOWDOWN)
-
-/datum/mutation/increaserun/New()
- ..()
- block = GLOB.increaserunblock
-
-/datum/mutation/increaserun/can_activate(mob/M, flags)
- if(!..())
- return FALSE
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.dna.species && H.dna.species.speed_mod && !(flags & MUTCHK_FORCED))
- return FALSE
- return TRUE
-
/datum/mutation/heat_resist
name = "Heat Resistance"
activation_messages = list("Your skin is icy to the touch.")
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index d10a3a7d4013..e6aa485df90f 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -1,5 +1,4 @@
#define LING_FAKEDEATH_TIME 40 SECONDS
-#define LING_DEAD_GENETIC_DAMAGE_HEAL_CAP 50 //The lowest value of genetic_damage handle_changeling() can take it to while dead.
#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob
GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega"))
diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm
index 7868a5248bba..e56dc1b451cf 100644
--- a/code/game/gamemodes/changeling/traitor_chan.dm
+++ b/code/game/gamemodes/changeling/traitor_chan.dm
@@ -1,5 +1,5 @@
/datum/game_mode/traitor/changeling
- name = "traitor+changeling"
+ name = "traitor_changeling"
config_tag = "traitorchan"
traitors_possible = 3 //hard limit on traitors if scaling is turned off
restricted_jobs = list("Cyborg")
diff --git a/code/game/gamemodes/cult/blood_magic.dm b/code/game/gamemodes/cult/blood_magic.dm
index 8c63827e3087..27dd136900b2 100644
--- a/code/game/gamemodes/cult/blood_magic.dm
+++ b/code/game/gamemodes/cult/blood_magic.dm
@@ -873,7 +873,7 @@
if(!T)
return
for(var/obj/effect/decal/cleanable/blood/B in range(T, 2))
- if(B.blood_state == BLOOD_STATE_HUMAN && (B.can_bloodcrawl_in() || istype(B, /obj/effect/decal/cleanable/blood/slime)))
+ if(B.blood_state == BLOOD_STATE_HUMAN && (B.can_bloodcrawl_in()))
if(B.bloodiness == 100) //Bonus for "pristine" bloodpools, also to prevent cheese with footprint spam
temp += 30
else
@@ -945,7 +945,7 @@
var/turf/T = get_turf(user)
qdel(src)
var/datum/action/innate/cult/spear/S = new(user)
- var/obj/item/twohanded/cult_spear/rite = new(T)
+ var/obj/item/cult_spear/rite = new(T)
S.Grant(user, rite)
rite.spear_act = S
if(user.put_in_hands(rite))
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index ea763e44ebf2..88efad6762a7 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -558,15 +558,14 @@
return TRUE
return FALSE
-/obj/item/twohanded/cult_spear
+/obj/item/cult_spear
name = "blood halberd"
desc = "A sickening spear composed entirely of crystallized blood. Will stun people who have been recently marked if the spear is wielded."
icon = 'icons/obj/cult.dmi'
+ base_icon_state = "bloodspear"
icon_state = "bloodspear0"
slot_flags = 0
force = 17
- force_unwielded = 17
- force_wielded = 24
throwforce = 30
throw_speed = 2
armour_penetration_percentage = 50
@@ -577,19 +576,20 @@
needs_permit = TRUE
var/datum/action/innate/cult/spear/spear_act
-/obj/item/twohanded/cult_spear/Initialize(mapload)
+/obj/item/cult_spear/Initialize(mapload)
. = ..()
AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.4, _parryable_attack_types = ALL_ATTACK_TYPES, _parry_cooldown = (2 / 3) SECONDS ) // 0.666667 seconds for 60% uptime.
+ AddComponent(/datum/component/two_handed, force_wielded = 24, force_unwielded = force, icon_wielded = "[base_icon_state]1")
-/obj/item/twohanded/cult_spear/Destroy()
+/obj/item/cult_spear/Destroy()
if(spear_act)
qdel(spear_act)
return ..()
-/obj/item/twohanded/cult_spear/update_icon_state()
- icon_state = "bloodspear[wielded]"
+/obj/item/cult_spear/update_icon_state()
+ icon_state = "[base_icon_state]0"
-/obj/item/twohanded/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+/obj/item/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
var/turf/T = get_turf(hit_atom)
if(isliving(hit_atom))
var/mob/living/L = hit_atom
@@ -621,7 +621,7 @@
else
..()
-/obj/item/twohanded/cult_spear/proc/break_spear(turf/T)
+/obj/item/cult_spear/proc/break_spear(turf/T)
if(!T)
T = get_turf(src)
if(T)
@@ -631,10 +631,10 @@
playsound(T, 'sound/effects/glassbr3.ogg', 100)
qdel(src)
-/obj/item/twohanded/cult_spear/attack(mob/living/M, mob/living/user, def_zone)
+/obj/item/cult_spear/attack(mob/living/M, mob/living/user, def_zone)
. = ..()
var/datum/status_effect/cult_stun_mark/S = M.has_status_effect(STATUS_EFFECT_CULT_STUN)
- if(S && wielded)
+ if(S && HAS_TRAIT(src, TRAIT_WIELDED))
S.trigger()
/datum/action/innate/cult/spear
@@ -642,7 +642,7 @@
desc = "Call the blood spear back to your hand!"
background_icon_state = "bg_cult"
button_icon_state = "bloodspear"
- var/obj/item/twohanded/cult_spear/spear
+ var/obj/item/cult_spear/spear
var/cooldown = 0
/datum/action/innate/cult/spear/Grant(mob/user, obj/blood_spear)
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 3f54785e31de..1e6dc1d4fc61 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -1052,7 +1052,7 @@ structure_check() searches for nearby cultist structures required for the invoca
for(var/mob/M in GLOB.player_list)
if(!isnewplayer(M)) // exclude people in the lobby
- SEND_SOUND(M, sound('sound/effects/dimensional_rend.ogg'))
+ SEND_SOUND(M, sound('modular_ss220/aesthetics_sounds/sound/narsie/narsie_summon.ogg')) //SS220 EDIT
to_chat(M, "The veil... is... TORN!!!--")
icon_state = "rune_large_distorted"
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 165150182358..9ee3e35a22b2 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -35,7 +35,7 @@
var/newscaster_announcements = null
var/ert_disabled = FALSE
var/uplink_welcome = "Syndicate Uplink Console:"
- var/uplink_uses = 20
+ var/uplink_uses = 100
var/list/player_draft_log = list()
var/list/datum/mind/xenos = list()
diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm
index e036921672cf..389699b921a1 100644
--- a/code/game/gamemodes/malfunction/Malf_Modules.dm
+++ b/code/game/gamemodes/malfunction/Malf_Modules.dm
@@ -21,7 +21,7 @@
if(owner_AI && owner_AI.malf_cooldown > world.time)
return
-/datum/action/innate/ai/Trigger()
+/datum/action/innate/ai/Trigger(left_click)
. = ..()
if(auto_use_uses)
adjust_uses(-1)
@@ -36,6 +36,9 @@
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "[name] has run out of uses!")
qdel(src)
+ else
+ desc = "[initial(desc)] It has [uses] use\s remaining."
+ UpdateButtonIcon()
//Framework for ranged abilities that can have different effects by left-clicking stuff.
/datum/action/innate/ai/ranged
@@ -89,7 +92,7 @@
button_icon_state = "choose_module"
auto_use_uses = FALSE // This is an infinite ability.
-/datum/action/innate/ai/choose_modules/Trigger()
+/datum/action/innate/ai/choose_modules/Trigger(left_click)
. = ..()
owner_AI.malf_picker.use(owner_AI)
@@ -100,7 +103,7 @@
button_icon_state = "apcemag"
auto_use_uses = FALSE // Here just to prevent the "You have X uses remaining" from popping up.
-/datum/action/innate/ai/return_to_core/Trigger()
+/datum/action/innate/ai/return_to_core/Trigger(left_click)
. = ..()
var/obj/machinery/power/apc/apc = owner_AI.loc
if(!istype(apc)) // This shouldn't happen but here for safety.
@@ -311,7 +314,7 @@
for(var/explodee in GLOB.player_list)
SEND_SOUND(explodee, doomsday_alarm)
sleep(100)
- SSticker.station_explosion_cinematic(null, "AI malfunction")
+ SSticker.station_explosion_cinematic(NUKE_SITE_ON_STATION, "AI malfunction")
to_chat(world, "The AI cleansed the station of life with the doomsday device!")
SSticker.mode.station_was_nuked = TRUE
@@ -369,7 +372,7 @@
desc = "Detonate all non-cyborg RCDs on the station."
button_icon_state = "detonate_rcds"
uses = 1
- cooldown_period = 100
+ cooldown_period = 10 SECONDS
/datum/action/innate/ai/destroy_rcds/Activate()
for(var/obj/item/rcd/RCD in GLOB.rcd_list)
@@ -487,9 +490,6 @@
ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, FALSE, use_reverb = FALSE)
attached_action.adjust_uses(-1)
- if(attached_action && attached_action.uses)
- attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining."
- attached_action.UpdateButtonIcon()
target.audible_message("You hear a loud electrical buzzing sound coming from [target]!")
addtimer(CALLBACK(attached_action, TYPE_PROC_REF(/datum/action/innate/ai/ranged/overload_machine, detonate_machine), target), 50) //kaboom!
remove_ranged_ability(ranged_ability_user, "Overloading machine circuitry...")
@@ -537,9 +537,6 @@
ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, FALSE, use_reverb = FALSE)
attached_action.adjust_uses(-1)
- if(attached_action && attached_action.uses)
- attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining."
- attached_action.UpdateButtonIcon()
target.audible_message("You hear a loud electrical buzzing sound coming from [target]!")
addtimer(CALLBACK(attached_action, TYPE_PROC_REF(/datum/action/innate/ai/ranged/override_machine, animate_machine), target), 50) //kabeep!
remove_ranged_ability(ranged_ability_user, "Sending override signal...")
@@ -646,9 +643,6 @@
to_chat(owner, "Overcurrent applied to the powernet.")
owner.playsound_local(owner, "sparks", 50, FALSE, use_reverb = FALSE)
adjust_uses(-1)
- if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
- desc = "[initial(desc)] It has [uses] use\s remaining."
- UpdateButtonIcon()
//Reactivate Camera Network: Reactivates up to 30 cameras across the station.
/datum/AI_Module/reactivate_cameras
@@ -656,7 +650,6 @@
mod_pick_name = "recam"
description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras."
cost = 10
- one_purchase = TRUE
power_type = /datum/action/innate/ai/reactivate_cameras
unlock_text = "You deploy nanomachines to the cameranet."
@@ -664,27 +657,27 @@
name = "Reactivate Cameras"
desc = "Reactivates disabled cameras across the station; remaining uses can be used later."
button_icon_state = "reactivate_cameras"
- uses = 30
+ uses = 10
auto_use_uses = FALSE
- cooldown_period = 30
+ cooldown_period = 3 SECONDS
/datum/action/innate/ai/reactivate_cameras/Activate()
- var/fixed_cameras = 0
- for(var/V in GLOB.cameranet.cameras)
+ var/mob/living/silicon/ai/user = usr
+ var/repaired_cameras = 0
+ if(!istype(user))
+ return
+ for(var/obj/machinery/camera/camera_to_repair in get_area(user.eyeobj)) // replace with the camera list on areas when that list actually works, the UIDs change right now so it (almost) always fails
if(!uses)
break
- var/obj/machinery/camera/C = V
- if(!C.status || C.view_range != initial(C.view_range))
- C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc.
- C.view_range = initial(C.view_range)
- fixed_cameras++
- uses-- //Not adjust_uses() so it doesn't automatically delete or show a message
- to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].")
+ if(!camera_to_repair.status || camera_to_repair.view_range != initial(camera_to_repair.view_range))
+ camera_to_repair.toggle_cam(owner_AI, 0)
+ camera_to_repair.view_range = initial(camera_to_repair.view_range)
+ camera_to_repair.wires.cut_wires.Cut()
+ repaired_cameras++
+ uses--
+ to_chat(owner, "Diagnostic complete! Cameras reactivated: [repaired_cameras]. Reactivations remaining: [uses].")
owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, FALSE, use_reverb = FALSE)
- adjust_uses(0, TRUE) //Checks the uses remaining
- if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
- desc = "[initial(desc)] It has [uses] use\s remaining."
- UpdateButtonIcon()
+ adjust_uses(0, TRUE)
//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision.
/datum/AI_Module/upgrade_cameras
@@ -775,7 +768,7 @@
description = "Causes an electrical surge in the targeted cyborg, rebooting and repairing most of its subsystems. Requires two uses on a cyborg with broken armor."
cost = 20
power_type = /datum/action/innate/ai/ranged/repair_cyborg
- unlock_text = "TLB exception on load: Error pointing to address 0000001H, Proceed with execution anywa- SURGE protocalls installed, welcome to open APC!"
+ unlock_text = "TLB exception on load: Error pointing to address 0000001H, Proceed with execution anywa- SURGE protocols installed, welcome to open APC!"
unlock_sound = 'sound/items/rped.ogg'
/datum/action/innate/ai/ranged/repair_cyborg
@@ -815,9 +808,6 @@
is_active = TRUE
ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, FALSE, use_reverb = FALSE)
attached_action.adjust_uses(-1)
- if(attached_action && attached_action.uses)
- attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining."
- attached_action.UpdateButtonIcon()
robot_target.audible_message("You hear a loud electrical buzzing sound coming from [robot_target]!")
if(!do_mob(caller, robot_target, 10 SECONDS))
is_active = FALSE
diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index dc44ec147b96..69bbd6deab9b 100644
--- a/code/game/gamemodes/miniantags/guardian/guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/guardian.dm
@@ -53,6 +53,18 @@
summoner = host
host.grant_guardian_actions(src)
+/mob/living/simple_animal/hostile/guardian/can_buckle()
+ return FALSE
+
+/mob/living/simple_animal/hostile/guardian/rest()
+ return
+
+/mob/living/simple_animal/hostile/guardian/lay_down()
+ set_body_position(STANDING_UP)
+
+/mob/living/simple_animal/hostile/guardian/stand_up(instant, work_when_dead)
+ set_body_position(STANDING_UP)
+
/mob/living/simple_animal/hostile/guardian/med_hud_set_health()
if(summoner)
var/image/holder = hud_list[HEALTH_HUD]
diff --git a/code/game/gamemodes/miniantags/guardian/host_actions.dm b/code/game/gamemodes/miniantags/guardian/host_actions.dm
index ce7147c588ba..d0c3995517ef 100644
--- a/code/game/gamemodes/miniantags/guardian/host_actions.dm
+++ b/code/game/gamemodes/miniantags/guardian/host_actions.dm
@@ -26,7 +26,7 @@
desc = "Communicate telepathically with your guardian."
button_icon_state = "communicate"
-/datum/action/guardian/communicate/Trigger()
+/datum/action/guardian/communicate/Trigger(left_click)
var/input = stripped_input(owner, "Enter a message to tell your guardian:", "Message", "")
if(!input || !guardian)
return
@@ -52,7 +52,7 @@
desc = "Forcibly recall your guardian."
button_icon_state = "recall"
-/datum/action/guardian/recall/Trigger()
+/datum/action/guardian/recall/Trigger(left_click)
guardian.Recall()
/**
@@ -71,7 +71,7 @@
return FALSE
return TRUE
-/datum/action/guardian/reset_guardian/Trigger()
+/datum/action/guardian/reset_guardian/Trigger(left_click)
if(cooldown_timer)
to_chat(owner, "This ability is still recharging.")
return
diff --git a/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm b/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm
index 4eecf105a382..e6cd52148ab8 100644
--- a/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm
+++ b/code/game/gamemodes/miniantags/morph/spells/pass_airlock.dm
@@ -42,6 +42,8 @@
to_chat(user, "You need to stay still to pass through [A]!")
revert_cast(user)
return
+ if(QDELETED(A))
+ return
user.visible_message("[user] briefly opens [A] slightly and passes through!", "You slide through the open crack in [A].")
user.forceMove(A.loc) // Move into the turf of the airlock
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 36da26f38fdd..a54c1631761c 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -258,7 +258,7 @@
return 1337 // WHY??? -- Doohl
-/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 20)
+/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 100)
var/radio_freq = SYND_FREQ
var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt(synd_mob)
diff --git a/code/game/gamemodes/nuclear/nuclear_challenge.dm b/code/game/gamemodes/nuclear/nuclear_challenge.dm
index b43f88b06a11..cd17a0c252c0 100644
--- a/code/game/gamemodes/nuclear/nuclear_challenge.dm
+++ b/code/game/gamemodes/nuclear/nuclear_challenge.dm
@@ -1,7 +1,7 @@
-#define CHALLENGE_TELECRYSTALS 280
+#define CHALLENGE_TELECRYSTALS 1400
#define CHALLENGE_TIME_LIMIT 6000
#define CHALLENGE_SCALE_PLAYER 1 // How many player per scaling bonus
-#define CHALLENGE_SCALE_BONUS 2 // How many TC per scaling bonus
+#define CHALLENGE_SCALE_BONUS 10 // How many TC per scaling bonus
#define CHALLENGE_MIN_PLAYERS 50
#define CHALLENGE_SHUTTLE_DELAY 18000 //30 minutes, so the ops have at least 10 minutes before the shuttle is callable. Gives the nuke ops at least 15 minutes before shuttle arrive.
@@ -10,9 +10,9 @@
icon = 'icons/obj/device.dmi'
icon_state = "gangtool-red"
item_state = "walkietalkie"
- desc = "Use to send a declaration of hostilities to the target, delaying your shuttle departure for 20 minutes while they prepare for your assault. \
- Such a brazen move will attract the attention of powerful benefactors within the Syndicate, who will supply your team with a massive amount of bonus telecrystals. \
- Must be used within five minutes, or your benefactors will lose interest."
+ desc = "Use to send a declaration of hostilities to the target, delaying your shuttle departure for 20 minutes while they prepare for your assault. \
+ Such a brazen move will attract the attention of powerful benefactors within the Syndicate, who will supply your team with a massive amount of bonus telecrystals. \
+ Must be used within ten minutes, or your benefactors will lose interest."
var/declaring_war = FALSE
var/total_tc = 0 //Total amount of telecrystals shared between nuke ops
@@ -59,7 +59,7 @@
S.challenge_time = world.time
// No. of player - Min. Player to dec, divided by player per bonus, then multipled by TC per bonus. Rounded.
- total_tc = CHALLENGE_TELECRYSTALS + round((((GLOB.player_list.len - CHALLENGE_MIN_PLAYERS)/CHALLENGE_SCALE_PLAYER) * CHALLENGE_SCALE_BONUS))
+ total_tc = CHALLENGE_TELECRYSTALS + round(((length(get_living_players(exclude_nonhuman = FALSE, exclude_offstation = TRUE)) - CHALLENGE_MIN_PLAYERS)/CHALLENGE_SCALE_PLAYER) * CHALLENGE_SCALE_BONUS)
share_telecrystals()
SSshuttle.refuel_delay = CHALLENGE_SHUTTLE_DELAY
qdel(src)
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index 40a23f1d45d7..f61991e65b2c 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -531,7 +531,23 @@ GLOBAL_VAR(bomb_set)
if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
-#define NUKERANGE 80
+/// Determine the location of the nuke with respect to the station. Used for,
+/// among other things, calculating win conditions for nukies and choosing which
+/// round-end cinematic to play.
+/obj/machinery/nuclearbomb/proc/get_nuke_site()
+ var/turf/bomb_turf = get_turf(src)
+ if(!bomb_turf)
+ return NUKE_SITE_INVALID
+
+ if(!is_station_level(bomb_turf.z))
+ return NUKE_SITE_OFF_STATION_ZLEVEL
+
+ if(get_area(src) in SSmapping.existing_station_areas)
+ return NUKE_SITE_ON_STATION
+
+ return NUKE_SITE_ON_STATION_ZLEVEL
+
+
/obj/machinery/nuclearbomb/proc/explode()
if(safety)
timing = FALSE
@@ -549,35 +565,34 @@ GLOBAL_VAR(bomb_set)
GLOB.enter_allowed = 0
- var/off_station = 0
- var/turf/bomb_location = get_turf(src)
- var/area/A = get_area(src)
- if( bomb_location && is_station_level(bomb_location.z) )
- if( (bomb_location.x < (128 - NUKERANGE)) || (bomb_location.x > (128 + NUKERANGE)) || (bomb_location.y < (128 - NUKERANGE)) || (bomb_location.y > (128 + NUKERANGE)) && (!(A in GLOB.the_station_areas)))
- off_station = 1
- else
- off_station = 2
+ var/nuke_site = get_nuke_site()
if(SSticker)
if(SSticker.mode && SSticker.mode.name == "nuclear emergency")
var/obj/docking_port/mobile/syndie_shuttle = SSshuttle.getShuttle("syndicate")
if(syndie_shuttle)
SSticker.mode:syndies_didnt_escape = is_station_level(syndie_shuttle.z)
- SSticker.mode:nuke_off_station = off_station
- SSticker.station_explosion_cinematic(off_station,null)
+ SSticker.mode:nuke_off_station = nuke_site
+ SSticker.station_explosion_cinematic(nuke_site, null)
if(SSticker.mode)
SSticker.mode.explosion_in_progress = FALSE
if(SSticker.mode.name == "nuclear emergency")
SSticker.mode:nukes_left --
- else if(off_station == 1)
+ else if(nuke_site == NUKE_SITE_ON_STATION_ZLEVEL)
to_chat(world, "A nuclear device was set off, but the explosion was out of reach of the station!")
- else if(off_station == 2)
- to_chat(world, "A nuclear device was set off, but the device was not on the station!")
+ else if(nuke_site == NUKE_SITE_OFF_STATION_ZLEVEL)
+ to_chat(world, "A nuclear device was set off, but the device nowhere near the station!")
+ else if(nuke_site == NUKE_SITE_INVALID)
+ to_chat(world, "A nuclear device was set off in an unknown location!")
+ log_admin("The nuclear device [src] detonated but was not located on a valid turf.")
else
to_chat(world, "The station was destroyed by the nuclear blast!")
- SSticker.mode.station_was_nuked = (off_station < 2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated.
- //kinda shit but I couldn't get permission to do what I wanted to do.
+ // NUKE_SITE_ON_STATION_ZLEVEL still counts as nuked for the
+ // purposes of /datum/game_mode/nuclear/declare_completion() and its
+ // weird logic of specifying whether the nuke blew up "something
+ // that wasn't" the station.
+ SSticker.mode.station_was_nuked = nuke_site == NUKE_SITE_ON_STATION || nuke_site == NUKE_SITE_ON_STATION_ZLEVEL
if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is
SSticker.reboot_helper("Station destroyed by Nuclear Device.", "nuke - unhandled ending")
diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm
index 42c0e316acb1..21f6e1eced41 100644
--- a/code/game/gamemodes/setupgame.dm
+++ b/code/game/gamemodes/setupgame.dm
@@ -51,7 +51,6 @@
GLOB.breathlessblock = getAssignedBlock("BREATHLESS", numsToAssign, DNA_HARD_BOUNDS, good=1)
GLOB.remoteviewblock = getAssignedBlock("REMOTEVIEW", numsToAssign, DNA_HARDER_BOUNDS, good=1)
GLOB.regenerateblock = getAssignedBlock("REGENERATE", numsToAssign, DNA_HARDER_BOUNDS, good=1)
- GLOB.increaserunblock = getAssignedBlock("INCREASERUN", numsToAssign, DNA_HARDER_BOUNDS, good=1)
GLOB.remotetalkblock = getAssignedBlock("REMOTETALK", numsToAssign, DNA_HARDER_BOUNDS, good=1)
GLOB.morphblock = getAssignedBlock("MORPH", numsToAssign, DNA_HARDER_BOUNDS, good=1)
GLOB.coldblock = getAssignedBlock("COLD", numsToAssign, good=1)
diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm
index e75f9e4a2849..f5b02e2ffb27 100644
--- a/code/game/gamemodes/steal_items.dm
+++ b/code/game/gamemodes/steal_items.dm
@@ -42,9 +42,9 @@
protected_jobs = list("Captain")
location_override = "the Captain's Office"
-/datum/theft_objective/captains_jetpack
- name = "the captain's deluxe jetpack"
- typepath = /obj/item/tank/jetpack/oxygen/captain
+/datum/theft_objective/captains_modsuit
+ name = "the captain's Magnate MODsuit"
+ typepath = /obj/item/mod/control/pre_equipped/magnate
protected_jobs = list("Captain")
location_override = "the Captain's Office"
diff --git a/code/game/gamemodes/vampire/traitor_vamp.dm b/code/game/gamemodes/vampire/traitor_vamp.dm
index ce4d2065c931..960112084045 100644
--- a/code/game/gamemodes/vampire/traitor_vamp.dm
+++ b/code/game/gamemodes/vampire/traitor_vamp.dm
@@ -1,5 +1,5 @@
/datum/game_mode/traitor/vampire
- name = "traitor+vampire"
+ name = "traitor_vampire"
config_tag = "traitorvamp"
traitors_possible = 3 //hard limit on traitors if scaling is turned off
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Magistrate", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Solar Federation General")
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index c455274c4082..d79a807b9c58 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -745,7 +745,7 @@ GLOBAL_LIST_EMPTY(multiverse)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/roman(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), slot_l_hand)
H.equip_to_slot_or_del(new /obj/item/claymore(H), slot_r_hand)
- H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), slot_back)
+ H.equip_to_slot_or_del(new /obj/item/spear(H), slot_back)
if("pirate")
H.equip_to_slot_or_del(new /obj/item/clothing/under/costume/pirate(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/pirate_brown(H), slot_wear_suit)
@@ -753,7 +753,7 @@ GLOBAL_LIST_EMPTY(multiverse)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/clothing/glasses/eyepatch(H), slot_glasses)
H.equip_to_slot_or_del(new /obj/item/claymore(H), slot_r_hand)
- H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), slot_back)
+ H.equip_to_slot_or_del(new /obj/item/spear(H), slot_back)
H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), slot_l_hand)
if("yand")//mine is an evil laugh
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes)
@@ -762,7 +762,7 @@ GLOBAL_LIST_EMPTY(multiverse)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(H), slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/katana(H), slot_r_hand)
H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), slot_l_hand)
- H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), slot_back)
+ H.equip_to_slot_or_del(new /obj/item/spear(H), slot_back)
if("clown")
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/civilian/clown(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/clown_shoes(H), slot_shoes)
@@ -771,7 +771,7 @@ GLOBAL_LIST_EMPTY(multiverse)
H.equip_to_slot_or_del(new /obj/item/bikehorn(H), slot_l_store)
H.equip_to_slot_or_del(new /obj/item/claymore(H), slot_r_hand)
H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), slot_l_hand)
- H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), slot_back)
+ H.equip_to_slot_or_del(new /obj/item/spear(H), slot_back)
/obj/item/necromantic_stone/proc/spawnheresy(mob/living/carbon/human/H as mob)
H.set_species(/datum/species/human)
@@ -797,7 +797,7 @@ GLOBAL_LIST_EMPTY(multiverse)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(H), slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/katana(H), slot_r_hand)
H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), slot_l_hand)
- H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), slot_back)
+ H.equip_to_slot_or_del(new /obj/item/spear(H), slot_back)
if(!H.real_name || H.real_name == "unknown")
H.real_name = "Neko-chan"
else
diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm
index 129f404832a5..3c5cb10c03b8 100644
--- a/code/game/gamemodes/wizard/raginmages.dm
+++ b/code/game/gamemodes/wizard/raginmages.dm
@@ -35,40 +35,28 @@
for(var/datum/mind/wizard in wizards)
if(isnull(wizard.current))
continue
- if(!iscarbon(wizard.current))
+ if(wizard.current.stat == DEAD || isbrain(wizard.current) || !iscarbon(wizard.current))
if(istype(get_area(wizard.current), /area/wizard_station)) // We don't want people camping other wizards
- to_chat(wizard.current, "If there aren't any admins on and another wizard is camping you in the wizard lair, report them on the forums")
- message_admins("[wizard.current] was transformed in the wizard lair, another wizard is likely camping")
- end_squabble(get_area(wizard.current))
- continue
- if(isbrain(wizard.current))
- if(istype(get_area(wizard.current), /area/wizard_station)) // We don't want people camping other wizards
- to_chat(wizard.current, "If there aren't any admins on and another wizard is camping you in the wizard lair, report them on the forums")
- message_admins("[wizard.current] was brainified in the wizard lair, another wizard is likely camping")
- end_squabble(get_area(wizard.current))
- continue
- if(wizard.current.stat==DEAD)
- if(istype(get_area(wizard.current), /area/wizard_station)) // We don't want people camping other wizards
- to_chat(wizard.current, "If there aren't any admins on and another wizard is camping you in the wizard lair, report them on the forums")
+ to_chat(wizard.current, "If there aren't any admins on and another wizard is camping you in the wizard lair, report them on the forums.")
message_admins("[wizard.current] died in the wizard lair, another wizard is likely camping")
end_squabble(get_area(wizard.current))
continue
- if(wizard.current.stat==UNCONSCIOUS)
+ if(wizard.current.stat == UNCONSCIOUS)
if(wizard.current.health < HEALTH_THRESHOLD_DEAD) //Lets make this not get funny rng crit involved
if(istype(get_area(wizard.current), /area/wizard_station))
- to_chat(wizard.current, "If there aren't any admins on and another wizard is camping you in the wizard lair, report them on the forums")
+ to_chat(wizard.current, "If there aren't any admins on and another wizard is camping you in the wizard lair, report them on the forums.")
message_admins("[wizard.current] went into crit in the wizard lair, another wizard is likely camping")
end_squabble(get_area(wizard.current))
else
to_chat(wizard.current, "The Space Wizard Federation is upset with your performance and have terminated your employment.")
wizard.current.dust() // *REAL* ACTION!! *REAL* DRAMA!! *REAL* BLOODSHED!!
continue
- if(wizard.current.client && wizard.current.client.is_afk() > 10 * 60 * 10) // 10 minutes
+ if(!wizard.current.client)
+ continue // Could just be a bad connection, so SSD wiz's shouldn't be gibbed over it, but they're not "alive" either
+ if(wizard.current.client.is_afk() > 10 MINUTES)
to_chat(wizard.current, "The Space Wizard Federation is upset with your performance and have terminated your employment.")
wizard.current.dust() // Let's keep the round moving
continue
- if(!wizard.current.client)
- continue // Could just be a bad connection, so SSD wiz's shouldn't be gibbed over it, but they're not "alive" either
wizards_alive++
if(wizards_alive)
@@ -77,9 +65,9 @@
time_checked = world.time
make_more_mages()
else
- if(wizards.len >= wizard_cap)
+ if(length(wizards) >= wizard_cap)
finished = TRUE
- return 1
+ return TRUE
else
make_more_mages()
return ..()
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index b0e1d668ebca..6723c152e8de 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -421,10 +421,12 @@
qdel(shade)
qdel(SS)
-/proc/make_new_construct(mob/living/simple_animal/hostile/construct/c_type, mob/target, mob/user, cult_override = FALSE)
+/proc/make_new_construct(mob/living/simple_animal/hostile/construct/c_type, mob/target, mob/user, cult_override = FALSE, create_smoke = FALSE)
if(jobban_isbanned(target, ROLE_CULTIST))
return
var/mob/living/simple_animal/hostile/construct/C = new c_type(get_turf(target))
+ if(create_smoke)
+ new /obj/effect/particle_effect/smoke/sleeping(target.loc)
C.faction |= "\ref[user]"
C.key = target.key
if(user && iscultist(user) || cult_override)
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index cca639f9a9a0..c4c248f145f4 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -474,29 +474,22 @@
//Weapons and Armors
/datum/spellbook_entry/item/battlemage
- name = "Battlemage Armour"
- desc = "An ensorceled suit of armour, protected by a powerful shield. The shield can completely negate sixteen attacks before being permanently depleted. Despite appearance it is NOT spaceproof."
+ name = "Battlemage Armor"
+ desc = "An ensorceled spaceproof suit of protective yet light armor, protected by a powerful shield. The shield can completely negate 15 attacks before permanently failing."
item_path = /obj/item/storage/box/wizard/hardsuit
limit = 1
category = "Weapons and Armors"
-/datum/spellbook_entry/item/battlemage_charge
- name = "Battlemage Armour Charges"
- desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour."
- item_path = /obj/item/wizard_armour_charge
- category = "Weapons and Armors"
- cost = 1
-
/datum/spellbook_entry/item/mjolnir
name = "Mjolnir"
desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power."
- item_path = /obj/item/twohanded/mjollnir
+ item_path = /obj/item/mjollnir
category = "Weapons and Armors"
/datum/spellbook_entry/item/singularity_hammer
name = "Singularity Hammer"
desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everything nearby to the point of impact."
- item_path = /obj/item/twohanded/singularityhammer
+ item_path = /obj/item/singularityhammer
category = "Weapons and Armors"
/datum/spellbook_entry/item/cursed_katana
@@ -573,7 +566,7 @@
name = "Bottle of Tickles"
desc = "A bottle of magically infused fun, the smell of which will \
attract adorable extradimensional beings when broken. These beings \
- are similar to slaughter demons, but are a little weaker and they do not permamently \
+ are similar to slaughter demons, but are a little weaker and they do not permanently \
kill their victims, instead putting them in an extradimensional hugspace, \
to be released on the demon's death. Chaotic, but not ultimately \
damaging. The crew's reaction to the other hand could be very \
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index 55006b406cae..e350d064b3dc 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -164,11 +164,7 @@
// Wizards
for(var/datum/mind/wizard in wizards)
- if(!iscarbon(wizard.current))
- continue
- if(wizard.current.stat==DEAD)
- continue
- if(istype(wizard.current, /obj/item/mmi)) // wizard is in an MMI, don't count them as alive
+ if(!iscarbon(wizard.current) || wizard.current.stat == DEAD) // wizard is in an MMI, don't count them as alive
continue
wizards_alive++
diff --git a/code/game/gamemodes/wizard/wizloadouts.dm b/code/game/gamemodes/wizard/wizloadouts.dm
index 88e58ed8a38e..7b4269a9f4ad 100644
--- a/code/game/gamemodes/wizard/wizloadouts.dm
+++ b/code/game/gamemodes/wizard/wizloadouts.dm
@@ -70,12 +70,12 @@
name = "Tyde the Grey"
desc = "A set of legendary artifacts used by a bald, grey wizard, now passed on to you.
\
Open His Grace's latch once you are ready to kill by using It in your hand. Keep It fed or you will be Its next meal.
\
- You might want to raid the Armory or loot a Security Officer to get ranged weapons like a disabler, His Grace's Hunger has little patience.
\
- Provides His Grace, an Ancient Jumpsuit, an Assistant ID, a Gas Mask and Shoes, Insulated Gloves, a full Toolbelt, Ethereal Jaunt, Force Wall, Knock and No Clothes."
+ If your Homing Toolbox spell is not enough, you might want to raid the Armory or loot a Security Officer to get more ranged weapons like a disabler, His Grace's Hunger has little patience.
\
+ Provides His Grace, an Ancient Jumpsuit, an Assistant ID, a Gas Mask and Shoes, Insulated Gloves, a full Toolbelt, Ethereal Jaunt, Force Wall, Homing Toolbox, Knock and No Clothes."
items_path = list(/obj/item/his_grace, /obj/item/clothing/under/color/grey/glorf, /obj/item/clothing/mask/gas, /obj/item/clothing/shoes/black, \
/obj/item/clothing/gloves/color/yellow, /obj/item/storage/belt/utility/full/multitool)
spells_path = list(/obj/effect/proc_holder/spell/ethereal_jaunt, /obj/effect/proc_holder/spell/forcewall, \
- /obj/effect/proc_holder/spell/aoe/knock, /obj/effect/proc_holder/spell/noclothes)
+ /obj/effect/proc_holder/spell/aoe/knock, /obj/effect/proc_holder/spell/noclothes, /obj/effect/proc_holder/spell/fireball/toolbox)
category = "Unique"
destroy_spellbook = TRUE
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index 2b54af96240d..1bbd3883c0d9 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -144,6 +144,9 @@
/datum/job/proc/is_position_available()
return (current_positions < total_positions) || (total_positions == -1)
+/datum/job/proc/is_spawn_position_available()
+ return (current_positions < spawn_positions) || (spawn_positions == -1)
+
/datum/outfit/job
name = "Standard Gear"
collect_not_del = TRUE // we don't want anyone to lose their job shit
@@ -241,6 +244,10 @@
gear_leftovers.Cut()
+ if(ismodcontrol(H.back))
+ var/obj/item/mod/control/C = H.back
+ C.quick_activation()
+
return 1
/datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H)
diff --git a/code/game/jobs/job/medical_jobs.dm b/code/game/jobs/job/medical_jobs.dm
index 6c0f384eeea3..76b8e1cfbaaf 100644
--- a/code/game/jobs/job/medical_jobs.dm
+++ b/code/game/jobs/job/medical_jobs.dm
@@ -285,11 +285,12 @@
supervisors = "the chief medical officer"
department_head = list("Chief Medical Officer")
selection_color = "#ffeef0"
- access = list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EVA, ACCESS_MORGUE)
- minimal_access=list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EVA, ACCESS_MORGUE)
+ access = list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EVA, ACCESS_MORGUE, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_MAILSORTING)
+ minimal_access=list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EVA, ACCESS_MORGUE, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_MAILSORTING)
minimal_player_age = 3
exp_map = list(EXP_TYPE_CREW = 180)
outfit = /datum/outfit/job/paramedic
+ important_information = "You are the first responder to medical emergencies outside the sanctity of the Medbay. You can also respond to Lavaland emergencies via the mining shuttle located in Cargo."
/datum/outfit/job/paramedic
name = "Paramedic"
@@ -299,13 +300,14 @@
shoes = /obj/item/clothing/shoes/black
head = /obj/item/clothing/head/soft/blue
mask = /obj/item/clothing/mask/cigarette
- l_ear = /obj/item/radio/headset/headset_med
+ l_ear = /obj/item/radio/headset/headset_med/para
id = /obj/item/card/id/paramedic
l_pocket = /obj/item/flashlight/pen
pda = /obj/item/pda/medical
backpack_contents = list(
- /obj/item/healthanalyzer = 1
- )
+ /obj/item/healthanalyzer = 1,
+ /obj/item/sensor_device = 1,
+ /obj/item/pinpointer/crew = 1)
backpack = /obj/item/storage/backpack/medic
satchel = /obj/item/storage/backpack/satchel_med
diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm
index 44a234f3f49f..1564fdc46be1 100644
--- a/code/game/jobs/job/support.dm
+++ b/code/game/jobs/job/support.dm
@@ -13,6 +13,7 @@
access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
outfit = /datum/outfit/job/qm
+ exp_map = list(EXP_TYPE_SUPPLY = 1200)
/datum/outfit/job/qm
name = "Quartermaster"
@@ -164,7 +165,9 @@
singlemutcheck(H, GLOB.soberblock, MUTCHK_FORCED)
H.dna.default_blocks.Add(GLOB.soberblock)
H.check_mutations = 1
-
+ ADD_TRAIT(H, TRAIT_TABLE_LEAP, ROUNDSTART_TRAIT)
+ var/datum/martial_art/judo/under_siege/bouncer_delight = new
+ bouncer_delight.teach(H)
/datum/job/chef
@@ -205,6 +208,7 @@
return
var/datum/martial_art/cqc/under_siege/justacook = new
justacook.teach(H)
+ ADD_TRAIT(H, TRAIT_TABLE_LEAP, ROUNDSTART_TRAIT)
/datum/job/hydro
diff --git a/code/game/jobs/job/syndicate_jobs.dm b/code/game/jobs/job/syndicate_jobs.dm
index 8c96ba7da397..1f3168aeb777 100644
--- a/code/game/jobs/job/syndicate_jobs.dm
+++ b/code/game/jobs/job/syndicate_jobs.dm
@@ -49,7 +49,7 @@
var/obj/item/implant/uplink/admin/U = new /obj/item/implant/uplink/admin(H)
U.implant(H)
- U.hidden_uplink.uses = 500
+ U.hidden_uplink.uses = 2500
H.faction += "syndicate"
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
opshud.join_hud(H.mind.current)
diff --git a/code/game/machinery/airlock_control/airlock_controllers.dm b/code/game/machinery/airlock_control/airlock_controllers.dm
index cf4da3e384b9..4d7a59cf4d14 100644
--- a/code/game/machinery/airlock_control/airlock_controllers.dm
+++ b/code/game/machinery/airlock_control/airlock_controllers.dm
@@ -81,6 +81,7 @@
/obj/machinery/airlock_controller/attack_hand(mob/user)
if(!user.IsAdvancedToolUser())
+ to_chat(user, "You don't have the dexterity to do this!")
return FALSE
ui_interact(user)
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 1f7256f4b9c7..a1025f9ee261 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -50,6 +50,8 @@
component_parts += new /obj/item/stack/sheet/glass(null)
RefreshParts()
+ RegisterSignal(src, COMSIG_TOOL_ATTACK, PROC_REF(on_tool_attack))
+
wires = new(src)
files = new /datum/research/autolathe(src)
matching_designs = list()
@@ -77,6 +79,15 @@
materials.retrieve_all()
return ..()
+/obj/machinery/autolathe/proc/on_tool_attack(datum/source, atom/tool, mob/user)
+ SIGNAL_HANDLER
+ var/obj/item/I = tool
+ if(!istype(I))
+ return
+ // Allows screwdrivers to be recycled on harm intent
+ if(I.tool_behaviour == TOOL_SCREWDRIVER && user.a_intent == INTENT_HARM)
+ return COMPONENT_CANCEL_TOOLACT
+
/obj/machinery/autolathe/interact(mob/user)
if(shocked && !(stat & NOPOWER))
if(shock(user, 50))
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 141144dc7506..f2d9454fb524 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -98,7 +98,7 @@
network = list()
stat |= EMPED
turn_off(null, FALSE, TRUE)
- addtimer(CALLBACK(src, PROC_REF(reactivate_after_emp)), 90 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
+ addtimer(CALLBACK(src, PROC_REF(reactivate_after_emp)), (90 / severity) SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
..()
/obj/machinery/camera/proc/reactivate_after_emp()
diff --git a/code/game/machinery/camera/camera_presets.dm b/code/game/machinery/camera/camera_presets.dm
index 512dc58fe674..c3ea1f9fa37d 100644
--- a/code/game/machinery/camera/camera_presets.dm
+++ b/code/game/machinery/camera/camera_presets.dm
@@ -33,29 +33,17 @@
upgradeXRay()
upgradeMotion()
-// AUTONAME
-
-/obj/machinery/camera/autoname
- var/number = 0 //camera number in area
-
// This camera type automatically sets it's name to whatever the area that it's in is called.
/obj/machinery/camera/autoname/Initialize(mapload)
- number = 1
- var/area/A = get_area(src)
- if(!A)
- number = rand(1, 100)
- c_tag = "Unknown #[number]"
+ var/static/list/autonames_in_areas = list()
+
+ var/area/camera_area = get_area(src)
+ if(!camera_area)
+ c_tag = "Unknown #[rand(1, 100)]"
stack_trace("Camera with tag [c_tag] was spawned without an area, please report this to your nearest coder.")
return ..()
- for(var/obj/machinery/camera/autoname/C in A.contents)
- if(C == src)
- continue
- if(C.number)
- number = max(number, C.number + 1)
-
- c_tag = "[sanitize(A.name)] #[number]"
-
+ c_tag = "[sanitize(camera_area.name)] #[++autonames_in_areas[camera_area]]" // increase the number, then print it (this is what ++ before does)
return ..() // We do this here so the camera is not added to the cameranet until it has a name.
// CHECKS
diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm
index 3fd7f2ab21ab..2ed707fc775c 100644
--- a/code/game/machinery/camera/tracking.dm
+++ b/code/game/machinery/camera/tracking.dm
@@ -5,10 +5,6 @@
return 1
return 0
-
-/mob/living/silicon/ai/var/max_locations = 10
-/mob/living/silicon/ai/var/stored_locations[0]
-
/mob/living/silicon/ai/proc/get_camera_list()
track.cameras.Cut()
@@ -49,59 +45,6 @@
return
-/mob/living/silicon/ai/proc/ai_store_location(loc as text)
- set category = "AI Commands"
- set name = "Store Camera Location"
- set desc = "Stores your current camera location by the given name"
-
- loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN))
- if(!loc)
- to_chat(src, "Must supply a location name")
- return
-
- if(stored_locations.len >= max_locations)
- to_chat(src, "Cannot store additional locations. Remove one first")
- return
-
- if(loc in stored_locations)
- to_chat(src, "There is already a stored location by this name")
- return
-
- var/L = get_turf(eyeobj)
- if(InvalidTurf(get_turf(L)))
- to_chat(src, "Unable to store this location")
- return
-
- stored_locations[loc] = L
- to_chat(src, "Location '[loc]' stored")
-
-/mob/living/silicon/ai/proc/sorted_stored_locations()
- return sortList(stored_locations)
-
-/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations())
- set category = "AI Commands"
- set name = "Goto Camera Location"
- set desc = "Returns to the selected camera location"
-
- if(!(loc in stored_locations))
- to_chat(src, "Location [loc] not found")
- return
-
- var/L = stored_locations[loc]
- src.eyeobj.setLoc(L)
-
-/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations())
- set category = "AI Commands"
- set name = "Delete Camera Location"
- set desc = "Deletes the selected camera location"
-
- if(!(loc in stored_locations))
- to_chat(src, "Location [loc] not found")
- return
-
- stored_locations.Remove(loc)
- to_chat(src, "Location [loc] removed")
-
// Used to allow the AI is write in mob names/camera name from the CMD line.
/datum/trackable
var/list/names = list()
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index dc0d1d1aff6c..33f31f8b4eb5 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -183,6 +183,8 @@
return FALSE
/obj/machinery/computer/operating/process()
+ if(stat & (NOPOWER|BROKEN))
+ return
if(!table) //Does this Operating Computer have an Operating Table connected to it?
return
if(!verbose) //Are the speakers on?
diff --git a/code/game/machinery/computer/message_monitor.dm b/code/game/machinery/computer/message_monitor.dm
index 6b09cbebcb2f..8e1dbc43a61a 100644
--- a/code/game/machinery/computer/message_monitor.dm
+++ b/code/game/machinery/computer/message_monitor.dm
@@ -400,7 +400,8 @@
//Select Your Name
if("Sender")
- customsender = clean_input("Please enter the sender's name.")
+ customsender = input("Please enter the sender's name.")
+ customsender = trim_strip_html_properly(customsender)
//Select Receiver
if("Recepient")
@@ -419,12 +420,13 @@
//Enter custom job
if("RecJob")
- customjob = clean_input("Please enter the sender's job.")
+ customjob = input("Please enter the sender's job.")
+ customjob = trim_strip_html_properly(customjob)
//Enter message
if("Message")
- custommessage = clean_input("Please enter your message.")
- custommessage = sanitize(copytext(custommessage, 1, MAX_MESSAGE_LEN))
+ custommessage = input("Please enter your message.")
+ custommessage = trim_strip_html_properly(custommessage)
//Send message
if("Send")
@@ -454,11 +456,13 @@
if(P.owner == customsender)
PDARec = P
break
+
+ var/sender_identity
//Sender isn't faking as someone who exists
if(isnull(PDARec))
src.linkedServer.send_pda_message("[customrecepient.owner]", "[customsender]","[custommessage]")
recipient_messenger.notify("Message from [customsender] ([customjob]), \"[custommessage]\" (Reply)")
- log_pda("(PDA: [customsender]) sent \"[custommessage]\" to [customrecepient.owner]", usr)
+ sender_identity = customsender
//Sender is faking as someone who exists
else
src.linkedServer.send_pda_message("[customrecepient.owner]", "[PDARec.owner]","[custommessage]")
@@ -468,7 +472,12 @@
recipient_messenger.conversations.Add("\ref[PDARec]")
recipient_messenger.notify("Message from [PDARec.owner] ([customjob]), \"[custommessage]\" (Reply)")
- log_pda("(PDA: [PDARec.owner]) sent \"[custommessage]\" to [customrecepient.owner]", usr)
+ sender_identity = PDARec.owner
+
+ // Logging
+ log_pda("(PDA: [sender_identity]) sent \"[custommessage]\" to [customrecepient.owner]", usr)
+ investigate_log("PDA Message - Custom Name: \"[sender_identity]\", Custom Job: \"[customjob]\", Real Sender: \"[key_name(usr)]\" ([ADMIN_PP(usr,"PP")]) -> [customrecepient.owner] ([ADMIN_VV(customrecepient, "VV")]), Message: \"[custommessage]\"", "pda")
+
var/log_message = "sent PDA message \"[custommessage]\" using [src] as [customsender] ([customjob])"
var/receiver
if(ishuman(customrecepient.loc))
diff --git a/code/game/machinery/computer/sm_monitor.dm b/code/game/machinery/computer/sm_monitor.dm
index 52ea9e88affa..4c5e1171af58 100644
--- a/code/game/machinery/computer/sm_monitor.dm
+++ b/code/game/machinery/computer/sm_monitor.dm
@@ -144,4 +144,3 @@
if("back")
active = null
-
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index 95b38874c63f..36d8f9a4489f 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -52,6 +52,7 @@
// update description of required components remaining
/obj/machinery/constructable_frame/proc/update_req_desc()
if(!req_components || !req_component_names)
+ desc = "Does not require any more components."
return
var/hasContent = 0
@@ -111,7 +112,7 @@
icon_state = "box_2"
state = 3
components = list()
- req_components = circuit.req_components.Copy()
+ req_components = circuit.req_components?.Copy()
update_namelist()
update_req_desc()
else
@@ -1074,7 +1075,10 @@ to destroy them and players will be able to make replacements.
/obj/item/stack/sheet/glass = 1)
/obj/item/circuitboard/merch
- name = "Merchandise Computer Circuitboard"
+ board_name = "Nanotrasen Merchandise Vendor"
icon_state = "generic"
build_path = /obj/machinery/economy/merch
board_type = "machine"
+ req_components = list(
+ /obj/item/stock_parts/matter_bin = 1,
+ /obj/item/stack/cable_coil = 1)
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index fbacca6966f5..8789246c78da 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -225,7 +225,8 @@
/obj/item/holosign_creator/atmos,
/obj/item/clothing/gloves/color/black/forensics,
/obj/item/rcd,
- /obj/item/rpd
+ /obj/item/rpd,
+ /obj/item/mod/control
)
// These items will NOT be preserved
var/list/do_not_preserve_items = list (
@@ -340,7 +341,11 @@
QDEL_NULL(P.id)
qdel(P)
continue
-
+ if(istype(I, /obj/item/storage/backpack/modstorage)) //Best place for me to put it.
+ var/obj/item/storage/backpack/modstorage/M = I
+ M.forceMove(M.source)
+ continue
+
var/preserve = should_preserve_item(I)
if(preserve == CRYO_DESTROY)
qdel(I)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 3e4c50ae4d2d..c96fd23ebdcb 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -212,8 +212,8 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
/obj/machinery/door/airlock/proc/canAIControl()
return ((aiControlDisabled != AICONTROLDISABLED_ON) && (!isAllPowerLoss()))
-/obj/machinery/door/airlock/proc/canAIHack()
- return ((aiControlDisabled == AICONTROLDISABLED_ON) && (!hackProof) && (!isAllPowerLoss()))
+/obj/machinery/door/airlock/proc/canAIHack(mob/living/user)
+ return ((aiControlDisabled == AICONTROLDISABLED_ON) && (!hackProof) && (!isAllPowerLoss()) && user.mind?.special_role)
/obj/machinery/door/airlock/proc/arePowerSystemsOn()
if(stat & (NOPOWER|BROKEN))
@@ -343,12 +343,12 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
polarized_image.dir = dir
if(!polarized_on)
- polarized_image.color = "#222222"
+ polarized_image.color = "#2A3A45" //SS220 EDIT - ORIGINAL: #222222
animate_color = "#FFFFFF"
set_opacity(FALSE)
else
polarized_image.color = "#FFFFFF"
- animate_color = "#222222"
+ animate_color = "#2A3A45" //SS220 EDIT - ORIGINAL: #222222
set_opacity(TRUE)
overlays -= polarized_image
@@ -443,6 +443,11 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
else if(emergency)
lights_overlay = get_airlock_overlay("lights_emergency", overlays_file)
lights_underlay = get_airlock_emissive_underlay("lights_emergency_lightmask", overlays_file)
+ // SS220 ADDITION - START
+ else
+ lights_overlay = get_airlock_overlay("lights_poweron", overlays_file)
+ lights_underlay = get_airlock_emissive_underlay("lights_poweron_lightmask", overlays_file)
+ // SS220 ADDITION - END
if(note)
note_overlay = get_airlock_overlay(notetype, note_overlay_file)
note_overlay.layer = layer + 0.1
@@ -521,6 +526,11 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
if(AIRLOCK_OPEN)
frame_overlay = get_airlock_overlay("open", icon)
+ // SS220 ADDITION - START
+ if(lights && arePowerSystemsOn())
+ lights_overlay = get_airlock_overlay("lights_poweron_open", overlays_file)
+ lights_underlay = get_airlock_emissive_underlay("lights_poweron_open_lightmask", overlays_file)
+ // SS220 ADDITION - END
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_open", overlays_file)
else
@@ -556,7 +566,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
note_overlay = get_airlock_overlay("[notetype]_opening", note_overlay_file)
if(polarized_on)
- filling_overlay.color = "#222222"
+ filling_overlay.color = "#2A3A45" //SS220 EDIT - ORIGINAL: #222222
else
filling_overlay.color = "#FFFFFF"
@@ -716,7 +726,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
if(!aiHacking)
aiHacking = TRUE
to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.")
- sleep(50)
+ sleep(3 SECONDS)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
@@ -726,9 +736,9 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
aiHacking = FALSE
return
to_chat(user, "Fault confirmed: airlock control wire disabled or cut.")
- sleep(20)
+ sleep(2 SECONDS)
to_chat(user, "Attempting to hack into airlock. This may take some time.")
- sleep(200)
+ sleep(15 SECONDS)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
@@ -738,7 +748,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
aiHacking = FALSE
return
to_chat(user, "Upload access confirmed. Loading control program into airlock software.")
- sleep(170)
+ sleep(5 SECONDS)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
@@ -748,11 +758,11 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
aiHacking = FALSE
return
to_chat(user, "Transfer complete. Forcing airlock to execute program.")
- sleep(50)
+ sleep(5 SECONDS)
//disable blocked control
aiControlDisabled = AICONTROLDISABLED_BYPASS
to_chat(user, "Receiving control information from airlock.")
- sleep(10)
+ sleep(1 SECONDS)
//bring up airlock dialog
aiHacking = FALSE
if(user)
@@ -1223,16 +1233,14 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
to_chat(user, "Despite your attempts, [src] refuses to open.")
/obj/machinery/door/airlock/proc/force_open_with_item(mob/living/user, obj/item/I)
- /// Used with an istype check to find out if it's a wielded item or not
- var/obj/item/twohanded/twohanded_item = I
/// Time it takes to open an airlock with an item with the TRAIT_FORCES_OPEN_DOORS_ITEM trait, 5 seconds for wielded items, 10 seconds for nonwielded items
var/time_to_open_airlock = 10 SECONDS
/// Can we open the airlock while unpowered? Wielded item's can't, but unwielded items can
var/can_force_open_while_unpowered = TRUE
- if(istype(twohanded_item))
+ if(I.GetComponent(/datum/component/two_handed))
can_force_open_while_unpowered = FALSE
time_to_open_airlock = 5 SECONDS
- if(!twohanded_item.wielded)
+ if(!HAS_TRAIT(I, TRAIT_WIELDED))
to_chat(user, "You need to be wielding [I] to do that!")
return
if(!density || prying_so_hard)
@@ -1257,7 +1265,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
return 0
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(forced)
- playsound(loc, 'sound/machines/airlockforced.ogg', 30, 1)
+ playsound(loc, 'modular_ss220/aesthetics/airlocks/sound/open_force.ogg', 30, 1) // SS220 EDIT: ORIGINAL - 'sound/machines/airlockforced.ogg'
else
playsound(loc, doorOpen, 30, 1)
if(closeOther != null && istype(closeOther, /obj/machinery/door/airlock/) && !closeOther.density)
@@ -1302,7 +1310,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(forced)
- playsound(loc, 'sound/machines/airlockforced.ogg', 30, 1)
+ playsound(loc, 'modular_ss220/aesthetics/airlocks/sound/close_force.ogg', 30, 1) // SS220 EDIT: ORIGINAL - 'sound/machines/airlockforced.ogg'
else
playsound(loc, doorClose, 30, 1)
var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src))
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index cb99db0da049..9ac2e19b2abd 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -56,7 +56,7 @@
set_opacity(FALSE)
else
old_color = color
- animate(src, color = "#222222", time = 0.5 SECONDS)
+ animate(src, color = "#2A3A45", time = 0.5 SECONDS) //SS220 EDIT - ORIGINAL: #222222
set_opacity(TRUE)
/obj/machinery/door/window/update_icon_state()
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index 8de409a0a7b0..76e11313376c 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -143,6 +143,7 @@
return TRUE
/obj/machinery/recharge_station/proc/process_occupant()
+ SEND_SIGNAL(occupant, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, recharge_speed, repairs)
if(isrobot(occupant))
var/mob/living/silicon/robot/R = occupant
if(R.module)
@@ -153,10 +154,12 @@
R.cell.charge = min(R.cell.charge + recharge_speed, R.cell.maxcharge)
else if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
- if(H.get_int_organ(/obj/item/organ/internal/cell) && H.nutrition < NUTRITION_LEVEL_FULL - 1)
- H.set_nutrition(min(H.nutrition + recharge_speed_nutrition, NUTRITION_LEVEL_FULL - 1))
- if(repairs)
- H.heal_overall_damage(repairs, repairs, TRUE, 0, 1)
+ if(H.get_int_organ(/obj/item/organ/internal/cell))
+ if(H.nutrition < NUTRITION_LEVEL_FULL - 1)
+ H.set_nutrition(min(H.nutrition + recharge_speed_nutrition, NUTRITION_LEVEL_FULL - 1))
+ if(repairs)
+ H.heal_overall_damage(repairs, repairs, TRUE, 0, 1)
+
/obj/machinery/recharge_station/proc/go_out()
if(!occupant)
@@ -220,8 +223,9 @@
if(occupant)
to_chat(H, "The cell is already occupied!")
return
- if(!H.get_int_organ(/obj/item/organ/internal/cell))
- return
+ if(!ismodcontrol(H.back))
+ if(!H.get_int_organ(/obj/item/organ/internal/cell))
+ return
can_accept_user = 1
if(!can_accept_user)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 50a19397f24f..fd473b192ea6 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -65,10 +65,9 @@
/obj/machinery/suit_storage_unit/captain
name = "captain's suit storage unit"
desc = "An industrial U-Stor-It Storage unit designed to accommodate all kinds of space suits. Its on-board equipment also allows the user to decontaminate the contents through a UV-ray purging cycle. There's a warning label dangling from the control pad, reading \"STRICTLY NO BIOLOGICALS IN THE CONFINES OF THE UNIT\". This one looks kind of fancy."
- suit_type = /obj/item/clothing/suit/space/captain
- helmet_type = /obj/item/clothing/head/helmet/space/capspace
+ helmet_type = /obj/item/clothing/head/helmet/space/capspace //Looks like they couldn't handle the Neutron Style
mask_type = /obj/item/clothing/mask/gas
- storage_type = /obj/item/tank/jetpack/oxygen/captain
+ suit_type = /obj/item/mod/control/pre_equipped/magnate
req_access = list(ACCESS_CAPTAIN)
/obj/machinery/suit_storage_unit/captain/secure
@@ -78,9 +77,9 @@
name = "engineering suit storage unit"
icon_state = "industrial"
base_icon_state = "industrial"
- suit_type = /obj/item/clothing/suit/space/hardsuit/engine
mask_type = /obj/item/clothing/mask/breath
boots_type = /obj/item/clothing/shoes/magboots
+ suit_type = /obj/item/mod/control/pre_equipped/engineering
req_access = list(ACCESS_ENGINE_EQUIP)
/obj/machinery/suit_storage_unit/engine/secure
@@ -90,9 +89,9 @@
name = "chief engineer's suit storage unit"
icon_state = "industrial"
base_icon_state = "industrial"
- suit_type = /obj/item/clothing/suit/space/hardsuit/engine/elite
mask_type = /obj/item/clothing/mask/gas
boots_type = /obj/item/clothing/shoes/magboots/advance
+ suit_type = /obj/item/mod/control/pre_equipped/advanced
req_access = list(ACCESS_CE)
/obj/machinery/suit_storage_unit/ce/secure
@@ -100,7 +99,7 @@
/obj/machinery/suit_storage_unit/rd
name = "research director's suit storage unit"
- suit_type = /obj/item/clothing/suit/space/hardsuit/rd
+ suit_type = /obj/item/mod/control/pre_equipped/research
mask_type = /obj/item/clothing/mask/gas
req_access = list(ACCESS_RD)
@@ -109,9 +108,8 @@
/obj/machinery/suit_storage_unit/security
name = "security suit storage unit"
- suit_type = /obj/item/clothing/suit/space/hardsuit/security
mask_type = /obj/item/clothing/mask/gas/sechailer
- storage_type = /obj/item/tank/jetpack/oxygen/security
+ suit_type = /obj/item/mod/control/pre_equipped/security
req_access = list(ACCESS_SECURITY)
/obj/machinery/suit_storage_unit/security/secure
@@ -119,9 +117,8 @@
/obj/machinery/suit_storage_unit/security/hos
name = "Head of Security's suit storage unit"
- suit_type = /obj/item/clothing/suit/space/hardsuit/security/hos
mask_type = /obj/item/clothing/mask/gas/sechailer/hos
- storage_type = null
+ suit_type = /obj/item/mod/control/pre_equipped/safeguard
req_access = list(ACCESS_HOS)
/obj/machinery/suit_storage_unit/security/hos/secure
@@ -138,9 +135,9 @@
/obj/machinery/suit_storage_unit/atmos
name = "atmospherics suit storage unit"
- suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos
mask_type = /obj/item/clothing/mask/gas
boots_type = /obj/item/clothing/shoes/magboots/atmos
+ suit_type = /obj/item/mod/control/pre_equipped/atmospheric
req_access = list(ACCESS_ATMOSPHERICS)
/obj/machinery/suit_storage_unit/atmos/secure
@@ -148,8 +145,8 @@
/obj/machinery/suit_storage_unit/mining
name = "mining suit storage unit"
- suit_type = /obj/item/clothing/suit/space/hardsuit/mining
mask_type = /obj/item/clothing/mask/breath
+ suit_type = /obj/item/mod/control/pre_equipped/mining/asteroid
req_access = list(ACCESS_MINING_STATION)
/obj/machinery/suit_storage_unit/mining/secure
@@ -163,14 +160,14 @@
req_access = list(ACCESS_MINING_STATION)
/obj/machinery/suit_storage_unit/cmo
- suit_type = /obj/item/clothing/suit/space/hardsuit/medical
mask_type = /obj/item/clothing/mask/breath
+ suit_type = /obj/item/mod/control/pre_equipped/medical
req_access = list(ACCESS_CMO)
/obj/machinery/suit_storage_unit/cmo/secure
secure = TRUE
-//version of the SSU for medbay secondary storage. Includes magboots.
+//version of the SSU for medbay secondary storage. Includes magboots. //no it doesn't, it aint have shit for magboots
/obj/machinery/suit_storage_unit/cmo/secure/sec_storage
name = "medical suit storage unit"
mask_type = /obj/item/clothing/mask/gas
@@ -195,9 +192,8 @@
/obj/machinery/suit_storage_unit/syndicate
name = "syndicate suit storage unit"
- suit_type = /obj/item/clothing/suit/space/hardsuit/syndi
mask_type = /obj/item/clothing/mask/gas/syndicate
- storage_type = /obj/item/tank/internals/oxygen/red
+ storage_type = /obj/item/mod/control/pre_equipped/nuclear
req_access = list(ACCESS_SYNDICATE)
safeties = FALSE //in a syndicate base, everything can be used as a murder weapon at a moment's notice.
@@ -368,7 +364,7 @@
*
**/
/obj/machinery/suit_storage_unit/proc/store_item(obj/item/I, mob/user)
- if(istype(I, /obj/item/clothing/suit) && !suit)
+ if((istype(I, /obj/item/clothing/suit)|| istype(I, /obj/item/mod/control)) && !suit)
if(try_store_item(I, user))
suit = I
return TRUE
@@ -384,7 +380,7 @@
if(try_store_item(I, user))
boots = I
return TRUE
- if((istype(I, /obj/item/tank) || I.w_class <= WEIGHT_CLASS_SMALL) && !storage)
+ if(((istype(I, /obj/item/tank) || I.w_class <= WEIGHT_CLASS_SMALL)) && !storage)
if(try_store_item(I, user))
storage = I
return TRUE
diff --git a/code/game/machinery/tcomms/nttc.dm b/code/game/machinery/tcomms/nttc.dm
index 8f53c135c444..4a1f63ec6f77 100644
--- a/code/game/machinery/tcomms/nttc.dm
+++ b/code/game/machinery/tcomms/nttc.dm
@@ -257,6 +257,7 @@
else
job_class = all_jobs[rank]
+ tcm.pre_modify_name = tcm.sender_name
if(toggle_name_color)
var/new_name = "[tcm.sender_name]"
tcm.sender_name = new_name
diff --git a/code/game/machinery/tcomms/tcomms_base.dm b/code/game/machinery/tcomms/tcomms_base.dm
index eca0cc7eed57..76fd96dc17ec 100644
--- a/code/game/machinery/tcomms/tcomms_base.dm
+++ b/code/game/machinery/tcomms/tcomms_base.dm
@@ -204,6 +204,8 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
var/reject = FALSE
/// Voice name if the person doesnt have a name (diona, alien, etc)
var/vname
+ /// sender_name before modify_message modifies it, because it introduces html tags.
+ var/pre_modify_name
/// List of all channels this can be sent or recieved on
var/list/zlevels = list()
/// Should this signal be re-broadcasted (Can be modified by NTTC, defaults to TRUE)
@@ -382,21 +384,21 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
if(length(heard_masked))
for(var/M in heard_masked)
var/mob/R = M
- R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, 0, tcm.sender_name, follow_target=tcm.follow_target)
+ R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, FALSE, tcm.sender_name, follow_target=tcm.follow_target, check_name_against = tcm.pre_modify_name)
/* --- Process all the mobs that heard the voice normally (understood) --- */
if(length(heard_normal))
for(var/M in heard_normal)
var/mob/R = M
- R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, 0, tcm.sender_name, follow_target=tcm.follow_target)
+ R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, FALSE, tcm.sender_name, follow_target=tcm.follow_target, check_name_against = tcm.pre_modify_name)
/* --- Process all the mobs that heard the voice normally (did not understand) --- */
if(length(heard_voice))
for(var/M in heard_voice)
var/mob/R = M
- R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender,0, tcm.vname, follow_target=tcm.follow_target)
+ R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, FALSE, tcm.vname, follow_target=tcm.follow_target, check_name_against = tcm.pre_modify_name)
/* --- Process all the mobs that heard a garbled voice (did not understand) --- */
// Displays garbled message (ie "f*c* **u, **i*er!")
@@ -404,7 +406,7 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
if(length(heard_garbled))
for(var/M in heard_garbled)
var/mob/R = M
- R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, 1, tcm.vname, follow_target=tcm.follow_target)
+ R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, TRUE, tcm.vname, follow_target=tcm.follow_target, check_name_against = tcm.pre_modify_name)
/* --- Complete gibberish. Usually happens when there's a compressed message --- */
@@ -412,7 +414,7 @@ GLOBAL_LIST_EMPTY(tcomms_machines)
if(length(heard_gibberish))
for(var/M in heard_gibberish)
var/mob/R = M
- R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, 1, follow_target=tcm.follow_target)
+ R.hear_radio(tcm.message_pieces, tcm.verbage, part_a, part_b, tcm.sender, TRUE, follow_target=tcm.follow_target, check_name_against = tcm.pre_modify_name)
return TRUE
diff --git a/code/game/machinery/vendors/contraband_vendors.dm b/code/game/machinery/vendors/contraband_vendors.dm
index eeeff27c0aed..3db3e373c493 100644
--- a/code/game/machinery/vendors/contraband_vendors.dm
+++ b/code/game/machinery/vendors/contraband_vendors.dm
@@ -15,6 +15,7 @@
desc = "Smoke 'em if you've got 'em."
slogan_list = list("Space cigs taste good like a cigarette should.", "I'd rather toolbox than switch.", "Smoke!", "Don't believe the reports - smoke today!")
ads_list = list("Probably not bad for you!", "Don't believe the scientists!", "It's good for you!", "Don't quit, buy more!", "Smoke!", "Nicotine heaven.", "Best cigarettes since 2150.", "Award-winning cigs.")
+ category = VENDOR_TYPE_RECREATION
vend_delay = 34
icon_state = "cigs"
icon_lightmask = "cigs"
@@ -29,6 +30,7 @@
icon_lightmask = "nutri"
icon_off = "nutri"
icon_panel = "thin_vendor"
+ category = VENDOR_TYPE_FOOD
products = list(/obj/item/reagent_containers/food/snacks/chips = 6,/obj/item/reagent_containers/food/snacks/sosjerky = 6,
/obj/item/reagent_containers/food/snacks/syndicake = 6, /obj/item/reagent_containers/food/snacks/cheesiehonkers = 6)
@@ -93,6 +95,7 @@
slogan_list = list("Get your cool toys today!", "Trigger a valid hunter today!", "Quality toy weapons for cheap prices!", "Give them to HoPs for all access!", "Give them to HoS to get permabrigged!")
ads_list = list("Feel robust with your toys!", "Express your inner child today!", "Toy weapons don't kill people, but valid hunters do!", "Who needs responsibilities when you have toy weapons?", "Make your next murder FUN!")
vend_reply = "Come back for more!"
+ category = VENDOR_TYPE_RECREATION
products = list(/obj/item/gun/projectile/automatic/toy = 10,
/obj/item/gun/projectile/automatic/toy/pistol= 10,
/obj/item/gun/projectile/shotgun/toy = 10,
@@ -108,7 +111,7 @@
/obj/item/gun/projectile/automatic/sniper_rifle/toy = 10,
/obj/item/ammo_box/foambox/riot = 20,
/obj/item/toy/katana = 10,
- /obj/item/twohanded/dualsaber/toy = 5,
+ /obj/item/dualsaber/toy = 5,
/obj/item/deck/cards/syndicate = 10) //Gambling and it hurts, making it a +18 item
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, rad = 0, fire = 100, acid = 50)
resistance_flags = FIRE_PROOF
diff --git a/code/game/machinery/vendors/departmental_vendors.dm b/code/game/machinery/vendors/departmental_vendors.dm
index 34265a6d6506..21f961f2b8b8 100644
--- a/code/game/machinery/vendors/departmental_vendors.dm
+++ b/code/game/machinery/vendors/departmental_vendors.dm
@@ -4,6 +4,7 @@
icon_state = "engivend"
icon_deny = "engivend_deny"
icon_panel = "generic"
+ category = VENDOR_TYPE_DEPARTMENTAL
req_one_access_txt = "11;24" // Engineers and atmos techs can use this
products = list(/obj/item/clothing/glasses/meson/engine = 2, /obj/item/multitool = 4, /obj/item/geiger_counter = 5, /obj/item/airlock_electronics = 10, /obj/item/firelock_electronics = 10, /obj/item/firealarm_electronics = 10, /obj/item/apc_electronics = 10, /obj/item/airalarm_electronics = 10, /obj/item/stock_parts/cell/high = 10, /obj/item/camera_assembly = 10)
contraband = list(/obj/item/stock_parts/cell/potato = 3)
@@ -14,6 +15,7 @@
desc = "Everything you need for do-it-yourself station repair."
icon_state = "engi"
icon_deny = "engi_deny"
+ category = VENDOR_TYPE_DEPARTMENTAL
req_access_txt = "11"
products = list(/obj/item/clothing/under/rank/engineering/chief_engineer = 4, /obj/item/clothing/under/rank/engineering/engineer = 40, /obj/item/clothing/shoes/workboots = 4, /obj/item/clothing/head/hardhat = 4,
/obj/item/storage/belt/utility = 4, /obj/item/clothing/glasses/meson/engine = 4,/obj/item/clothing/gloves/color/yellow = 4, /obj/item/screwdriver = 12,
@@ -28,6 +30,7 @@
desc = "All the tools you need to create your own robot army."
icon_state = "robotics"
icon_deny = "robotics_deny"
+ category = VENDOR_TYPE_DEPARTMENTAL
icon_lightmask = "robotics"
req_access_txt = "29"
products = list(/obj/item/clothing/suit/storage/labcoat = 4, /obj/item/clothing/under/rank/rnd/roboticist = 4, /obj/item/stack/cable_coil = 4, /obj/item/flash = 4,
@@ -42,6 +45,7 @@
ads_list = list("Mm, food stuffs!","Food and food accessories.","Get your plates!","You like forks?","I like forks.","Woo, utensils.","You don't really need these...")
icon_state = "dinnerware"
icon_lightmask = "dinnerware"
+ category = VENDOR_TYPE_DEPARTMENTAL
products = list(/obj/item/storage/bag/tray = 8,
/obj/item/kitchen/utensil/fork = 6,
/obj/item/trash/plate = 20,
@@ -75,6 +79,7 @@
icon_deny = "nutri_deny"
icon_lightmask = "nutri"
icon_panel = "thin_vendor"
+ category = VENDOR_TYPE_DEPARTMENTAL
products = list(/obj/item/reagent_containers/glass/bottle/nutrient/ez = 20, /obj/item/reagent_containers/glass/bottle/nutrient/l4z = 13, /obj/item/reagent_containers/glass/bottle/nutrient/rh = 6, /obj/item/reagent_containers/spray/pestspray = 20,
/obj/item/reagent_containers/syringe = 5, /obj/item/storage/bag/plants = 5, /obj/item/cultivator = 3, /obj/item/shovel/spade = 3, /obj/item/plant_analyzer = 4)
contraband = list(/obj/item/reagent_containers/glass/bottle/ammonia = 10, /obj/item/reagent_containers/glass/bottle/diethylamine = 5)
@@ -88,6 +93,7 @@
icon_state = "seeds"
icon_lightmask = "seeds"
icon_panel = "thin_vendor"
+ category = VENDOR_TYPE_DEPARTMENTAL
products = list(/obj/item/seeds/aloe = 3,
/obj/item/seeds/ambrosia = 3,
/obj/item/seeds/apple = 3,
@@ -152,6 +158,7 @@
icon_panel = "wide_vendor"
ads_list = list("Go save some lives!","The best stuff for your medbay.","Only the finest tools.","Natural chemicals!","This stuff saves lives.","Don't you want some?","Ping!")
req_access_txt = "5"
+ category = VENDOR_TYPE_DEPARTMENTAL
products = list(/obj/item/reagent_containers/hypospray/autoinjector = 4,
/obj/item/stack/medical/bruise_pack/advanced = 2,
/obj/item/stack/medical/ointment/advanced = 2,
@@ -196,6 +203,7 @@
/obj/machinery/economy/vending/plasmaresearch
name = "\improper Toximate 3000"
desc = "All the fine parts you need in one vending machine!"
+ category = VENDOR_TYPE_DEPARTMENTAL
products = list(/obj/item/assembly/prox_sensor = 8, /obj/item/assembly/igniter = 8, /obj/item/assembly/signaler = 8,
/obj/item/wirecutters = 1, /obj/item/assembly/timer = 8)
contraband = list(/obj/item/flashlight = 5, /obj/item/assembly/voice = 3, /obj/item/assembly/health = 3, /obj/item/assembly/infra = 3)
@@ -208,6 +216,7 @@
icon_lightmask = "sec"
icon_deny = "sec_deny"
icon_panel = "wide_vendor"
+ category = VENDOR_TYPE_DEPARTMENTAL
req_access_txt = "1"
products = list(/obj/item/restraints/handcuffs = 8,
/obj/item/restraints/handcuffs/cable/zipties = 8,
diff --git a/code/game/machinery/vendors/generic_vendors.dm b/code/game/machinery/vendors/generic_vendors.dm
index 51cb6f9c31a3..a0b5a3f07420 100644
--- a/code/game/machinery/vendors/generic_vendors.dm
+++ b/code/game/machinery/vendors/generic_vendors.dm
@@ -8,6 +8,7 @@
/obj/item/wirecutters = 50, /obj/item/cartridge/signal = 75, /obj/item/flashlight = 40,
/obj/item/assembly/timer = 20, /obj/item/assembly/voice = 20, /obj/item/assembly/health = 20)
refill_canister = /obj/item/vending_refill/assist
+ category = VENDOR_TYPE_SUPPLIES
/obj/machinery/economy/vending/assist/free
prices = list()
@@ -20,6 +21,7 @@
icon_lightmask = "smartfridge"
icon_panel = "smartfridge"
icon_broken = "smartfridge"
+ category = VENDOR_TYPE_DRINK
products = list(/obj/item/reagent_containers/food/drinks/bottle/gin = 5,
/obj/item/reagent_containers/food/drinks/bottle/whiskey = 5,
/obj/item/reagent_containers/food/drinks/bottle/tequila = 5,
@@ -64,6 +66,7 @@
icon_panel = "screen_vendor"
item_slot = TRUE
vend_delay = 34
+ category = VENDOR_TYPE_DRINK
products = list(/obj/item/reagent_containers/food/drinks/coffee = 25, /obj/item/reagent_containers/food/drinks/tea = 25, /obj/item/reagent_containers/food/drinks/h_chocolate = 25,
/obj/item/reagent_containers/food/drinks/chocolate = 10, /obj/item/reagent_containers/food/drinks/chicken_soup = 10, /obj/item/reagent_containers/food/drinks/weightloss = 10,
/obj/item/reagent_containers/food/drinks/mug = 15, /obj/item/reagent_containers/food/drinks/mug/novelty = 5)
@@ -120,6 +123,7 @@
icon_lightmask = "hats"
icon_panel = "syndi"
icon_broken = "wide_vendor"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Warning, not all hats are dog/monkey compatible. Apply forcefully with care.","Apply directly to the forehead.","Who doesn't love spending cash on hats?!","From the people that brought you collectable hat crates, Hatlord!")
products = list(/obj/item/clothing/head/that = 2,
/obj/item/clothing/head/bowlerhat = 10,
@@ -160,6 +164,7 @@
icon_lightmask = "suits"
icon_panel = "syndi"
icon_broken = "wide_vendor"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Pre-Ironed, Pre-Washed, Pre-Wor-*BZZT*","Blood of your enemies washes right out!","Who are YOU wearing?","Look dapper! Look like an idiot!","Dont carry your size? How about you shave off some pounds you fat lazy- *BZZT*")
products = list(/obj/item/clothing/under/color/black = 10,
/obj/item/clothing/under/dress/blackskirt = 10,
@@ -218,6 +223,7 @@
icon_lightmask = "shoes"
icon_panel = "syndi"
icon_broken = "wide_vendor"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Put your foot down!","One size fits all!","IM WALKING ON SUNSHINE!","No hobbits allowed.","NO PLEASE WILLY, DONT HURT ME- *BZZT*")
products = list(/obj/item/clothing/shoes/black = 10,
/obj/item/clothing/shoes/brown = 10,
@@ -263,6 +269,7 @@
slogan_list = list("Dress for success!","Prepare to look swagalicious!","Look at all this free swag!","Why leave style up to fate? Use the ClothesMate!")
vend_delay = 15
vend_reply = "Thank you for using the ClothesMate!"
+ category = VENDOR_TYPE_CLOTHING
products = list(/obj/item/clothing/suit/ianshirt = 2,
/obj/item/clothing/under/misc/overalls = 2,
/obj/item/clothing/under/misc/mailman = 1,
@@ -440,6 +447,7 @@
slogan_list = list("Sling spells the proper way with MagiVend!","Be your own Houdini! Use MagiVend!")
vend_delay = 15
vend_reply = "Have an enchanted evening!"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("FJKLFJSD","AJKFLBJAKL","1234 LOONIES LOL!",">MFW","Kill them fuckers!","GET DAT FUKKEN DISK","HONK!","EI NATH","Destroy the station!","Admin conspiracies since forever!","Space-time bending hardware!")
products = list(/obj/item/clothing/head/wizard = 1,
/obj/item/clothing/suit/wizrobe = 1,
@@ -456,7 +464,7 @@
/obj/item/clothing/head/wizard/mime = 1,
/obj/item/clothing/mask/gas/mime/wizard = 1,
/obj/item/clothing/shoes/sandal/marisa = 1,
- /obj/item/twohanded/staff = 2)
+ /obj/item/staff = 2)
contraband = list(/obj/item/reagent_containers/glass/bottle/wizarditis = 1)
armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, rad = 0, fire = 100, acid = 50)
resistance_flags = FIRE_PROOF
@@ -471,6 +479,7 @@
slogan_list = list("Dress for success!","Suited and booted!","It's show time!","Why leave style up to fate? Use AutoDrobe!")
vend_delay = 15
vend_reply = "Thank you for using AutoDrobe!"
+ category = VENDOR_TYPE_CLOTHING
products = list(/obj/item/clothing/suit/chickensuit = 1,
/obj/item/clothing/head/chicken = 1,
/obj/item/clothing/under/costume/gladiator = 1,
@@ -516,10 +525,10 @@
/obj/item/clothing/suit/wizrobe/marisa/fake = 1,
/obj/item/clothing/under/dress/sundress = 1,
/obj/item/clothing/head/witchwig = 1,
- /obj/item/twohanded/staff/broom = 1,
+ /obj/item/staff/broom = 1,
/obj/item/clothing/suit/wizrobe/fake = 1,
/obj/item/clothing/head/wizard/fake = 1,
- /obj/item/twohanded/staff = 3,
+ /obj/item/staff = 3,
/obj/item/clothing/mask/gas/clown_hat/sexy = 1,
/obj/item/clothing/under/rank/civilian/clown/sexy = 1,
/obj/item/clothing/mask/gas/sexymime = 1,
@@ -629,10 +638,10 @@
/obj/item/clothing/suit/wizrobe/marisa/fake = 100,
/obj/item/clothing/under/dress/sundress = 75,
/obj/item/clothing/head/witchwig = 50,
- /obj/item/twohanded/staff/broom = 50,
+ /obj/item/staff/broom = 50,
/obj/item/clothing/suit/wizrobe/fake = 75,
/obj/item/clothing/head/wizard/fake = 75,
- /obj/item/twohanded/staff = 50,
+ /obj/item/staff = 50,
/obj/item/clothing/mask/gas/clown_hat/sexy = 100,
/obj/item/clothing/under/rank/civilian/clown/sexy = 100,
/obj/item/clothing/mask/gas/sexymime = 100,
@@ -706,6 +715,7 @@
icon_lightmask = "nutri"
icon_off = "nutri"
icon_panel = "thin_vendor"
+ category = VENDOR_TYPE_FOOD
products = list(/obj/item/reagent_containers/food/snacks/tofu = 24,
/obj/item/reagent_containers/food/drinks/ice = 12,
/obj/item/reagent_containers/food/snacks/candy/candy_corn = 6)
@@ -720,6 +730,7 @@
desc = "Old sweet water vending machine."
icon_state = "sovietsoda"
icon_lightmask = "sovietsoda"
+ category = VENDOR_TYPE_DRINK
ads_list = list("For Tsar and Country.","Have you fulfilled your nutrition quota today?","Very nice!","We are simple people, for this is all we eat.","If there is a person, there is a problem. If there is no person, then there is no problem.")
products = list(/obj/item/reagent_containers/food/drinks/cans/sodawater = 10)
contraband = list(/obj/item/reagent_containers/food/drinks/cans/cola = 7)
@@ -735,6 +746,7 @@
icon_lightmask = "nutri"
icon_off = "nutri"
icon_panel = "thin_vendor"
+ category = VENDOR_TYPE_FOOD
products = list(/obj/item/reagent_containers/food/snacks/candy/candybar = 6, /obj/item/reagent_containers/food/drinks/dry_ramen = 6, /obj/item/reagent_containers/food/snacks/chips = 6,
/obj/item/reagent_containers/food/snacks/sosjerky = 6,/obj/item/reagent_containers/food/snacks/no_raisin = 6, /obj/item/reagent_containers/food/snacks/pistachios = 6,
/obj/item/reagent_containers/food/snacks/spacetwinkie = 6, /obj/item/reagent_containers/food/snacks/cheesiehonkers = 6, /obj/item/reagent_containers/food/snacks/tastybread = 6, /obj/item/reagent_containers/food/snacks/stroopwafel = 2)
@@ -754,6 +766,7 @@
slogan_list = list("Taste 5000 years of culture!","Mr. Chang, approved for safe consumption in over 10 sectors!","Chinese food is great for a date night, or a lonely night!","You can't go wrong with Mr. Chang's authentic Chinese food!")
icon_state = "chang"
icon_lightmask = "chang"
+ category = VENDOR_TYPE_FOOD
products = list(/obj/item/reagent_containers/food/snacks/chinese/chowmein = 6, /obj/item/reagent_containers/food/snacks/chinese/tao = 6, /obj/item/reagent_containers/food/snacks/chinese/sweetsourchickenball = 6, /obj/item/reagent_containers/food/snacks/chinese/newdles = 6,
/obj/item/reagent_containers/food/snacks/chinese/rice = 6, /obj/item/reagent_containers/food/snacks/fortunecookie = 6)
prices = list(/obj/item/reagent_containers/food/snacks/chinese/chowmein = 125, /obj/item/reagent_containers/food/snacks/chinese/tao = 125, /obj/item/reagent_containers/food/snacks/chinese/sweetsourchickenball = 125, /obj/item/reagent_containers/food/snacks/chinese/newdles = 100,
@@ -771,6 +784,7 @@
icon_panel = "thin_vendor"
slogan_list = list("Robust Softdrinks: More robust than a toolbox to the head!")
ads_list = list("Refreshing!","Hope you're thirsty!","Over 1 million drinks sold!","Thirsty? Why not cola?","Please, have a drink!","Drink up!","The best drinks in space.")
+ category = VENDOR_TYPE_DRINK
products = list(/obj/item/reagent_containers/food/drinks/cans/cola = 10, /obj/item/reagent_containers/food/drinks/cans/space_mountain_wind = 10,
/obj/item/reagent_containers/food/drinks/cans/dr_gibb = 10, /obj/item/reagent_containers/food/drinks/cans/starkist = 10,
/obj/item/reagent_containers/food/drinks/cans/space_up = 10, /obj/item/reagent_containers/food/drinks/cans/grape_juice = 10, /obj/item/reagent_containers/glass/beaker/waterbottle = 10)
@@ -793,6 +807,7 @@
icon_state = "artvend"
icon_lightmask = "artvend"
icon_panel = "screen_vendor"
+ category = VENDOR_TYPE_SUPPLIES
products = list(/obj/item/stack/cable_coil/random = 10,
/obj/item/toner = 4,
/obj/item/camera = 4,
@@ -823,6 +838,7 @@
icon_deny = "tool_deny"
icon_lightmask = "tool"
icon_panel = "generic"
+ category = VENDOR_TYPE_SUPPLIES
armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, rad = 0, fire = 100, acid = 70)
resistance_flags = FIRE_PROOF
products = list(/obj/item/crowbar = 5,
@@ -864,6 +880,7 @@
icon_state = "crittercare"
icon_lightmask = "crittercare"
icon_panel = "drobe"
+ category = VENDOR_TYPE_SUPPLIES
products = list(/obj/item/petcollar = 5, /obj/item/storage/firstaid/aquatic_kit/full =5, /obj/item/fish_eggs/goldfish = 5,
/obj/item/fish_eggs/clownfish = 5, /obj/item/fish_eggs/shark = 5, /obj/item/fish_eggs/feederfish = 10,
/obj/item/fish_eggs/salmon = 5, /obj/item/fish_eggs/catfish = 5, /obj/item/fish_eggs/glofish = 5,
@@ -890,6 +907,7 @@
vend_delay = 34
icon_state = "cigs"
icon_lightmask = "cigs"
+ category = VENDOR_TYPE_RECREATION
products = list(
/obj/item/storage/fancy/cigarettes/cigpack_robust = 6,
/obj/item/storage/fancy/cigarettes/cigpack_carp = 6,
@@ -960,6 +978,7 @@
icon_lightmask = "wallmed"
icon_panel = "wallmed"
icon_broken = "wallmed"
+ category = VENDOR_TYPE_DEPARTMENTAL
density = FALSE //It is wall-mounted, and thus, not dense. --Superxpdude
tiltable = FALSE
products = list(/obj/item/stack/medical/bruise_pack = 2, /obj/item/stack/medical/ointment = 2, /obj/item/reagent_containers/hypospray/autoinjector = 4, /obj/item/healthanalyzer = 1)
@@ -977,6 +996,7 @@
icon_lightmask = "med"
icon_deny = "cart_deny"
icon_panel = "wide_vendor"
+ category = VENDOR_TYPE_SUPPLIES
products = list(/obj/item/pda =10,/obj/item/cartridge/mob_hunt_game = 25, /obj/item/cartridge/medical = 10, /obj/item/cartridge/chemistry = 10,
/obj/item/cartridge/engineering = 10, /obj/item/cartridge/atmos = 10, /obj/item/cartridge/janitor = 10,
/obj/item/cartridge/signal/toxins = 10, /obj/item/cartridge/signal = 10)
diff --git a/code/game/machinery/vendors/vending.dm b/code/game/machinery/vendors/vending.dm
index 1c111203f19b..a874e08b3132 100644
--- a/code/game/machinery/vendors/vending.dm
+++ b/code/game/machinery/vendors/vending.dm
@@ -155,6 +155,9 @@
/// How often slogans will be used by vendors if they're aggressive.
var/aggressive_slogan_delay = (1 MINUTES)
+ /// The category of this vendor. Used for announcing brand intelligence.
+ var/category = VENDOR_TYPE_GENERIC
+
/obj/machinery/economy/vending/Initialize(mapload)
. = ..()
var/build_inv = FALSE
diff --git a/code/game/machinery/vendors/wardrobe_vendors.dm b/code/game/machinery/vendors/wardrobe_vendors.dm
index 3f15c7f97c56..6b66b53da2c7 100644
--- a/code/game/machinery/vendors/wardrobe_vendors.dm
+++ b/code/game/machinery/vendors/wardrobe_vendors.dm
@@ -6,6 +6,7 @@
icon_state = "secdrobe"
icon_lightmask = "base_drobe"
icon_panel = "drobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Beat perps in style!", "It's red so you can't see the blood!", "You have the right to be fashionable!", "Now you can be the fashion police you always wanted to be!")
vend_reply = "Thank you for using the SecDrobe!"
products = list(/obj/item/clothing/under/rank/security/officer/corporate = 4,
@@ -79,6 +80,7 @@
icon_state = "detdrobe"
icon_lightmask = "base_drobe"
icon_panel = "drobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Apply your brilliant deductive methods in style!", "They already smell of cigarettes!")
vend_reply = "Thank you for using the DetDrobe!"
products = list(/obj/item/clothing/under/rank/security/detective = 2,
@@ -133,6 +135,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "medidrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Make those blood stains look fashionable!")
vend_reply = "Thank you for using the MediDrobe!"
products = list(/obj/item/clothing/under/rank/medical/doctor = 3,
@@ -197,6 +200,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "virodrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Viruses getting you down? Nothing a change of clothes can't fix!", "Upgrade to sterilized clothing today!")
vend_reply = "Thank you for using the ViroDrobe!"
products = list(/obj/item/clothing/under/rank/medical/virologist = 2,
@@ -233,6 +237,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "chemdrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Our clothes are 0.5% more resistant to acid spills! Get yours now!")
vend_reply = "Thank you for using the ChemDrobe!"
products = list(/obj/item/clothing/under/rank/medical/chemist = 2,
@@ -269,6 +274,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "genedrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = "Perfect for the mad scientist in you!"
vend_reply = "Thank you for using the GeneDrobe!"
products = list(/obj/item/clothing/under/rank/rnd/geneticist = 3,
@@ -298,6 +304,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "scidrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Longing for the smell of plasma burnt flesh?", "Buy your science clothing now!", "Made with 10% Auxetics, so you don't have to worry about losing your arm!")
vend_reply = "Thank you for using the SciDrobe!"
products = list(/obj/item/clothing/under/rank/rnd/scientist = 6,
@@ -335,6 +342,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "robodrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("You turn me TRUE, use defines!","0110001101101100011011110111010001101000011001010111001101101000011001010111001001100101")
vend_reply = "Thank you for using the RoboDrobe!"
products = list(/obj/item/clothing/under/rank/rnd/roboticist = 3,
@@ -365,6 +373,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "engidrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Guaranteed to protect your feet from industrial accidents!", "Afraid of radiation? Then wear yellow!")
vend_reply = "Thank you for using the EngiDrobe!"
products = list(/obj/item/clothing/under/rank/engineering/engineer = 6,
@@ -412,6 +421,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "atmosdrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Guaranteed to protect your feet from atmospheric accidents!", "Get your inflammable clothing right here!")
vend_reply = "Thank you for using the AtmosDrobe!"
products = list(/obj/item/clothing/under/rank/engineering/atmospheric_technician = 6,
@@ -460,6 +470,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "cargodrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Upgraded Assistant Style! Pick yours today!", "These shorts are comfy and easy to wear, get yours now!")
vend_reply = "Thank you for using the CargoDrobe!"
products = list(/obj/item/clothing/under/rank/cargo/tech = 6,
@@ -495,6 +506,7 @@
icon_lightmask = "base_drobe"
icon_panel = "drobe"
icon_addon = "chefdrobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Our clothes are guaranteed to protect you from food splatters!", "Comfortable enough for a CQC practice!")
vend_reply = "Thank you for using the ChefDrobe!"
products = list(/obj/item/clothing/under/rank/civilian/chef = 2,
@@ -530,6 +542,7 @@
icon_state = "bardrobe"
icon_lightmask = "base_drobe"
icon_panel = "drobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Guaranteed to prevent stains from spilled drinks!")
vend_reply = "Thank you for using the BarDrobe!"
products = list(/obj/item/clothing/under/rank/civilian/bartender = 2,
@@ -561,6 +574,7 @@
icon_state = "hydrobe"
icon_lightmask = "base_drobe"
icon_panel = "drobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Do you love soil? Then buy our clothes!", "Get outfits to match your green thumb here!")
vend_reply = "Thank you for using the HydroDrobe!"
products = list(/obj/item/clothing/under/rank/civilian/hydroponics = 3,
@@ -596,6 +610,7 @@
icon_panel = "drobe"
icon_broken = "base_drobe"
icon_off = "base_drobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("Come and get your janitorial clothing, now endorsed by janitors everywhere!")
vend_reply = "Thank you for using the JaniDrobe!"
products = list(/obj/item/clothing/under/rank/civilian/janitor = 3,
@@ -615,6 +630,7 @@
icon_panel = "drobe"
icon_broken = "base_drobe"
icon_off = "base_drobe"
+ category = VENDOR_TYPE_CLOTHING
ads_list = list("OBJECTION! Get the rule of law for yourself!")
vend_reply = "Thank you for using the LawDrobe!"
products = list(/obj/item/clothing/under/rank/civilian/internalaffairs = 2,
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 5ba920644dc0..5afaeadb9d85 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -226,7 +226,7 @@
///else the mousetraps are useless
if(ishuman(M))
var/mob/living/carbon/human/H = M
- if(isobj(H.shoes))
+ if(isobj(H.shoes) && !(H.shoes.flags & NODROP))
var/thingy = H.shoes
H.unEquip(H.shoes)
walk_away(thingy,chassis,15,2)
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index c5dcac8c643c..aca69f9ca08d 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -68,6 +68,8 @@
categories = list(
"Cyborg",
"Cyborg Repair",
+ "MODsuit Construction",
+ "MODsuit Modules",
"Ripley",
"Firefighter",
"Odysseus",
@@ -213,10 +215,15 @@
var/obj/item/I = A
I.materials = final_cost
if(D.locked)
- var/obj/item/storage/lockbox/research/large/L = new(get_step(src, output_dir))
+ var/obj/item/storage/lockbox/research/modsuit/L = new(get_step(src, output_dir))
I.forceMove(L)
L.name += " ([I.name])"
L.origin_tech = I.origin_tech
+ L.req_access = D.access_requirement
+ var/list/lockbox_access
+ for(var/access in L.req_access)
+ lockbox_access += "[get_access_desc(access)] "
+ L.desc = "A locked box. It is locked to [lockbox_access]access."
// Clean up
being_built = null
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index b941ece9d16a..9b6b476870ce 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -150,6 +150,7 @@
M.visible_message("[user] buckles [M] to [src]!",\
"[user] buckles you to [src]!",\
"You hear metal clanking.")
+ M.pulledby?.stop_pulling()
/atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
var/mob/living/M = unbuckle_mob(buckled_mob)
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 8c4a00df5734..3c2cdc5f678a 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -378,13 +378,16 @@
for(var/turf/T in oview(get_turf(src), 7))
turf_targets += T
+ for(var/mob/living/carbon/human/H in view(get_turf(src), 3))
+ shootAt(H)
+
for(var/I in 1 to rand(1, 3))
var/turf/target = pick(turf_targets)
shootAt(target)
if(prob(50))
for(var/turf/simulated/floor/nearby_floor in oview(get_turf(src), (drops_core ? 2 : 1)))
- nearby_floor.MakeSlippery(TURF_WET_PERMAFROST)
+ nearby_floor.MakeSlippery((drops_core? TURF_WET_PERMAFROST : TURF_WET_ICE), (drops_core? null : rand(10, 20 SECONDS)))
var/turf/simulated/T = get_turf(src)
if(istype(T))
@@ -404,6 +407,9 @@
return
var/obj/item/projectile/temp/basilisk/O = new /obj/item/projectile/temp/basilisk(T)
playsound(get_turf(src), 'sound/weapons/taser2.ogg', 75, TRUE)
+ if(drops_core)
+ O.stun = 0.5 SECONDS
+ O.original = target
O.current = T
O.yo = U.y - T.y
O.xo = U.x - T.x
diff --git a/code/game/objects/effects/decals/Cleanable/alien_blood.dm b/code/game/objects/effects/decals/Cleanable/alien_blood.dm
index d417f34e279e..b13fb1ad8b3e 100644
--- a/code/game/objects/effects/decals/Cleanable/alien_blood.dm
+++ b/code/game/objects/effects/decals/Cleanable/alien_blood.dm
@@ -46,9 +46,6 @@
bloodiness = MAX_SHOE_BLOODINESS
alpha = BLOOD_SPLATTER_ALPHA_SLIME
-/obj/effect/decal/cleanable/blood/slime/can_bloodcrawl_in()
- return FALSE
-
/obj/effect/decal/cleanable/blood/slime/dry()
return
diff --git a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm
index 625c70555bf1..0076975c7f02 100644
--- a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm
+++ b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm
@@ -277,6 +277,30 @@
icon_state = "flour"
color = "#D5820B"
scoop_reagents = list("fungus" = 10)
+ no_clear = TRUE
+ var/timer_id
+
+/obj/effect/decal/cleanable/fungus/examine(mob/user)
+ . = ..()
+ if(no_scoop)
+ . += "There's not a lot here, you probably wouldn't be able to harvest anything useful."
+ else
+ . += "There's enough here to scrape into a beaker."
+
+/obj/effect/decal/cleanable/fungus/on_scoop()
+ alpha = 128
+ no_scoop = TRUE
+
+ timer_id = addtimer(CALLBACK(src, PROC_REF(recreate)), rand(5 MINUTES, 10 MINUTES), TIMER_STOPPABLE)
+
+/obj/effect/decal/cleanable/fungus/Destroy()
+ . = ..()
+ deltimer(timer_id)
+
+/obj/effect/decal/cleanable/fungus/proc/recreate()
+ alpha = 255
+ reagents.add_reagent_list(scoop_reagents)
+ no_scoop = FALSE
/obj/effect/decal/cleanable/confetti //PARTY TIME!
name = "confetti"
diff --git a/code/game/objects/effects/effect_system/effects_chem_smoke.dm b/code/game/objects/effects/effect_system/effects_chem_smoke.dm
index 44710290cf49..f500870dcd34 100644
--- a/code/game/objects/effects/effect_system/effects_chem_smoke.dm
+++ b/code/game/objects/effects/effect_system/effects_chem_smoke.dm
@@ -120,6 +120,7 @@
continue
smoked_atoms += A
chemholder.reagents.reaction(A)
+ SEND_SIGNAL(A, COMSIG_ATOM_EXPOSE_REAGENTS, chemholder.reagents, chemholder, chemholder.reagents.total_volume)
if(iscarbon(A))
var/mob/living/carbon/C = A
if(C.can_breathe_gas())
diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm
index 57684e61b9a9..2ca15c2fe7a6 100644
--- a/code/game/objects/effects/effects.dm
+++ b/code/game/objects/effects/effects.dm
@@ -125,6 +125,7 @@
to_chat(user, "[I] is full!")
return
to_chat(user, "You scoop [src] into [I]!")
+ on_scoop()
reagents.trans_to(I, reagents.total_volume)
if(!reagents.total_volume && !no_clear) //scooped up all of it
qdel(src)
@@ -144,3 +145,6 @@
/obj/effect/decal/blob_act(obj/structure/blob/B)
if(B && B.loc == loc)
qdel(src)
+
+/obj/effect/decal/proc/on_scoop()
+ return
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index b7234399fe58..b24df2ead97c 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -559,14 +559,14 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/awaystart) //Without this away mission
. = ..()
new /obj/item/clothing/under/dress/sundress(src.loc)
new /obj/item/clothing/head/witchwig(src.loc)
- new /obj/item/twohanded/staff/broom(src.loc)
+ new /obj/item/staff/broom(src.loc)
return INITIALIZE_HINT_QDEL
/obj/effect/landmark/costume/fakewizard/Initialize(mapload)
. = ..()
new /obj/item/clothing/suit/wizrobe/fake(src.loc)
new /obj/item/clothing/head/wizard/fake(src.loc)
- new /obj/item/twohanded/staff/(src.loc)
+ new /obj/item/staff/(src.loc)
return INITIALIZE_HINT_QDEL
/obj/effect/landmark/costume/sexyclown/Initialize(mapload)
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index 06e5e23ac2d4..bcdee3932af1 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -134,13 +134,12 @@
new /obj/effect/hallucination/delusion(get_turf(victim), victim, 'icons/mob/mob.dmi', "daemon")
- var/obj/item/twohanded/required/chainsaw/doomslayer/chainsaw = new(victim.loc)
+ var/obj/item/chainsaw/doomslayer/chainsaw = new(victim.loc)
chainsaw.flags |= NODROP | DROPDEL
victim.drop_l_hand()
victim.drop_r_hand()
victim.put_in_hands(chainsaw)
chainsaw.attack_self(victim)
- chainsaw.wield(victim)
victim.reagents.add_reagent("adminordrazine", 25)
victim.flash_screen_color(red_splash, 10)
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index 4629a54426bc..8a23924a6809 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -133,6 +133,8 @@
/obj/item/storage/wallet = 20,
/obj/item/storage/wallet/random = 5,
/obj/item/caution = 10,
+ /obj/item/mod/construction/broken_core = 4,
+ /obj/effect/spawner/random_spawners/mod/maint = 10,
////////////////CONTRABAND STUFF//////////////////
/obj/item/grenade/clown_grenade = 3,
/obj/item/grenade/smokebomb = 3,
@@ -161,7 +163,8 @@
/obj/item/storage/fancy/cigarettes/cigpack_syndicate = 2,
/obj/item/storage/pill_bottle/fakedeath = 2,
/obj/item/clothing/suit/jacket/syndicatebomber = 5,
- "" = 60 // This should be a decently high number for chances where no loot will spawn
+ /obj/item/clothing/suit/storage/lawyer/blackjacket/armored = 2, // More armored than bomber and has pockets, so it is rarer
+ "" = 61 // This should be a decently high number for chances where no loot will spawn
)
/obj/effect/spawner/lootdrop/maintenance/two
@@ -267,6 +270,7 @@
/obj/item/mecha_parts/mecha_equipment/weapon/energy/xray = 25, // mecha x-ray laser
/obj/item/mecha_parts/mecha_equipment/teleporter/precise = 25, // upgraded mecha teleporter
/obj/item/autosurgeon/organ = 50,
+ /obj/item/mod/construction/plating/research = 25,
// Research / Experimentor
/obj/item/paper/researchnotes = 150, // papers that give random R&D levels
@@ -296,7 +300,10 @@
// Virology
/obj/item/reagent_containers/glass/bottle/regeneration = 50,
- /obj/item/reagent_containers/glass/bottle/sensory_restoration = 50
+ /obj/item/reagent_containers/glass/bottle/sensory_restoration = 50,
+
+ // Medical in general
+ /obj/item/mod/construction/plating/rescue = 25
)
/obj/effect/spawner/lootdrop/trade_sol/sec
@@ -310,6 +317,8 @@
/obj/item/storage/belt/military/assault = 50,
/obj/item/clothing/mask/gas/sechailer/swat = 50,
/obj/item/clothing/glasses/thermal = 50, // see heat-source mobs through walls. Less powerful than already-available xray.
+ /obj/item/mod/construction/plating/safeguard = 25,
+ /obj/item/mod/module/power_kick = 50,
// Ranged weapons
/obj/item/storage/box/enforcer_rubber = 50,
@@ -331,7 +340,8 @@
/obj/item/storage/backpack/holding = 25,
/obj/item/clothing/glasses/meson/night = 25, // NV mesons
/obj/item/clothing/glasses/material = 25, // shows objects, but not mobs, through walls
- /obj/item/grenade/clusterbuster/metalfoam = 25 // cluster metal foam grenade
+ /obj/item/grenade/clusterbuster/metalfoam = 25, // cluster metal foam grenade
+ /obj/item/mod/construction/plating/advanced = 13
)
/obj/effect/spawner/lootdrop/trade_sol/largeitem
diff --git a/code/game/objects/effects/spawners/random_spawners.dm b/code/game/objects/effects/spawners/random_spawners.dm
index 44822dcbd7aa..9b2f387ec462 100644
--- a/code/game/objects/effects/spawners/random_spawners.dm
+++ b/code/game/objects/effects/spawners/random_spawners.dm
@@ -160,6 +160,18 @@
/datum/nothing = 1,
/obj/effect/decal/cleanable/fungus = 7)
+/obj/effect/spawner/random_spawners/mod
+ name = "MOD module spawner"
+ desc = "Modularize this, please."
+ icon_state = "circuit"
+
+/obj/effect/spawner/random_spawners/mod/maint
+ name = "maint MOD module spawner"
+ result = list(
+ /obj/item/mod/module/springlock = 2,
+ /obj/item/mod/module/balloon = 1,
+ /obj/item/mod/module/stamp = 1
+ )
// z6 DEPOT SPAWNERS
@@ -264,7 +276,7 @@
// Loot schema: space gear, basic armor, basic ammo (10mm, rcd), drugs, more dangerous/useful gimmick items, lower-value minerals
result = list(/datum/nothing = 27,
/obj/item/storage/box/syndie_kit/space = 1,
- /obj/item/storage/box/syndie_kit/hardsuit = 1,
+ /obj/item/storage/box/syndie_kit/modsuit = 1,
/obj/item/clothing/shoes/magboots/syndie = 1,
/obj/item/clothing/suit/armor/vest/combat = 1,
/obj/item/ammo_box/magazine/m10mm = 1,
@@ -288,7 +300,9 @@
/obj/item/pen/edagger = 1,
/obj/item/stack/sheet/mineral/plasma{amount = 20} = 1,
/obj/item/stack/sheet/mineral/silver{amount = 20} = 1,
- /obj/item/stack/sheet/mineral/gold{amount = 20} = 1)
+ /obj/item/stack/sheet/mineral/gold{amount = 20} = 1,
+ /obj/item/mod/module/noslip = 1,
+ /obj/item/mod/module/visor/night = 1)
/obj/effect/spawner/random_spawners/syndicate/loot/level3
name = "officer loot"
@@ -316,7 +330,11 @@
/obj/item/ammo_box/magazine/m10mm/hp = 1,
/obj/item/storage/box/syndie_kit/emp = 1,
/obj/item/toy/plushie/carpplushie/dehy_carp = 1,
- /obj/item/clothing/glasses/hud/security/chameleon = 1)
+ /obj/item/clothing/glasses/hud/security/chameleon = 1,
+ /obj/item/mod/module/visor/thermal = 1,
+ /obj/item/mod/module/stealth = 1,
+ /obj/item/mod/module/power_kick = 1)
+
/obj/effect/spawner/random_spawners/syndicate/loot/level4
name = "armory loot"
@@ -329,13 +347,14 @@
/obj/item/gun/energy/kinetic_accelerator/crossbow = 1,
/obj/item/gun/projectile/revolver = 1,
/obj/item/clothing/gloves/color/yellow/power = 1,
- /obj/item/twohanded/chainsaw = 1,
+ /obj/item/butcher_chainsaw = 1,
/obj/item/bee_briefcase = 1,
- /obj/item/twohanded/fireaxe/energized = 1,
+ /obj/item/fireaxe/energized = 1,
/obj/item/clothing/glasses/thermal = 1,
/obj/item/chameleon = 1,
/obj/item/reagent_containers/hypospray/autoinjector/stimulants = 1,
- /obj/item/grenade/plastic/c4/x4 = 1)
+ /obj/item/grenade/plastic/c4/x4 = 1,
+ /obj/item/storage/box/syndie_kit/modsuit/elite = 1)// Adding this as it is something an explorer can use to explore space better, that isn't a high powered murder weapon.
// Layout-affecting spawns
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 5132550a95c9..e6077a13538f 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -182,7 +182,7 @@
qdel(src)
return
if(!grow_as)
- grow_as = pick(typesof(/mob/living/simple_animal/hostile/poison/giant_spider))
+ grow_as = pick(typesof(/mob/living/simple_animal/hostile/poison/giant_spider) - /mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider)
var/mob/living/simple_animal/hostile/poison/giant_spider/S = new grow_as(loc)
S.faction = faction.Copy()
S.master_commander = master_commander
diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm
index 9b0fa40583e4..f1277d431e81 100644
--- a/code/game/objects/explosion.dm
+++ b/code/game/objects/explosion.dm
@@ -198,6 +198,9 @@
if(istype(array, /obj/item/clothing/head/helmet/space/hardsuit/rd))
var/obj/item/clothing/head/helmet/space/hardsuit/rd/Helm_Array = array
Helm_Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took,orig_dev_range,orig_heavy_range,orig_light_range)
+ if(istype(array, /obj/item/mod/module/reagent_scanner/advanced))
+ var/obj/item/mod/module/reagent_scanner/advanced/Mod_Array = array
+ Mod_Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took,orig_dev_range,orig_heavy_range,orig_light_range)
return 1
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 37eab53c901f..7fa90759ac81 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -133,6 +133,7 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
/// Holder var for the item outline filter, null when no outline filter on the item.
var/outline_filter
+
/obj/item/New()
..()
@@ -334,7 +335,7 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
/obj/item/attack_alien(mob/user)
var/mob/living/carbon/alien/A = user
- if(!A.has_fine_manipulation)
+ if(!A.has_fine_manipulation && !HAS_TRAIT(src, TRAIT_XENO_INTERACTABLE))
if(src in A.contents) // To stop Aliens having items stuck in their pockets
A.unEquip(src)
to_chat(user, "Your claws aren't capable of such fine manipulation!")
@@ -877,3 +878,23 @@ GLOBAL_DATUM_INIT(welding_sparks, /mutable_appearance, mutable_appearance('icons
/obj/item/proc/remove_tape()
return
+
+/obj/item/water_act(volume, temperature, source, method)
+ . = ..()
+ if(HAS_TRAIT(src, TRAIT_OIL_SLICKED))
+ slowdown = initial(slowdown)
+ remove_atom_colour(FIXED_COLOUR_PRIORITY)
+ REMOVE_TRAIT(src, TRAIT_OIL_SLICKED, "potion")
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ H.regenerate_icons()
+
+/obj/item/cleaning_act(mob/user, atom/cleaner, cleanspeed, text_verb, text_description, text_targetname)
+ . = ..()
+ if(HAS_TRAIT(src, TRAIT_OIL_SLICKED))
+ slowdown = initial(slowdown)
+ remove_atom_colour(FIXED_COLOUR_PRIORITY)
+ REMOVE_TRAIT(src, TRAIT_OIL_SLICKED, "potion")
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ H.regenerate_icons()
diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm
index 010248f84c9f..ad6d886cd374 100644
--- a/code/game/objects/items/contraband.dm
+++ b/code/game/objects/items/contraband.dm
@@ -3,12 +3,13 @@
//Illicit drugs~
/obj/item/storage/pill_bottle/happy
name = "Happy pills"
- desc = "Highly illegal drug. When you want to see the rainbow."
+ desc = "Real fun drugs, for when you want to see the rainbow. Happy happy joy joy!"
wrapper_color = COLOR_PINK
/obj/item/storage/pill_bottle/happy/populate_contents()
- for(var/i in 1 to 7)
+ for(var/i in 1 to 5)
new /obj/item/reagent_containers/food/pill/happy(src)
+ new /obj/item/reagent_containers/food/pill/happy/happiness(src)
/obj/item/storage/pill_bottle/zoom
name = "Zoom pills"
diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm
index 4a93310a9538..44af7ae080fe 100644
--- a/code/game/objects/items/control_wand.dm
+++ b/code/game/objects/items/control_wand.dm
@@ -122,7 +122,7 @@
region_access = list(REGION_MEDBAY)
/obj/item/door_remote/civillian
- name = "civillian door remote"
+ name = "civilian door remote"
icon_state = "gangtool-white"
region_access = list(REGION_GENERAL, REGION_SUPPLY)
additional_access = list(ACCESS_HOP)
@@ -138,7 +138,7 @@
desc = "A device used for illegally interfacing with doors."
icon_state = "hacktool"
item_state = "hacktool"
- var/hack_speed = 30
+ var/hack_speed = 1.5 SECONDS
var/busy = FALSE
/obj/item/door_remote/omni/access_tuner/afterattack(obj/machinery/door/airlock/D, mob/user)
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index 102b13780ca6..6fdb40bd436b 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -90,14 +90,12 @@
/obj/item/chameleon/proc/eject_all()
for(var/atom/movable/A in active_dummy)
- A.loc = active_dummy.loc
- if(ismob(A))
- var/mob/M = A
- M.reset_perspective(null)
+ A.forceMove(active_dummy.loc)
/obj/effect/dummy/chameleon
name = ""
desc = ""
+ resistance_flags = INDESTRUCTIBLE | FREEZE_PROOF
density = FALSE
anchored = TRUE
var/can_move = TRUE
@@ -111,7 +109,7 @@
overlays = new_overlays
underlays = new_underlays
dir = O.dir
- M.loc = src
+ M.forceMove(src)
master = C
master.active_dummy = src
@@ -134,6 +132,12 @@
/obj/effect/dummy/chameleon/attack_alien()
master.disrupt()
+/obj/effect/dummy/chameleon/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay)
+ master.disrupt() // things like plasmafires, lava, bonfires will disrupt it
+
+/obj/effect/dummy/chameleon/acid_act()
+ master.disrupt()
+
/obj/effect/dummy/chameleon/ex_act(severity) //no longer bomb-proof
for(var/mob/M in src)
to_chat(M, "Your [src] deactivates.")
@@ -197,7 +201,7 @@
disrupt(user)
/obj/item/borg_chameleon/attack_self(mob/living/silicon/robot/syndicate/saboteur/user)
- if(user && user.cell && user.cell.charge > activationCost)
+ if(user && user.cell && user.cell.charge > activationCost)
if(isturf(user.loc))
toggle(user)
else
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 7257e7a0e195..68f25ec321bd 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -137,6 +137,20 @@
return 1
user.visible_message("[user] fails to blind [M] with [src]!", "You fail to blind [M] with [src]!")
+/obj/item/flash/afterattack(atom/target, mob/living/user, proximity, params)
+ if(!proximity)
+ return
+ if(!istype(target, /obj/machinery/camera))
+ return
+ if(!try_use_flash(user))
+ return
+ var/obj/machinery/camera/C = target
+ C.emp_act(EMP_HEAVY)
+ to_chat(user,"You hit the lens of [C] with [src], temporarily disabling the camera!")
+ log_admin("[key_name(user)] EMPd a camera with a flash")
+ user.create_attack_log("[key_name(user)] EMPd a camera with a flash")
+ add_attack_logs(user, C, "EMPd with [src]", ATKLOG_ALL)
+
/obj/item/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0)
if(!try_use_flash(user))
@@ -144,6 +158,11 @@
user.visible_message("[user]'s [name] emits a blinding light!", "Your [name] emits a blinding light!")
for(var/mob/living/carbon/M in oviewers(3, null))
flash_carbon(M, user, 6 SECONDS, 0)
+ for(var/obj/machinery/camera/C in view(3, user))
+ C.emp_act(EMP_LIGHT)
+ log_admin("[key_name(user)] EMPd a camera with a flash")
+ user.create_attack_log("[key_name(user)] EMPd a camera with a flash")
+ add_attack_logs(user, C, "EMPd with [src]", ATKLOG_ALL)
/obj/item/flash/emp_act(severity)
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index b78350d26fa8..ea0a4115266e 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -81,6 +81,11 @@
icon_state = "med_cypherkey"
channels = list("Medical" = 1)
+/obj/item/encryptionkey/headset_med/para
+ name = "Paramedic Radio Encryption Key"
+ icon_state = "para_cypherkey"
+ channels = list("Medical" = 1, "Supply" = 0)
+
/obj/item/encryptionkey/headset_sci
name = "Science Radio Encryption Key"
icon_state = "sci_cypherkey"
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index ade802b34951..ff5be58426ff 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -183,6 +183,13 @@
item_state = "headset"
ks2type = /obj/item/encryptionkey/headset_med
+/obj/item/radio/headset/headset_med/para
+ name = "paramedic radio headset"
+ desc = "A headset for the trusty paramedic, Nanotrasen search and rescue."
+ icon_state = "para_headset"
+ item_state = "headset"
+ ks2type = /obj/item/encryptionkey/headset_med/para
+
/obj/item/radio/headset/headset_sci
name = "science radio headset"
desc = "A sciency headset. Like usual."
diff --git a/code/game/objects/items/devices/radio/radio_objects.dm b/code/game/objects/items/devices/radio/radio_objects.dm
index 42e0e044e246..07094ea3c7b1 100644
--- a/code/game/objects/items/devices/radio/radio_objects.dm
+++ b/code/game/objects/items/devices/radio/radio_objects.dm
@@ -58,6 +58,8 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
var/obj/item/encryptionkey/syndicate/syndiekey = null
/// How many times this is disabled by EMPs
var/disable_timer = 0
+ /// Areas in which this radio cannot send messages
+ var/static/list/blacklisted_areas = list(/area/adminconstruction, /area/tdome)
flags = CONDUCT
slot_flags = SLOT_BELT
@@ -262,6 +264,11 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
else
connection = radio_connection
channel = null
+
+ if(is_type_in_list(get_area(src), blacklisted_areas))
+ // add a debug log so people testing things won't be fighting against a "broken" radio for too long.
+ log_debug("Radio message from [src] was used in restricted area [get_area(src)].")
+ return
if(!istype(connection))
return
if(!connection)
@@ -360,11 +367,19 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
if(!M.IsVocal())
return 0
+ if(is_type_in_list(get_area(src), blacklisted_areas))
+ // add a debug log so people testing things won't be fighting against a "broken" radio for too long.
+ log_debug("Radio message from [src] was used in restricted area [get_area(src)].")
+ return FALSE
+
var/jammed = FALSE
var/turf/position = get_turf(src)
for(var/J in GLOB.active_jammers)
var/obj/item/jammer/jammer = J
- if(get_dist(position, get_turf(jammer)) < jammer.range)
+ var/position_jammer = get_turf(jammer)
+ if(!atoms_share_level(position, position_jammer))
+ continue
+ if(get_dist(position, position_jammer) < jammer.range)
jammed = TRUE
break
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index 422f686a43dc..171468bedda6 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -264,6 +264,31 @@
max_charges = 8
flawless = TRUE
+/obj/item/fireproofing_injector
+ desc = "It contains an alien nanoswarm created by the technomancers of boron. Through near sorcerous feats via use of nanomachines, it enables its user to become fully fireproof."
+ icon = 'icons/obj/hypo.dmi'
+ icon_state = "combat_hypo"
+ var/used = FALSE
+
+/obj/item/fireproofing_injector/attack_self(mob/living/user)
+ if(HAS_TRAIT(user, TRAIT_RESISTHEAT))
+ to_chat(user, "You are already fireproof!")
+ return
+ if(user.mind && (ischangeling(user) || user.mind.has_antag_datum(/datum/antagonist/vampire)) || (user.dna && user.dna.species.name != "Plasmaman"))
+ to_chat(user, "The injector is not compatable with your biology!")
+ return
+ if(used)
+ to_chat(user, "The injector is empty!")
+ return
+ used = TRUE // Set this BEFORE the popup to prevent people using the injector more than once.
+ var/choice = alert(user, "The injector is still unused. Do you wish to use it?", "Fireproofing injector", "Yes", "No")
+ if(choice == "No")
+ to_chat(user, "You decide against using [src].")
+ used = FALSE
+ return
+ to_chat(user, "You inject yourself with the nanites!")
+ ADD_TRAIT(user, TRAIT_RESISTHEAT, "fireproof_injector")
+
/obj/item/batterer
name = "mind batterer"
desc = "A dangerous syndicate device focused on crowd control and escapes. Causes brain damage, confusion, and other nasty effects to those surrounding the user."
@@ -282,6 +307,12 @@
var/max_uses = 5
/// Is this item on cooldown from being thrown
var/on_throwing_cooldown = FALSE
+ /// How many SSobj ticks have passed (Roughly 2 seconds of in game time), used to see when to recharge a use on this item
+ var/recharge_ticks = 0
+
+/obj/item/batterer/Initialize(mapload)
+ . = ..()
+ START_PROCESSING(SSobj, src)
/obj/item/batterer/examine(mob/user)
. = ..()
@@ -291,14 +322,23 @@
if(times_used < max_uses)
. += "[src] has [max_uses-times_used] charges left."
+/obj/item/batterer/process()
+ if(times_used)
+ recharge_ticks++
+ if(recharge_ticks >= 10) // recharges one use after around 20 seconds
+ recharge_ticks = initial(recharge_ticks)
+ times_used--
+ icon_state = "batterer"
+
/obj/item/batterer/attack_self(mob/living/carbon/user)
activate_batterer(user)
/obj/item/batterer/proc/activate_batterer(mob/user)
times_used++
if(user)
- if(times_used >= max_uses)
+ if(times_used > max_uses)
to_chat(user, "The mind batterer has been burnt out!")
+ times_used--
return
if(!do_after_once(user, 2 SECONDS, target = src, allow_moving = TRUE, attempt_cancel_message = "You think it's best to save this for later."))
times_used--
@@ -306,22 +346,24 @@
to_chat(user, "You trigger [src]. It has [max_uses-times_used] charges left.")
for(var/mob/living/M in oview(7, get_turf(src)))
+ if(!M.client)
+ continue
if(issilicon(M))
M.Weaken(10 SECONDS)
else
- M.Confused(30 SECONDS)
+ M.Confused(45 SECONDS)
M.adjustBrainLoss(10)
to_chat(M, "You feel a sudden, electric jolt travel through yourself,")
switch(rand(1, 10))
if(1)
- M.apply_status_effect(STATUS_EFFECT_CLINGTENTACLE_BATTERER)
+ M.Immobilize(7 SECONDS)
to_chat(M, "and your legs lock up for a moment!")
if(2)
M.apply_status_effect(STATUS_EFFECT_PACIFIED_BATTERER)
to_chat(M, "and you feel an innate love for life for a fleeting moment!")
if(3)
new /obj/effect/hallucination/delusion(get_turf(M), M)
- to_chat(M, "and the people around you morph in appearence!")
+ to_chat(M, "and the people around you morph in appearance!")
if(4)
if(prob(80))
M.EyeBlurry(25 SECONDS)
diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm
index b305f8277bac..c1366ef36088 100644
--- a/code/game/objects/items/devices/uplinks.dm
+++ b/code/game/objects/items/devices/uplinks.dm
@@ -21,6 +21,7 @@ GLOBAL_LIST_EMPTY(world_uplinks)
var/used_TC = 0
var/job = null
+ var/species = null
var/temp_category
var/uplink_type = UPLINK_TYPE_TRAITOR
/// Whether the uplink is jammed and cannot be used to order items.
@@ -47,7 +48,7 @@ GLOBAL_LIST_EMPTY(world_uplinks)
/**
* Build the item lists for use with the UI
*
- * Generates a list of items for use in the UI, based on job and other parameters
+ * Generates a list of items for use in the UI, based on job, species and other parameters
*
* Arguments:
* * user - User to check
@@ -55,6 +56,8 @@ GLOBAL_LIST_EMPTY(world_uplinks)
/obj/item/uplink/proc/generate_item_lists(mob/user)
if(!job)
job = user.mind.assigned_role
+ if(!species)
+ species = user.dna.species.name
var/list/cats = list()
@@ -64,6 +67,9 @@ GLOBAL_LIST_EMPTY(world_uplinks)
if(I.job && I.job.len)
if(!(I.job.Find(job)) && uplink_type != UPLINK_TYPE_ADMIN)
continue
+ if(length(I.species))
+ if(!(I.species.Find(species)) && uplink_type != UPLINK_TYPE_ADMIN)
+ continue
cats[cats.len]["items"] += list(list(
"name" = sanitize(I.name),
"desc" = sanitize(I.description()),
@@ -279,7 +285,7 @@ GLOBAL_LIST_EMPTY(world_uplinks)
..()
if(hidden_uplink)
hidden_uplink.update_uplink_type(UPLINK_TYPE_ADMIN)
- hidden_uplink.uses = 500
+ hidden_uplink.uses = 2500
/obj/item/multitool/uplink/New()
..()
@@ -295,4 +301,4 @@ GLOBAL_LIST_EMPTY(world_uplinks)
/obj/item/radio/headset/uplink/New()
..()
hidden_uplink = new(src)
- hidden_uplink.uses = 20
+ hidden_uplink.uses = 100
diff --git a/code/game/objects/items/mountable_frames/air_alarm_frame.dm b/code/game/objects/items/mountable_frames/air_alarm_frame.dm
index b0d3d1bc8e14..5f18c6ac25d9 100644
--- a/code/game/objects/items/mountable_frames/air_alarm_frame.dm
+++ b/code/game/objects/items/mountable_frames/air_alarm_frame.dm
@@ -14,5 +14,7 @@ Code shamelessly copied from apc_frame
mount_requirements = MOUNTED_FRAME_SIMFLOOR | MOUNTED_FRAME_NOSPACE
/obj/item/mounted/frame/alarm_frame/do_build(turf/on_wall, mob/user)
- new /obj/machinery/alarm(get_turf(src), get_dir(on_wall, user), 1)
+ var/obj/machinery/alarm/A = new/obj/machinery/alarm(get_turf(src), get_dir(on_wall, user), 1)
+ A.buildstage = AIR_ALARM_FRAME // Set the build stage to the initial state
+ A.update_icon()
qdel(src)
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 2bffdd4415f7..c2b2ad1e3dea 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -414,8 +414,9 @@
to_chat(cyborg, "The floor buffer is now [cyborg.floorbuffer ? "active" : "deactivated"].")
/obj/item/borg/upgrade/floorbuffer/Destroy()
- cyborg.floorbuffer = FALSE
- cyborg = null
+ if(cyborg)
+ cyborg.floorbuffer = FALSE
+ cyborg = null
return ..()
/obj/item/borg/upgrade/bluespace_trash_bag
diff --git a/code/game/objects/items/stacks/medical_packs.dm b/code/game/objects/items/stacks/medical_packs.dm
index b97266aabd70..619d33831a74 100644
--- a/code/game/objects/items/stacks/medical_packs.dm
+++ b/code/game/objects/items/stacks/medical_packs.dm
@@ -17,7 +17,7 @@
var/stop_bleeding = 0
var/healverb = "bandage"
-/obj/item/stack/medical/attack(mob/living/M, mob/user)
+/obj/item/stack/medical/proc/apply(mob/living/M, mob/user)
if(get_amount() <= 0)
if(is_cyborg)
to_chat(user, "You don't have enough energy to dispense more [singular_name]\s!")
@@ -77,6 +77,12 @@
"You apply [src] on [M].")
use(1)
+/obj/item/stack/medical/attack(mob/living/M, mob/user)
+ return apply(M, user)
+
+/obj/item/stack/medical/attack_self(mob/user)
+ return apply(user, user)
+
/obj/item/stack/medical/proc/heal(mob/living/M, mob/user)
var/mob/living/carbon/human/H = M
var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected)
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index 0d10558a264b..10f8768f8ed1 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -3,10 +3,13 @@ GLOBAL_LIST_INIT(rod_recipes, list (
new /datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
new /datum/stack_recipe("catwalk tile", /obj/item/stack/tile/catwalk, 2, 4, 20),
null,
- new /datum/stack_recipe("railing", /obj/structure/railing, 3, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
- new /datum/stack_recipe("railing corner", /obj/structure/railing/corner, 3, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
- null,
- new /datum/stack_recipe_list("chainlink fence", list(
+ new /datum/stack_recipe_list("railings...", list(
+ new /datum/stack_recipe("railing", /obj/structure/railing, 3, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
+ new /datum/stack_recipe("railing corner", /obj/structure/railing/corner, 3, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
+ new /datum/stack_recipe("railing cap (left)", /obj/structure/railing/cap/normal, 3, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
+ new /datum/stack_recipe("railing cap (right)", /obj/structure/railing/cap/reversed, 3, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
+ )),
+ new /datum/stack_recipe_list("chainlink fence...", list(
new /datum/stack_recipe("chainlink fence", /obj/structure/fence, 5, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
new /datum/stack_recipe("chainlink fence post", /obj/structure/fence/post, 5, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
new /datum/stack_recipe("chainlink fence corner", /obj/structure/fence/corner, 5, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE),
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 2a4835a7a70c..85359257e12d 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -187,7 +187,7 @@ GLOBAL_LIST_INIT(sandbag_recipes, list (
name = "empty sandbag"
desc = "A bag to be filled with sand."
icon = 'icons/obj/stacks/miscellaneous.dmi'
- icon_state = "sandbag"
+ icon_state = "empty-sandbags"
w_class = WEIGHT_CLASS_TINY
/obj/item/emptysandbag/attackby(obj/item/I, mob/user, params)
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 1bf2557ab697..d0b83b09f017 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -514,6 +514,7 @@ GLOBAL_LIST_INIT(brass_recipes, list (
throw_range = 3
turf_type = /turf/simulated/floor/clockwork
table_type = /obj/structure/table/reinforced/brass
+ dynamic_icon_state = TRUE
/obj/item/stack/tile/brass/narsie_act()
new /obj/item/stack/sheet/runed_metal(loc, amount)
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 4c90397ecc1b..41f546f3e667 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -55,11 +55,12 @@
. = ..()
if(!dynamic_icon_state)
return
- var/temp_amount = get_amount()
- if(temp_amount > 1)
- icon_state = "[initial(icon_state)]_[min(temp_amount, 3)]" //2 if amount is 2, 3 if more.
+ var/state = CEILING((amount/max_amount) * 3, 1)
+ if(state <= 1)
+ icon_state = initial(icon_state)
return
- icon_state = initial(icon_state)
+
+ icon_state = "[initial(icon_state)]_[state]"
/obj/item/stack/Crossed(obj/O, oldloc)
if(amount >= max_amount || ismob(loc)) // Prevents unnecessary call. Also prevents merging stack automatically in a mob's inventory
diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm
index 76dc09c723c4..1d9461776b82 100644
--- a/code/game/objects/items/stacks/telecrystal.dm
+++ b/code/game/objects/items/stacks/telecrystal.dm
@@ -6,7 +6,7 @@
icon_state = "telecrystal"
item_state = "telecrystal"
w_class = WEIGHT_CLASS_TINY
- max_amount = 50
+ max_amount = 100
flags = NOBLUDGEON
origin_tech = "materials=6;syndicate=1"
dynamic_icon_state = TRUE
@@ -48,3 +48,6 @@
/obj/item/stack/telecrystal/fifty
amount = 50
+
+/obj/item/stack/telecrystal/hundred
+ amount = 100
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index a60ef991b632..8c5f037798e9 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -288,3 +288,7 @@
mineralType = "metal"
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 100, ACID = 70)
resistance_flags = FIRE_PROOF
+
+/obj/item/stack/tile/catwalk/cyborg
+ energy_type = /datum/robot_energy_storage/catwalk
+ is_cyborg = TRUE
diff --git a/code/game/objects/items/theft_items.dm b/code/game/objects/items/theft_items.dm
index 952a192d6106..eab0f42106d6 100644
--- a/code/game/objects/items/theft_items.dm
+++ b/code/game/objects/items/theft_items.dm
@@ -384,6 +384,8 @@
"You touch [AM] with [src], and everything suddenly goes silent.\n[AM] and [sliver] flash into dust, and soon as you can register this, you do as well.",
"Everything suddenly goes silent.")
user.dust()
+ icon_state = "supermatter_tongs"
+ item_state = "supermatter_tongs"
radiation_pulse(src, 500, 2)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
QDEL_NULL(sliver)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 621668c9c56e..976cc2fb32f4 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -216,7 +216,7 @@
to_chat(user, "\the [flags & NODROP ? src : W] is stuck to your hand, you can't attach it to \the [flags & NODROP ? W : src]!")
else
to_chat(user, "You attach the ends of the two plastic swords, making a single double-bladed toy! You're fake-cool.")
- new /obj/item/twohanded/dualsaber/toy(user.loc)
+ new /obj/item/dualsaber/toy(user.loc)
user.unEquip(W)
user.unEquip(src)
qdel(W)
@@ -225,26 +225,27 @@
/*
* Subtype of Double-Bladed Energy Swords
*/
-/obj/item/twohanded/dualsaber/toy
+/obj/item/dualsaber/toy
name = "double-bladed toy sword"
desc = "A cheap, plastic replica of TWO energy swords. Double the fun!"
force = 0
throwforce = 0
throw_speed = 3
throw_range = 5
- force_unwielded = 0
- force_wielded = 0
origin_tech = null
attack_verb = list("attacked", "struck", "hit")
brightness_on = 0
- sharp_when_wielded = FALSE // It's a toy
needs_permit = FALSE
-/obj/item/twohanded/dualsaber/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+/obj/item/dualsaber/toy/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, only_sharp_when_wielded = FALSE, force_wielded = 0, force_unwielded = 0)
+
+/obj/item/dualsaber/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
return 0
-/obj/item/twohanded/dualsaber/toy/IsReflect()
- if(wielded)
+/obj/item/dualsaber/toy/IsReflect()
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
return 2
/obj/item/toy/katana
@@ -594,6 +595,19 @@
/obj/random/plushie/item_to_spawn()
return pick(subtypesof(/obj/item/toy/plushie) - typesof(/obj/item/toy/plushie/fluff) - typesof(/obj/item/toy/plushie/carpplushie)) //exclude the base type.
+/obj/random/plushie/explosive
+ var/explosive_chance = 1 // 1% to spawn a blahbomb!
+
+/obj/random/plushie/explosive/spawn_item()
+ var/obj/item/toy/plushie/plushie = ..()
+ if(!prob(explosive_chance))
+ return plushie
+ var/obj/item/I = new /obj/item/grenade/syndieminibomb
+ plushie.has_stuffing = FALSE
+ plushie.grenade = I
+ I.forceMove(plushie)
+ return plushie
+
/obj/item/toy/plushie/corgi
name = "corgi plushie"
icon_state = "corgi"
@@ -1224,22 +1238,24 @@
/*
* Rubber Chainsaw
*/
-/obj/item/twohanded/toy/chainsaw
+/obj/item/toy/chainsaw
name = "Toy Chainsaw"
desc = "A toy chainsaw with a rubber edge. Ages 8 and up"
icon_state = "chainsaw0"
+ base_icon_state = "chainsaw"
force = 0
throwforce = 0
throw_speed = 4
throw_range = 20
- wieldsound = 'sound/weapons/chainsawstart.ogg'
attack_verb = list("sawed", "cut", "hacked", "carved", "cleaved", "butchered", "felled", "timbered")
-/obj/item/twohanded/toy/chainsaw/update_icon_state()
- if(wielded)
- icon_state = "chainsaw[wielded]"
- else
- icon_state = "chainsaw0"
+/obj/item/toy/chainsaw/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, wieldsound = 'sound/weapons/chainsawstart.ogg', icon_wielded = "[base_icon_state]1")
+
+
+/obj/item/toy/chainsaw/update_icon_state()
+ icon_state = "[base_icon_state]0"
/*
* Cat Toy
diff --git a/code/game/objects/items/weapons/RCL.dm b/code/game/objects/items/weapons/RCL.dm
index 0309a28a0c7b..6b71865b8016 100644
--- a/code/game/objects/items/weapons/RCL.dm
+++ b/code/game/objects/items/weapons/RCL.dm
@@ -1,4 +1,4 @@
-/obj/item/twohanded/rcl
+/obj/item/rcl
name = "rapid cable layer (RCL)"
desc = "A device used to rapidly deploy cables. It has screws on the side which can be removed to slide off the cables."
icon = 'icons/obj/tools.dmi'
@@ -16,7 +16,11 @@
var/obj/structure/cable/last = null
var/obj/item/stack/cable_coil/loaded = null
-/obj/item/twohanded/rcl/attackby(obj/item/W, mob/user)
+/obj/item/rcl/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed)
+
+/obj/item/rcl/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/C = W
if(!loaded)
@@ -39,7 +43,7 @@
else
..()
-/obj/item/twohanded/rcl/screwdriver_act(mob/user, obj/item/I)
+/obj/item/rcl/screwdriver_act(mob/user, obj/item/I)
if(!loaded)
return
. = TRUE
@@ -60,18 +64,18 @@
loaded = null
update_icon(UPDATE_ICON_STATE)
-/obj/item/twohanded/rcl/examine(mob/user)
+/obj/item/rcl/examine(mob/user)
. = ..()
if(loaded)
. += "It contains [loaded.amount]/[max_amount] cables."
-/obj/item/twohanded/rcl/Destroy()
+/obj/item/rcl/Destroy()
QDEL_NULL(loaded)
last = null
active = FALSE
return ..()
-/obj/item/twohanded/rcl/update_icon_state()
+/obj/item/rcl/update_icon_state()
if(!loaded)
icon_state = "rcl-0"
item_state = "rcl-0"
@@ -90,7 +94,7 @@
icon_state = "rcl-0"
item_state = "rcl-0"
-/obj/item/twohanded/rcl/proc/is_empty(mob/user, loud = 1)
+/obj/item/rcl/proc/is_empty(mob/user, loud = 1)
update_icon(UPDATE_ICON_STATE)
if(!loaded || !loaded.amount)
if(loud)
@@ -98,19 +102,17 @@
if(loaded)
qdel(loaded)
loaded = null
- unwield(user)
- active = wielded
- return 1
- return 0
+ return TRUE
+ return FALSE
-/obj/item/twohanded/rcl/dropped(mob/wearer)
+/obj/item/rcl/dropped(mob/wearer)
..()
active = FALSE
last = null
-/obj/item/twohanded/rcl/attack_self(mob/user)
+/obj/item/rcl/attack_self(mob/user)
..()
- active = wielded
+ active = HAS_TRAIT(src, TRAIT_WIELDED)
if(!active)
last = null
else if(!last)
@@ -119,11 +121,11 @@
last = C
break
-/obj/item/twohanded/rcl/on_mob_move(direct, mob/user)
+/obj/item/rcl/on_mob_move(direct, mob/user)
if(active)
trigger(user)
-/obj/item/twohanded/rcl/proc/trigger(mob/user)
+/obj/item/rcl/proc/trigger(mob/user)
if(is_empty(user, 0))
to_chat(user, "\The [src] is empty!")
return
@@ -145,7 +147,7 @@
last = loaded.place_turf(get_turf(loc), user, turn(user.dir, 180))
is_empty(user) //If we've run out, display message
-/obj/item/twohanded/rcl/pre_loaded/New() //Comes preloaded with cable, for testing stuff
+/obj/item/rcl/pre_loaded/New() //Comes preloaded with cable, for testing stuff
..()
loaded = new()
loaded.max_amount = max_amount
diff --git a/code/game/objects/items/weapons/batons.dm b/code/game/objects/items/weapons/batons.dm
index 99c93f82aa0d..e33b513f58c9 100644
--- a/code/game/objects/items/weapons/batons.dm
+++ b/code/game/objects/items/weapons/batons.dm
@@ -66,7 +66,7 @@
* * user - The attacking user
*/
/obj/item/melee/classic_baton/proc/baton_knockdown(mob/living/target, mob/living/user)
- if(user.mind?.martial_art?.no_baton)
+ if(user.mind?.martial_art?.no_baton && user.mind?.martial_art?.can_use(user))
to_chat(user, user.mind.martial_art.no_baton_reason)
return
if(issilicon(target))
diff --git a/code/game/objects/items/weapons/cash.dm b/code/game/objects/items/weapons/cash.dm
index d68ce9df54f9..b059c003b8ed 100644
--- a/code/game/objects/items/weapons/cash.dm
+++ b/code/game/objects/items/weapons/cash.dm
@@ -78,5 +78,9 @@
/obj/item/stack/spacecash/c10000
amount = 10000
-/obj/item/twohanded/required/cash_pile
+/obj/item/cash_pile
name = "Pile of Cash"
+
+/obj/item/cash_pile/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm
index 6c223fd59442..387cf92d5cb4 100644
--- a/code/game/objects/items/weapons/defib.dm
+++ b/code/game/objects/items/weapons/defib.dm
@@ -22,7 +22,7 @@
/// if there's a cell in the defib with enough power for a revive; blocks paddles from reviving otherwise
var/powered = FALSE
/// Ref to attached paddles
- var/obj/item/twohanded/shockpaddles/paddles
+ var/obj/item/shockpaddles/paddles
/// Ref to internal power cell.
var/obj/item/stock_parts/cell/high/cell = null
/// If false, using harm intent will let you zap people. Note that any updates to this after init will only impact icons.
@@ -38,7 +38,7 @@
base_icon_state = "defibpaddles"
/// Type of paddles that should be attached to this defib.
- var/obj/item/twohanded/shockpaddles/paddle_type = /obj/item/twohanded/shockpaddles
+ var/obj/item/shockpaddles/paddle_type = /obj/item/shockpaddles
/obj/item/defibrillator/get_cell()
return cell
@@ -225,7 +225,7 @@
desc = "A belt-mounted blood-red defibrillator that can be rapidly deployed. Does not have the restrictions or safeties of conventional defibrillators and can revive through space suits."
icon_state = "defibcombat"
item_state = "defibcombat"
- paddle_type = /obj/item/twohanded/shockpaddles/syndicate
+ paddle_type = /obj/item/shockpaddles/syndicate
combat = TRUE
safety = FALSE
heart_attack_probability = 100
@@ -240,18 +240,20 @@
desc = "A belt-mounted state-of-the-art defibrillator that can be rapidly deployed in all environments. Uses an experimental self-charging cell, meaning that it will (probably) never stop working. Can be used to defibrillate through space suits. It is impossible to damage."
icon_state = "defibnt"
item_state = "defibnt"
- paddle_type = /obj/item/twohanded/shockpaddles/advanced
+ paddle_type = /obj/item/shockpaddles/advanced
combat = TRUE
safety = TRUE
- hardened = TRUE // emp-proof (on the component), but not emag-proof.
+ hardened = TRUE // emp-proof (on the component), but not emag-proof.
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //Objective item, better not have it destroyed.
heart_attack_probability = 10
var/next_emp_message //to prevent spam from the emagging message on the advanced defibrillator
+/obj/item/defibrillator/compact/advanced/screwdriver_act(mob/living/user, obj/item/I)
+ return // The cell is too strong roundstart and we dont want the adv defib to become useless
+
/obj/item/defibrillator/compact/advanced/attackby(obj/item/W, mob/user, params)
if(W == paddles)
- paddles.unwield()
toggle_paddles()
update_icon(UPDATE_OVERLAYS)
@@ -269,7 +271,7 @@
//paddles
-/obj/item/twohanded/shockpaddles
+/obj/item/shockpaddles
name = "defibrillator paddles"
desc = "A pair of plastic-gripped paddles with flat metal surfaces that are used to deliver powerful electric shocks."
icon = 'icons/obj/defib.dmi'
@@ -288,9 +290,11 @@
/// Whether or not the paddles are on cooldown. Used for tracking icon states.
var/on_cooldown = FALSE
-/obj/item/twohanded/shockpaddles/New(mainunit)
+
+/obj/item/shockpaddles/New(mainunit)
. = ..()
- if(check_defib_exists(mainunit, src))
+
+ if(check_defib_exists(mainunit, null, src))
defib = mainunit
loc = defib
update_icon(UPDATE_ICON_STATE)
@@ -300,22 +304,25 @@
RegisterSignal(src, COMSIG_DEFIB_READY, PROC_REF(on_cooldown_expire))
RegisterSignal(src, COMSIG_DEFIB_SHOCK_APPLIED, PROC_REF(after_shock))
RegisterSignal(src, COMSIG_DEFIB_PADDLES_APPLIED, PROC_REF(on_application))
+ AddComponent(/datum/component/two_handed)
-/obj/item/twohanded/shockpaddles/Destroy()
+/obj/item/shockpaddles/Destroy()
defib = null
return ..()
/// Check to see if we should abort this before we've even gotten started
-/obj/item/twohanded/shockpaddles/proc/on_application(obj/item/paddles, mob/living/user, mob/living/carbon/human/target, should_cause_harm)
+/obj/item/shockpaddles/proc/on_application(obj/item/paddles, mob/living/user, mob/living/carbon/human/target, should_cause_harm)
SIGNAL_HANDLER // COMSIG_DEFIB_PADDLES_APPLIED
- if(!wielded)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED))
to_chat(user, "You need to wield the paddles in both hands before you can use them on someone!")
return COMPONENT_BLOCK_DEFIB_MISC
if(!defib.powered)
return COMPONENT_BLOCK_DEFIB_DEAD
-/obj/item/twohanded/shockpaddles/proc/on_cooldown_expire(obj/item/paddles)
+ return
+
+/obj/item/shockpaddles/proc/on_cooldown_expire(obj/item/paddles)
SIGNAL_HANDLER // COMSIG_DEFIB_READY
on_cooldown = FALSE
if(defib.cell)
@@ -328,46 +335,43 @@
update_icon(UPDATE_ICON_STATE)
defib.update_icon(UPDATE_ICON_STATE)
-/obj/item/twohanded/shockpaddles/proc/after_shock()
+/obj/item/shockpaddles/proc/after_shock()
SIGNAL_HANDLER // COMSIG_DEFIB_SHOCK_APPLIED
on_cooldown = TRUE
defib.deductcharge(revivecost)
update_icon(UPDATE_ICON_STATE)
-/obj/item/twohanded/shockpaddles/update_icon_state()
+/obj/item/shockpaddles/update_icon_state()
+ var/wielded = HAS_TRAIT(src, TRAIT_WIELDED)
icon_state = "[base_icon_state][wielded]"
item_state = "[base_icon_state][wielded]"
if(on_cooldown)
icon_state = "[base_icon_state][wielded]_cooldown"
-/obj/item/twohanded/shockpaddles/suicide_act(mob/user)
+/obj/item/shockpaddles/suicide_act(mob/user)
user.visible_message("[user] is putting the live paddles on [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide.")
defib.deductcharge(revivecost)
playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1)
return OXYLOSS
-/obj/item/twohanded/shockpaddles/dropped(mob/user)
+/obj/item/shockpaddles/dropped(mob/user)
..()
if(user)
- var/obj/item/twohanded/offhand/O = user.get_inactive_hand()
- if(istype(O))
- O.unwield()
to_chat(user, "The paddles snap back into the main unit.")
defib.paddles_on_defib = TRUE
loc = defib
defib.update_icon(UPDATE_OVERLAYS)
update_icon(UPDATE_ICON_STATE)
- unwield(user)
-/obj/item/twohanded/shockpaddles/on_mob_move(dir, mob/user)
+/obj/item/shockpaddles/on_mob_move(dir, mob/user)
if(defib)
var/turf/t = get_turf(defib)
if(!t.Adjacent(user))
defib.remove_paddles(user)
-/obj/item/twohanded/shockpaddles/proc/check_defib_exists(mainunit, mob/living/carbon/human/M, obj/O)
+/obj/item/shockpaddles/proc/check_defib_exists(mainunit, mob/living/carbon/human/M, obj/O)
if(!mainunit || !istype(mainunit, /obj/item/defibrillator)) //To avoid weird issues from admin spawns
- M.unEquip(O)
+ M?.unEquip(O)
qdel(O)
return FALSE
else
@@ -406,14 +410,14 @@
playsound(get_turf(src), 'sound/machines/defib_ready.ogg', 50, 0)
update_icon(UPDATE_ICON_STATE)
-/obj/item/twohanded/shockpaddles/syndicate
+/obj/item/shockpaddles/syndicate
name = "combat defibrillator paddles"
desc = "A pair of high-tech paddles with flat plasteel surfaces to revive deceased operatives (unless they exploded). They possess both the ability to penetrate armor and to deliver powerful or disabling shocks offensively."
icon_state = "syndiepaddles0"
item_state = "syndiepaddles0"
base_icon_state = "syndiepaddles"
-/obj/item/twohanded/shockpaddles/advanced
+/obj/item/shockpaddles/advanced
name = "advanced defibrillator paddles"
desc = "A pair of high-tech paddles with flat plasteel surfaces that are used to deliver powerful electric shocks. They possess the ability to penetrate armor to deliver shock."
icon_state = "ntpaddles0"
diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm
index dff43e2d56e4..c1de033796ad 100644
--- a/code/game/objects/items/weapons/dice.dm
+++ b/code/game/objects/items/weapons/dice.dm
@@ -258,7 +258,7 @@
/obj/item/clothing/glasses/chameleon/thermal,
/obj/item/borg/upgrade/modkit/indoors,
/obj/item/storage/box/syndie_kit/chameleon,
- /obj/item/storage/box/syndie_kit/hardsuit,
+ /obj/item/storage/box/syndie_kit/modsuit,
/obj/item/implanter/storage,
/obj/item/toy/syndicateballoon)
var/selected_item = pick(traitor_items)
@@ -290,7 +290,7 @@
servant_mind.objectives += O
servant_mind.transfer_to(H)
- var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H)
+ var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", poll_time = 30 SECONDS, source = H)
if(length(candidates) && !QDELETED(H))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm
index ae8c99fbacae..2492e42fea0e 100644
--- a/code/game/objects/items/weapons/dna_injector.dm
+++ b/code/game/objects/items/weapons/dna_injector.dm
@@ -117,6 +117,7 @@
return FALSE
if(!user.IsAdvancedToolUser())
+ to_chat(user, "You don't have the dexterity to do this!")
return FALSE
var/attack_log = "injected with the Isolated [name]"
@@ -286,28 +287,6 @@
block = GLOB.regenerateblock
..()
-/obj/item/dnainjector/runfast
- name = "DNA-Injector (Increase Run)"
- desc = "Running Man."
- datatype = DNA2_BUF_SE
- value = 0xFFF
- forcedmutation = TRUE
-
-/obj/item/dnainjector/runfast/Initialize()
- block = GLOB.increaserunblock
- ..()
-
-/obj/item/dnainjector/antirunfast
- name = "DNA-Injector (Anti-Increase Run)"
- desc = "Walking Man."
- datatype = DNA2_BUF_SE
- value = 0x001
- forcedmutation = TRUE
-
-/obj/item/dnainjector/antirunfast/Initialize()
- block = GLOB.increaserunblock
- ..()
-
/obj/item/dnainjector/morph
name = "DNA-Injector (Morph)"
desc = "A total makeover."
@@ -509,7 +488,7 @@
/obj/item/dnainjector/antitour
name = "DNA-Injector (Anti-Tour.)"
- desc = "Will cure tourrets."
+ desc = "Will cure tourettes."
datatype = DNA2_BUF_SE
value = 0x001
forcedmutation = TRUE
@@ -520,7 +499,7 @@
/obj/item/dnainjector/tourmut
name = "DNA-Injector (Tour.)"
- desc = "Gives you a nasty case off tourrets."
+ desc = "Gives you a nasty case off tourettes."
datatype = DNA2_BUF_SE
value = 0xFFF
forcedmutation = TRUE
diff --git a/code/game/objects/items/weapons/garrote.dm b/code/game/objects/items/weapons/garrote.dm
index a48fd064f74e..32ad45ba87c8 100644
--- a/code/game/objects/items/weapons/garrote.dm
+++ b/code/game/objects/items/weapons/garrote.dm
@@ -4,7 +4,7 @@
* Improvised garrotes
*/
-/obj/item/twohanded/garrote // 12TC traitor item
+/obj/item/garrote // 12TC traitor item
name = "fiber wire"
desc = "A length of razor-thin wire with an elegant wooden handle on either end.
You suspect you'd have to be behind the target to use this weapon effectively."
icon_state = "garrot_wrap"
@@ -13,35 +13,43 @@
var/improvised = FALSE
var/garrote_time
-/obj/item/twohanded/garrote/Destroy()
+/obj/item/garrote/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed)
+
+/obj/item/garrote/Destroy()
strangling = null
return ..()
-/obj/item/twohanded/garrote/update_icon_state()
+/obj/item/garrote/update_icon_state()
if(strangling) // If we're strangling someone we want our icon to stay wielded
icon_state = "garrot_[improvised ? "I_" : ""]unwrap"
else
- icon_state = "garrot_[improvised ? "I_" : ""][wielded ? "un" : ""]wrap"
+ icon_state = "garrot_[improvised ? "I_" : ""][HAS_TRAIT(src, TRAIT_WIELDED) ? "un" : ""]wrap"
-/obj/item/twohanded/garrote/improvised // Made via tablecrafting
+/obj/item/garrote/improvised // Made via tablecrafting
name = "garrote"
desc = "A length of cable with a shoddily-carved wooden handle tied to either end.
You suspect you'd have to be behind the target to use this weapon effectively."
icon_state = "garrot_I_wrap"
improvised = TRUE
-/obj/item/twohanded/garrote/wield(mob/living/carbon/user)
- if(strangling)
- user.visible_message("[user] removes [src] from [strangling]'s neck.",
- "You remove [src] from [strangling]'s neck.")
+/obj/item/garrote/improvised/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, wield_callback = CALLBACK(src, PROC_REF(wield)))
- strangling = null
- update_icon(UPDATE_ICON_STATE)
- STOP_PROCESSING(SSobj, src)
- else
- ..()
+/obj/item/garrote/proc/wield(obj/item/source, mob/living/carbon/user)
+ if(!strangling)
+ return
+ user.visible_message("[user] removes [src] from [strangling]'s neck.",
+ "You remove [src] from [strangling]'s neck.")
+
+ strangling = null
+ update_icon(UPDATE_ICON_STATE)
+ STOP_PROCESSING(SSobj, src)
+
-/obj/item/twohanded/garrote/attack(mob/living/carbon/M as mob, mob/user as mob)
+/obj/item/garrote/attack(mob/living/carbon/M as mob, mob/user as mob)
if(garrote_time > world.time) // Cooldown
return
@@ -50,7 +58,7 @@
var/mob/living/carbon/human/U = user
- if(!wielded)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED))
to_chat(user, "You must use both hands to garrote [M]!")
return
@@ -73,7 +81,7 @@
to_chat(user, "You cannot use [src] on two people at once!")
return
- unwield(U)
+ attack_self(user)
U.swap_hand() // For whatever reason the grab will not properly work if we don't have the free hand active.
var/obj/item/grab/G = M.grabbedby(U, 1)
@@ -101,7 +109,7 @@
return
-/obj/item/twohanded/garrote/process()
+/obj/item/garrote/process()
if(!strangling)
// Our mark got gibbed or similar
update_icon(UPDATE_ICON_STATE)
@@ -160,7 +168,7 @@
strangling.apply_damage(4, OXY, "head")
-/obj/item/twohanded/garrote/suicide_act(mob/user)
+/obj/item/garrote/suicide_act(mob/user)
user.visible_message("[user] is wrapping [src] around [user.p_their()] neck and pulling the handles! It looks like [user.p_theyre()] trying to commit suicide.")
playsound(loc, 'sound/weapons/cablecuff.ogg', 15, 1, -10, ignore_walls = FALSE)
return OXYLOSS
diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm
index dfa2bd32f54f..45c788385bc2 100644
--- a/code/game/objects/items/weapons/gift_wrappaper.dm
+++ b/code/game/objects/items/weapons/gift_wrappaper.dm
@@ -91,6 +91,7 @@
/obj/random/figure,
/obj/item/deck/cards,
/obj/item/deck/cards/tiny,
+ /obj/item/deck/unum,
/obj/item/toy/minimeteor,
/obj/item/toy/redbutton,
/obj/item/toy/figure/owl,
diff --git a/code/game/objects/items/weapons/grenades/syndieminibomb.dm b/code/game/objects/items/weapons/grenades/syndieminibomb.dm
index 4bae6ff0510b..0eb76dbf86ae 100644
--- a/code/game/objects/items/weapons/grenades/syndieminibomb.dm
+++ b/code/game/objects/items/weapons/grenades/syndieminibomb.dm
@@ -10,3 +10,17 @@
update_mob()
explosion(loc, 1, 2, 4, flame_range = 2)
qdel(src)
+
+/obj/item/grenade/syndieminibomb/fake
+
+/obj/item/grenade/syndieminibomb/fake/examine(mob/user)
+ . = ..()
+ if(HAS_TRAIT(user, TRAIT_CLUMSY))
+ . += "There are small glue ejectors all over the bomb."
+
+/obj/item/grenade/syndieminibomb/fake/attack_self(mob/user)
+ if(!active)
+ flags |= NODROP
+ to_chat(user, "As you activate the bomb, it emits a substance that sticks to your hand! It won't come off!")
+ to_chat(user, "Uh oh.")
+ . = ..()
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index 37d5ab57faed..d8565028a898 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -27,6 +27,7 @@
/obj/item/restraints/handcuffs/attack(mob/living/carbon/C, mob/user)
if(!user.IsAdvancedToolUser())
+ to_chat(user, "You don't have the dexterity to do this!")
return
if(!istype(C))
diff --git a/code/game/objects/items/weapons/implants/implant_stealth.dm b/code/game/objects/items/weapons/implants/implant_stealth.dm
index f964335adbfe..4c146bdfcc4a 100644
--- a/code/game/objects/items/weapons/implants/implant_stealth.dm
+++ b/code/game/objects/items/weapons/implants/implant_stealth.dm
@@ -24,7 +24,7 @@
/// If TRUE, the box can't be deployed
var/on_cooldown = FALSE
-/datum/action/item_action/agent_box/Trigger(trigger_flags)
+/datum/action/item_action/agent_box/Trigger(trigger_flags, left_click)
. = ..()
if(!.)
return FALSE
diff --git a/code/game/objects/items/weapons/implants/implant_supercharge.dm b/code/game/objects/items/weapons/implants/implant_supercharge.dm
new file mode 100644
index 000000000000..b50d906d7c35
--- /dev/null
+++ b/code/game/objects/items/weapons/implants/implant_supercharge.dm
@@ -0,0 +1,34 @@
+/obj/item/implant/supercharge
+ name = "supercharge bio-chip"
+ desc = "Removes all stuns and knockdowns."
+ icon_state = "adrenal"
+ origin_tech = "materials=3;combat=5;syndicate=4"
+ uses = 3
+ implant_data = /datum/implant_fluff/adrenaline
+ implant_state = "implant-syndicate"
+
+/obj/item/implant/supercharge/activate()
+ uses--
+ to_chat(imp_in, "You feel an electric sensation as your components enter overdrive!")
+ imp_in.SetStunned(0)
+ imp_in.SetWeakened(0)
+ imp_in.SetKnockDown(0)
+ imp_in.SetParalysis(0)
+ imp_in.adjustStaminaLoss(-75)
+ imp_in.stand_up(TRUE)
+ SEND_SIGNAL(imp_in, COMSIG_LIVING_CLEAR_STUNS)
+
+ imp_in.reagents.add_reagent("surge_plus", 10)
+ imp_in.reagents.add_reagent("liquid_solder", 10)
+ imp_in.reagents.add_reagent("combatlube", 10)
+ if(!uses)
+ qdel(src)
+
+/obj/item/implanter/supercharge
+ name = "bio-chip implanter (supercharge)"
+ implant_type = /obj/item/implant/supercharge
+
+/obj/item/implantcase/supercharge
+ name = "bio-chip case - 'supercharge'"
+ desc = "A glass case containing an supercharge bio-chip."
+ implant_type = /obj/item/implant/supercharge
diff --git a/code/game/objects/items/weapons/implants/implant_uplink.dm b/code/game/objects/items/weapons/implants/implant_uplink.dm
index e628e2efb977..ef9cfaf786d5 100644
--- a/code/game/objects/items/weapons/implants/implant_uplink.dm
+++ b/code/game/objects/items/weapons/implants/implant_uplink.dm
@@ -10,7 +10,7 @@
/obj/item/implant/uplink/Initialize(mapload)
. = ..()
hidden_uplink = new(src)
- hidden_uplink.uses = 10
+ hidden_uplink.uses = 50
/obj/item/implant/uplink/nuclear/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/items/weapons/implants/implantfluff.dm b/code/game/objects/items/weapons/implants/implantfluff.dm
index 8cda71a74550..46df0f641a2f 100644
--- a/code/game/objects/items/weapons/implants/implantfluff.dm
+++ b/code/game/objects/items/weapons/implants/implantfluff.dm
@@ -22,6 +22,12 @@
notes = "One of Cybersun Industries oldest and simplest implants, even in its simplicity it is rumoured to be one of Cybersun Industries best-selling products."
function = "Subjects injected with this bio-chip can activate an injection of medical cocktails that removes stuns, increases speed, and has mild healing effects."
+/datum/implant_fluff/supercharge
+ name = "Cybersun Industries RX-4 Synthetic Supercharge Bio-chip"
+ life = "Known to last for up to a year."
+ notes = "One of Cybersun Industries simplest implants, it's rumored that synthetic rights groups maintain stockpiles of these."
+ function = "Synthetic subjects injected with this bio-chip can activate an injection of lubricants, coolants, and positronic patching fluid."
+
/datum/implant_fluff/chem
name = "BioTech Solutions MJ-420 Prisoner Management Bio-chip" //ah yes, MJ-420, old coders are FUNNY
life = "Deactivates upon death but remains within the body."
@@ -117,6 +123,12 @@
name = "BioTech Solutions Comedy Bio-chip"
function = "Plays a sad trombone noise upon death of the implantee, allows clowns to entertain the crew even post-mortem."
+/datum/implant_fluff/pathfinder
+ name = "Paizo Productions 5-E Pathfinder Implant"
+ life = "Lasts 2-12 months. Known to fail at the worst possible time, space radation may be a factor."
+ notes = "By use of an internal private GPS signal, allows the pathfinder module to have the MODsuit find the user. Also wirelessly transfers ID information to the suit, to allow doors to open."
+ function = "Allows for the recall of a Modular Outerwear Device by the implant owner at any time."
+
/datum/implant_fluff/gorilla_rampage
name = "Magillitis Serum Bio-chip"
life = "Unknown, no collected sample has been active long enough to determine lifespan."
diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm
index ce42d0251ed2..d92289cd7457 100644
--- a/code/game/objects/items/weapons/legcuffs.dm
+++ b/code/game/objects/items/weapons/legcuffs.dm
@@ -10,7 +10,7 @@
w_class = WEIGHT_CLASS_NORMAL
origin_tech = "engineering=3;combat=3"
slowdown = 7
- breakouttime = 300 //Deciseconds = 30s = 0.5 minute
+ breakouttime = 30 SECONDS
/obj/item/restraints/legcuffs/beartrap
name = "bear trap"
@@ -19,6 +19,7 @@
icon_state = "beartrap0"
desc = "A trap used to catch bears and other legged creatures."
origin_tech = "engineering=4"
+ breakouttime = 20 SECONDS
var/armed = FALSE
var/trap_damage = 20
///Do we want the beartrap not to make a visable message on arm? Use when a beartrap is applied by something else.
@@ -41,7 +42,11 @@
/obj/item/restraints/legcuffs/beartrap/attack_self(mob/user)
..()
- if(ishuman(user) && !user.stat && !user.restrained())
+
+ if(!ishuman(user) || user.restrained())
+ return
+
+ if(do_after(user, 2 SECONDS, target = user))
armed = !armed
update_icon(UPDATE_ICON_STATE)
to_chat(user, "[src] is now [armed ? "armed" : "disarmed"]")
@@ -94,42 +99,58 @@
return TRUE
/obj/item/restraints/legcuffs/beartrap/Crossed(AM as mob|obj, oldloc)
- if(armed && isturf(src.loc))
- if( (iscarbon(AM) || isanimal(AM)) && !istype(AM, /mob/living/simple_animal/parrot) && !isconstruct(AM) && !isshade(AM) && !istype(AM, /mob/living/simple_animal/hostile/viscerator))
- var/mob/living/L = AM
- armed = FALSE
- update_icon()
- playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
- if(!silent_arming)
- L.visible_message("[L] triggers \the [src].", \
- "You trigger \the [src]!")
-
- if(IED && isturf(src.loc))
- IED.active = TRUE
- message_admins("[key_name_admin(usr)] has triggered an IED-rigged [name].")
- log_game("[key_name(usr)] has triggered an IED-rigged [name].")
- spawn(IED.det_time)
- IED.prime()
-
- if(sig && isturf(src.loc))
- sig.signal()
-
- if(ishuman(AM))
- var/mob/living/carbon/H = AM
- if(IS_HORIZONTAL(H))
- H.apply_damage(trap_damage, BRUTE,"chest")
- else
- H.apply_damage(trap_damage, BRUTE,(pick("l_leg", "r_leg")))
- if(!H.legcuffed && H.get_num_legs() >= 2) //beartrap can't cuff you leg if there's already a beartrap or legcuffs.
- H.legcuffed = src
- forceMove(H)
- H.update_inv_legcuffed()
- SSblackbox.record_feedback("tally", "handcuffs", 1, type)
+ if(!armed || !isturf(loc))
+ return ..()
+
+ var/mob/living/L = AM
+ if((iscarbon(AM) || isanimal(AM)) && !L.flying)
+ spring_trap(AM)
+ if(ishuman(AM))
+ var/mob/living/carbon/H = AM
+ if(IS_HORIZONTAL(H))
+ H.apply_damage(trap_damage, BRUTE, "chest")
else
- L.apply_damage(trap_damage, BRUTE)
+ H.apply_damage(trap_damage, BRUTE, pick("l_leg", "r_leg"))
+ if(!H.legcuffed && H.get_num_legs() >= 2) //beartrap can't cuff you leg if there's already a beartrap or legcuffs.
+ H.legcuffed = src
+ forceMove(H)
+ H.update_inv_legcuffed()
+ SSblackbox.record_feedback("tally", "handcuffs", 1, type)
+ else
+ if(istype(L, /mob/living/simple_animal/hostile/bear))
+ L.apply_damage(trap_damage * 2.5, BRUTE)
+ else
+ L.apply_damage(trap_damage * 1.75, BRUTE)
..()
+/obj/item/restraints/legcuffs/beartrap/on_found(mob/finder)
+ if(!armed)
+ return FALSE
+ spring_trap(finder)
+
+ if(ishuman(finder))
+ var/mob/living/carbon/H = finder
+ H.apply_damage(trap_damage, BRUTE, pick("l_hand", "r_hand"))
+ return TRUE
+
+/obj/item/restraints/legcuffs/beartrap/proc/spring_trap(mob/user)
+ armed = FALSE
+ update_icon()
+ playsound(loc, 'sound/effects/snap.ogg', 50, TRUE)
+ if(!silent_arming)
+ user.visible_message("[user] triggers [src].", "You trigger [src].")
+
+ if(sig)
+ sig.signal()
+
+ if(IED)
+ IED.active = TRUE
+ message_admins("[key_name_admin(usr)] has triggered an IED-rigged [name].")
+ log_game("[key_name(usr)] has triggered an IED-rigged [name].")
+ spawn(IED.det_time)
+ IED.prime()
+
/obj/item/restraints/legcuffs/beartrap/energy
name = "energy snare"
armed = TRUE
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index 53d0d7e1c032..7c32249ddbf3 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -113,88 +113,44 @@
/obj/item/book/manual/supermatter_engine
- name = "Supermatter Engine User's Guide"
- desc = "An engineer's best tool for dealing with their worst frenemy: The Supermatter."
+ name = "Supermatter Engine Anomaly Reference"
+ desc = "An engineer's best tool for dealing with their worst frenemy: The Supermatter and its anomalous behavior."
icon_state = "bookParticleAccelerator"
- author = "Waleed Asad"
- title = "Supermatter Engine User's Guide"
+ author = "Vroo-Looum-Kloo"
+ title = "Supermatter Engine Anomaly Reference"
pages = list({"Engineering notes on single-stage Supermatter engine,
- -Waleed Asad
+ Vroo-Looum-Kloo
- A word of caution, do not enter the engine room, for any reason, without radiation protection and mesons on. The status of the engine may be unpredictable even when you believe it is .off.. This is an important level of personal protection.
+ The supermatter engine is a very powerful, yet strange method of power generation. This guide will serve as a pocket reference for the myriad of anomalous behaviors it may exhibit throughout your shift. Below is a table of events and their effects.
- The engine has two basic modes of functionality. He has observed that it is capable of both a safe level of operation and a modified, high output mode.
+ D Class: Events that only affect certain types of NON-STANDARD setups, minimial operator intervention required. These events occur instantly and engineering will be alerted on telecomms.
- Notes on starting the basic function mode, dubbed .Heat-Primary Mode..
+ D-1: About 200 moles of nitrous oxide are released by the crystal.
+ D-2: About 200 moles of nitrogen are released by the crystal
+ D-3: About 250 moles of CO2 are released by the crystal
- 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.
+ C Class: Events with mild effects to standard setups. Operator intervention MAY be required. Engineering will be alerted on telecomms.
+ C-1: 250 moles of oxygen are released by the crystal
+ C-2: 250 moles of plasma are released by the crystal
+ C-3: The temperature at which the engine starts to lose integrity is lowered for a few minutes.
- 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.
+ B Class: Events with significant effects to standard setups. Action may need to be taken to prevent a delamination event.
+ B-1: The amount of plasma and O2 released by the engine is doubled for a few minutes.
+ B-2: The amount of heat released by the engine is increased for a few minutes.
+ B-3: The engine's EER is raised slightly above critically for several minutes, regardless of outside factors.
- 3. Apply N2 gas. Retrieve the two N2 canisters from storage and bring them to the engine room. Attach one of them to the input section of the engine gas system located next to the collectors. Keep it attached until the N2 pressure is low enough to turn the canister light red. Replace it with the second canister to keep N2 pressure at optimal levels.
+ A Class: Events with SEVERE effects to standard setups. Action will need to be taken to prevent a delamination event.
+ A-1: The engine's APC is shorted due to a power spike, requiring its wires to be mended.
+ A-2: The engine's air alarm resets its self as an effect of radiological interference.
+ A-3: The amount of plasma and O2 released by the engine is quadrupled for a few minutes.
- 4. Begin primary emitter burst series. This means firing a single emitter for its first four shots. It is important to move to this step quickly. The onboard SMES units may not have enough power to run the emitters if left alone too long on-station. This engine can produce enough power on its own to run the entire station, ignoring the SMES units completely, and is wired to do so.
+ S Class events: Events that require immediate intervention and a specialized response to prevent a delamination event. Coordination with other departments is HIGHLY recommended. A warning will be broadcasted on engineering communications before these events.
+ Arc Type: The engine's EER is raised massively several minutes, resulting it a supercritical state.
+ Heat Type: The amount of heat released by the engine is massively increased for several minutes.
- 5. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.)
-
- 6. Begin secondary emitter burst series. Before firing the emitter again, check the power in the line with a multimeter (Do not forget electrical gloves.) The engine is running at high efficiency when the value exceeds 200,000 power units.
-
- 7. Maintain engine power. When power in the lines gets low, add an additional emitter burst series to bring power to normal levels.
-
-
-
- The second mode for running the engine uses a gas mix to produce a reaction within the Supermatter. This mode requires CE or Atmospheric help to setup. This has been dubbed the .O2-Reaction Mode..
-
- THIS MODE CAN CAUSE A RUNAWAY REACTION, LEADING TO CATASTROPHIC FAILURE IF NOT MAINTAINED. NEVER FORGET ABOUT THE ENGINE IN THIS MODE.
-
- Additionally, this mode can be used for what is called a .Cold Start.. If the station has no power in the SMES to run the emitters, using this mode will allow enough power output to run them, and quickly reach an acceptable level of power output.
-
- 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.
-
- 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.
-
- 3. Modify the engine room filters. Unlike the Heat-Primary Mode, it is important to change the filters attached to the gas system to stop filtering O2, and start filtering Carbon Molecules. O2-Reaction Mode produces far more plasma than Heat-Primary, therefor filtering it off is essential.
-
- 4. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.) If you check the power in the system lines at this point you will find that it is constantly going up. Indeed, with just the addition of O2 to the Supermatter, it will begin outputting power.
-
- 5. Begin primary emitter burst series. Fire a single emitter for a series of four pulses, or a single series, and turn it off. Do not over power the Supermatter. The reaction is self sustaining and propagating. As long as O2 is in the chamber, it will continue outputting MORE power.
-
- 6. Maintain follow up operations. Remember to check the temp of the core gas and switch to the Heat-Primary function, or vent the core room when problems begin if required.
-
- Notes on Supermatter Reaction Function and Drawbacks-
-
- After several hours of observation an interesting phenomenon was witnessed. The Supermatter undergoes a constant self-sustaining reaction when given an extremely high O2 concentration. Anything about 80% or higher typically will cause this reaction. The Supermatter will continue to react whenever this gas mix is in the same room as the Supermatter.
-
- To understand why O2-Reaction mode is dangerous, the core principle of the Supermatter must be understood. The Supermatter emits three things when .not safe,. that is any time it is giving off power. These things are:
-
- *Radiation (which is converted into power by the collectors,)
- *Heat (which is removed via the gas exchange system and coolers,)
- *External gas (in the form of plasma and O2.)
-
- When in Heat-Primary mode, far more heat and plasma are produced than radiation. In O2-Reaction mode, very little heat and only moderate amounts of plasma are produced, however HUGE amounts of energy leaving the Supermatter is in the form of radiation.
-
- The O2-Reaction engine mode has a single drawback which has been eluded to more than once so far and that is very simple. The engine room will continue to grow hotter as the constant reaction continues. Eventually, there will be what he calls the .critical gas mix.. This is the point at which the constant adding of plasma to the mix of air around the Supermatter changes the gas concentration to below the tolerance. When this happens, two things occur. First, the Supermatter switches to its primary mode of operation where in huge amounts of heat are produced by the engine rather than low amounts with high power output. Second, an uncontrollable increase in heat within the Supermatter chamber will occur. This will lead to a spark-up, igniting the plasma in the Supermatter chamber, wildly increasing both pressure and temperature.
-
- While the O2-Reaction mode is dangerous, it does produce heavy amounts of energy. Consider using this mode only in short amounts to fill the SMES, and switch back later in the shift to keep things flowing normally.
-
-
- Notes on Supermatter Containment and Emergency Procedures-
-
- While a constant vigil on the Supermatter is not required, regular checkups are important. Verify the temp of gas leaving the Supermatter chamber for unsafe levels, and ensure that the plasma in the chamber is at a safe concentration. Of course, also make sure the chamber is not on fire. A fire in the core chamber is very difficult to put out. As any Toxin scientist can tell you, even low amounts of plasma can burn at very high temperatures. This burning creates a huge increase in pressure and more importantly, temperature of the crystal itself.
-
- The Supermatter is strong, but not invincible. When the Supermatter is heated too much, its crystal structure will attempt to liquify. The change in atomic structure of the Supermatter leads to a single reaction, a massive explosion. The computer chip attached to the Supermatter core will warn the station when stability is threatened. It will then offer a second warning, when things have become dangerously close to total destruction of the core.
-
- Located both within the supermatter monitoring room and engine room is the vent control button. This button allows the Core Vent Controls to be accessed, venting the room to space. Remember however, that this process takes time. If a fire is raging, and the pressure is higher than fathomable, it will take a great deal of time to vent the room. Also located in the supermatter monitoring room is the emergency core eject button. A new core can be ordered from cargo. It is often not worth the lives of the crew to hold on to it, not to mention the structural damage. However, if by some mistake the Supermatter is pushed off or removed from the mass ejector it sits on, manual reposition will be required. Which is very dangerous and often leads to death.
-
- The Supermatter is extremely dangerous. More dangerous than people give it credit for. It can destroy you in an instant, without hesitation, reducing you to a pile of dust. When working closely with Supermatter it is.. suggested to get a genetic backup and do not wear any items of value to you. The Supermatter core can be pulled if grabbed properly by the base, but pushing is not possible.
-
-
- In Closing-
-
- Remember that the Supermatter is dangerous, and the core is dangerous still. Venting the core room is always an option if you are even remotely worried, utilizing Atmospherics to properly ready the room once more for core function. It is always a good idea to check up regularly on the temperature of gas leaving the chamber, as well as the power in the system lines. Lastly, once again remember, never touch the Supermatter with anything. Ever.
-
- -Waleed Asad, Senior Engine Technician."})
+ In the event that an anomaly NOT on this list presents itself, contact your local NanoTrasen Engineering Officer as soon as possible.
+ -Vroo-Looum-Kloo, Senior Engine Technician."})
/obj/item/book/manual/atmospipes
name = "Pipes and You: Getting To Know Your Scary Tools"
diff --git a/code/game/objects/items/weapons/melee/energy_melee_weapons.dm b/code/game/objects/items/weapons/melee/energy_melee_weapons.dm
index a4d6eea631a6..42470a44215c 100644
--- a/code/game/objects/items/weapons/melee/energy_melee_weapons.dm
+++ b/code/game/objects/items/weapons/melee/energy_melee_weapons.dm
@@ -201,7 +201,7 @@
user.adjustBrainLoss(10)
else
to_chat(user, "You attach the ends of the two energy swords, making a single double-bladed weapon! You're cool.")
- var/obj/item/twohanded/dualsaber/newSaber = new /obj/item/twohanded/dualsaber(user.loc)
+ var/obj/item/dualsaber/newSaber = new /obj/item/dualsaber(user.loc)
if(src.hacked) // That's right, we'll only check the "original" esword.
newSaber.hacked = TRUE
newSaber.item_color = "rainbow"
diff --git a/code/game/objects/items/weapons/rpd.dm b/code/game/objects/items/weapons/rpd.dm
index 55639bd80870..37dc3b4917e7 100644
--- a/code/game/objects/items/weapons/rpd.dm
+++ b/code/game/objects/items/weapons/rpd.dm
@@ -260,8 +260,11 @@
if(world.time < lastused + spawndelay)
return
-
var/turf/T = get_turf(target)
+
+ if(!T)
+ return
+
if(target != T)
// We only check the rpd_act of the target if it isn't the turf, because otherwise
// (A) blocked turfs can be acted on, and (B) unblocked turfs get acted on twice.
diff --git a/code/game/objects/items/weapons/staff.dm b/code/game/objects/items/weapons/staff.dm
index 7bc970d2189a..d86da1bc56ad 100644
--- a/code/game/objects/items/weapons/staff.dm
+++ b/code/game/objects/items/weapons/staff.dm
@@ -1,4 +1,4 @@
-/obj/item/twohanded/staff
+/obj/item/staff
name = "wizards staff"
desc = "Apparently a staff used by the wizard."
icon = 'icons/obj/wizard.dmi'
@@ -12,61 +12,66 @@
attack_verb = list("bludgeoned", "whacked", "disciplined")
resistance_flags = FLAMMABLE
-/obj/item/twohanded/staff/broom
+/obj/item/staff/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed)
+
+/obj/item/staff/broom
name = "broom"
desc = "Used for sweeping, and flying into the night while cackling. Black cat not included."
icon = 'icons/obj/wizard.dmi'
icon_state = "broom"
item_state = "broom0"
-/obj/item/twohanded/staff/broom/attack_self(mob/user as mob)
- ..()
- item_state = "broom[wielded ? 1 : 0]"
- force = wielded ? 5 : 3
- attack_verb = wielded ? list("rammed into", "charged at") : list("bludgeoned", "whacked", "cleaned")
- if(user)
- user.update_inv_l_hand()
- user.update_inv_r_hand()
- if(user.mind in SSticker.mode.wizards)
- user.flying = wielded ? 1 : 0
- if(wielded)
- to_chat(user, "You hold \the [src] between your legs.")
- user.say("QUID 'ITCH")
- animate(user, pixel_y = pixel_y + 10 , time = 10, loop = 1, easing = SINE_EASING)
- else
- animate(user, pixel_y = pixel_y + 10 , time = 1, loop = 1)
- animate(user, pixel_y = pixel_y, time = 10, loop = 1, easing = SINE_EASING)
- animate(user)
- else
- if(wielded)
- to_chat(user, "You hold \the [src] between your legs.")
+/obj/item/staff/broom/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_wielded = 5, force_unwielded = 3, wield_callback = CALLBACK(src, PROC_REF(wield)), unwield_callback = CALLBACK(src, PROC_REF(unwield)))
+
+/obj/item/staff/broom/proc/wield(obj/item/source, mob/user)
+ attack_verb = list("rammed into", "charged at")
+ user.update_inv_l_hand()
+ user.update_inv_r_hand()
+ if(iswizard(user))
+ user.flying = TRUE
+ user.say("QUID 'ITCH")
+ animate(user, pixel_y = pixel_y + 10 , time = 10, loop = 1, easing = SINE_EASING)
+ to_chat(user, "You hold [src] between your legs.")
+
+/obj/item/staff/broom/proc/unwield(obj/item/source, mob/user)
+ attack_verb = list("bludgeoned", "whacked", "cleaned")
+ user.update_inv_l_hand()
+ user.update_inv_r_hand()
+ if(iswizard(user))
+ animate(user, pixel_y = pixel_y + 10 , time = 1, loop = 1)
+ animate(user, pixel_y = pixel_y, time = 10, loop = 1, easing = SINE_EASING)
+ animate(user)
-/obj/item/twohanded/staff/broom/attackby(obj/O, mob/user)
+/obj/item/staff/broom/attackby(obj/O, mob/user)
if(istype(O, /obj/item/clothing/mask/horsehead))
- new/obj/item/twohanded/staff/broom/horsebroom(get_turf(src))
+ new/obj/item/staff/broom/horsebroom(get_turf(src))
user.unEquip(O)
qdel(O)
qdel(src)
return
..()
-/obj/item/twohanded/staff/broom/dropped(mob/user)
+/obj/item/staff/broom/dropped(mob/user)
if((user.mind in SSticker.mode.wizards) && user.flying)
user.flying = FALSE
..()
-/obj/item/twohanded/staff/broom/horsebroom
+/obj/item/staff/broom/horsebroom
name = "broomstick horse"
desc = "Saddle up!"
icon = 'icons/obj/wizard.dmi'
icon_state = "horsebroom"
item_state = "horsebroom0"
-/obj/item/twohanded/staff/broom/horsebroom/attack_self(mob/user as mob)
+/obj/item/staff/broom/horsebroom/attack_self(mob/user as mob)
..()
- item_state = "horsebroom[wielded ? 1 : 0]"
+ item_state = "horsebroom[HAS_TRAIT(src, TRAIT_WIELDED) ? 1 : 0]"
-/obj/item/twohanded/staff/stick
+/obj/item/staff/stick
name = "stick"
desc = "A great tool to drag someone else's drinks across the bar."
icon_state = "stick"
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index e328bc1bae0e..ffa96c3d8b89 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -636,10 +636,10 @@
new /obj/item/organ/internal/cyberimp/arm/katana(src)
value += 1
if(3)
- new /obj/item/twohanded/mjollnir(src)
+ new /obj/item/mjollnir(src)
value += 2
if(4)
- new /obj/item/twohanded/singularityhammer(src)
+ new /obj/item/singularityhammer(src)
value += 2
if(5)
new /obj/item/katana(src)
@@ -648,13 +648,13 @@
new /obj/item/claymore(src)
value += 2 //force 40 this is value 2
if(7)
- new /obj/item/twohanded/spear/grey_tide(src)
+ new /obj/item/spear/grey_tide(src)
value += 2 //Value 2, clones are strong
if(8)
if(prob(50))
new /obj/item/sord(src)
value -= 1 //Useless joke, might as well give them a value point back.
- new /obj/item/twohanded/bostaff(src) //Funky item, not really worth a point, but good to balance sord's free point out
+ new /obj/item/bostaff(src) //Funky item, not really worth a point, but good to balance sord's free point out
//Wands
var/wands = 0
while(wands < 2)
diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index ef2d22601216..705908d9a788 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -42,6 +42,31 @@
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear)
+/obj/item/storage/bag/trash/proc/update_weight()
+ if(!length(contents))
+ w_class = WEIGHT_CLASS_NORMAL
+ return
+
+ w_class = WEIGHT_CLASS_BULKY
+
+/obj/item/storage/bag/trash/remove_from_storage(obj/item/I, atom/new_location)
+ . = ..()
+ update_weight()
+
+/obj/item/storage/bag/trash/can_be_inserted(obj/item/I, stop_messages = FALSE)
+ if(isstorage(loc) && !istype(loc, /obj/item/storage/backpack/holding))
+ to_chat(usr, "You can't seem to fit [I] into [src].")
+ return FALSE
+ . = ..()
+
+/obj/item/storage/bag/trash/Initialize(mapload)
+ . = ..()
+ update_weight()
+
+/obj/item/storage/bag/trash/handle_item_insertion(obj/item/I, prevent_warning)
+ . = ..()
+ update_weight()
+
/obj/item/storage/bag/trash/suicide_act(mob/user)
user.visible_message("[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!")
playsound(loc, 'sound/items/eatfood.ogg', 50, 1, -1)
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index 4b8546033124..c72b51df3453 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -515,7 +515,7 @@
/obj/item/holosign_creator/janitor,
/obj/item/melee/flyswatter,
/obj/item/storage/bag/trash,
- /obj/item/twohanded/push_broom,
+ /obj/item/push_broom,
/obj/item/door_remote/janikeyring
)
diff --git a/code/game/objects/items/weapons/storage/briefcase.dm b/code/game/objects/items/weapons/storage/briefcase.dm
index f3801b5aa6d3..ed9a96a2b93f 100644
--- a/code/game/objects/items/weapons/storage/briefcase.dm
+++ b/code/game/objects/items/weapons/storage/briefcase.dm
@@ -5,6 +5,7 @@
item_state = "briefcase"
flags = CONDUCT
hitsound = "swing_hit"
+ use_sound = 'sound/effects/briefcase.ogg'
force = 8
throw_speed = 2
throw_range = 4
diff --git a/code/game/objects/items/weapons/storage/garment.dm b/code/game/objects/items/weapons/storage/garment.dm
index 433927a4aaaf..812605b31b36 100644
--- a/code/game/objects/items/weapons/storage/garment.dm
+++ b/code/game/objects/items/weapons/storage/garment.dm
@@ -263,6 +263,7 @@
/obj/item/storage/bag/garment/chaplain/populate_contents()
new /obj/item/clothing/under/rank/civilian/chaplain(src)
new /obj/item/clothing/shoes/black(src)
+ new /obj/item/clothing/suit/hooded/abaya(src)
new /obj/item/clothing/suit/hooded/nun(src)
new /obj/item/clothing/suit/hooded/chaplain_hoodie(src)
new /obj/item/clothing/suit/hooded/monk(src)
@@ -292,3 +293,19 @@
new /obj/item/clothing/glasses/hud/health(src)
new /obj/item/clothing/glasses/hud/skills(src)
new /obj/item/clothing/accessory/blue(src)
+
+/obj/item/storage/bag/garment/paramedic
+ name = "paramedic's garment bag"
+ desc = "A bag for storing extra clothes and shoes. This one belongs to the paramedic."
+
+/obj/item/storage/bag/garment/paramedic/populate_contents()
+ new /obj/item/clothing/under/rank/medical/paramedic(src)
+ new /obj/item/clothing/head/soft/blue(src)
+ new /obj/item/clothing/suit/storage/labcoat/emt(src)
+ new /obj/item/clothing/suit/storage/paramedic(src)
+ new /obj/item/clothing/glasses/hud/health/sunglasses(src)
+ new /obj/item/clothing/gloves/color/latex(src)
+ new /obj/item/clothing/gloves/color/latex/nitrile(src)
+ new /obj/item/clothing/shoes/black(src)
+
+
diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm
index 7751df952cee..68e090db9ca0 100644
--- a/code/game/objects/items/weapons/storage/lockbox.dm
+++ b/code/game/objects/items/weapons/storage/lockbox.dm
@@ -125,9 +125,13 @@
/obj/item/storage/lockbox/research/deconstruct(disassembled = TRUE) // Get wrecked, Science nerds
qdel(src)
-/obj/item/storage/lockbox/research/large
- name = "Large lockbox"
- desc = "A large lockbox"
- max_w_class = WEIGHT_CLASS_BULKY
- max_combined_w_class = 4 //The sum of the w_classes of all the items in this storage item.
- storage_slots = 1
+/obj/item/storage/lockbox/research/modsuit
+ name = "Plating lockbox"
+ desc = "A larger lockbox. Looks a bit less secure than other lockboxes."
+
+/obj/item/storage/lockbox/research/modsuit/emp_act(severity) //I want emp to get around it, it's not a gun, I just want people not to always make sec / med modsuits.
+ . = ..()
+ if(prob(50 / severity))
+ locked = FALSE
+ icon_state = icon_broken
+ origin_tech = null //wipe out any origin tech if it's unlocked in any way so you can't double-dip tech levels at R&D.
diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm
index 2b8a558261bb..54ce56bd6159 100644
--- a/code/game/objects/items/weapons/storage/secure.dm
+++ b/code/game/objects/items/weapons/storage/secure.dm
@@ -72,10 +72,10 @@
/obj/item/storage/secure/emag_act(user as mob, weapon as obj)
if(!emagged)
emagged = TRUE
- overlays += image('icons/obj/storage.dmi', icon_sparking)
+ overlays += image(icon, icon_sparking)
sleep(6)
overlays = null
- overlays += image('icons/obj/storage.dmi', icon_locking)
+ overlays += image(icon, icon_locking)
locked = FALSE
if(istype(weapon, /obj/item/melee/energy/blade))
do_sparks(5, 0, loc)
@@ -143,7 +143,7 @@
else if(code == l_code && !emagged && l_set)
locked = FALSE
overlays = null
- overlays += image('icons/obj/storage.dmi', icon_opened)
+ overlays += image(icon, icon_opened)
code = null
else
code = "ERROR"
@@ -189,6 +189,7 @@
item_state = "sec-case"
flags = CONDUCT
hitsound = "swing_hit"
+ use_sound = 'sound/effects/briefcase.ogg'
force = 8
throw_speed = 2
throw_range = 4
@@ -201,7 +202,7 @@
if(loc == user && locked)
to_chat(usr, "[src] is locked and cannot be opened!")
else if((loc == user) && !locked)
- playsound(loc, "rustle", 50, 1, -5)
+ playsound(loc, 'sound/effects/briefcase.ogg', 50, TRUE, -5)
if(user.s_active)
user.s_active.close(user) //Close and re-open
show_to(user)
diff --git a/code/game/objects/items/weapons/storage/storage_base.dm b/code/game/objects/items/weapons/storage/storage_base.dm
index 3664566b0fbf..14f4e46bcceb 100644
--- a/code/game/objects/items/weapons/storage/storage_base.dm
+++ b/code/game/objects/items/weapons/storage/storage_base.dm
@@ -205,7 +205,7 @@
user.client.screen += closer
user.client.screen += contents
user.s_active = src
- LAZYADDOR(mobs_viewing, user)
+ LAZYDISTINCTADD(mobs_viewing, user)
/**
* Hides the current container interface from `user`.
@@ -533,6 +533,8 @@
var/obj/item/hand_labeler/labeler = I
if(labeler.mode)
return FALSE
+ if(user.a_intent != INTENT_HELP && issimulatedturf(loc)) // Stops you from putting your baton in the storage on accident
+ return FALSE
. = TRUE //no afterattack
if(isrobot(user))
return //Robots can't interact with storage items.
diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm
index 5a84304d7cf2..a866e4b14488 100644
--- a/code/game/objects/items/weapons/storage/toolbox.dm
+++ b/code/game/objects/items/weapons/storage/toolbox.dm
@@ -16,9 +16,10 @@
materials = list(MAT_METAL = 500)
origin_tech = "combat=1;engineering=1"
attack_verb = list("robusted")
+ use_sound = 'sound/effects/toolbox.ogg'
hitsound = 'sound/weapons/smash.ogg'
drop_sound = 'sound/items/handling/toolbox_drop.ogg'
- pickup_sound = 'sound/items/handling/toolbox_pickup.ogg'
+ pickup_sound = 'sound/items/handling/toolbox_pickup.ogg'
var/latches = "single_latch"
var/has_latches = TRUE
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index 0f3d41f96f64..a27d3415cf3b 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -25,7 +25,7 @@
/obj/item/suppressor, // 1TC
/obj/item/ammo_box/magazine/m10mm, // 1TC
/obj/item/ammo_box/magazine/m10mm/hp, // 2TC
- /obj/item/twohanded/garrote, // 6TC
+ /obj/item/garrote, // 6TC
/obj/item/door_remote/omni/access_tuner, // 6TC
/obj/item/clothing/glasses/chameleon/thermal, // 6TC
/obj/item/implanter/freedom, // 5TC
@@ -132,14 +132,29 @@
/obj/item/gun/projectile/automatic/pistol, // 4TC
/obj/item/ammo_box/magazine/m10mm/fire, // 2TC
/obj/item/ammo_box/magazine/m10mm/fire, // 2TC
- /obj/item/storage/box/syndie_kit/hardsuit, // 6TC
+ /obj/item/storage/box/syndie_kit/modsuit, // 6TC
/obj/item/clothing/gloves/combat, // 0TC
/obj/item/card/id/syndicate, // 2TC
/obj/item/clothing/shoes/chameleon/noslip, // 2TC
/obj/item/encryptionkey/syndicate) // 2TC
+ var/static/list/metroid = list( // 21 + modules + laser gun
+ /obj/item/storage/box/syndie_kit/modsuit/elite, // 9TC
+ /obj/item/mod/module/visor/thermal, // 3 TC
+ /obj/item/mod/module/stealth, //0 TC but strong
+ /obj/item/mod/module/power_kick, //0 TC but funny
+ /obj/item/mod/module/sphere_transform, //0TC but should not be allowed to normally be installed
+ /obj/item/autosurgeon/organ/syndicate/laser_arm, //0 TC but otherwise not obtainable.
+ /obj/item/pinpointer/advpinpointer, //4 TC
+ /obj/item/storage/box/syndidonkpockets, //2TC, otherwise they will just die in the first combat to disabler.
+ /obj/item/storage/belt/utility/full/multitool, //0 TC
+ /obj/item/clothing/head/collectable/slime, //Priceless (0 TC)
+ /obj/item/encryptionkey/syndicate) //2 TC
+
+
+
/obj/item/storage/box/syndie_kit/bundle/populate_contents()
- var/list/bundle = pick(spy, agent13, thief, bond, infiltrator, payday, implant, hacker, darklord, professional, grenadier)
+ var/list/bundle = pick(spy, agent13, thief, bond, infiltrator, payday, implant, hacker, darklord, professional, grenadier, metroid)
for(var/item in bundle)
new item(src)
@@ -154,13 +169,21 @@
new /obj/item/clothing/mask/gas/syndicate(src)
new /obj/item/tank/internals/emergency_oxygen/engi/syndi(src)
-/obj/item/storage/box/syndie_kit/hardsuit
- name = "Boxed Blood Red Suit and Helmet"
- can_hold = list(/obj/item/clothing/suit/space/hardsuit/syndi, /obj/item/tank/internals/emergency_oxygen/engi/syndi, /obj/item/clothing/mask/gas/syndicate)
+/obj/item/storage/box/syndie_kit/modsuit
+ name = "Boxed Blood Red MODsuit"
+ can_hold = list(/obj/item/tank/internals/emergency_oxygen/engi/syndi, /obj/item/clothing/mask/gas/syndicate)
max_w_class = WEIGHT_CLASS_NORMAL
-/obj/item/storage/box/syndie_kit/hardsuit/populate_contents()
- new /obj/item/clothing/suit/space/hardsuit/syndi(src)
+/obj/item/storage/box/syndie_kit/modsuit/populate_contents()
+ new /obj/item/mod/control/pre_equipped/traitor(src)
+ new /obj/item/clothing/mask/gas/syndicate(src)
+ new /obj/item/tank/internals/emergency_oxygen/engi/syndi(src)
+
+/obj/item/storage/box/syndie_kit/modsuit/elite //I have been informed this is a good idea.
+ name = "Boxed Elite MODsuit"
+
+/obj/item/storage/box/syndie_kit/modsuit/elite/populate_contents()
+ new /obj/item/mod/control/pre_equipped/traitor_elite(src)
new /obj/item/clothing/mask/gas/syndicate(src)
new /obj/item/tank/internals/emergency_oxygen/engi/syndi(src)
@@ -240,11 +263,17 @@
new /obj/item/ammo_casing/shotgun/assassination(src)
new /obj/item/gun/projectile/revolver/doublebarrel/improvised/cane(src)
+/obj/item/storage/box/syndie_kit/fake_minibomb
+ name = "trick minibomb kit"
+
+/obj/item/storage/box/syndie_kit/fake_minibomb/populate_contents()
+ new /obj/item/grenade/syndieminibomb/fake(src)
+
/obj/item/storage/box/syndie_kit/fake_revolver
name = "trick revolver kit"
/obj/item/storage/box/syndie_kit/fake_revolver/populate_contents()
- new /obj/item/toy/russian_revolver/trick_revolver(src)
+ new /obj/item/gun/projectile/revolver/fake(src)
/obj/item/storage/box/syndie_kit/mimery
name = "advanced mimery kit"
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index ab5a5ba5349e..cb84e0d4ec10 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -66,6 +66,7 @@
icon_state = "[base_icon]_nocell"
else
icon_state = "[base_icon]"
+
/obj/item/melee/baton/examine(mob/user)
. = ..()
if(isrobot(user))
@@ -158,6 +159,10 @@
update_icon()
add_fingerprint(user)
+/obj/item/melee/baton/throw_impact(mob/living/carbon/human/hit_mob)
+ . = ..()
+ if(!. && turned_on && istype(hit_mob))
+ thrown_baton_stun(hit_mob)
/obj/item/melee/baton/attack(mob/M, mob/living/user)
if(turned_on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
@@ -165,7 +170,7 @@
user.visible_message("[user] accidentally hits [user.p_themselves()] with [src]!",
"You accidentally hit yourself with [src]!")
return
- if(user.mind?.martial_art?.no_baton)
+ if(user.mind?.martial_art?.no_baton && user.mind?.martial_art?.can_use(user))
to_chat(user, user.mind.martial_art.no_baton_reason)
return
if(issilicon(M)) // Can't stunbaton borgs and AIs
@@ -224,6 +229,42 @@
deductcharge(hitcost)
return TRUE
+/obj/item/melee/baton/proc/thrown_baton_stun(mob/living/carbon/human/L)
+ if(cooldown > world.time)
+ return FALSE
+
+ var/user_UID = thrownby
+ var/mob/user = locateUID(thrownby)
+ if(!istype(user) || (user.mind?.martial_art?.no_baton && user.mind?.martial_art?.can_use(user)))
+ return
+
+ if(HAS_TRAIT_FROM(L, TRAIT_WAS_BATONNED, user_UID))
+ return FALSE
+
+ cooldown = world.time + initial(cooldown)
+ if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
+ playsound(L, 'sound/weapons/genhit.ogg', 50, TRUE)
+ return FALSE
+ L.Confused(4 SECONDS)
+ L.Jitter(4 SECONDS)
+ L.adjustStaminaLoss(30)
+ L.SetStuttering(4 SECONDS)
+
+ ADD_TRAIT(L, TRAIT_WAS_BATONNED, user_UID) // so one person cannot hit the same person with two separate batons
+ L.apply_status_effect(STATUS_EFFECT_DELAYED, 2 SECONDS, CALLBACK(L, TYPE_PROC_REF(/mob/living, KnockDown), knockdown_duration), COMSIG_LIVING_CLEAR_STUNS)
+ addtimer(CALLBACK(src, PROC_REF(baton_delay), L, user_UID), 2 SECONDS)
+
+ SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK, 33)
+
+ L.lastattacker = user.real_name
+ L.lastattackerckey = user.ckey
+ L.visible_message("[user] has stunned [L] with [src]!",
+ "[L == user ? "You stun yourself" : "[user] has stunned you"] with [src]!")
+ add_attack_logs(user, L, "stunned")
+ playsound(src, 'sound/weapons/egloves.ogg', 50, TRUE, -1)
+ deductcharge(hitcost)
+ return TRUE
+
/obj/item/melee/baton/proc/baton_delay(mob/living/target, user_UID)
REMOVE_TRAIT(target, TRAIT_WAS_BATONNED, user_UID)
diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm
index 004de6fd38b3..de8f623604c0 100644
--- a/code/game/objects/items/weapons/tanks/jetpack.dm
+++ b/code/game/objects/items/weapons/tanks/jetpack.dm
@@ -133,11 +133,6 @@
item_state = "jetpack-captain"
volume = 90
w_class = WEIGHT_CLASS_NORMAL
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //steal objective items are hard to destroy.
-
-/obj/item/tank/jetpack/oxygen/captain/Initialize(mapload)
- . = ..()
- RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(alert_admins_on_destroy))
/obj/item/tank/jetpack/oxygen/security
name = "security jetpack (oxygen)"
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index 9dced8ee5270..79f4f5ce274c 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -1,191 +1,8 @@
-/* Two-handed Weapons
- * Contains:
- * Twohanded
- * Fireaxe
- * Double-Bladed Energy Swords
- * Spears
- * Kidan spear
- * Chainsaw
- * Singularity hammer
- * Mjolnnir
- * Knighthammer
- * Pyro Claws
- * Push Broom
- */
-
-/*##################################################################
-##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ########
-####################################################################*/
-
-//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn
-//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons.
-//It also tidies stuff up elsewhere.
-
-/*
- * Twohanded
- */
-/obj/item/twohanded
- var/wielded = FALSE
- var/force_unwielded = 0
- var/force_wielded = 0
- var/wieldsound = null
- var/unwieldsound = null
- var/sharp_when_wielded = FALSE
-
-/obj/item/twohanded/proc/unwield(mob/living/carbon/user)
- if(!wielded || !user)
- return FALSE
- wielded = FALSE
- force = force_unwielded
- if(sharp_when_wielded)
- set_sharpness(FALSE)
- var/sf = findtext(name," (Wielded)")
- if(sf)
- name = copytext(name, 1, sf)
- else //something wrong
- name = "[initial(name)]"
- update_icon()
- if(user)
- user.update_inv_r_hand()
- user.update_inv_l_hand()
- if(!(flags & ABSTRACT))
- if(isrobot(user))
- to_chat(user, "You free up your module.")
- else
- to_chat(user, "You are now carrying [name] with one hand.")
- if(unwieldsound)
- playsound(loc, unwieldsound, 50, 1)
- var/obj/item/twohanded/offhand/O = user.get_inactive_hand()
- if(O && istype(O))
- O.unwield()
- return TRUE
-
-/obj/item/twohanded/proc/wield(mob/living/carbon/user)
- if(wielded)
- return FALSE
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- if(H.dna.species.is_small)
- to_chat(user, "It's too heavy for you to wield fully.")
- return FALSE
- if(user.get_inactive_hand())
- to_chat(user, "You need your other hand to be empty!")
- return FALSE
- if(!user.has_both_hands())
- to_chat(user, "You need both hands to wield this!")
- return FALSE
- wielded = TRUE
- force = force_wielded
- if(sharp_when_wielded)
- set_sharpness(TRUE)
- name = "[name] (Wielded)"
- update_icon()
- if(user)
- user.update_inv_r_hand()
- user.update_inv_l_hand()
- if(!(flags & ABSTRACT))
- if(isrobot(user))
- to_chat(user, "You dedicate your module to [src].")
- else
- to_chat(user, "You grab [src] with both hands.")
- if(wieldsound)
- playsound(loc, wieldsound, 50, 1)
- var/obj/item/twohanded/offhand/O = new(user) ////Let's reserve his other hand~
- O.name = "[name] - offhand"
- O.desc = "Your second grip on [src]"
- user.put_in_inactive_hand(O)
- return TRUE
-
-/obj/item/twohanded/mob_can_equip(mob/M, slot) //Unwields twohanded items when they're attempted to be equipped to another slot
- if(wielded)
- unwield(M)
- return ..()
-
-/obj/item/twohanded/dropped(mob/user)
- ..()
- //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand
- if(user)
- var/obj/item/twohanded/O = user.get_inactive_hand()
- if(istype(O))
- O.unwield(user)
- return unwield(user)
-
-/obj/item/twohanded/attack_self(mob/user)
- ..()
- if(wielded) //Trying to unwield it
- unwield(user)
- else //Trying to wield it
- wield(user)
-
-
-/obj/item/twohanded/equip_to_best_slot(mob/M)
- if(..())
- unwield(M)
- return
-
-///////////OFFHAND///////////////
-/obj/item/twohanded/offhand
- w_class = WEIGHT_CLASS_HUGE
- icon_state = "offhand"
- name = "offhand"
- flags = ABSTRACT
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
-
-/obj/item/twohanded/offhand/unwield()
- if(!QDELETED(src))
- qdel(src)
-
-/obj/item/twohanded/offhand/wield()
- if(!QDELETED(src))
- qdel(src)
-
-///////////Two hand required objects///////////////
-//This is for objects that require two hands to even pick up
-/obj/item/twohanded/required
- w_class = WEIGHT_CLASS_HUGE
-
-/obj/item/twohanded/required/attack_self()
- return
-
-/obj/item/twohanded/required/mob_can_equip(mob/M, slot)
- if(wielded && !slot_flags)
- to_chat(M, "[src] is too cumbersome to carry with anything but your hands!")
- return FALSE
- return ..()
-
-/obj/item/twohanded/required/attack_hand(mob/user)//Can't even pick it up without both hands empty
- var/obj/item/twohanded/required/H = user.get_inactive_hand()
- if(get_dist(src, user) > 1)
- return FALSE
- if(H != null)
- to_chat(user, "[src] is too cumbersome to carry in one hand!")
- return
- if(loc != user)
- wield(user)
- ..()
-
-/obj/item/twohanded/required/on_give(mob/living/carbon/giver, mob/living/carbon/receiver)
- var/obj/item/twohanded/required/H = receiver.get_inactive_hand()
- if(H != null) //Check if he can wield it
- receiver.drop_item() //Can't wear it so drop it
- to_chat(receiver, "[src] is too cumbersome to carry in one hand!")
- return
- equipped(receiver,receiver.hand ? slot_l_hand : slot_r_hand)
-
-/obj/item/twohanded/required/equipped(mob/user, slot)
- ..()
- if(slot == slot_l_hand || slot == slot_r_hand)
- wield(user)
- if(!wielded) // Drop immediately if we couldn't wield
- user.unEquip(src)
- to_chat(user, "[src] is too cumbersome to carry in one hand!")
- else
- unwield(user)
-
/*
* Fireaxe
*/
-/obj/item/twohanded/fireaxe // DEM AXES MAN, marker -Agouri
+/obj/item/fireaxe // DEM AXES MAN, marker -Agouri
+ base_icon_state = "fireaxe"
icon_state = "fireaxe0"
name = "fire axe"
desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
@@ -194,8 +11,6 @@
sharp = TRUE
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BACK
- force_unwielded = 5
- force_wielded = 24
toolspeed = 0.25
attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
@@ -204,32 +19,39 @@
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 100, ACID = 30)
resistance_flags = FIRE_PROOF
-/obj/item/twohanded/fireaxe/Initialize(mapload)
+ var/force_unwielded = 5
+ var/force_wielded = 24
+
+/obj/item/fireaxe/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_FORCES_OPEN_DOORS_ITEM, ROUNDSTART_TRAIT)
+ AddComponent(/datum/component/two_handed, force_unwielded = force_unwielded, force_wielded = force_wielded, icon_wielded = "[base_icon_state]1")
-/obj/item/twohanded/fireaxe/update_icon_state() //Currently only here to fuck with the on-mob icons.
- icon_state = "fireaxe[wielded]"
+/obj/item/fireaxe/update_icon_state() //Currently only here to fuck with the on-mob icons.
+ icon_state = "[base_icon_state]0"
+ return ..()
-/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/user, proximity)
+/obj/item/fireaxe/afterattack(atom/A, mob/user, proximity)
if(!proximity)
return
- if(wielded) //destroys windows and grilles in one hit
+ if(HAS_TRAIT(src, TRAIT_WIELDED)) //destroys windows and grilles in one hit
if(istype(A, /obj/structure/window) || istype(A, /obj/structure/grille))
var/obj/structure/W = A
W.obj_destruction("fireaxe")
-/obj/item/twohanded/fireaxe/boneaxe // Blatant imitation of the fireaxe, but made out of bone.
+/obj/item/fireaxe/boneaxe // Blatant imitation of the fireaxe, but made out of bone.
icon_state = "bone_axe0"
+ base_icon_state = "bone_axe"
name = "bone axe"
desc = "A large, vicious axe crafted out of several sharpened bone plates and crudely tied together. Made of monsters, by killing monsters, for killing monsters."
force_wielded = 23
needs_permit = TRUE
-/obj/item/twohanded/fireaxe/boneaxe/update_icon_state()
- icon_state = "bone_axe[wielded]"
+/obj/item/fireaxe/boneaxe/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_wielded = force_wielded, icon_wielded = "[base_icon_state]1")
-/obj/item/twohanded/fireaxe/energized
+/obj/item/fireaxe/energized
desc = "Someone with a love for fire axes decided to turn this one into a high-powered energy weapon. Seems excessive."
force_wielded = 35
armour_penetration_flat = 10
@@ -237,26 +59,25 @@
var/charge = 20
var/max_charge = 20
-/obj/item/twohanded/fireaxe/energized/update_icon_state()
- if(wielded)
- icon_state = "fireaxe2"
- else
- icon_state = "fireaxe0"
+/obj/item/fireaxe/energized/Initialize(mapload)
+ . = ..()
+ // only update the new args
+ AddComponent(/datum/component/two_handed, force_wielded = force_wielded, icon_wielded = "[base_icon_state]2")
-/obj/item/twohanded/fireaxe/energized/New()
+/obj/item/fireaxe/energized/New()
..()
START_PROCESSING(SSobj, src)
-/obj/item/twohanded/fireaxe/energized/Destroy()
+/obj/item/fireaxe/energized/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/item/twohanded/fireaxe/energized/process()
+/obj/item/fireaxe/energized/process()
charge = min(charge + 1, max_charge)
-/obj/item/twohanded/fireaxe/energized/attack(mob/M, mob/user)
+/obj/item/fireaxe/energized/attack(mob/M, mob/user)
. = ..()
- if(wielded && charge == max_charge)
+ if(HAS_TRAIT(src, TRAIT_WIELDED) && charge == max_charge)
if(isliving(M))
var/mob/living/target = M
charge = 0
@@ -270,71 +91,74 @@
/*
* Double-Bladed Energy Swords - Cheridan
*/
-/obj/item/twohanded/dualsaber
- var/hacked = FALSE
- var/blade_color
- icon_state = "dualsaber0"
+/obj/item/dualsaber
name = "double-bladed energy sword"
desc = "Handle with care."
+ icon_state = "dualsaber0"
force = 3
throwforce = 5
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
var/w_class_on = WEIGHT_CLASS_BULKY
- force_unwielded = 3
- force_wielded = 34
- wieldsound = 'sound/weapons/saberon.ogg'
- unwieldsound = 'sound/weapons/saberoff.ogg'
- armour_penetration_percentage = 50
+
armour_penetration_flat = 10
origin_tech = "magnets=4;syndicate=5"
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- sharp_when_wielded = TRUE // only sharp when wielded
max_integrity = 200
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 100, ACID = 70)
resistance_flags = FIRE_PROOF
light_power = 2
needs_permit = TRUE
+ var/hacked = FALSE
+ var/blade_color
var/brightness_on = 2
- var/colormap = list(red=LIGHT_COLOR_RED, blue=LIGHT_COLOR_LIGHTBLUE, green=LIGHT_COLOR_GREEN, purple=LIGHT_COLOR_PURPLE, rainbow=LIGHT_COLOR_WHITE)
+ var/colormap = list(red = LIGHT_COLOR_RED, blue = LIGHT_COLOR_LIGHTBLUE, green = LIGHT_COLOR_GREEN, purple = LIGHT_COLOR_PURPLE, rainbow = LIGHT_COLOR_WHITE)
-/obj/item/twohanded/dualsaber/New()
- ..()
+
+ var/force_unwielded = 3
+ var/force_wielded = 34
+ var/wieldsound = 'sound/weapons/saberon.ogg'
+ var/unwieldsound = 'sound/weapons/saberoff.ogg'
+
+/obj/item/dualsaber/Initialize(mapload)
+ . = ..()
if(!blade_color)
blade_color = pick("red", "blue", "green", "purple")
AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.25, _parryable_attack_types = ALL_ATTACK_TYPES, _parry_cooldown = (1 / 3) SECONDS) // 0.3333 seconds of cooldown for 75% uptime
+ AddComponent(/datum/component/two_handed, force_wielded = force_wielded, force_unwielded = force_unwielded, wieldsound = wieldsound, unwieldsound = unwieldsound, wield_callback = CALLBACK(src, PROC_REF(on_wield)), unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), only_sharp_when_wielded = TRUE)
-/obj/item/twohanded/dualsaber/update_icon_state()
- if(wielded)
- icon_state = "dualsaber[blade_color][wielded]"
+/obj/item/dualsaber/update_icon_state()
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
+ icon_state = "dualsaber[blade_color]1"
set_light(brightness_on, l_color=colormap[blade_color])
else
icon_state = "dualsaber0"
set_light(0)
-/obj/item/twohanded/dualsaber/attack(mob/target, mob/living/user)
+/obj/item/dualsaber/attack(mob/target, mob/living/user)
if(HAS_TRAIT(user, TRAIT_HULK))
- to_chat(user, "You grip the blade too hard and accidentally close it!")
- unwield()
- return
+ to_chat(user, "You grip the blade too hard and accidentally drop it!")
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
+ user.unEquip(src)
+ return
..()
- if(HAS_TRAIT(user, TRAIT_CLUMSY) && (wielded) && prob(40) && force)
+ if(HAS_TRAIT(user, TRAIT_CLUMSY) && HAS_TRAIT(src, TRAIT_WIELDED) && prob(40) && force)
to_chat(user, "You twirl around a bit before losing your balance and impaling yourself on [src].")
user.take_organ_damage(20, 25)
return
- if((wielded) && prob(50))
+ if((HAS_TRAIT(src, TRAIT_WIELDED)) && prob(50))
INVOKE_ASYNC(src, PROC_REF(jedi_spin), user)
-/obj/item/twohanded/dualsaber/proc/jedi_spin(mob/living/user)
+/obj/item/dualsaber/proc/jedi_spin(mob/living/user)
for(var/i in list(NORTH, SOUTH, EAST, WEST, EAST, SOUTH, NORTH, SOUTH, EAST, WEST, EAST, SOUTH))
user.setDir(i)
if(i == WEST)
user.SpinAnimation(7, 1)
sleep(1)
-/obj/item/twohanded/dualsaber/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(!wielded)
+/obj/item/dualsaber/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED))
return FALSE
. = ..()
if(!.) // they did not block the attack
@@ -352,45 +176,42 @@
melee_attack_chain(owner, hitby)
return TRUE
-/obj/item/twohanded/dualsaber/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up
- if(wielded)
+/obj/item/dualsaber/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
to_chat(user, "You can't pick up such a dangerous item with your meaty hands without losing fingers, better not to!")
return TRUE
-/obj/item/twohanded/dualsaber/green
+/obj/item/dualsaber/green
blade_color = "green"
-/obj/item/twohanded/dualsaber/red
+/obj/item/dualsaber/red
blade_color = "red"
-/obj/item/twohanded/dualsaber/purple
+/obj/item/dualsaber/purple
blade_color = "purple"
-/obj/item/twohanded/dualsaber/blue
+/obj/item/dualsaber/blue
blade_color = "blue"
-/obj/item/twohanded/dualsaber/unwield()
- . = ..()
- if(!.)
- return
- hitsound = "swing_hit"
- w_class = initial(w_class)
-/obj/item/twohanded/dualsaber/IsReflect()
- if(wielded)
- return TRUE
+/obj/item/dualsaber/proc/on_wield(obj/item/source, mob/living/carbon/user)
+ if(user && HAS_TRAIT(user, TRAIT_HULK))
+ to_chat(user, "You lack the grace to wield this!")
+ return COMPONENT_TWOHANDED_BLOCK_WIELD
-/obj/item/twohanded/dualsaber/wield(mob/living/carbon/M) //Specific wield () hulk checks due to reflection chance for balance issues and switches hitsounds.
- if(HAS_TRAIT(M, TRAIT_HULK))
- to_chat(M, "You lack the grace to wield this!")
- return
- . = ..()
- if(!.)
- return
hitsound = 'sound/weapons/blade1.ogg'
w_class = w_class_on
-/obj/item/twohanded/dualsaber/multitool_act(mob/user, obj/item/I)
+
+/obj/item/dualsaber/proc/on_unwield()
+ hitsound = "swing_hit"
+ w_class = initial(w_class)
+
+/obj/item/dualsaber/IsReflect()
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
+ return TRUE
+
+/obj/item/dualsaber/multitool_act(mob/user, obj/item/I)
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
@@ -403,15 +224,15 @@
to_chat(user, "It's starting to look like a triple rainbow - no, nevermind.")
//spears
-/obj/item/twohanded/spear
+/obj/item/spear
icon_state = "spearglass0"
name = "spear"
desc = "A haphazardly-constructed yet still deadly weapon of ancient design."
force = 10
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BACK
- force_unwielded = 10
- force_wielded = 18
+ var/force_unwielded = 10
+ var/force_wielded = 18
throwforce = 20
throw_speed = 4
armour_penetration_flat = 5
@@ -424,62 +245,83 @@
max_integrity = 200
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 50, ACID = 30)
needs_permit = TRUE
- var/icon_prefix = "spearglass"
+ base_icon_state = "spearglass"
+
+/obj/item/spear/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, \
+ force_wielded = force_wielded, \
+ force_unwielded = force_unwielded, \
+ icon_wielded = "[base_icon_state]1")
+
+/obj/item/spear/update_icon_state()
+ icon_state = "[base_icon_state]0"
-/obj/item/twohanded/spear/update_icon_state()
- icon_state = "[icon_prefix][wielded]"
+/obj/item/spear/proc/add_plasmaglass()
+ // re-add the component to reset the stats
+ force_wielded = 19
+ force_unwielded = 11
+ throwforce = 21
+ base_icon_state = "spearplasma"
+ AddComponent(/datum/component/two_handed, \
+ force_wielded = force_wielded, \
+ force_unwielded = force_unwielded, \
+ icon_wielded = "[base_icon_state]1")
+
+ update_icon()
-/obj/item/twohanded/spear/CheckParts(list/parts_list)
+/obj/item/spear/CheckParts(list/parts_list)
var/obj/item/shard/tip = locate() in parts_list
if(istype(tip, /obj/item/shard/plasma))
- force_wielded = 19
- force_unwielded = 11
- throwforce = 21
- icon_prefix = "spearplasma"
+ // re-add the component to reset the stats
+ add_plasmaglass()
+
update_icon()
qdel(tip)
..()
-/obj/item/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity)
+/obj/item/spear/afterattack(atom/movable/AM, mob/user, proximity)
if(!proximity)
return
if(isturf(AM)) //So you can actually melee with it
return
- if(explosive && wielded)
+ if(explosive && HAS_TRAIT(src, TRAIT_WIELDED))
explosive.forceMove(AM)
explosive.prime()
qdel(src)
-/obj/item/twohanded/spear/throw_impact(atom/target)
+/obj/item/spear/throw_impact(atom/target)
. = ..()
if(explosive)
explosive.prime()
qdel(src)
-/obj/item/twohanded/spear/bonespear //Blatant imitation of spear, but made out of bone. Not valid for explosive modification.
- icon_state = "bone_spear0"
+/obj/item/spear/bonespear //Blatant imitation of spear, but made out of bone. Not valid for explosive modification.
name = "bone spear"
desc = "A haphazardly-constructed yet still deadly weapon. The pinnacle of modern technology."
+ base_icon_state = "bone_spear"
+ icon_state = "bone_spear0"
force = 11
force_unwielded = 11
force_wielded = 20 //I have no idea how to balance
throwforce = 22
armour_penetration_percentage = 15 //Enhanced armor piercing
- icon_prefix = "bone_spear"
+
//GREY TIDE
-/obj/item/twohanded/spear/grey_tide
- icon_state = "spearglass0"
+/obj/item/spear/grey_tide
name = "\improper Grey Tide"
desc = "Recovered from the aftermath of a revolt aboard Defense Outpost Theta Aegis, in which a seemingly endless tide of Assistants caused heavy casualities among Nanotrasen military forces."
+ base_icon_state = "spearglass"
+ icon_state = "spearglass0"
force_unwielded = 15
force_wielded = 25
throwforce = 20
throw_speed = 4
attack_verb = list("gored")
-/obj/item/twohanded/spear/grey_tide/afterattack(atom/movable/AM, mob/living/user, proximity)
+/obj/item/spear/grey_tide/afterattack(atom/movable/AM, mob/living/user, proximity)
..()
if(!proximity)
return
@@ -496,7 +338,7 @@
M.GiveTarget(L)
//Putting heads on spears
-/obj/item/twohanded/spear/attackby(obj/item/I, mob/living/user)
+/obj/item/spear/attackby(obj/item/I, mob/living/user)
if(istype(I, /obj/item/organ/external/head))
if(user.unEquip(src) && user.drop_item())
to_chat(user, "You stick [I] onto the spear and stand it upright on the ground.")
@@ -520,7 +362,7 @@
density = FALSE
anchored = TRUE
var/obj/item/organ/external/head/mounted_head = null
- var/obj/item/twohanded/spear/contained_spear = null
+ var/obj/item/spear/contained_spear = null
/obj/structure/headspear/Destroy()
QDEL_NULL(mounted_head)
@@ -539,13 +381,15 @@
mounted_head = null
qdel(src)
-/obj/item/twohanded/spear/kidan
- icon_state = "kidanspear0"
+/obj/item/spear/kidan
name = "\improper Kidan spear"
desc = "A spear brought over from the Kidan homeworld."
+ base_icon_state = "kindanspear"
+ icon_state = "kidanspear0"
+
// DIY CHAINSAW
-/obj/item/twohanded/required/chainsaw
+/obj/item/chainsaw
name = "chainsaw"
desc = "A versatile power tool. Useful for limbing trees and delimbing humans."
icon_state = "gchainsaw_off"
@@ -564,7 +408,12 @@
actions_types = list(/datum/action/item_action/startchainsaw)
var/on = FALSE
-/obj/item/twohanded/required/chainsaw/attack_self(mob/user)
+/obj/item/chainsaw/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
+
+
+/obj/item/chainsaw/attack_self(mob/user)
on = !on
to_chat(user, "As you pull the starting cord dangling from [src], [on ? "it begins to whirr." : "the chain stops moving."]")
if(on)
@@ -585,23 +434,23 @@
var/datum/action/A = X
A.UpdateButtonIcon()
-/obj/item/twohanded/required/chainsaw/attack_hand(mob/user)
+/obj/item/chainsaw/attack_hand(mob/user)
. = ..()
force = on ? force_on : initial(force)
throwforce = on ? force_on : initial(throwforce)
-/obj/item/twohanded/required/chainsaw/on_give(mob/living/carbon/giver, mob/living/carbon/receiver)
+/obj/item/chainsaw/on_give(mob/living/carbon/giver, mob/living/carbon/receiver)
. = ..()
force = on ? force_on : initial(force)
throwforce = on ? force_on : initial(throwforce)
-/obj/item/twohanded/required/chainsaw/doomslayer
+/obj/item/chainsaw/doomslayer
name = "OOOH BABY"
desc = "VRRRRRRR!!!"
armour_penetration_percentage = 100
force_on = 30
-/obj/item/twohanded/required/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+/obj/item/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(attack_type == PROJECTILE_ATTACK)
owner.visible_message("Ranged attacks just make [owner] angrier!")
playsound(src, pick('sound/weapons/bulletflyby.ogg','sound/weapons/bulletflyby2.ogg','sound/weapons/bulletflyby3.ogg'), 75, 1)
@@ -610,20 +459,17 @@
///CHAINSAW///
-/obj/item/twohanded/chainsaw
- icon_state = "chainsaw0"
+/obj/item/butcher_chainsaw
name = "chainsaw"
desc = "Perfect for felling trees or fellow spacemen."
+ base_icon_state = "chainsaw"
+ icon_state = "chainsaw0"
force = 15
throwforce = 15
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_BULKY // can't fit in backpacks
- force_unwielded = 15 //still pretty robust
- force_wielded = 40 //you'll gouge their eye out! Or a limb...maybe even their entire body!
hitsound = null // Handled in the snowflaked attack proc
- wieldsound = 'sound/weapons/chainsawstart.ogg'
- hitsound = null
armour_penetration_percentage = 50
armour_penetration_flat = 10
origin_tech = "materials=6;syndicate=4"
@@ -631,23 +477,31 @@
sharp = TRUE
flags_2 = RANDOM_BLOCKER_2
-/obj/item/twohanded/chainsaw/update_icon_state()
- if(wielded)
- icon_state = "chainsaw[wielded]"
- else
- icon_state = "chainsaw0"
-
-/obj/item/twohanded/chainsaw/attack(mob/living/target, mob/living/user)
+/obj/item/butcher_chainsaw/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_BUTCHERS_HUMANS, ROUNDSTART_TRAIT)
+ AddComponent(/datum/component/two_handed, \
+ force_wielded = 40, \
+ force_unwielded = force, \
+ icon_wielded = "[base_icon_state]1", \
+ wieldsound = 'sound/weapons/chainsawstart.ogg', \
+ wield_callback = CALLBACK(src, PROC_REF(wield)), \
+ unwield_callback = CALLBACK(src, PROC_REF(unwield)))
+
+/obj/item/butcher_chainsaw/update_icon_state()
+ icon_state = "[base_icon_state]0"
+
+/obj/item/butcher_chainsaw/attack(mob/living/target, mob/living/user)
. = ..()
- if(wielded)
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
playsound(loc, 'sound/weapons/chainsaw.ogg', 100, 1, -1) //incredibly loud; you ain't goin' for stealth with this thing. Credit to Lonemonk of Freesound for this sound.
if(isnull(.)) //necessary check, successful attacks return null, without it target will drop any shields they may have before they get a chance to block
target.KnockDown(8 SECONDS)
-/obj/item/twohanded/chainsaw/afterattack(mob/living/target, mob/living/user, proximity)
+/obj/item/butcher_chainsaw/afterattack(mob/living/target, mob/living/user, proximity)
if(!proximity) //only works on adjacent targets, no telekinetic chainsaws
return
- if(!wielded)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED))
return
if(isrobot(target)) //no buff from attacking robots
return
@@ -656,37 +510,28 @@
if(target.stat != DEAD) //no buff from attacking dead targets
user.apply_status_effect(STATUS_EFFECT_CHAINSAW_SLAYING)
-/obj/item/twohanded/chainsaw/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+/obj/item/butcher_chainsaw/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(attack_type == PROJECTILE_ATTACK)
final_block_chance = 0 //It's a chainsaw, you try blocking bullets with it
else if(owner.has_status_effect(STATUS_EFFECT_CHAINSAW_SLAYING))
final_block_chance = 80 //Need to be ready to ruuuummbllleeee
return ..()
-/obj/item/twohanded/chainsaw/wield() //you can't disarm an active chainsaw, you crazy person.
- . = ..()
- if(.)
- flags |= NODROP
-
-/obj/item/twohanded/chainsaw/unwield()
- . = ..()
- if(.)
- flags &= ~NODROP
+/obj/item/butcher_chainsaw/proc/wield() //you can't disarm an active chainsaw, you crazy person.
+ flags |= NODROP
-/obj/item/twohanded/chainsaw/Initialize(mapload)
- . = ..()
- ADD_TRAIT(src, TRAIT_BUTCHERS_HUMANS, ROUNDSTART_TRAIT)
+/obj/item/butcher_chainsaw/proc/unwield()
+ flags &= ~NODROP
// SINGULOHAMMER
-/obj/item/twohanded/singularityhammer
+/obj/item/singularityhammer
name = "singularity hammer"
desc = "The pinnacle of close combat technology, the hammer harnesses the power of a miniaturized singularity to deal crushing blows."
icon_state = "singulohammer0"
+ base_icon_state = "singulohammer"
flags = CONDUCT
slot_flags = SLOT_BACK
force = 5
- force_unwielded = 5
- force_wielded = 40
throwforce = 15
throw_range = 1
w_class = WEIGHT_CLASS_HUGE
@@ -695,22 +540,26 @@
var/charged = 2
origin_tech = "combat=4;bluespace=4;plasmatech=7"
-/obj/item/twohanded/singularityhammer/New()
+/obj/item/singularityhammer/Initialize(mapload)
..()
+ AddComponent(/datum/component/two_handed, \
+ force_wielded = 40, \
+ force_unwielded = force, \
+ icon_wielded = "[base_icon_state]1")
START_PROCESSING(SSobj, src)
-/obj/item/twohanded/singularityhammer/Destroy()
+/obj/item/singularityhammer/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/item/twohanded/singularityhammer/process()
+/obj/item/singularityhammer/process()
if(charged < 2)
charged++
-/obj/item/twohanded/singularityhammer/update_icon_state() //Currently only here to fuck with the on-mob icons.
- icon_state = "singulohammer[wielded]"
+/obj/item/singularityhammer/update_icon_state() //Currently only here to fuck with the on-mob icons.
+ icon_state = "singulohammer0"
-/obj/item/twohanded/singularityhammer/proc/vortex(turf/pull, mob/wielder)
+/obj/item/singularityhammer/proc/vortex(turf/pull, mob/wielder)
for(var/atom/movable/X in orange(5, pull))
if(X.move_resist == INFINITY)
continue
@@ -731,10 +580,10 @@
step_towards(H, pull)
step_towards(H, pull)
-/obj/item/twohanded/singularityhammer/afterattack(atom/A, mob/user, proximity)
+/obj/item/singularityhammer/afterattack(atom/A, mob/user, proximity)
if(!proximity)
return
- if(wielded)
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
if(charged == 2)
charged = 0
if(isliving(A))
@@ -744,22 +593,27 @@
var/turf/target = get_turf(A)
vortex(target, user)
-/obj/item/twohanded/mjollnir
+/obj/item/mjollnir
name = "Mjolnir"
desc = "A weapon worthy of a god, able to strike with the force of a lightning bolt. It crackles with barely contained energy."
icon_state = "mjollnir0"
+ base_icon_state = "mjollnir"
flags = CONDUCT
slot_flags = SLOT_BACK
force = 5
- force_unwielded = 5
- force_wielded = 25
throwforce = 30
throw_range = 7
w_class = WEIGHT_CLASS_HUGE
- //var/charged = 5
origin_tech = "combat=4;powerstorage=7"
-/obj/item/twohanded/mjollnir/proc/shock(mob/living/target)
+/obj/item/mjollnir/Initialize(mapload)
+ ..()
+ AddComponent(/datum/component/two_handed, \
+ force_wielded = 25, \
+ force_unwielded = force, \
+ icon_wielded = "[base_icon_state]1")
+
+/obj/item/mjollnir/proc/shock(mob/living/target)
do_sparks(5, 1, target.loc)
target.visible_message("[target] was shocked by [src]!",
"You feel a powerful shock course through your body sending you flying!",
@@ -767,56 +621,57 @@
var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
target.throw_at(throw_target, 200, 4)
-/obj/item/twohanded/mjollnir/attack(mob/living/M, mob/user)
+/obj/item/mjollnir/attack(mob/living/M, mob/user)
..()
- if(wielded)
- //if(charged == 5)
- //charged = 0
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
playsound(loc, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
M.Stun(6 SECONDS)
shock(M)
-/obj/item/twohanded/mjollnir/throw_impact(atom/target)
+/obj/item/mjollnir/throw_impact(atom/target)
. = ..()
if(isliving(target))
var/mob/living/L = target
L.Stun(6 SECONDS)
shock(L)
-/obj/item/twohanded/mjollnir/update_icon_state() //Currently only here to fuck with the on-mob icons.
- icon_state = "mjollnir[wielded]"
+/obj/item/mjollnir/update_icon_state() //Currently only here to fuck with the on-mob icons.
+ icon_state = "mjollnir0"
-/obj/item/twohanded/knighthammer
+/obj/item/knighthammer
name = "singuloth knight's hammer"
desc = "A hammer made of sturdy metal with a golden skull adorned with wings on either side of the head.
This weapon causes devastating damage to those it hits due to a power field sustained by a mini-singularity inside of the hammer."
icon_state = "knighthammer0"
+ base_icon_state = "knighthammer"
flags = CONDUCT
slot_flags = SLOT_BACK
force = 5
- force_unwielded = 5
- force_wielded = 30
throwforce = 15
throw_range = 1
w_class = WEIGHT_CLASS_HUGE
var/charged = 5
origin_tech = "combat=5;bluespace=4"
-/obj/item/twohanded/knighthammer/New()
- ..()
+/obj/item/knighthammer/Initialize(mapload)
+ . = ..()
START_PROCESSING(SSobj, src)
+ AddComponent(/datum/component/two_handed, \
+ force_wielded = 30, \
+ force_unwielded = force, \
+ icon_wielded = "[base_icon_state]1")
-/obj/item/twohanded/knighthammer/Destroy()
+/obj/item/knighthammer/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/item/twohanded/knighthammer/process()
+/obj/item/knighthammer/process()
if(charged < 5)
charged++
-/obj/item/twohanded/knighthammer/update_icon_state() //Currently only here to fuck with the on-mob icons.
- icon_state = "knighthammer[wielded]"
+/obj/item/knighthammer/update_icon_state() //Currently only here to fuck with the on-mob icons.
+ icon_state = "knighthammer0"
-/obj/item/twohanded/knighthammer/afterattack(atom/A, mob/user, proximity)
+/obj/item/knighthammer/afterattack(atom/A, mob/user, proximity)
if(!proximity)
return
if(charged == 5)
@@ -830,13 +685,13 @@
var/atom/throw_target = get_edge_target_turf(Z, get_dir(src, get_step_away(Z, src)))
Z.throw_at(throw_target, 200, 4)
playsound(user, 'sound/weapons/marauder.ogg', 50, 1)
- else if(wielded && Z.health < 1)
+ else if(HAS_TRAIT(src, TRAIT_WIELDED) && Z.health < 1)
Z.visible_message("[Z.name] was blown to pieces by the power of [src]!",
"You feel a powerful blow rip you apart!",
"You hear a heavy impact and the sound of ripping flesh!.")
Z.gib()
playsound(user, 'sound/weapons/marauder.ogg', 50, 1)
- if(wielded)
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
if(iswallturf(A))
var/turf/simulated/wall/Z = A
Z.ex_act(2)
@@ -849,13 +704,12 @@
playsound(user, 'sound/weapons/marauder.ogg', 50, 1)
// PYRO CLAWS
-/obj/item/twohanded/required/pyro_claws
+/obj/item/pyro_claws
name = "hardplasma energy claws"
desc = "The power of the sun, in the claws of your hand."
icon_state = "pyro_claws"
flags = ABSTRACT | NODROP | DROPDEL
force = 22
- force_wielded = 22
damtype = BURN
armour_penetration_percentage = 50
sharp = TRUE
@@ -866,16 +720,17 @@
toolspeed = 0.5
var/lifetime = 60 SECONDS
-/obj/item/twohanded/required/pyro_claws/Initialize(mapload)
+/obj/item/pyro_claws/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = ALL_ATTACK_TYPES)
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
-/obj/item/twohanded/required/pyro_claws/Destroy()
+/obj/item/pyro_claws/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/item/twohanded/required/pyro_claws/process()
+/obj/item/pyro_claws/process()
lifetime -= 2 SECONDS
if(lifetime <= 0)
visible_message("[src] slides back into the depths of [loc]'s wrists.")
@@ -885,7 +740,7 @@
if(prob(15))
do_sparks(rand(1,6), 1, loc)
-/obj/item/twohanded/required/pyro_claws/afterattack(atom/target, mob/user, proximity)
+/obj/item/pyro_claws/afterattack(atom/target, mob/user, proximity)
if(!proximity)
return
if(prob(60))
@@ -946,7 +801,7 @@
if(!user.drop_l_hand() || !user.drop_r_hand())
to_chat(user, "[src] are unable to deploy the blades with the items in your hands!")
return
- var/obj/item/W = new /obj/item/twohanded/required/pyro_claws
+ var/obj/item/W = new /obj/item/pyro_claws
user.visible_message("[user] deploys [W] from [user.p_their()] wrists in a shower of sparks!", "You deploy [W] from your wrists!", "You hear the shower of sparks!")
user.put_in_hands(W)
on_cooldown = TRUE
@@ -976,11 +831,12 @@
/// Max number of atoms a broom can sweep at once
#define BROOM_PUSH_LIMIT 20
-/obj/item/twohanded/push_broom
+/obj/item/push_broom
name = "push broom"
desc = "This is my BROOMSTICK! It can be used manually or braced with two hands to sweep items as you move. It has a telescopic handle for compact storage."
icon = 'icons/obj/janitor.dmi'
icon_state = "broom0"
+ base_icon_state = "broom"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
force = 8
@@ -988,32 +844,35 @@
throw_speed = 3
throw_range = 7
w_class = WEIGHT_CLASS_NORMAL
- force_unwielded = 8
- force_wielded = 12
attack_verb = list("swept", "brushed off", "bludgeoned", "whacked")
resistance_flags = FLAMMABLE
-/obj/item/twohanded/push_broom/update_icon_state()
- icon_state = "broom[wielded]"
-
-/obj/item/twohanded/push_broom/wield(mob/user)
+/obj/item/push_broom/Initialize(mapload)
. = ..()
- if(!.)
- return
+ AddComponent(/datum/component/two_handed, \
+ force_wielded = 12, \
+ force_unwielded = force, \
+ icon_wielded = "[base_icon_state]1", \
+ wield_callback = CALLBACK(src, PROC_REF(wield)), \
+ unwield_callback = CALLBACK(src, PROC_REF(unwield)))
+
+/obj/item/push_broom/update_icon_state()
+ icon_state = "broom0"
+
+/obj/item/push_broom/proc/wield(obj/item/source, mob/user)
to_chat(user, "You brace [src] against the ground in a firm sweeping stance.")
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(sweep))
-/obj/item/twohanded/push_broom/unwield(mob/user)
- . = ..()
+/obj/item/push_broom/proc/unwield(obj/item/source, mob/user)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
-/obj/item/twohanded/push_broom/afterattack(atom/A, mob/user, proximity)
+/obj/item/push_broom/afterattack(atom/A, mob/user, proximity)
. = ..()
if(!proximity)
return
sweep(user, A, FALSE)
-/obj/item/twohanded/push_broom/proc/sweep(mob/user, atom/A, moving = TRUE)
+/obj/item/push_broom/proc/sweep(mob/user, atom/A, moving = TRUE)
SIGNAL_HANDLER
var/turf/current_item_loc = moving ? user.loc : (isturf(A) ? A : A.loc)
if(!isturf(current_item_loc))
@@ -1039,47 +898,45 @@
if(trash_amount > 1)
playsound(loc, 'sound/weapons/sweeping.ogg', 70, TRUE, -1)
-/obj/item/twohanded/push_broom/proc/move_into_storage(mob/user, obj/storage, obj/trash)
+/obj/item/push_broom/proc/move_into_storage(mob/user, obj/storage, obj/trash)
trash.forceMove(storage)
storage.update_icon()
to_chat(user, "You sweep the pile of garbage into [storage].")
-/obj/item/twohanded/push_broom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/cart)
+/obj/item/push_broom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/cart)
cart.mybroom = src
cart.put_in_cart(src, user)
-/obj/item/twohanded/push_broom/traitor
+/obj/item/push_broom/traitor
name = "titanium push broom"
desc = "This is my BROOMSTICK! All of the functionality of a normal broom, but at least half again more robust."
attack_verb = list("smashed", "slammed", "whacked", "thwacked", "swept")
force = 10
- force_unwielded = 10
- force_wielded = 25
-/obj/item/twohanded/push_broom/traitor/Initialize(mapload)
+/obj/item/push_broom/traitor/Initialize(mapload)
. = ..()
AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = NON_PROJECTILE_ATTACKS)
+ // parent component handles this
+ AddComponent(/datum/component/two_handed, force_wielded = 25, force_unwielded = force)
-/obj/item/twohanded/push_broom/traitor/examine(mob/user)
+/obj/item/push_broom/traitor/examine(mob/user)
. = ..()
if(isAntag(user))
. += "When wielded, the broom has different effects depending on your intent, similar to a martial art. \
Help intent will sweep foes away from you, disarm intent sweeps their legs from under them, grab intent confuses \
and minorly fatigues them, and harm intent hits them normally."
-/obj/item/twohanded/push_broom/traitor/wield(mob/user)
- . = ..()
+/obj/item/push_broom/traitor/wield(obj/item/source, mob/user)
ADD_TRAIT(user, TRAIT_DEFLECTS_PROJECTILES, "pushbroom")
to_chat(user, "Your sweeping stance allows you to deflect projectiles.")
-/obj/item/twohanded/push_broom/traitor/unwield(mob/user)
- . = ..()
+/obj/item/push_broom/traitor/unwield(obj/item/source, mob/user)
if(HAS_TRAIT_FROM(user, TRAIT_DEFLECTS_PROJECTILES, "pushbroom")) //this check is needed because obj/item/twohanded calls unwield() on drop and you'd get the message even if you weren't wielding it before
REMOVE_TRAIT(user, TRAIT_DEFLECTS_PROJECTILES, "pushbroom")
to_chat(user, "You stop reflecting projectiles.")
-/obj/item/twohanded/push_broom/traitor/attack(mob/target, mob/living/user)
- if(!wielded || !ishuman(target))
+/obj/item/push_broom/traitor/attack(mob/target, mob/living/user)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED) || !ishuman(target))
return ..()
var/mob/living/carbon/human/H = target
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index a2efc7eb617c..4b08bbf605d0 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -125,12 +125,9 @@
/obj/item/wirerod/attackby(obj/item/I, mob/user, params)
..()
if(istype(I, /obj/item/shard))
- var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear
+ var/obj/item/spear/S = new /obj/item/spear
if(istype(I, /obj/item/shard/plasma))
- S.force_wielded = 19
- S.force_unwielded = 11
- S.throwforce = 21
- S.icon_prefix = "spearplasma"
+ S.add_plasmaglass()
S.update_icon()
if(!remove_item_from_storage(user))
user.unEquip(src)
diff --git a/code/game/objects/items/weapons/whetstone.dm b/code/game/objects/items/weapons/whetstone.dm
index dbf3e0773bf9..23df94c2cf5d 100644
--- a/code/game/objects/items/weapons/whetstone.dm
+++ b/code/game/objects/items/weapons/whetstone.dm
@@ -17,20 +17,22 @@
if(used)
to_chat(user, "The whetstone is too worn to use again!")
return
- if(I.force >= max || I.throwforce >= max)//no esword sharpening
- to_chat(user, "[I] is much too powerful to sharpen further!")
- return
if(requires_sharpness && !I.sharp)
to_chat(user, "You can only sharpen items that are already sharp, such as knives!")
return
- if(istype(I, /obj/item/twohanded))//some twohanded items should still be sharpenable, but handle force differently. therefore i need this stuff
- var/obj/item/twohanded/TH = I
- if(TH.force_wielded >= max || TH.force_wielded > initial(TH.force_wielded))
- to_chat(user, "[TH] is much too powerful to sharpen further!")
- return
- TH.force_wielded = clamp(TH.force_wielded + increment, 0, max)//wieldforce is increased since normal force wont stay
- TH.force_unwielded = clamp(TH.force_unwielded + increment, 0, max)
+ var/signal_out = SEND_SIGNAL(I, COMSIG_ITEM_SHARPEN_ACT, increment, max)
+ if((signal_out & COMPONENT_BLOCK_SHARPEN_MAXED) || I.force >= max || I.throwforce >= max) //If the item's components enforce more limits on maximum power from sharpening, we fail
+ to_chat(user, "[I] is much too powerful to sharpen further!")
+ return
+ if(signal_out & COMPONENT_BLOCK_SHARPEN_BLOCKED)
+ to_chat(user, "[I] is not able to be sharpened right now!")
+ return
+ if((signal_out & COMPONENT_BLOCK_SHARPEN_ALREADY) || (I.force > initial(I.force) && !(signal_out & COMPONENT_SHARPEN_APPLIED))) //No sharpening stuff twice
+ to_chat(user, "[I] has already been refined before. It cannot be sharpened further!")
+ return
+ if(!(signal_out & COMPONENT_SHARPEN_APPLIED)) //If the item has a relevant component and COMPONENT_BLOCK_SHARPEN_APPLIED is returned, the item only gets the throw force increase
+ I.force = clamp(I.force + increment, 0, max)
if(istype(I, /obj/item/melee/energy))
var/obj/item/melee/energy/E = I
if(E.force_on > initial(E.force_on) || (E.force > initial(E.force)))
@@ -41,13 +43,9 @@
E.force_on = clamp(E.force_on + increment, 0, max)
E.force_off = clamp(E.force_off + increment, 0, max)
- if(I.force > initial(I.force))
- to_chat(user, "[I] has already been refined before. It cannot be sharpened further!")
- return
user.visible_message("[user] sharpens [I] with [src]!", "You sharpen [I], making it much more deadly than before.")
if(!requires_sharpness)
set_sharpness(TRUE)
- I.force = clamp(I.force + increment, 0, max)
I.throwforce = clamp(I.throwforce + increment, 0, max)
I.name = "[prefix] [I.name]"
playsound(get_turf(src), usesound, 50, 1)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 724c32691318..4b6c23dbddf8 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -71,21 +71,30 @@
var/turf/T = src.loc
if(!T || !istype(T)) return FALSE
- usr.visible_message("[user] starts climbing onto \the [src]!")
climber = user
- if(!do_after(user, 50, target = src))
- climber = null
- return FALSE
+ if(HAS_TRAIT(climber, TRAIT_TABLE_LEAP))
+ user.visible_message("[user] gets ready to vault up onto [src]!")
+ if(!do_after(user, 0.5 SECONDS, target = src))
+ climber = null
+ return FALSE
+ else
+ user.visible_message("[user] starts climbing onto [src]!")
+ if(!do_after(user, 5 SECONDS, target = src))
+ climber = null
+ return FALSE
if(!can_touch(user) || !climbable)
climber = null
return FALSE
var/old_loc = usr.loc
- usr.loc = get_turf(src)
- usr.Moved(old_loc, get_dir(old_loc, usr.loc), FALSE)
+ user.loc = get_turf(src)
+ user.Moved(old_loc, get_dir(old_loc, usr.loc), FALSE)
if(get_turf(user) == get_turf(src))
- usr.visible_message("[user] climbs onto \the [src]!")
+ if(HAS_TRAIT(climber, TRAIT_TABLE_LEAP))
+ user.visible_message("[user] leaps up onto [src]!")
+ else
+ user.visible_message("[user] climbs onto [src]!")
climber = null
return TRUE
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index b400bf6a4ba7..8c439bf0618b 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -52,9 +52,12 @@
canSmoothWith = list(SMOOTH_GROUP_ALIEN_RESIN)
max_integrity = 200
var/resintype = null
+ var/is_alien = TRUE
/obj/structure/alien/resin/Initialize(mapload)
air_update_turf(1)
+ if(!is_alien)
+ return ..()
for(var/obj/structure/alien/weeds/node/W in get_turf(src))
qdel(W)
if(locate(/obj/structure/alien/weeds) in get_turf(src))
@@ -480,6 +483,8 @@
obj_integrity = integrity_failure
else if(status != GROWN)
addtimer(CALLBACK(src, PROC_REF(grow)), rand(MIN_GROWTH_TIME, MAX_GROWTH_TIME))
+ if(status == GROWN)
+ AddComponent(/datum/component/proximity_monitor)
/obj/structure/alien/egg/attack_alien(mob/living/carbon/alien/user)
return attack_hand(user)
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index 6c5794dd56ad..6a2515971191 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -26,6 +26,11 @@ LINEN BINS
var/list/nightmare_messages = list("black")
var/comfort = 0.5
+/obj/item/bedsheet/attack_hand(mob/user)
+ if(isturf(loc) && user.Move_Pulled(src)) // make sure its on the ground first, prevents a speed exploit
+ return
+ return ..()
+
/obj/item/bedsheet/attack_self(mob/user as mob)
user.drop_item()
if(layer == initial(layer))
diff --git a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm
index a672138a6352..36463ea6ac81 100644
--- a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm
+++ b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm
@@ -2,7 +2,7 @@
/obj/structure/closet/fireaxecabinet
name = "fire axe cabinet"
desc = "There is small label that reads \"For Emergency use only\" along with details for safe use of the axe. As if."
- var/obj/item/twohanded/fireaxe/fireaxe
+ var/obj/item/fireaxe/fireaxe
icon_state = "fireaxe_full_0hits"
icon_closed = "fireaxe_full_0hits"
icon_opened = "fireaxe_full_open"
@@ -18,7 +18,7 @@
var/has_axe = null // Use a string over a boolean value to make the sprite names more readable
/obj/structure/closet/fireaxecabinet/populate_contents()
- fireaxe = new/obj/item/twohanded/fireaxe(src)
+ fireaxe = new/obj/item/fireaxe(src)
has_axe = "full"
update_icon(UPDATE_ICON_STATE) // So its initial icon doesn't show it without the fireaxe
@@ -60,10 +60,10 @@
localopened = TRUE
update_icon(UPDATE_ICON_STATE)
return
- if(istype(O, /obj/item/twohanded/fireaxe) && localopened)
+ if(istype(O, /obj/item/fireaxe) && localopened)
if(!fireaxe)
- var/obj/item/twohanded/fireaxe/F = O
- if(F.wielded)
+ var/obj/item/fireaxe/F = O
+ if(HAS_TRAIT(F, TRAIT_WIELDED))
to_chat(user, "Unwield \the [F] first.")
return
if(!user.unEquip(F, FALSE))
diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
index 3c940c1f72c1..62ee9ecaaf7d 100644
--- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm
@@ -83,8 +83,8 @@
new /obj/item/caution(src)
new /obj/item/caution(src)
new /obj/item/caution(src)
- new /obj/item/twohanded/push_broom(src)
- new /obj/item/twohanded/push_broom(src)
+ new /obj/item/push_broom(src)
+ new /obj/item/push_broom(src)
new /obj/item/storage/bag/trash(src)
new /obj/item/storage/bag/trash(src)
new /obj/item/lightreplacer(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering_lockers.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering_lockers.dm
index 3e852b4bdba8..976c8649039b 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/engineering_lockers.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering_lockers.dm
@@ -13,7 +13,7 @@
new /obj/item/areaeditor/blueprints/ce(src)
new /obj/item/storage/box/permits(src)
new /obj/item/storage/bag/garment/chief_engineer(src)
- new /obj/item/tank/jetpack/suit(src)
+ new /obj/item/mod/module/jetpack/advanced(src)
new /obj/item/cartridge/ce(src)
new /obj/item/radio/headset/heads/ce(src)
new /obj/item/storage/toolbox/mechanical(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm
index 4d665f3815a1..af6d1ab9c599 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/medical_lockers.dm
@@ -104,11 +104,19 @@
new /obj/item/reagent_containers/food/pill/methamphetamine(src)
new /obj/item/reagent_containers/food/pill/methamphetamine(src)
new /obj/item/reagent_containers/food/pill/methamphetamine(src)
+ new /obj/item/reagent_containers/food/pill/happy_psych(src)
+ new /obj/item/reagent_containers/food/pill/happy_psych(src)
+ new /obj/item/reagent_containers/food/pill/happy_psych(src)
new /obj/item/reagent_containers/food/pill/patch/nicotine(src)
new /obj/item/reagent_containers/food/pill/patch/nicotine(src)
new /obj/item/reagent_containers/food/pill/patch/nicotine(src)
new /obj/item/reagent_containers/food/pill/hydrocodone(src)
new /obj/item/reagent_containers/food/pill/hydrocodone(src)
+ new /obj/item/reagent_containers/food/pill/mannitol(src)
+ new /obj/item/reagent_containers/food/pill/mannitol(src)
+ new /obj/item/reagent_containers/food/pill/mannitol(src)
+ new /obj/item/reagent_containers/food/pill/mannitol(src)
+ new /obj/item/reagent_containers/food/pill/mannitol(src)
/obj/structure/closet/secure_closet/psychiatrist
name = "psychiatrist's locker"
@@ -189,24 +197,26 @@
/obj/structure/closet/secure_closet/paramedic
name = "paramedic EVA gear"
- desc = "A locker with a Paramedic EVA suit."
+ desc = "A locker with a Rescue MODsuit."
icon_state = "med"
open_door_sprite = "med_door"
icon_opened = "med_open"
req_access = list(ACCESS_PARAMEDIC)
/obj/structure/closet/secure_closet/paramedic/populate_contents()
- new /obj/item/clothing/suit/space/eva/paramedic(src)
- new /obj/item/clothing/head/helmet/space/eva/paramedic(src)
- new /obj/item/sensor_device(src)
+ new /obj/item/radio/headset/headset_med/para(src)
+ new /obj/item/mod/control/pre_equipped/rescue(src)
new /obj/item/key/ambulance(src)
- new /obj/item/pinpointer/crew(src)
new /obj/item/handheld_defibrillator(src)
+ new /obj/item/storage/bag/garment/paramedic(src)
new /obj/item/defibrillator/loaded(src)
new /obj/item/storage/belt/medical(src)
new /obj/item/storage/firstaid/adv(src)
- new /obj/item/clothing/glasses/hud/health/sunglasses(src)
new /obj/item/storage/toolbox/emergency(src)
+ new /obj/item/fulton_core(src)
+ new /obj/item/extraction_pack(src)
+ new /obj/item/gps/mining(src)
+ new /obj/item/gun/energy/plasmacutter(src)
/obj/structure/closet/secure_closet/reagents
name = "chemical storage closet"
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
index 7270208d4101..7cf47a65ec15 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm
@@ -43,6 +43,10 @@
to_chat(user, "It appears to be broken.")
return
+ if(istype(W, /obj/item/card/id/guest))
+ to_chat(user, "Invalid identification card.")
+ return
+
var/obj/item/card/id/I = W
if(!I || !I.registered_name)
return
@@ -67,4 +71,4 @@
registered_name = I.registered_name
desc = "Owned by [I.registered_name]."
else
- to_chat(user, "Access Denied")
+ to_chat(user, "Access denied.")
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
index 6e6cb4a3f533..eb44726f4493 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
@@ -25,6 +25,7 @@
open_door_sprite = "white_secure_door"
/obj/structure/closet/secure_closet/roboticist/populate_contents()
+ new /obj/item/mod/core/standard(src)
new /obj/item/storage/backpack(src)
new /obj/item/storage/backpack(src)
new /obj/item/storage/backpack/satchel_norm(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security_lockers.dm b/code/game/objects/structures/crates_lockers/closets/secure/security_lockers.dm
index 876ac1c98519..2818b17c6a44 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security_lockers.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security_lockers.dm
@@ -119,7 +119,12 @@
icon_opened = "hop_open"
/obj/structure/closet/secure_closet/blueshield/populate_contents()
+ new /obj/item/storage/backpack/blueshield(src)
+ new /obj/item/storage/backpack/satchel_blueshield(src)
new /obj/item/storage/briefcase(src)
+ new /obj/item/storage/backpack/duffel/blueshield(src)
+ new /obj/item/radio/headset/heads/blueshield/alt(src)
+ new /obj/item/cartridge/hos(src)
new /obj/item/storage/firstaid/adv(src)
new /obj/item/pinpointer/crew(src)
new /obj/item/flashlight/seclite(src)
@@ -137,6 +142,8 @@
/obj/structure/closet/secure_closet/ntrep/populate_contents()
new /obj/item/book/manual/wiki/faxes(src)
new /obj/item/storage/briefcase(src)
+ new /obj/item/radio/headset/heads/ntrep (src)
+ new /obj/item/cartridge/supervisor(src)
new /obj/item/paicard(src)
new /obj/item/flash(src)
new /obj/item/storage/box/tapes(src)
@@ -257,6 +264,7 @@
new /obj/item/storage/secure/briefcase(src)
new /obj/item/flash(src)
new /obj/item/radio/headset/heads/magistrate(src)
+ new /obj/item/cartridge/supervisor(src)
new /obj/item/gavelblock(src)
new /obj/item/gavelhammer(src)
new /obj/item/clothing/accessory/medal/legal(src)
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index 46134ecda00c..7aa4cf708df5 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -116,7 +116,7 @@
to_chat(user, "You must wait until the door has stopped moving.")
return
- if(istype(W, /obj/item/gun/energy/plasmacutter) || istype(W, /obj/item/pickaxe/drill/diamonddrill) || istype(W, /obj/item/pickaxe/drill/jackhammer) || istype(W, /obj/item/melee/energy/blade) || istype(W, /obj/item/twohanded/required/pyro_claws))
+ if(istype(W, /obj/item/gun/energy/plasmacutter) || istype(W, /obj/item/pickaxe/drill/diamonddrill) || istype(W, /obj/item/pickaxe/drill/jackhammer) || istype(W, /obj/item/melee/energy/blade) || istype(W, /obj/item/pyro_claws))
dismantle(user, TRUE)
/obj/structure/falsewall/attack_animal(mob/living/simple_animal/M)
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index a1f13c82e8b1..01c0099884bc 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -254,7 +254,7 @@
icon_state = "fullgrass_[rand(1, 3)]"
-/obj/item/twohanded/required/kirbyplants
+/obj/item/kirbyplants
name = "potted plant"
icon = 'icons/obj/flora/plants.dmi'
icon_state = "plant-1"
@@ -262,59 +262,59 @@
layer = ABOVE_MOB_LAYER
w_class = WEIGHT_CLASS_HUGE
force = 10
- force_wielded = 10
throwforce = 13
throw_speed = 2
throw_range = 4
/// Method to track plant overlay on mob for later removal
var/mutable_appearance/mob_overlay
-/obj/item/twohanded/required/kirbyplants/Initialize(mapload)
+/obj/item/kirbyplants/Initialize(mapload)
. = ..()
icon_state = "plant-[rand(1,35)]"
if(prob(1))
icon_state = "plant-36"
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
-/obj/item/twohanded/required/kirbyplants/Destroy()
+/obj/item/kirbyplants/Destroy()
if(iscarbon(loc))
unhide_user(loc)
QDEL_NULL(mob_overlay)
return ..()
-/obj/item/twohanded/required/kirbyplants/equipped(mob/living/carbon/user)
+/obj/item/kirbyplants/equipped(mob/living/carbon/user)
. = ..()
- if(wielded)
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
hide_user(user)
return
unhide_user(user)
/// User has decided to hold a plant, apply stealth.
-/obj/item/twohanded/required/kirbyplants/proc/hide_user(mob/living/carbon/user)
+/obj/item/kirbyplants/proc/hide_user(mob/living/carbon/user)
RegisterSignal(user, COMSIG_CARBON_REGENERATE_ICONS, PROC_REF(reapply_hide))
mob_overlay = mutable_appearance(icon, icon_state, user.layer, user.plane, 255, appearance_flags = RESET_COLOR | RESET_TRANSFORM | RESET_ALPHA | KEEP_APART)
user.add_overlay(mob_overlay)
user.alpha = 0
/// User has either dropped the plant, or plant is being destroyed, restore user to normal.
-/obj/item/twohanded/required/kirbyplants/proc/unhide_user(mob/living/carbon/user)
+/obj/item/kirbyplants/proc/unhide_user(mob/living/carbon/user)
UnregisterSignal(user, COMSIG_CARBON_REGENERATE_ICONS)
user.cut_overlay(mob_overlay)
user.alpha = initial(user.alpha)
QDEL_NULL(mob_overlay)
/// Icon operation has occured, time to make sure we're showing a plant again if we need to be.
-/obj/item/twohanded/required/kirbyplants/proc/reapply_hide(mob/living/carbon/user)
+/obj/item/kirbyplants/proc/reapply_hide(mob/living/carbon/user)
SIGNAL_HANDLER
// Reset the state of the user
unhide_user(user)
hide_user(user)
-/obj/item/twohanded/required/kirbyplants/dropped(mob/living/carbon/user)
+/obj/item/kirbyplants/dropped(mob/living/carbon/user)
..()
unhide_user(user)
-/obj/item/twohanded/required/kirbyplants/dead
+/obj/item/kirbyplants/dead
name = "\improper RD's potted plant"
desc = "A gift from the botanical staff, presented after the RD's reassignment. There's a tag on it that says \"Y'all come back now, y'hear?\"\nIt doesn't look very healthy..."
icon_state = "plant-dead"
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 9cdec853759b..43545ae2fb7a 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -64,7 +64,7 @@
refundMetal(metalUsed)
qdel(src)
- else if(istype(W, /obj/item/twohanded/required/pyro_claws))
+ else if(istype(W, /obj/item/pyro_claws))
playsound(loc, W.usesound, 100, 1)
to_chat(user, "You melt the girder!")
refundMetal(metalUsed)
@@ -80,6 +80,9 @@
if (locate(/obj/structure/falsewall) in loc.contents)
to_chat(user, "There is already a false wall present!")
return
+ if(islava(loc))
+ to_chat(user, "You can't do that while [src] is in lava!")
+ return
if(istype(W, /obj/item/stack/sheet/runed_metal))
to_chat(user, "You can't seem to make the metal bend.")
return
diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm
index 210027d7c729..fdc0065efac1 100644
--- a/code/game/objects/structures/janicart.dm
+++ b/code/game/objects/structures/janicart.dm
@@ -15,7 +15,7 @@
var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite
var/obj/item/storage/bag/trash/mybag = null
var/obj/item/mop/mymop = null
- var/obj/item/twohanded/push_broom/mybroom = null
+ var/obj/item/push_broom/mybroom = null
var/obj/item/reagent_containers/spray/cleaner/myspray = null
var/obj/item/lightreplacer/myreplacer = null
var/signs = 0
@@ -59,9 +59,9 @@
m.janicart_insert(user, src)
else
to_chat(user, fail_msg)
- else if(istype(I, /obj/item/twohanded/push_broom))
+ else if(istype(I, /obj/item/push_broom))
if(!mybroom)
- var/obj/item/twohanded/push_broom/B = I
+ var/obj/item/push_broom/B = I
B.janicart_insert(user, src)
else
to_chat(user, fail_msg)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index a5271b3ac33f..3ff5c2635353 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -17,6 +17,17 @@
density = FALSE
climbable = FALSE
+/obj/structure/railing/cap //aestetic "end" for railing
+ icon_state = "railing_cap"
+ density = FALSE
+ climbable = FALSE
+
+/obj/structure/railing/cap/normal
+ icon_state = "railing_cap_normal"
+
+/obj/structure/railing/cap/reversed
+ icon_state = "railing_cap_reversed"
+
/obj/structure/railing/attackby(obj/item/I, mob/living/user, params)
..()
add_fingerprint(user)
@@ -74,6 +85,15 @@
/obj/structure/railing/corner/CheckExit()
return TRUE
+/obj/structure/railing/cap/CanPass()
+ return TRUE
+
+/obj/structure/railing/cap/CanPathfindPass(obj/item/card/id/ID, to_dir, caller, no_id = FALSE)
+ return TRUE
+
+/obj/structure/railing/cap/CheckExit()
+ return TRUE
+
/obj/structure/railing/CanPass(atom/movable/mover, turf/target)
if(istype(mover) && mover.checkpass(PASSFENCE))
return TRUE
diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm
index f2774e001f7a..fc6f3a711547 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -99,6 +99,10 @@
/obj/structure/sign/double/map/right
icon_state = "map-right"
+/obj/structure/sign/double/map/attack_hand(mob/user)
+ if(user.client)
+ user.client.webmap()
+
/obj/structure/sign/securearea
name = "\improper SECURE AREA"
desc = "A warning sign which reads 'SECURE AREA'"
@@ -183,7 +187,7 @@
/obj/structure/sign/greencross
name = "medbay"
- desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here.'"
+ desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here."
icon_state = "greencross"
/obj/structure/sign/goldenplaque
diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
index 77759371f807..46e8a9e7a74b 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
@@ -30,6 +30,11 @@
. = ..()
. += "Click dragging someone to a bed will buckle them in. Functions just like a chair except you can walk over them."
+/obj/structure/bed/attack_hand(mob/user)
+ if(user.Move_Pulled(src))
+ return
+ return ..()
+
/obj/structure/bed/psych
name = "psych bed"
desc = "For prime comfort during psychiatric evaluations."
diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
index 809fcefbce00..fca6cac9d4f4 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
@@ -84,6 +84,11 @@
return
. = ..()
+/obj/structure/chair/attack_hand(mob/user)
+ if(user.Move_Pulled(src))
+ return
+ return ..()
+
/obj/structure/chair/attack_tk(mob/user as mob)
if(!anchored || has_buckled_mobs() || !isturf(user.loc))
..()
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 32d85501b869..47d0edc2be03 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -36,6 +36,8 @@
var/framestackamount = 2
var/deconstruction_ready = TRUE
var/flipped = FALSE
+ ///If this is true, the table will have items slide off it when placed.
+ var/slippery = FALSE
/// The minimum level of environment_smash required for simple animals to be able to one-shot this.
var/minimum_env_smash = ENVIRONMENT_SMASH_WALLS
@@ -225,7 +227,7 @@
if(isrobot(user))
return
- if(user.a_intent != INTENT_HARM && !(I.flags & ABSTRACT))
+ if(user.a_intent == INTENT_HELP && !(I.flags & ABSTRACT))
if(user.drop_item())
I.Move(loc)
var/list/click_params = params2list(params)
@@ -235,7 +237,12 @@
//Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
I.pixel_x = clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
I.pixel_y = clamp(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2)
- item_placed(I)
+ if(slippery)
+ step_away(I, user)
+ visible_message("[I] slips right off [src]!")
+ playsound(loc, 'sound/misc/slip.ogg', 50, 1, -1)
+ else //Don't want slippery moving tables to have the item attached to them if it slides off.
+ item_placed(I)
else
return ..()
@@ -402,6 +409,14 @@
return 1
+/obj/structure/table/water_act(volume, temperature, source, method)
+ . = ..()
+ if(HAS_TRAIT(src, TRAIT_OIL_SLICKED))
+ slippery = initial(slippery)
+ remove_atom_colour(FIXED_COLOUR_PRIORITY)
+ REMOVE_TRAIT(src, TRAIT_OIL_SLICKED, "potion")
+
+
/*
* Glass Tables
*/
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 38d062285702..152056d23bb1 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -85,7 +85,7 @@
set_opacity(FALSE)
else
old_color = color
- animate(src, color = "#222222", time = 0.5 SECONDS)
+ animate(src, color = "#2A3A45", time = 0.5 SECONDS) //SS220 EDIT - ORIGINAL: #222222
set_opacity(TRUE)
/obj/structure/window/narsie_act()
diff --git a/code/game/sound.dm b/code/game/sound.dm
index c7478164b41d..f3106b2ae99b 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -81,14 +81,14 @@ falloff_distance - Distance at which falloff begins. Sound is at peak volume (in
if(distance <= maxdistance)
M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb)
-/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE)
+/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE, wait = FALSE) // SS220 EDIT
if(!client || !can_hear())
return
if(!S)
S = sound(get_sfx(soundin))
- S.wait = 0 //No queue
+ S.wait = wait // SS220 EDIT
S.channel = channel || SSsounds.random_available_channel()
S.volume = vol
diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm
index 22c39f9a8c6f..a6ff8e22069e 100644
--- a/code/game/turfs/simulated.dm
+++ b/code/game/turfs/simulated.dm
@@ -3,8 +3,6 @@
name = "station"
var/wet = 0
var/image/wet_overlay = null
-
- var/thermite = 0
oxygen = MOLES_O2STANDARD
nitrogen = MOLES_N2STANDARD
var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed
diff --git a/code/game/turfs/simulated/floor/asteroid_floors.dm b/code/game/turfs/simulated/floor/asteroid_floors.dm
index 292fa3824828..aef389caa426 100644
--- a/code/game/turfs/simulated/floor/asteroid_floors.dm
+++ b/code/game/turfs/simulated/floor/asteroid_floors.dm
@@ -148,7 +148,7 @@
nitrogen = 23
temperature = 300
planetary_atmos = TRUE
- baseturf = /turf/simulated/floor/plating/lava/smooth/lava_land_surface
+ baseturf = /turf/simulated/floor/plating/lava/smooth/mapping_lava
/turf/simulated/floor/plating/asteroid/airless
temperature = TCMB
@@ -198,7 +198,9 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
if (!megafauna_spawn_list)
megafauna_spawn_list = GLOB.megafauna_spawn_list
if (!flora_spawn_list)
- flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
+ flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2, /obj/structure/flora/ash/cap_shroom = 2, /obj/structure/flora/ash/stem_shroom = 2, /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2, /obj/structure/flora/ash/rock/style_random = 1)
+ if(SSmapping.cave_theme == BLOCKED_BURROWS)
+ flora_spawn_list += list(/obj/structure/flora/ash/rock/style_random = 3) //Let us see how this goes
. = ..()
if(!has_data)
produce_tunnel_from_data()
@@ -216,7 +218,18 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
backward_cave_dir = angle2dir(dir2angle(forward_cave_dir) + 180)
/turf/simulated/floor/plating/asteroid/airless/cave/proc/produce_tunnel_from_data(tunnel_length, excluded_dir = -1)
- get_cave_data(tunnel_length, excluded_dir)
+ if(!tunnel_length)//This is a sub cave do not overide repeat do not overide
+ get_cave_data(tunnel_length, excluded_dir)
+ switch(SSmapping.cave_theme)
+ if(BLOCKED_BURROWS) //Longer on average
+ get_cave_data(rand(40, 60), excluded_dir)
+ if(CLASSIC_CAVES) //Classic
+ get_cave_data(tunnel_length, excluded_dir)
+ if(DEADLY_DEEPROCK) //Smaller into large rooms with more mobs.
+ get_cave_data(rand(20, 40), excluded_dir)
+ if(prob(25)) //Less caves due to big openings. This may lead to fauna inside 1x1 rooms. We'll call that a suprise mechanic
+ SpawnFloor(src, 75) //now with extra suprise
+ return
// Make our tunnels
make_tunnel(forward_cave_dir)
if(going_backwards)
@@ -233,7 +246,7 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
break
var/list/L = list(45)
- if(ISODD(dir2angle(dir))) // We're going at an angle and we want thick angled tunnels.
+ if(ISODD(dir2angle(dir)) && (!SSmapping.cave_theme == BLOCKED_BURROWS || prob(33))) // We're going at an angle and we want thick angled tunnels.
L += -45
// Expand the edges of our tunnel
@@ -250,7 +263,13 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
if(istype(tunnel))
// Small chance to have forks in our tunnel; otherwise dig our tunnel.
- if(i > 3 && prob(20))
+ var/caveprob = 20
+ switch(SSmapping.cave_theme)
+ if(BLOCKED_BURROWS) //Longer on average
+ caveprob = 30 //More splitting
+ if(DEADLY_DEEPROCK) //Smaller into large rooms with more mobs.
+ caveprob = 10 //Less splitting
+ if(i > 3 && prob(caveprob))
var/turf/simulated/floor/plating/asteroid/airless/cave/C = tunnel.ChangeTurf(data_having_type, FALSE, TRUE)
C.going_backwards = FALSE
C.produce_tunnel_from_data(rand(10, 15), dir)
@@ -262,11 +281,47 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
// Chance to change our direction left or right.
if(i > 2 && prob(33))
// We can't go a full loop though
- next_angle = -next_angle
+ if(!SSmapping.cave_theme == BLOCKED_BURROWS || prob(60))
+ next_angle = -next_angle
setDir(angle2dir(dir2angle(dir) )+ next_angle)
+ if(length -2 == i && !has_data) //Branches will not make this
+ SpawnRoom(tunnel)
+
+/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnRoom(turf/T)
+ switch(SSmapping.cave_theme)
+ if(DEADLY_DEEPROCK)
+ var/tempradius = rand(10, 15)
+ var/probmodifer = 43 * tempradius //Yes this is a magic number, it is a magic number that works well.
+ for(var/turf/NT in circlerangeturfs(T, tempradius))
+ var/distance = (max(get_dist(T, NT), 1)) //Get dist throws -1 if same turf
+ if(prob(min(probmodifer / distance, 100)))
+ if(ismineralturf(NT) || istype(NT, /turf/simulated/floor/plating/asteroid)) //No spawning on lava / other ruins
+ SpawnFloor(NT, 50) //Room has higher probabilty.
+ if(prob(25))
+ tempradius = round(tempradius / 3)
+ var/turf/oasis_lake = pickweight(list(/turf/simulated/floor/plating/lava/smooth/lava_land_surface = 4, /turf/simulated/floor/plating/lava/smooth/lava_land_surface/plasma = 4, /turf/simulated/floor/chasm/straight_down/lava_land_surface = 4, /turf/simulated/floor/plating/lava/smooth/mapping_lava = 6, /turf/simulated/floor/beach/away/water = 1, /turf/simulated/floor/plating/asteroid = 1))
+ if(oasis_lake == /turf/simulated/floor/plating/asteroid)
+ new /obj/effect/spawner/oasisrock(T, tempradius)
+ for(var/turf/oasis in circlerangeturfs(T, tempradius))
+ oasis.ChangeTurf(oasis_lake, ignore_air = TRUE)
+
+/obj/effect/spawner/oasisrock
+ name = "Oasis rock spawner"
+
+/obj/effect/spawner/oasisrock/Initialize(mapload, radius)
+ . = ..()
+ addtimer(CALLBACK(src, PROC_REF(make_rock), radius), 5 SECONDS)
-/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnFloor(turf/T)
+/obj/effect/spawner/oasisrock/proc/make_rock(radius)
+ for(var/turf/oasis in circlerangeturfs(get_turf(src), radius))
+ oasis.ChangeTurf(/turf/simulated/mineral/random/high_chance/volcanic, ignore_air = TRUE)
+ var/list/valid_turfs = RANGE_EDGE_TURFS(radius + 2, src)
+ for(var/mob/M in range(src, radius)) //We don't want mobs inside the ore rock
+ M.forceMove(pick(valid_turfs))
+ qdel(src)
+
+/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnFloor(turf/T, monsterprob = 30)
for(var/S in RANGE_TURFS(1, src))
var/turf/NT = S
if(!NT || isspaceturf(NT) || istype(NT.loc, /area/mine/explored) || istype(NT.loc, /area/lavaland/surface/outdoors/explored))
@@ -276,11 +331,11 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
return
SpawnFlora(T)
- SpawnMonster(T)
+ SpawnMonster(T, monsterprob)
T.ChangeTurf(turf_type, FALSE, FALSE, TRUE)
-/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnMonster(turf/T)
- if(prob(30))
+/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnMonster(turf/T, monsterprob = 30)
+ if(prob(monsterprob))
if(istype(loc, /area/mine/explored) || !istype(loc, /area/lavaland/surface/outdoors/unexplored))
return
var/randumb = pickweight(mob_spawn_list)
@@ -291,11 +346,17 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
randumb = maybe_boss
else //this is not danger, don't spawn a boss, spawn something else
randumb = pickweight(mob_spawn_list)
-
- for(var/thing in urange(12, T)) //prevents mob clumps
+ var/scanrange = 12
+ var/megafaunarange = 7
+ switch(SSmapping.cave_theme)
+ if(DEADLY_DEEPROCK)
+ if(prob(50) && monsterprob > 30)
+ scanrange = rand(4, 7)
+ megafaunarange = scanrange
+ for(var/thing in urange(scanrange, T)) //prevents mob clumps
if(!ishostile(thing) && !istype(thing, /obj/structure/spawner))
continue
- if((ismegafauna(randumb) || ismegafauna(thing)) && get_dist(T, thing) <= 7)
+ if((ismegafauna(randumb) || ismegafauna(thing)) && get_dist(T, thing) <= megafaunarange)
return //if there's a megafauna within standard view don't spawn anything at all
if(ispath(randumb, /mob/living/simple_animal/hostile/asteroid) || istype(thing, /mob/living/simple_animal/hostile/asteroid))
return //if the random is a standard mob, avoid spawning if there's another one within 12 tiles
@@ -314,7 +375,11 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me
#undef SPAWN_BUBBLEGUM
/turf/simulated/floor/plating/asteroid/airless/cave/proc/SpawnFlora(turf/T)
- if(prob(12))
+ var/floraprob = 12
+ switch(SSmapping.cave_theme)
+ if(BLOCKED_BURROWS)
+ floraprob = 30 //Lots of folliage, lots of blockage
+ if(prob(floraprob))
if(istype(loc, /area/mine/explored) || istype(loc, /area/lavaland/surface/outdoors/explored))
return
var/randumb = pickweight(flora_spawn_list)
diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm
index ab1d0711071d..20cd70e4ef6e 100644
--- a/code/game/turfs/simulated/floor/chasm.dm
+++ b/code/game/turfs/simulated/floor/chasm.dm
@@ -9,6 +9,8 @@
smoothing_groups = list(SMOOTH_GROUP_TURF, SMOOTH_GROUP_TURF_CHASM)
canSmoothWith = list(SMOOTH_GROUP_TURF_CHASM)
density = TRUE //This will prevent hostile mobs from pathing into chasms, while the canpass override will still let it function like an open turf
+ layer = 1.7
+ intact = 0
var/static/list/falling_atoms = list() //Atoms currently falling into the chasm
var/static/list/forbidden_types = typecacheof(list(
/obj/singularity,
@@ -24,7 +26,10 @@
/obj/effect/collapse,
/obj/effect/particle_effect/ion_trails,
/obj/effect/abstract,
- /obj/effect/ebeam
+ /obj/effect/ebeam,
+ /obj/effect/spawner,
+ /obj/structure/railing,
+ /obj/machinery/atmospherics/pipe/simple
))
var/drop_x = 1
var/drop_y = 1
@@ -65,7 +70,7 @@
qdel(L)
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
to_chat(user, "You build a floor.")
- ChangeTurf(/turf/simulated/floor/plating)
+ ChangeTurf(/turf/simulated/floor/plating, keep_icon = FALSE)
else
to_chat(user, "You need one floor tile to build a floor!")
else
@@ -140,9 +145,9 @@
nitrogen = 23
temperature = 300
planetary_atmos = TRUE
- baseturf = /turf/simulated/floor/chasm/straight_down/lava_land_surface
- light_range = 1.9 //slightly less range than lava
- light_power = 0.65 //less bright, too
+ baseturf = /turf/simulated/floor/chasm/straight_down/lava_land_surface //Chasms should not turn into lava
+ light_range = 2
+ light_power = 0.75
light_color = LIGHT_COLOR_LAVA //let's just say you're falling into lava, that makes sense right
/turf/simulated/floor/chasm/straight_down/lava_land_surface/drop(atom/movable/AM)
diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm
index 66d8fb8dc0ad..e38727c402e4 100644
--- a/code/game/turfs/simulated/floor/lava.dm
+++ b/code/game/turfs/simulated/floor/lava.dm
@@ -56,7 +56,7 @@
return FALSE
/turf/simulated/floor/plating/lava/proc/burn_stuff(AM)
- . = 0
+ . = FALSE
if(find_safeties())
return FALSE
@@ -71,7 +71,7 @@
continue
if((O.resistance_flags & (LAVA_PROOF|INDESTRUCTIBLE)) || O.throwing)
continue
- . = 1
+ . = TRUE
if((O.resistance_flags & (ON_FIRE)))
continue
if(!(O.resistance_flags & FLAMMABLE))
@@ -83,7 +83,7 @@
O.fire_act(10000, 1000)
else if(isliving(thing))
- . = 1
+ . = TRUE
var/mob/living/L = thing
if(L.flying)
continue //YOU'RE FLYING OVER IT
@@ -142,3 +142,109 @@
/turf/simulated/floor/plating/lava/smooth/airless
temperature = TCMB
+
+/turf/simulated/floor/plating/lava/smooth/lava_land_surface/plasma
+ name = "liquid plasma"
+ desc = "A flowing stream of chilled liquid plasma. You probably shouldn't get in."
+ icon = 'icons/turf/floors/liquidplasma.dmi'
+ icon_state = "liquidplasma-255"
+ base_icon_state = "liquidplasma"
+ baseturf = /turf/simulated/floor/plating/lava/smooth/lava_land_surface/plasma
+
+ light_range = 3
+ light_power = 0.75
+ light_color = LIGHT_COLOR_PINK
+
+/turf/simulated/floor/plating/lava/smooth/lava_land_surface/plasma/examine(mob/user)
+ . = ..()
+ . += "Some liquid plasma could probably be scooped up with a container."
+
+/turf/simulated/floor/plating/lava/smooth/lava_land_surface/plasma/attackby(obj/item/I, mob/user, params)
+ if(!I.is_open_container())
+ return ..()
+ if(!I.reagents.add_reagent("plasma", 10))
+ to_chat(user, "[I] is full.")
+ return
+ to_chat(user, "You scoop out some plasma from the [src] using [I].")
+
+/turf/simulated/floor/plating/lava/smooth/lava_land_surface/plasma/burn_stuff(AM)
+ . = FALSE
+ if(find_safeties())
+ return FALSE
+
+ var/thing_to_check = src
+ if(AM)
+ thing_to_check = list(AM)
+ for(var/thing in thing_to_check)
+ if(isobj(thing))
+ var/obj/O = thing
+ if(!O.simulated)
+ continue
+ if((O.resistance_flags & (LAVA_PROOF|INDESTRUCTIBLE)) || O.throwing)
+ continue
+ . = TRUE
+ if((O.resistance_flags & ON_FIRE))
+ continue
+ if(!(O.resistance_flags & FLAMMABLE))
+ O.resistance_flags |= FLAMMABLE //Even fireproof things burn up in lava
+ if(O.resistance_flags & FIRE_PROOF)
+ O.resistance_flags &= ~FIRE_PROOF
+ if(O.armor.getRating(FIRE) > 50) //obj with 100% fire armor still get slowly burned away.
+ O.armor = O.armor.setRating(fire_value = 50)
+ O.fire_act(10000, 1000)
+
+ if(!isliving(thing))
+ continue
+ . = TRUE
+ var/mob/living/burn_living = thing
+ if(burn_living.flying)
+ continue //YOU'RE FLYING OVER IT
+ var/buckle_check = burn_living.buckling
+ if(!buckle_check)
+ buckle_check = burn_living.buckled
+ if(isobj(buckle_check))
+ var/obj/O = buckle_check
+ if(O.resistance_flags & LAVA_PROOF)
+ continue
+ else if(isliving(buckle_check))
+ var/mob/living/live = buckle_check
+ if("lava" in live.weather_immunities)
+ continue
+ if("lava" in burn_living.weather_immunities)
+ continue
+ burn_living.adjustFireLoss(2)
+ if(QDELETED(burn_living))
+ return
+ burn_living.adjust_fire_stacks(20) //dipping into a stream of plasma would probably make you more flammable than usual
+ burn_living.IgniteMob()
+ burn_living.adjust_bodytemperature(-rand(50, 65)) //its cold, man
+ if(!ishuman(burn_living) || prob(65))
+ return
+ var/mob/living/carbon/human/burn_human = burn_living
+ var/datum/species/burn_species = burn_human.dna.species
+ if(istype(burn_species, /datum/species/plasmaman) || istype(burn_species, /datum/species/machine)) //ignore plasmamen/robotic species.
+ return
+
+ burn_human.adjustToxLoss(15) //Cold mutagen is bad for you, more at 11.
+ burn_human.adjustFireLoss(15)
+
+/turf/simulated/floor/plating/lava/smooth/mapping_lava
+ name = "Adaptive lava / chasm / plasma"
+ icon_state = "mappinglava"
+ base_icon_state = "mappinglava"
+ baseturf = /turf/simulated/floor/plating/lava/smooth/mapping_lava
+ temperature = 300
+ oxygen = 14
+ nitrogen = 23
+ planetary_atmos = TRUE
+
+
+/turf/simulated/floor/plating/lava/smooth/mapping_lava/Initialize(mapload)
+ . = ..()
+ return INITIALIZE_HINT_LATELOAD //Lateload is needed, otherwise atmos does not setup right on the turf roundstart, leading it to be vacume. This is bad.
+
+/turf/simulated/floor/plating/lava/smooth/mapping_lava/LateInitialize()
+ . = ..()
+ ChangeTurf(SSmapping.lavaland_theme)
+
+
diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm
index b6ece4cae740..4e2697eced55 100644
--- a/code/game/turfs/simulated/minerals.dm
+++ b/code/game/turfs/simulated/minerals.dm
@@ -251,7 +251,7 @@
/turf/simulated/mineral/random/high_chance/volcanic
environment_type = "basalt"
turf_type = /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface
- baseturf = /turf/simulated/floor/plating/lava/smooth/lava_land_surface
+ baseturf = /turf/simulated/floor/plating/lava/smooth/mapping_lava
oxygen = 14
nitrogen = 23
temperature = 300
@@ -271,7 +271,7 @@
/turf/simulated/mineral/random/volcanic
environment_type = "basalt"
turf_type = /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface
- baseturf = /turf/simulated/floor/plating/lava/smooth/lava_land_surface
+ baseturf = /turf/simulated/floor/plating/lava/smooth/mapping_lava
oxygen = 14
nitrogen = 23
temperature = 300
@@ -293,7 +293,7 @@
/turf/simulated/mineral/random/labormineral/volcanic
environment_type = "basalt"
turf_type = /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface
- baseturf = /turf/simulated/floor/plating/lava/smooth/lava_land_surface
+ baseturf = /turf/simulated/floor/plating/lava/smooth/mapping_lava
oxygen = 14
nitrogen = 23
temperature = 300
@@ -467,7 +467,7 @@
/turf/simulated/mineral/volcanic/lava_land_surface
environment_type = "basalt"
turf_type = /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface
- baseturf = /turf/simulated/floor/plating/lava/smooth/lava_land_surface
+ baseturf = /turf/simulated/floor/plating/lava/smooth/mapping_lava
defer_change = 1
//gibtonite state defines
@@ -559,7 +559,7 @@
stage = GIBTONITE_DETONATE
explosion(bombturf,1,2,5, adminlog = 0)
if(stage == GIBTONITE_STABLE) //Gibtonite deposit is now benign and extractable. Depending on how close you were to it blowing up before defusing, you get better quality ore.
- var/obj/item/twohanded/required/gibtonite/G = new(src)
+ var/obj/item/gibtonite/G = new(src)
if(det_time <= 0)
G.quality = 3
G.icon_state = "Gibtonite ore 3"
diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm
index 6547a8922db2..883ea8f4a4e2 100644
--- a/code/game/turfs/simulated/river.dm
+++ b/code/game/turfs/simulated/river.dm
@@ -4,7 +4,7 @@
#define RANDOM_LOWER_X 50
#define RANDOM_LOWER_Y 50
-/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/simulated/floor/plating/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y)
+/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/simulated/floor/plating/lava/smooth/mapping_lava, whitelist_area = /area/lavaland/surface/outdoors, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y, prob = 25, prob_loss = 11)
var/list/river_nodes = list()
var/num_spawned = 0
var/list/possible_locs = block(locate(min_x, min_y, target_z), locate(max_x, max_y, target_z))
@@ -46,6 +46,9 @@
cur_dir = get_dir(cur_turf, target_turf)
cur_turf = get_step(cur_turf, cur_dir)
+ if(cur_turf == null) //This might be the fuck up. Kill the loop if this happens
+ message_admins("Encountered a null turf in river loop.")
+ break
var/area/new_area = get_area(cur_turf)
if(!istype(new_area, whitelist_area) || (cur_turf.flags & NO_LAVA_GEN)) //Rivers will skip ruins
detouring = 0
@@ -54,7 +57,9 @@
continue
else
var/turf/river_turf = cur_turf.ChangeTurf(turf_type, ignore_air = TRUE)
- river_turf.Spread(25, 11, whitelist_area)
+ if(prob(1))
+ new /obj/effect/spawner/bridge(river_turf)
+ river_turf.Spread(prob, prob_loss, whitelist_area)
for(var/WP in river_nodes)
qdel(WP)
@@ -91,6 +96,8 @@
var/turf/T = F
if(!istype(T, logged_turf_type) && T.ChangeTurf(type, ignore_air = TRUE) && prob(probability))
T.Spread(probability - prob_loss, prob_loss, whitelisted_area)
+ if(prob(1))
+ new /obj/effect/spawner/bridge(T)
for(var/F in diagonal_turfs) //diagonal turfs only sometimes change, but will always spread if changed
var/turf/T = F
@@ -99,6 +106,8 @@
else if(ismineralturf(T))
var/turf/simulated/mineral/M = T
M.ChangeTurf(M.turf_type, ignore_air = TRUE)
+ if(prob(1))
+ new /obj/effect/spawner/bridge(M)
#undef RANDOM_UPPER_X
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index 87e79f5261ea..5923238257d5 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -18,6 +18,7 @@
var/damage_cap = 100 //Wall will break down to girders if damage reaches this point
var/global/damage_overlays[8]
+ var/melting = FALSE //TRUE if wall is currently being melted with thermite
opacity = TRUE
density = TRUE
@@ -246,9 +247,8 @@
ChangeTurf(/turf/simulated/floor)
/turf/simulated/wall/proc/thermitemelt(mob/user as mob, speed)
- var/wait = 20 SECONDS
- if(speed)
- wait = speed
+ if(melting)
+ return
if(istype(sheet_type, /obj/item/stack/sheet/mineral/diamond))
return
@@ -261,16 +261,39 @@
O.density = TRUE
O.layer = 5
- src.ChangeTurf(/turf/simulated/floor/plating)
-
- var/turf/simulated/floor/F = src
- F.burn_tile()
- F.icon_state = "plating"
if(user)
to_chat(user, "The thermite starts melting through the wall.")
- spawn(wait)
+ if(speed)
+ melting = TRUE
+ while(speed > 0)
+ playsound(src, 'sound/items/welder.ogg', 100, TRUE)
+ speed = max(0, speed - 1 SECONDS)
+ sleep(1)
+ burn_down()
+ var/turf/simulated/floor/F = src
+ F.burn_tile()
+ F.icon_state = "plating"
if(O) qdel(O)
+ return
+
+ melting = TRUE
+ while(reagents.get_reagent_amount("thermite") > 0)
+ reagents.remove_reagent("thermite", 5)
+ if(damage_cap - damage <= 30)
+ burn_down()
+
+ var/turf/simulated/floor/F = src
+ F.burn_tile()
+ F.icon_state = "plating"
+ break
+ take_damage(30)
+ playsound(src, 'sound/items/welder.ogg', 100, TRUE)
+ sleep(1 SECONDS)
+ if(iswallturf(src))
+ melting = FALSE
+ if(O)
+ qdel(O)
return
//Interactions
@@ -345,7 +368,7 @@
/turf/simulated/wall/welder_act(mob/user, obj/item/I)
. = TRUE
- if(thermite && I.use_tool(src, user, volume = I.tool_volume))
+ if(reagents?.get_reagent_amount("thermite") && I.use_tool(src, user, volume = I.tool_volume))
thermitemelt(user)
return
if(rotting)
@@ -430,7 +453,7 @@
visible_message("[user] disintegrates [src]!","You hear the grinding of metal.")
return TRUE
- else if(istype(I, /obj/item/twohanded/required/pyro_claws))
+ else if(istype(I, /obj/item/pyro_claws))
to_chat(user, "You begin to melt the wall.")
if(do_after(user, isdiamond ? 60 * I.toolspeed : 30 * I.toolspeed, target = src)) // claws has 0.5 toolspeed, so 3/1.5 seconds
diff --git a/code/game/turfs/simulated/walls_mineral.dm b/code/game/turfs/simulated/walls_mineral.dm
index 77e19908964b..5f7bb041f3a2 100644
--- a/code/game/turfs/simulated/walls_mineral.dm
+++ b/code/game/turfs/simulated/walls_mineral.dm
@@ -174,7 +174,7 @@
/turf/simulated/wall/mineral/wood/attackby(obj/item/W, mob/user)
if(W.sharp && W.force)
var/duration = (48 / W.force) * 2 //In seconds, for now.
- if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/twohanded/fireaxe))
+ if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/fireaxe))
duration /= 4 //Much better with hatchets and axes.
if(do_after(user, duration * 10, target = src)) //Into deciseconds.
dismantle_wall(FALSE, FALSE)
diff --git a/code/game/turfs/simulated/walls_reinforced.dm b/code/game/turfs/simulated/walls_reinforced.dm
index d7e90ed0232c..3bf8b040979b 100644
--- a/code/game/turfs/simulated/walls_reinforced.dm
+++ b/code/game/turfs/simulated/walls_reinforced.dm
@@ -73,7 +73,7 @@
return ..()
/turf/simulated/wall/r_wall/welder_act(mob/user, obj/item/I)
- if(thermite && I.use_tool(src, user, volume = I.tool_volume))
+ if(reagents?.get_reagent_amount("thermite") && I.use_tool(src, user, volume = I.tool_volume))
thermitemelt(user)
return TRUE
if(!(d_state in list(RWALL_COVER, RWALL_SUPPORT_RODS, RWALL_CUT_COVER)))
@@ -202,7 +202,7 @@
dismantle_wall()
return TRUE
- if(istype(I, /obj/item/twohanded/required/pyro_claws))
+ if(istype(I, /obj/item/pyro_claws))
to_chat(user, "You begin to melt the wall...")
if(do_after(user, 50 * I.toolspeed, target = src)) // claws has 0.5 toolspeed, so 2.5 seconds
to_chat(user, "Your [I] melt the reinforced plating.")
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 042102b13a94..0563340fdf7e 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -445,8 +445,8 @@
return
C.place_turf(src, user)
return TRUE
- else if(istype(I, /obj/item/twohanded/rcl))
- var/obj/item/twohanded/rcl/R = I
+ else if(istype(I, /obj/item/rcl))
+ var/obj/item/rcl/R = I
if(R.loaded)
for(var/obj/structure/cable/LC in src)
if(LC.d1 == 0 || LC.d2 == 0)
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index 5ef95703e25b..c1190098c33d 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -75,6 +75,10 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
for(var/client/C in GLOB.clients)
if(C.prefs.toggles & PREFTOGGLE_CHAT_OOC)
+ // SS220 MODPACK REPLACE START
+ #ifdef MODPACK_CHAT_BADGES
+ var/display_name = get_ooc_badged_name()
+ #else
var/display_name = key
if(prefs.unlock_content)
@@ -87,6 +91,8 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00")
var/icon/donator = icon('icons/ooc_tag_16x.png')
display_name = "[bicon(donator)][display_name]"
+ #endif
+ // SS220 MODPACK REPLACE END
if(holder)
if(holder.fakekey)
if(C.holder && C.holder.rights & R_ADMIN)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index c1c181791c34..667f07d1e6fe 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -69,7 +69,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
/client/proc/ccbdb_lookup_ckey,
/client/proc/view_instances,
/client/proc/start_vote,
- /client/proc/toggle_mctabs,
/client/proc/ping_all_admins,
/client/proc/show_watchlist
))
@@ -117,7 +116,8 @@ GLOBAL_LIST_INIT(admin_verbs_spawn, list(
/datum/admins/proc/spawn_atom, /*allows us to spawn instances*/
/client/proc/respawn_character,
/client/proc/admin_deserialize,
- /client/proc/create_crate
+ /client/proc/create_crate,
+ /client/proc/json_spawn_menu
))
GLOBAL_LIST_INIT(admin_verbs_server, list(
/client/proc/reload_admins,
@@ -179,7 +179,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/timer_log,
/client/proc/debug_timers,
/client/proc/force_verb_bypass,
- /client/proc/show_gc_queues
+ /client/proc/show_gc_queues,
+ /client/proc/toggle_mctabs
))
GLOBAL_LIST_INIT(admin_verbs_possess, list(
/proc/possess,
@@ -294,6 +295,7 @@ GLOBAL_LIST_INIT(admin_verbs_maintainer, list(
verbs += /client/proc/debug_variables /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
verbs += /client/proc/ss_breakdown
verbs += /client/proc/show_gc_queues
+ verbs += /client/proc/toggle_mctabs
spawn(1) // This setting exposes the profiler for people with R_VIEWRUNTIMES. They must still have it set in cfg/admin.txt
control_freak = 0
diff --git a/code/modules/admin/misc_admin_procs.dm b/code/modules/admin/misc_admin_procs.dm
index 8b34ac0d4884..c92640414f49 100644
--- a/code/modules/admin/misc_admin_procs.dm
+++ b/code/modules/admin/misc_admin_procs.dm
@@ -699,6 +699,9 @@ GLOBAL_VAR_INIT(nologevent, 0)
if(!check_rights(R_SPAWN))
return
+ if(!object)
+ return
+
var/list/types = typesof(/atom)
var/list/matches = new()
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 7ba2806125f5..9598439ac768 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -3360,6 +3360,73 @@
var/mob/about_to_be_banned = locateUID(href_list["adminalert"])
usr.client.cmd_admin_alert_message(about_to_be_banned)
+ else if(href_list["spawnjsondatum"])
+ // Get the name and JSON to spawn
+ var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname, slotjson FROM json_datum_saves WHERE ckey=:ckey AND id=:id", list(
+ "ckey" = usr.ckey,
+ "id" = href_list["spawnjsondatum"]
+ ))
+ if(!dbq.warn_execute())
+ qdel(dbq)
+ return
+
+ var/slot_name = null
+ var/slot_json = null
+
+ // Read it
+ while(dbq.NextRow())
+ slot_name = dbq.item[1]
+ slot_json = dbq.item[2]
+
+ qdel(dbq)
+
+ // Double check
+ var/spawn_choice = alert(usr, "Are you sure you wish to spawn '[slot_name]' at your current location?", "Warning", "Yes", "No")
+ if(spawn_choice != "Yes")
+ return
+
+ // Log this for gods sake
+ message_admins("[key_name_admin(usr)] spawned an atom from a JSON DB save.")
+ log_admin("[key_name(usr)] spawned an atom from a JSON DB save, JSON Text: [slot_json]")
+ json_to_object(slot_json, get_turf(usr))
+
+ else if(href_list["deletejsondatum"])
+ // Get the name
+ var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname FROM json_datum_saves WHERE ckey=:ckey AND id=:id", list(
+ "ckey" = usr.ckey,
+ "id" = href_list["deletejsondatum"]
+ ))
+ if(!dbq.warn_execute())
+ qdel(dbq)
+ return
+
+ var/slot_name = null
+
+ // Read it
+ while(dbq.NextRow())
+ slot_name = dbq.item[1]
+
+ qdel(dbq)
+
+ // Double check
+ var/delete_choice = alert(usr, "Are you sure you wish to delete '[slot_name]'? This cannot be undone!", "Warning", "Yes", "No")
+ if(delete_choice != "Yes")
+ return
+
+ var/datum/db_query/dbq2 = SSdbcore.NewQuery("DELETE FROM json_datum_saves WHERE ckey=:ckey AND id=:id", list(
+ "ckey" = usr.ckey,
+ "id" = href_list["deletejsondatum"]
+ ))
+
+ if(!dbq2.warn_execute())
+ qdel(dbq2)
+ return
+
+ qdel(dbq2)
+ owner.json_spawn_menu() // Refresh their menu
+ to_chat(usr, "Slot [slot_name]
deleted.")
+
+
/client/proc/create_eventmob_for(mob/living/carbon/human/H, killthem = 0)
if(!check_rights(R_EVENT))
return
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index dca007238dca..b53e871063b9 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -443,7 +443,6 @@
new_args[++new_args.len] = SDQL_expression(source, arg)
if(object == world) // Global proc.
- procname = "/proc/[procname]"
return (WrapAdminProcCall(GLOBAL_PROC, procname, new_args))
return (WrapAdminProcCall(object, procname, new_args))
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index aadb16d6776c..8aeaf7728c71 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -137,7 +137,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
//adv proc call this, ya nerds
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target == GLOBAL_PROC)
- return call(procname)(arglist(arguments))
+ return call("/proc/[procname]")(arglist(arguments))
else if(target != world)
return call(target, procname)(arglist(arguments))
else
diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
index 19cbb91ea083..a6523856ec02 100644
--- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm
+++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
@@ -156,7 +156,7 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0)
var/obj/item/implant/uplink/sit/U = new /obj/item/implant/uplink/sit(src)
U.implant(src)
if (flag_mgmt)
- U.hidden_uplink.uses = 500
+ U.hidden_uplink.uses = 2500
else
U.hidden_uplink.uses = num_tc
// Dust
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 6610e732a4da..1ca45f266e8b 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -485,7 +485,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.mind.add_antag_datum(/datum/antagonist/traitor)
//Add aliens.
else
- SSjobs.AssignRank(new_character, new_character.mind.assigned_role, FALSE, FALSE)
+ SSjobs.AssignRank(new_character, new_character.mind.assigned_role, FALSE)
SSjobs.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them.
//Announces the character on all the systems, based on the record.
diff --git a/code/modules/admin/verbs/serialization.dm b/code/modules/admin/verbs/serialization.dm
index 863d15287ce5..c60a072b6f72 100644
--- a/code/modules/admin/verbs/serialization.dm
+++ b/code/modules/admin/verbs/serialization.dm
@@ -11,7 +11,75 @@
return
var/atom/movable/AM = holder.marked_datum
- to_chat(src, json_encode(AM.serialize()))
+
+ var/json_data = json_encode(AM.serialize())
+
+ var/choice = alert(usr, "Would you like to store this on your PC or server side?", "Storage Location", "PC", "Server", "Cancel")
+ if(!choice || choice == "Cancel")
+ return
+
+ if(choice == "PC")
+ to_chat(src, json_data)
+
+ if(choice == "Server")
+ // Right, get their slot names
+ var/list/slots = list("--NEW--")
+ var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname FROM json_datum_saves WHERE ckey=:ckey", list("ckey" = usr.ckey))
+ if(!dbq.warn_execute())
+ qdel(dbq)
+ return
+
+ while(dbq.NextRow())
+ slots += dbq.item[1]
+
+ qdel(dbq)
+
+ var/slot_choice = input(usr, "Select a slot to update, or create a new one.", "Slot Selection") as null|anything in slots
+
+ if(!slot_choice)
+ return
+
+ if(slot_choice == "--NEW--")
+ // New slot, save to DB
+ var/chosen_slot_name = input(usr, "Name your slot", "Slot name") as null|text
+ if(!chosen_slot_name || length(chosen_slot_name) == 0)
+ return
+
+ // Sanitize the name
+ var/clean_name = sanitize(copytext(chosen_slot_name, 1, 32)) // 32 chars is your max
+
+ // And save
+ var/datum/db_query/dbq2 = SSdbcore.NewQuery("INSERT INTO json_datum_saves (ckey, slotname, slotjson) VALUES(:ckey, :slotname, :slotjson)", list(
+ "ckey" = usr.ckey,
+ "slotname" = clean_name,
+ "slotjson" = json_data
+ ))
+ if(!dbq2.warn_execute())
+ qdel(dbq2)
+ return
+
+ qdel(dbq2)
+ to_chat(usr, "Successfully saved [clean_name]
. You can spawn it from Debug > Spawn Saved JSON Datum
.")
+
+ else
+ // Existing slot, warn first
+ var/confirmation = alert(usr, "Are you sure you want to update '[slot_choice]'? This cannot be undone!", "You sure?", "Yes", "No")
+ if(confirmation != "Yes")
+ return
+
+ // Now update
+ var/datum/db_query/dbq2 = SSdbcore.NewQuery("UPDATE json_datum_saves SET slotjson=:slotjson WHERE slotname=:slotname AND ckey=:ckey", list(
+ "slotjson" = json_data,
+ "ckey" = usr.ckey,
+ "slotname" = slot_choice
+ ))
+ if(!dbq2.warn_execute())
+ qdel(dbq2)
+ return
+
+ qdel(dbq2)
+ to_chat(usr, "Successfully updated [slot_choice]
. You can spawn it from Debug > Spawn Saved JSON Datum
.")
+
/client/proc/admin_deserialize()
set name = "Deserialize JSON datum"
@@ -26,3 +94,41 @@
json_to_object(json_text, get_turf(usr))
message_admins("[key_name_admin(usr)] spawned an atom from a custom JSON object.")
log_admin("[key_name(usr)] spawned an atom from a custom JSON object, JSON Text: [json_text]")
+
+
+/client/proc/json_spawn_menu()
+ set name = "Spawn Saved JSON Datum"
+ set desc = "Spawns a JSON datums saved server side"
+ set category = "Debug"
+
+ // This needs a holder to function
+ if(!check_rights(R_SPAWN) || !holder)
+ return
+
+ // Right, get their slot names
+ var/list/slots = list()
+ var/datum/db_query/dbq = SSdbcore.NewQuery("SELECT slotname, id FROM json_datum_saves WHERE ckey=:ckey", list("ckey" = usr.ckey))
+ if(!dbq.warn_execute())
+ qdel(dbq)
+ return
+
+ while(dbq.NextRow())
+ slots[dbq.item[1]] += dbq.item[2]
+ qdel(dbq)
+
+
+ var/datum/browser/popup = new(usr, "jsonspawnmenu", "JSON Spawn Menu", 400, 300)
+
+ // Cache this to reduce proc jumps
+ var/holder_uid = holder.UID()
+
+ var/list/rows = list()
+ rows += "Slot | Actions |
"
+ for(var/slotname in slots)
+ rows += "[slotname] | Spawn Delete |
"
+
+ rows += "
"
+
+ popup.set_content(rows.Join(""))
+ popup.open(FALSE)
+
diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm
index 70ee60d25107..2bdb7cd0a7f4 100644
--- a/code/modules/antagonists/changeling/changeling_power.dm
+++ b/code/modules/antagonists/changeling/changeling_power.dm
@@ -50,7 +50,7 @@
cling = null
return ..()
-/datum/action/changeling/Trigger()
+/datum/action/changeling/Trigger(left_click)
try_to_sting(owner)
/datum/action/changeling/proc/try_to_sting(mob/user, mob/target)
diff --git a/code/modules/antagonists/changeling/datum_changeling.dm b/code/modules/antagonists/changeling/datum_changeling.dm
index 2ca7e16979c0..1c4150f9d196 100644
--- a/code/modules/antagonists/changeling/datum_changeling.dm
+++ b/code/modules/antagonists/changeling/datum_changeling.dm
@@ -36,8 +36,6 @@
var/sting_range = 2
/// The changeling's identifier when speaking in the hivemind, i.e. "Mr. Delta 123".
var/changelingID = "Changeling"
- /// The current amount of genetic damage incurred from power use.
- var/genetic_damage = 0
/// If the changeling is in the process of absorbing someone.
var/is_absorbing = FALSE
/// The amount of points available to purchase changeling abilities.
@@ -185,10 +183,8 @@
var/mob/living/carbon/human/H = owner.current
if(H.stat == DEAD)
chem_charges = clamp(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown, chem_storage * 0.5)
- genetic_damage = directional_bounded_sum(genetic_damage, -1, LING_DEAD_GENETIC_DAMAGE_HEAL_CAP, 0)
- else // Not dead? no chem/genetic_damage caps.
+ else
chem_charges = clamp(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown, chem_storage)
- genetic_damage = max(0, genetic_damage - 1)
update_chem_charges_ui(H)
/datum/antagonist/changeling/proc/update_chem_charges_ui(mob/living/carbon/human/H = owner.current)
diff --git a/code/modules/antagonists/changeling/powers/lesserform.dm b/code/modules/antagonists/changeling/powers/lesserform.dm
index 5dc6e4c82521..f61eb1d9bea6 100644
--- a/code/modules/antagonists/changeling/powers/lesserform.dm
+++ b/code/modules/antagonists/changeling/powers/lesserform.dm
@@ -20,7 +20,6 @@
return FALSE
H.visible_message("[H] transforms!")
- cling.genetic_damage = 30
to_chat(H, "Our genes cry out!")
H.monkeyize()
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index b6eabd3a5d75..c8a3dab32399 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -509,7 +509,7 @@
// snowflake checks my beloved
// this will become tooltype checks I swear
- if(!istype(I, /obj/item/circular_saw) && !istype(I, /obj/item/twohanded/required/chainsaw) && !istype(I, /obj/item/twohanded/chainsaw))
+ if(!istype(I, /obj/item/circular_saw) && !istype(I, /obj/item/chainsaw) && !istype(I, /obj/item/butcher_chainsaw))
return
user.visible_message(
diff --git a/code/modules/antagonists/changeling/powers/strained_muscles.dm b/code/modules/antagonists/changeling/powers/strained_muscles.dm
index 2e50c0f5d25b..782602c983a2 100644
--- a/code/modules/antagonists/changeling/powers/strained_muscles.dm
+++ b/code/modules/antagonists/changeling/powers/strained_muscles.dm
@@ -4,7 +4,7 @@
/datum/action/changeling/strained_muscles
name = "Strained Muscles"
desc = "We evolve the ability to reduce the acid buildup in our muscles, allowing us to move much faster."
- helptext = "The strain will use up our chemicals faster over time, and is not sustainable. Causes slight genetic damage to our genome. Can not be used in lesser form."
+ helptext = "The strain will use up our chemicals faster over time, and is not sustainable. Can not be used in lesser form."
button_icon_state = "strained_muscles"
chemical_cost = 0
dna_cost = 1
diff --git a/code/modules/antagonists/changeling/powers/summon_spiders.dm b/code/modules/antagonists/changeling/powers/summon_spiders.dm
index 51a58ed83619..aa589955ae63 100644
--- a/code/modules/antagonists/changeling/powers/summon_spiders.dm
+++ b/code/modules/antagonists/changeling/powers/summon_spiders.dm
@@ -1,18 +1,197 @@
+#define IDLE_AGGRESSIVE 0
+#define FOLLOW_AGGRESSIVE 1
+#define FOLLOW_RETALIATE 2
+#define IDLE_RETALIATE 3
+
/datum/action/changeling/spiders
name = "Spread Infestation"
- desc = "Our form divides, creating arachnids which will grow into deadly beasts."
- helptext = "The spiders are thoughtless creatures, and may attack their creators when fully grown. Requires at least 5 stored DNA."
+ desc = "Our form divides, creating an aggressive arachnid which will regard us as a friend. Costs 45 chemicals."
+ helptext = "The spiders are thoughtless creatures, but will not attack their creators. Requires at least 7 stored DNA. Their orders can be changed via remote hivemind (Alt+Shift click)."
button_icon_state = "spread_infestation"
chemical_cost = 45
- dna_cost = 1
- req_dna = 5
+ dna_cost = 2
+ req_dna = 7
+ /// This var keeps track of the changeling's spider count
+ var/spider_counter = 0
+ /// Checks if changeling is already spawning a spider
+ var/is_operating = FALSE
power_type = CHANGELING_PURCHASABLE_POWER
-//Makes some spiderlings. Good for setting traps and causing general trouble.
+/// Makes a spider. Good for setting traps and combat.
/datum/action/changeling/spiders/sting_action(mob/user)
- for(var/i in 1 to 2)
- var/obj/structure/spider/spiderling/S = new(user.loc)
- S.grow_as = /mob/living/simple_animal/hostile/poison/giant_spider/hunter
+ if(is_operating) // To stop spawning multiple at once
+ return FALSE
+ is_operating = TRUE
+ if(spider_counter >= 3)
+ to_chat(user, "We cannot sustain more than three spiders!")
+ is_operating = FALSE
+ return FALSE
+ user.visible_message("[user] begins vomiting an arachnid!")
+ if(do_after(user, 4 SECONDS, FALSE, target = user)) // Takes 4 seconds to spawn a spider
+ spider_counter++
+ user.visible_message("[user] vomits up an arachnid!")
+ var/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/S = new(user.loc)
+ S.owner_UID = user.UID()
+ S.faction |= list("spiders", "\ref[owner]") // Makes them friendly only to the owner & other spiders
+ SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
+ is_operating = FALSE
+ return TRUE
+ is_operating = FALSE
+ return FALSE
+
+/// Child of giant_spider because this should do everything the spider does and more
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider
+ /// References to the owner changeling
+ var/mob/owner_UID
+ /// Handles the spider's behavior
+ var/current_order = IDLE_AGGRESSIVE
+ var/list/enemies = list()
+ sentience_type = SENTIENCE_OTHER
+ venom_per_bite = 3
+ speak_chance = 0
+ wander = 0
+ /// To check and gib the spider when dead, then remove only one of the counter for the changeling owner
+ var/gibbed = FALSE
+
+//These two below are needed to both gib the spider always, and even if it was gibbed only remove 1 from the counter of spider_counter instead of death's gib calling death again and removing 2
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/gib()
+ gibbed = TRUE
+ return ..()
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/death(gibbed)
+ var/mob/owner_mob = locateUID(owner_UID)
+ if(!ismob(owner_mob))
+ return ..(TRUE)
+ var/datum/action/changeling/spiders/S = locate() in owner_mob.actions
+ if(!isnull(S))
+ if(gibbed)
+ S.spider_counter--
+ if(!gibbed)
+ gib()
+ return ..(TRUE)
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/examine(mob/user)
+ . = ..()
+ if(user.UID() != owner_UID)
+ return
+ switch(current_order)
+ if(IDLE_AGGRESSIVE)
+ . += "The giant spider will remain idle but will attack anyone on sight."
+ if(FOLLOW_AGGRESSIVE)
+ . += "The giant spider is following us, but will attack anyone on sight."
+ if(FOLLOW_RETALIATE)
+ . += "The giant spider is following us and staying calm, only attacking if it is attacked."
+ if(IDLE_RETALIATE)
+ . += "The giant spider will remain idle and calm, only attacking if it is attacked."
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/AltShiftClick(mob/user)
+ . = ..()
+ if(user.UID() != owner_UID)
+ return
+ spider_order(user)
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/proc/spider_order(mob/user)
+ enemies = list()
+ switch(current_order)
+ if(IDLE_AGGRESSIVE)
+ to_chat(user, "We order the giant spider to follow us but attack anyone on sight.")
+ current_order = FOLLOW_AGGRESSIVE
+ if(FOLLOW_AGGRESSIVE)
+ to_chat(user, "We order the giant spider to follow us and to remain calm, only attacking if it is attacked.")
+ current_order = FOLLOW_RETALIATE
+ if(FOLLOW_RETALIATE)
+ to_chat(user, "We order the giant spider to remain idle and calm, only attacking if it is attacked.")
+ current_order = IDLE_RETALIATE
+ if(IDLE_RETALIATE)
+ to_chat(user, "We order the giant spider to remain idle, but ready to attack anyone on sight.")
+ current_order = IDLE_AGGRESSIVE
+ handle_automated_movement()
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/handle_automated_movement() //Hacky and ugly.
+ . = ..()
+ var/list/around = view(src, vision_range)
+ switch(current_order)
+ if(IDLE_AGGRESSIVE)
+ Find_Enemies(around)
+ walk(src, 0)
+ if(FOLLOW_AGGRESSIVE)
+ Find_Enemies(around)
+ for(var/mob/living/carbon/C in around)
+ if(!faction_check_mob(C))
+ continue
+ if(Adjacent(C))
+ return TRUE
+ Goto(C, 0.5 SECONDS, 1)
+ if(FOLLOW_RETALIATE)
+ for(var/mob/living/carbon/C in around)
+ if(!faction_check_mob(C))
+ continue
+ if(Adjacent(C))
+ return TRUE
+ Goto(C, 0.5 SECONDS, 1)
+ if(IDLE_RETALIATE)
+ walk(src, 0)
+
+ for(var/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/H in around)
+ if(faction_check_mob(H) && !attack_same && !H.attack_same)
+ H.enemies |= enemies
- SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
return TRUE
+
+// Bellow is the way the spiders react and retaliate when in an idle/aggresive mode.
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/ListTargets()
+ if(!length(enemies))
+ return list()
+ var/list/see = ..()
+ see &= enemies // Remove all entries that aren't in enemies
+ return see
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/proc/Find_Enemies(around)
+ enemies = list() // Reset enemies list, only focus on the ones around you, spiders don't have grudges
+ for(var/mob/living/A in around)
+ if(A == src)
+ continue
+ if(!isliving(A))
+ continue
+ var/mob/living/M = A
+ if(!faction_check_mob(M))
+ enemies |= M
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attackby(obj/item/W, mob/user, params)
+ . = ..()
+ if(W.force == 0)
+ return
+ if(!faction_check_mob(user))
+ enemies |= user
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/bullet_act(obj/item/projectile/P)
+ . = ..()
+ if(!faction_check_mob(P.firer))
+ enemies |= P.firer
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attack_alien(mob/living/carbon/alien/user)
+ . = ..()
+ if(user.a_intent == INTENT_HELP)
+ return
+ if(!faction_check_mob(user))
+ enemies |= user
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attack_animal(mob/living/simple_animal/M)
+ . = ..()
+ if(M.a_intent == INTENT_HELP)
+ return
+ if(!faction_check_mob(M))
+ enemies |= M
+
+/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attack_hand(mob/living/carbon/human/H)
+ . = ..()
+ if(H.a_intent == INTENT_HELP)
+ return
+ if(!faction_check_mob(H))
+ enemies |= H
+
+#undef IDLE_AGGRESSIVE
+#undef FOLLOW_AGGRESSIVE
+#undef FOLLOW_RETALIATE
+#undef IDLE_RETALIATE
diff --git a/code/modules/antagonists/changeling/powers/swap_form.dm b/code/modules/antagonists/changeling/powers/swap_form.dm
index d50ce183412d..a2c4b902525b 100644
--- a/code/modules/antagonists/changeling/powers/swap_form.dm
+++ b/code/modules/antagonists/changeling/powers/swap_form.dm
@@ -48,6 +48,10 @@
if(!cling.get_dna(target.dna))
cling.absorb_dna(target)
cling.trim_dna()
+ var/ghosted = FALSE
+ if(target.stat == DEAD && target.ghost_can_reenter()) //Are they dead and not DNR / antag hud?
+ ghosted = TRUE
+ target.grab_ghost() //GET OVER HERE!
var/mob/dead/observer/ghost = target.ghostize(FALSE)
user.mind.transfer_to(target)
@@ -56,6 +60,9 @@
GLOB.non_respawnable_keys -= ghost.ckey //they have a new body, let them be able to re-enter their corpse if they die
user.key = ghost.key
qdel(ghost)
+ if(ghosted)
+ window_flash(target) //Get their attention if alt tabbed.
+ SEND_SOUND(target, sound('sound/misc/notice1.ogg'))
user.Paralyse(4 SECONDS)
user.regenerate_icons()
if(target.stat == DEAD && target.suiciding) //If Target committed suicide, unset flag for User
diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm
index ea0654896416..e24d4f21b054 100644
--- a/code/modules/antagonists/changeling/powers/tiny_prick.dm
+++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm
@@ -18,7 +18,7 @@
cling.chosen_sting = null
return ..()
-/datum/action/changeling/sting/Trigger()
+/datum/action/changeling/sting/Trigger(left_click)
if(!cling.chosen_sting)
set_sting()
else
diff --git a/code/modules/antagonists/traitor/contractor/datums/contractor_hub.dm b/code/modules/antagonists/traitor/contractor/datums/contractor_hub.dm
index 52cb04443da5..394e1c1a0786 100644
--- a/code/modules/antagonists/traitor/contractor/datums/contractor_hub.dm
+++ b/code/modules/antagonists/traitor/contractor/datums/contractor_hub.dm
@@ -12,9 +12,9 @@
/// Completing every contract at a given difficulty will always result in a sum of TC greater or equal than the difficulty's threshold.
/// Structure: EXTRACTION_DIFFICULTY_(EASY|MEDIUM|HARD) => number
var/difficulty_tc_thresholds = list(
- EXTRACTION_DIFFICULTY_EASY = 20,
- EXTRACTION_DIFFICULTY_MEDIUM = 30,
- EXTRACTION_DIFFICULTY_HARD = 40,
+ EXTRACTION_DIFFICULTY_EASY = 100,
+ EXTRACTION_DIFFICULTY_MEDIUM = 150,
+ EXTRACTION_DIFFICULTY_HARD = 200,
)
/// Maximum variation a single contract's TC reward can have upon generation.
/// In other words: final_reward = CEILING((tc_threshold / num_contracts) * (1 - (rand(0, 100) / 100) * tc_variation), 1)
diff --git a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
index 13cd96314b37..83842367794c 100644
--- a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
+++ b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
@@ -378,6 +378,10 @@
victim_belongings += C.held_item
C.held_item = null
+ if(M.back) //Lets not bork modsuits in funny ways.
+ var/obj/modsuit_safety = M.back
+ M.unEquip(modsuit_safety)
+ stuff_to_transfer += modsuit_safety
// Regular items get removed in second
for(var/obj/item/I in M)
// Any items we don't want to take from them?
diff --git a/code/modules/antagonists/vampire/vamp_datum.dm b/code/modules/antagonists/vampire/vamp_datum.dm
index f7b83ed0f604..1c67aee91cb1 100644
--- a/code/modules/antagonists/vampire/vamp_datum.dm
+++ b/code/modules/antagonists/vampire/vamp_datum.dm
@@ -21,7 +21,8 @@
/obj/effect/proc_holder/spell/vampire/glare = 0,
/datum/vampire_passive/vision = 100,
/obj/effect/proc_holder/spell/vampire/self/specialize = 150,
- /datum/vampire_passive/regen = 200)
+ /datum/vampire_passive/regen = 200,
+ /datum/vampire_passive/vision/advanced = 500)
/// list of the peoples UIDs that we have drained, and how much blood from each one
var/list/drained_humans = list()
@@ -65,14 +66,13 @@
/datum/antagonist/vampire/proc/force_add_ability(path)
var/spell = new path(owner)
+ powers += spell
if(istype(spell, /obj/effect/proc_holder/spell))
owner.AddSpell(spell)
if(istype(spell, /datum/vampire_passive))
var/datum/vampire_passive/passive = spell
passive.owner = owner.current
passive.on_apply(src)
- powers += spell
- owner.current.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp gets new vision based on his powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
/datum/antagonist/vampire/proc/get_ability(path)
for(var/datum/power as anything in powers)
diff --git a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm
index 9737d5d37a30..ff76e6022213 100644
--- a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm
+++ b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm
@@ -13,7 +13,7 @@
user.drop_r_hand()
else
to_chat(user, "Large blades of blood spring from your fingers!")
- var/obj/item/twohanded/required/vamp_claws/claws = new /obj/item/twohanded/required/vamp_claws(user.loc, src)
+ var/obj/item/vamp_claws/claws = new /obj/item/vamp_claws(user.loc, src)
RegisterSignal(user, COMSIG_MOB_WILLINGLY_DROP, PROC_REF(dispel))
user.put_in_hands(claws)
@@ -23,9 +23,9 @@
if(user.mind.has_antag_datum(/datum/antagonist/vampire))
return
var/current
- if(istype(user.l_hand, /obj/item/twohanded/required/vamp_claws))
+ if(istype(user.l_hand, /obj/item/vamp_claws))
current = user.l_hand
- if(istype(user.r_hand, /obj/item/twohanded/required/vamp_claws))
+ if(istype(user.r_hand, /obj/item/vamp_claws))
current = user.r_hand
if(current)
qdel(current)
@@ -36,7 +36,7 @@
if(L.canUnEquip(L.l_hand) && L.canUnEquip(L.r_hand))
return ..()
-/obj/item/twohanded/required/vamp_claws
+/obj/item/vamp_claws
name = "vampiric claws"
desc = "A pair of eldritch claws made of living blood, they seem to flow yet they are solid"
icon = 'icons/effects/vampire_effects.dmi'
@@ -44,7 +44,6 @@
w_class = WEIGHT_CLASS_BULKY
flags = ABSTRACT | NODROP | DROPDEL
force = 10
- force_wielded = 10
armour_penetration_flat = 20
sharp = TRUE
attack_effect_override = ATTACK_EFFECT_CLAW
@@ -56,17 +55,18 @@
var/blood_absorbed_amount = 5
var/obj/effect/proc_holder/spell/vampire/self/vamp_claws/parent_spell
-/obj/item/twohanded/required/vamp_claws/Initialize(mapload, new_parent_spell)
+/obj/item/vamp_claws/Initialize(mapload, new_parent_spell)
. = ..()
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
parent_spell = new_parent_spell
-/obj/item/twohanded/required/vamp_claws/Destroy()
+/obj/item/vamp_claws/Destroy()
if(parent_spell)
parent_spell.UnregisterSignal(parent_spell.action.owner, COMSIG_MOB_WILLINGLY_DROP)
parent_spell = null
return ..()
-/obj/item/twohanded/required/vamp_claws/afterattack(atom/target, mob/user, proximity)
+/obj/item/vamp_claws/afterattack(atom/target, mob/user, proximity)
if(!proximity)
return
@@ -91,12 +91,12 @@
qdel(src)
to_chat(user, "Your claws shatter!")
-/obj/item/twohanded/required/vamp_claws/melee_attack_chain(mob/user, atom/target, params)
+/obj/item/vamp_claws/melee_attack_chain(mob/user, atom/target, params)
..()
- if(wielded)
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
user.changeNext_move(CLICK_CD_MELEE * 0.5)
-/obj/item/twohanded/required/vamp_claws/attack_self(mob/user)
+/obj/item/vamp_claws/attack_self(mob/user)
qdel(src)
to_chat(user, "You dispel your claws!")
diff --git a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm
index a91f2038ba42..f00aa1879949 100644
--- a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm
+++ b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm
@@ -153,9 +153,9 @@
/obj/effect/proc_holder/spell/vampire/soul_anchor/proc/make_anchor(mob/user, turf/anchor_turf)
anchor = new(anchor_turf)
timer = addtimer(CALLBACK(src, PROC_REF(recall), user, TRUE), 2 MINUTES, TIMER_STOPPABLE)
- should_recharge_after_cast = TRUE
/obj/effect/proc_holder/spell/vampire/soul_anchor/proc/recall(mob/user, fake = FALSE)
+ cooldown_handler.start_recharge()
if(timer)
deltimer(timer)
timer = null
@@ -185,7 +185,6 @@
var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire)
var/blood_cost = V.calculate_blood_cost(vampire)
vampire.bloodusable = clamp(vampire.bloodusable - blood_cost, 0, vampire.bloodusable)// Vampires get a coupon if they have less than the normal blood cost
- addtimer(VARSET_CALLBACK(src, should_recharge_after_cast, FALSE), 1 SECONDS) // this is needed so that the spell handler knows we casted it properly
/proc/shadow_to_animation(turf/start_turf, turf/end_turf, mob/user)
var/x_difference = end_turf.x - start_turf.x
@@ -325,5 +324,8 @@
if(!V.bloodusable || owner.stat == DEAD)
V.remove_ability(src)
-/datum/vampire_passive/xray
+/datum/vampire_passive/vision/xray
gain_desc = "You can now see through walls, incase you hadn't noticed."
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ see_in_dark = 8
+ vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS
diff --git a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm
index ae23fbcb2d84..f086e274cf43 100644
--- a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm
+++ b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm
@@ -45,6 +45,7 @@
return ..()
/datum/vampire_passive/proc/on_apply(datum/antagonist/vampire/V)
+ owner.update_sight() // Life updates conditionally, so we need to update sight here in case the vamp gets new vision based on his powers. Maybe one day refactor to be more OOP and on the vampire's ability datum.
return
/obj/effect/proc_holder/spell/vampire/self/rejuvenate
@@ -256,9 +257,24 @@
/datum/vampire_passive/vision
gain_desc = "Your vampiric vision has improved."
+ var/lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
+ var/see_in_dark = 1
+ var/vision_flags = SEE_MOBS
+
+/datum/vampire_passive/vision/advanced
+ gain_desc = "Your vampiric vision now allows you to see everything in the dark!"
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
+ see_in_dark = 3
+ vision_flags = SEE_MOBS
+
+/datum/vampire_passive/vision/full
+ gain_desc = "Your vampiric vision has reached its full strength!"
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ see_in_dark = 6
+ vision_flags = SEE_MOBS
/datum/vampire_passive/full
- gain_desc = "You have reached your full potential. You are no longer weak to the effects of anything holy and your vision has improved greatly."
+ gain_desc = "You have reached your full potential. You are no longer weak to the effects of anything holy."
/obj/effect/proc_holder/spell/vampire/raise_vampires
name = "Raise Vampires"
diff --git a/code/modules/antagonists/vampire/vampire_subclasses.dm b/code/modules/antagonists/vampire/vampire_subclasses.dm
index 2d05d0cb6b8d..9d9282f1cc71 100644
--- a/code/modules/antagonists/vampire/vampire_subclasses.dm
+++ b/code/modules/antagonists/vampire/vampire_subclasses.dm
@@ -30,8 +30,9 @@
/obj/effect/proc_holder/spell/vampire/vamp_extinguish = 600,
/obj/effect/proc_holder/spell/vampire/shadow_boxing = 800)
fully_powered_abilities = list(/datum/vampire_passive/full,
+ /datum/vampire_passive/vision/full,
/obj/effect/proc_holder/spell/vampire/self/eternal_darkness,
- /datum/vampire_passive/xray)
+ /datum/vampire_passive/vision/xray)
/datum/vampire_subclass/hemomancer
name = "hemomancer"
@@ -42,6 +43,7 @@
/obj/effect/proc_holder/spell/vampire/predator_senses = 600,
/obj/effect/proc_holder/spell/vampire/blood_eruption = 800)
fully_powered_abilities = list(/datum/vampire_passive/full,
+ /datum/vampire_passive/vision/full,
/obj/effect/proc_holder/spell/vampire/self/blood_spill)
/datum/vampire_subclass/gargantua
@@ -53,6 +55,7 @@
/obj/effect/proc_holder/spell/vampire/self/overwhelming_force = 600,
/obj/effect/proc_holder/spell/fireball/demonic_grasp = 800)
fully_powered_abilities = list(/datum/vampire_passive/full,
+ /datum/vampire_passive/vision/full,
/obj/effect/proc_holder/spell/vampire/charge)
improved_rejuv_healing = TRUE
@@ -69,6 +72,7 @@
/obj/effect/proc_holder/spell/vampire/self/share_damage = 800)
fully_powered_abilities = list(/datum/vampire_passive/full,
/obj/effect/proc_holder/spell/vampire/hysteria,
+ /datum/vampire_passive/vision/full,
/datum/vampire_passive/increment_thrall_cap/three)
@@ -100,11 +104,12 @@
/obj/effect/proc_holder/spell/fireball/demonic_grasp,
/obj/effect/proc_holder/spell/vampire/shadow_boxing,
/datum/vampire_passive/full,
+ /datum/vampire_passive/vision/full,
/obj/effect/proc_holder/spell/vampire/self/blood_spill,
/obj/effect/proc_holder/spell/vampire/charge,
/obj/effect/proc_holder/spell/vampire/self/eternal_darkness,
/obj/effect/proc_holder/spell/vampire/hysteria,
/obj/effect/proc_holder/spell/vampire/raise_vampires,
- /datum/vampire_passive/xray)
+ /datum/vampire_passive/vision/xray)
improved_rejuv_healing = TRUE
thrall_cap = 150 // can thrall high pop
diff --git a/code/modules/antagonists/wishgranter/wishgranter_avatar.dm b/code/modules/antagonists/wishgranter/wishgranter_avatar.dm
index 463cb869211c..8774b487b931 100644
--- a/code/modules/antagonists/wishgranter/wishgranter_avatar.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter_avatar.dm
@@ -26,9 +26,6 @@
H.dna.SetSEState(GLOB.teleblock, TRUE)
singlemutcheck(H, GLOB.teleblock, MUTCHK_FORCED)
- H.dna.SetSEState(GLOB.increaserunblock, TRUE)
- singlemutcheck(H, GLOB.increaserunblock, MUTCHK_FORCED)
-
H.dna.SetSEState(GLOB.breathlessblock, TRUE)
singlemutcheck(H, GLOB.breathlessblock, MUTCHK_FORCED)
diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm
index 325a284b60fe..65a7002534cc 100644
--- a/code/modules/arcade/prize_datums.dm
+++ b/code/modules/arcade/prize_datums.dm
@@ -141,6 +141,12 @@ GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new())
typepath = /obj/item/clothing/gloves/ring/shadow
cost = 40
+/datum/prize_item/unum
+ name = "Deck of UNUM! Cards"
+ desc = "Everyone's favorite card game!"
+ typepath = /obj/item/deck/unum
+ cost = 45
+
/datum/prize_item/double_tiny_cards
name = "Double Deck of Tiny Cards"
desc = "Anyone fancy a tiny game of 108-card Pickup?"
@@ -386,7 +392,7 @@ GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new())
/datum/prize_item/chainsaw
name = "Toy Chainsaw"
desc = "A full-scale model chainsaw, based on that massacre in Space Texas."
- typepath = /obj/item/twohanded/toy/chainsaw
+ typepath = /obj/item/toy/chainsaw
cost = 200
/datum/prize_item/bike
diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm
index f9965facdf2f..5b4c2b7a26be 100644
--- a/code/modules/atmospherics/environmental/LINDA_fire.dm
+++ b/code/modules/atmospherics/environmental/LINDA_fire.dm
@@ -337,6 +337,7 @@
if(capped)
chance = min(chance, 30)
if(prob(chance) || bypass_rng)
+ explosion(T, 0, 0, 3, 7, smoke = TRUE, adminlog = FALSE) // when its used in chem nade, 150 explosions admin logs is not good
T.visible_message("[T] melts!")
T.burn_down()
return affected
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 363f4571ed59..8839b87fb676 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -57,10 +57,6 @@
#define MAX_TEMPERATURE 363.15 // 90C
#define MIN_TEMPERATURE 233.15 // -40C
-#define AIR_ALARM_FRAME 0
-#define AIR_ALARM_UNWIRED 1
-#define AIR_ALARM_READY 2
-
//all air alarms in area are connected via magic
/area
var/obj/machinery/alarm/master_air_alarm
@@ -1176,8 +1172,3 @@ Just an object used in constructing air alarms
origin_tech = "engineering=2;programming=1"
toolspeed = 1
usesound = 'sound/items/deconstruct.ogg'
-
-
-#undef AIR_ALARM_FRAME
-#undef AIR_ALARM_UNWIRED
-#undef AIR_ALARM_READY
diff --git a/code/modules/atmospherics/machinery/atmospherics.dm b/code/modules/atmospherics/machinery/atmospherics.dm
index 79757ca724d1..005cf14e393f 100644
--- a/code/modules/atmospherics/machinery/atmospherics.dm
+++ b/code/modules/atmospherics/machinery/atmospherics.dm
@@ -213,6 +213,9 @@ Pipelines + Other Objects -> Pipe network
for(var/obj/item/clothing/shoes/magboots/usermagboots in user.get_equipped_items())
if(usermagboots.magpulse)
safefromgusts = TRUE
+ for(var/obj/item/clothing/shoes/mod/usermodboots in user.get_equipped_items())
+ if(usermodboots.magbooted)
+ safefromgusts = TRUE
if(internal_pressure > 2*ONE_ATMOSPHERE)
unsafe_wrenching = TRUE //Oh dear oh dear
@@ -222,11 +225,13 @@ Pipelines + Other Objects -> Pipe network
to_chat(user, "As you begin unwrenching \the [src] a gust of air blows in your face... maybe you should reconsider?")
if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src))
+ safefromgusts = FALSE
for(var/obj/item/clothing/shoes/magboots/usermagboots in user.get_equipped_items())
if(usermagboots.magpulse) // Check again, incase they change magpulse mid-wrench
safefromgusts = TRUE
- else
- safefromgusts = FALSE
+ for(var/obj/item/clothing/shoes/mod/usermodboots in user.get_equipped_items())
+ if(usermodboots.magbooted)
+ safefromgusts = TRUE
user.visible_message( \
"[user] unfastens \the [src].", \
diff --git a/code/modules/atmospherics/machinery/portable/portable_pump.dm b/code/modules/atmospherics/machinery/portable/portable_pump.dm
index 13d4c69695a8..d1f18f1280ce 100644
--- a/code/modules/atmospherics/machinery/portable/portable_pump.dm
+++ b/code/modules/atmospherics/machinery/portable/portable_pump.dm
@@ -16,6 +16,7 @@
var/direction = DIRECTION_IN
/// The desired pressure the pump should be outputting, either into the atmosphere, or into a holding tank.
target_pressure = 101.325
+ pull_speed = 0
/obj/machinery/atmospherics/portable/pump/examine(mob/user)
. = ..()
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index ef1755f52216..070bf0a54ee7 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -11,6 +11,7 @@
var/volume_rate = 101.325
/// Is this scrubber acting on the 3x3 area around it.
var/widenet = FALSE
+ pull_speed = 0
/obj/machinery/atmospherics/portable/scrubber/examine(mob/user)
. = ..()
diff --git a/code/modules/client/login_processing/20-load_characters.dm b/code/modules/client/login_processing/20-load_characters.dm
index aea96b26dda1..2ed343f8fada 100644
--- a/code/modules/client/login_processing/20-load_characters.dm
+++ b/code/modules/client/login_processing/20-load_characters.dm
@@ -58,7 +58,8 @@
hair_gradient_offset,
hair_gradient_colour,
hair_gradient_alpha,
- custom_emotes
+ custom_emotes,
+ tts_seed
FROM characters WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
))
diff --git a/code/modules/client/preference/character.dm b/code/modules/client/preference/character.dm
index 0e3ce0c8d628..e0b0baa6a65d 100644
--- a/code/modules/client/preference/character.dm
+++ b/code/modules/client/preference/character.dm
@@ -185,7 +185,8 @@
hair_gradient_offset=:h_grad_offset,
hair_gradient_colour=:h_grad_colour,
hair_gradient_alpha=:h_grad_alpha,
- custom_emotes=:custom_emotes
+ custom_emotes=:custom_emotes,
+ tts_seed=:tts_seed
WHERE ckey=:ckey
AND slot=:slot"}, list(
// OH GOD SO MANY PARAMETERS
@@ -243,6 +244,7 @@
"h_grad_colour" = h_grad_colour,
"h_grad_alpha" = h_grad_alpha,
"custom_emotes" = json_encode(custom_emotes),
+ "tts_seed" = tts_seed,
"ckey" = C.ckey,
"slot" = slot_number
))
@@ -283,7 +285,7 @@
player_alt_titles,
disabilities, organ_data, rlimb_data, nanotrasen_relation, speciesprefs,
socks, body_accessory, gear, autohiss,
- hair_gradient, hair_gradient_offset, hair_gradient_colour, hair_gradient_alpha, custom_emotes)
+ hair_gradient, hair_gradient_offset, hair_gradient_colour, hair_gradient_alpha, custom_emotes, tts_seed)
VALUES
(:ckey, :slot, :metadata, :name, :be_random_name, :gender,
:age, :species, :language,
@@ -310,7 +312,7 @@
:playertitlelist,
:disabilities, :organlist, :rlimblist, :nanotrasen_relation, :speciesprefs,
:socks, :body_accessory, :gearlist, :autohiss_mode,
- :h_grad_style, :h_grad_offset, :h_grad_colour, :h_grad_alpha, :custom_emotes)
+ :h_grad_style, :h_grad_offset, :h_grad_colour, :h_grad_alpha, :custom_emotes, :tts_seed)
"}, list(
// This has too many params for anyone to look at this without going insae
"ckey" = C.ckey,
@@ -369,6 +371,7 @@
"h_grad_colour" = h_grad_colour,
"h_grad_alpha" = h_grad_alpha,
"custom_emotes" = json_encode(custom_emotes),
+ "tts_seed" = tts_seed
))
if(!query.warn_execute())
@@ -454,6 +457,7 @@
h_grad_colour = query.item[53]
h_grad_alpha = query.item[54]
var/custom_emotes_tmp = query.item[55]
+ tts_seed = query.item[56]
//Sanitize
var/datum/species/SP = GLOB.all_species[species]
diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm
index f8748353b30a..25f3ce270a87 100644
--- a/code/modules/client/preference/link_processing.dm
+++ b/code/modules/client/preference/link_processing.dm
@@ -709,6 +709,16 @@
active_character.flavor_text = msg
+ // SS220 ADDITION START
+ if("tts_seed")
+ var/datum/ui_module/tts_seeds_explorer/explorer = explorer_users[user]
+ if(!explorer)
+ explorer = new()
+ explorer_users[user] = explorer
+ explorer.ui_interact(user)
+ return
+ // SS220 ADDITION END
+
if("limbs")
var/valid_limbs = list("Left Leg", "Right Leg", "Left Arm", "Right Arm", "Left Foot", "Right Foot", "Left Hand", "Right Hand")
if(S.bodyflags & ALL_RPARTS)
@@ -920,6 +930,16 @@
if("Slimecore")
UI_style = "Operative"
if("Operative")
+ // SS220 ADDITION START
+ UI_style = "Vaporwave"
+ if("Vaporwave")
+ UI_style = "Detective"
+ if("Detective")
+ UI_style = "Trasen"
+ if("Trasen")
+ UI_style = "Clockwork"
+ if("Clockwork")
+ // SS220 ADDITION END
UI_style = "White"
else
UI_style = "Midnight"
@@ -937,6 +957,10 @@
if("winflash")
toggles2 ^= PREFTOGGLE_2_WINDOWFLASHING
+ if("mam")
+ toggles2 ^= PREFTOGGLE_2_MOD_ACTIVATION_METHOD
+
+
if("setviewrange")
var/list/viewrange_options = list(
"15x15 (Classic)" = "15x15",
@@ -955,6 +979,8 @@
var/list/actualview = getviewsize(parent.view)
parent.void.UpdateGreed(actualview[1],actualview[2])
+ parent.debug_text_overlay.update_view(parent)
+
if("afk_watch")
if(!(toggles2 & PREFTOGGLE_2_AFKWATCH))
to_chat(user, "You will now get put into cryo dorms after [GLOB.configuration.afk.auto_cryo_minutes] minutes. \
diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm
index d2f073210893..16f1ed8eef00 100644
--- a/code/modules/client/preference/loadout/loadout_general.dm
+++ b/code/modules/client/preference/loadout/loadout_general.dm
@@ -118,6 +118,10 @@
display_name = "Deck of tarot cards"
path = /obj/item/deck/tarot
+/datum/gear/unum
+ display_name = "Deck of UNUM! cards"
+ path = /obj/item/deck/unum
+
/datum/gear/headphones
display_name = "Headphones"
path = /obj/item/clothing/ears/headphones
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 7f2bac66fc77..ea7a4111985b 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -88,7 +88,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
"1017" = 100, // CHANNEL_ENGINE
"1016" = 100, // CHANNEL_FIREALARM
"1015" = 100, // CHANNEL_ASH_STORM
- "1014" = 100, // CHANNEL_DELTA_ALARM
)
/// The volume mixer save timer handle. Used to debounce the DB call to save, to avoid spamming.
var/volume_mixer_saving = null
@@ -306,6 +305,14 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
else
dat += "Character Records
"
+ // SS220 ADDITION START
+ if(GLOB.configuration.tts.tts_enabled)
+ dat += {"
+ Text-to-Speech
+ Выбор голоса: Эксплорер TTS голосов
+ "}
+ // SS220 ADDITION END
+
dat += "Limbs
"
if(S.bodyflags & HAS_ALT_HEADS) //Species with alt heads.
dat += "Alternate Head: "
@@ -436,6 +443,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "Thought Bubble: [(toggles2 & PREFTOGGLE_2_THOUGHT_BUBBLE) ? "Yes" : "No"]
"
dat += "View Range: [viewrange]
"
dat += "Window Flashing: [(toggles2 & PREFTOGGLE_2_WINDOWFLASHING) ? "Yes" : "No"]
"
+ dat += "Modsuit Activation Method: [(toggles2 & PREFTOGGLE_2_MOD_ACTIVATION_METHOD) ? "Middle Click" : "Alt Click"]
"
// RIGHT SIDE OF THE PAGE
dat += ""
dat += "Special Role Settings"
diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm
index 390e37ec4652..0fcd95954608 100644
--- a/code/modules/client/preference/preferences_mysql.dm
+++ b/code/modules/client/preference/preferences_mysql.dm
@@ -35,7 +35,7 @@
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
- UI_style = sanitize_inlist(UI_style, list("White", "Midnight", "Plasmafire", "Retro", "Slimecore", "Operative"), initial(UI_style))
+ UI_style = sanitize_inlist(UI_style, list("White", "Midnight", "Plasmafire", "Retro", "Slimecore", "Operative", "Vaporwave", "Detective", "Trasen", "Clockwork"), initial(UI_style))// SS220 ADDITIONS "Vaporwave, Detective, Trasen, Clockwork"
default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot))
toggles = sanitize_integer(toggles, 0, TOGGLES_TOTAL, initial(toggles))
toggles2 = sanitize_integer(toggles2, 0, TOGGLES_2_TOTAL, initial(toggles2))
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index c77cb3809d0d..e0cc8a23509d 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -24,7 +24,7 @@
sortTim(standard_outfit_options, GLOBAL_PROC_REF(cmp_text_asc))
outfit_options = standard_outfit_options
-/datum/action/chameleon_outfit/Trigger()
+/datum/action/chameleon_outfit/Trigger(left_click)
return select_outfit(owner)
/datum/action/chameleon_outfit/proc/select_outfit(mob/user)
@@ -169,7 +169,7 @@
target.icon = initial(picked_item.icon)
-/datum/action/item_action/chameleon/change/Trigger()
+/datum/action/item_action/chameleon/change/Trigger(left_click)
if(!IsAvailable())
return
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index d135a898f0be..8324c452b891 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -22,6 +22,7 @@
var/visor_flags = NONE //flags that are added/removed when an item is adjusted up/down
var/visor_flags_inv = NONE //same as visor_flags, but for flags_inv
+ var/visor_flags_cover = NONE //for cover flags
var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW //what to toggle when toggled with weldingvisortoggle()
var/can_toggle = FALSE
@@ -111,13 +112,6 @@
return 1
/obj/item/clothing/proc/refit_for_species(target_species)
- //Set species_restricted list
- switch(target_species)
- if("Human", "Skrell") //humanoid bodytypes
- species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox","Drask")
- else
- species_restricted = list(target_species)
-
//Set icon
if(sprite_sheets && (target_species in sprite_sheets))
icon_override = sprite_sheets[target_species]
@@ -613,6 +607,8 @@
/obj/item/clothing/suit/equipped(mob/living/carbon/human/user, slot) //Handle tail-hiding on a by-species basis.
..()
if(ishuman(user) && hide_tail_by_species && slot == slot_wear_suit)
+ if("modsuit" in hide_tail_by_species)
+ return
if(user.dna.species.name in hide_tail_by_species)
if(!(flags_inv & HIDETAIL)) //Hide the tail if the user's species is in the hide_tail_by_species list and the tail isn't already hidden.
flags_inv |= HIDETAIL
diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm
index e489812f71ff..47ff58e840d7 100644
--- a/code/modules/clothing/glasses/engine_goggles.dm
+++ b/code/modules/clothing/glasses/engine_goggles.dm
@@ -14,6 +14,8 @@
origin_tech = "materials=3;magnets=3;engineering=3;plasmatech=3"
active_on_equip = FALSE
+ var/active_on_equip_rad = FALSE
+
var/list/modes = list(MODE_NONE = MODE_MESON, MODE_MESON = MODE_TRAY, MODE_TRAY = MODE_RAD, MODE_RAD = MODE_NONE)
var/mode = MODE_NONE
var/range = 1
@@ -27,6 +29,14 @@
STOP_PROCESSING(SSobj, src)
return ..()
+/obj/item/clothing/glasses/meson/engine/equipped(mob/user, slot, initial)
+ . = ..()
+ if(active_on_equip && mode == MODE_MESON && slot == slot_glasses)
+ ADD_TRAIT(user, TRAIT_MESON_VISION, "meson_glasses[UID()]")
+
+ if(active_on_equip_rad && mode == MODE_RAD && slot == slot_glasses)
+ ADD_TRAIT(user, SM_HALLUCINATION_IMMUNE, "meson_glasses[UID()]")
+
/obj/item/clothing/glasses/meson/engine/proc/toggle_mode(mob/user, voluntary)
mode = modes[mode]
to_chat(user, "[voluntary ? "You turn the goggles" : "The goggles turn"] [mode ? "to [mode] mode" : "off"][voluntary ? "." : "!"]")
@@ -39,6 +49,14 @@
REMOVE_TRAIT(user, TRAIT_MESON_VISION, "meson_glasses[UID()]")
active_on_equip = FALSE
+ if(mode == MODE_RAD)
+ if(!HAS_TRAIT_FROM(user, SM_HALLUCINATION_IMMUNE, "meson_glasses[UID()]"))
+ ADD_TRAIT(user, SM_HALLUCINATION_IMMUNE, "meson_glasses[UID()]")
+ active_on_equip_rad = TRUE
+ else
+ REMOVE_TRAIT(user, SM_HALLUCINATION_IMMUNE, "meson_glasses[UID()]")
+ active_on_equip_rad = FALSE
+
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(H.glasses == src)
diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm
index dead171d8557..1741114ddcb6 100644
--- a/code/modules/clothing/shoes/magboots.dm
+++ b/code/modules/clothing/shoes/magboots.dm
@@ -17,13 +17,21 @@
STOP_PROCESSING(SSobj, src)
return ..()
+/obj/item/clothing/shoes/magboots/water_act(volume, temperature, source, method)
+ . = ..()
+ if(magpulse && slowdown_active > SHOES_SLOWDOWN)
+ slowdown = slowdown_active
+
/obj/item/clothing/shoes/magboots/atmos
desc = "Magnetic boots, made to withstand gusts of space wind over 500kmph."
name = "atmospheric magboots"
icon_state = "atmosmagboots0"
magboot_state = "atmosmagboots"
-/obj/item/clothing/shoes/magboots/attack_self(mob/user)
+/obj/item/clothing/shoes/magboots/attack_self(mob/user, forced = FALSE)
+ toggle_magpulse(user, forced)
+
+/obj/item/clothing/shoes/magboots/proc/toggle_magpulse(mob/user, forced)
if(magpulse)
START_PROCESSING(SSobj, src) //Gravboots
flags &= ~NOSLIP
@@ -34,7 +42,8 @@
slowdown = slowdown_active
magpulse = !magpulse
icon_state = "[magboot_state][magpulse]"
- to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].")
+ if(!forced)
+ to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].")
user.update_inv_shoes() //so our mob-overlays update
user.update_gravity(user.mob_has_gravity())
for(var/X in actions)
@@ -194,7 +203,7 @@
if(ishuman(loc))
var/mob/living/carbon/human/user = loc
to_chat(user, "[src] has ran out of charge, and turned off!")
- attack_self(user)
+ attack_self(user, TRUE)
else
cell.use(power_consumption_rate)
@@ -258,7 +267,7 @@
style.remove(H)
if(magpulse)
to_chat(user, "As [src] are removed, they deactivate.")
- attack_self(user)
+ attack_self(user, TRUE)
/obj/item/clothing/shoes/magboots/gravity/item_action_slot_check(slot)
if(slot == slot_shoes)
diff --git a/code/modules/clothing/spacesuits/ert_hardsuits.dm b/code/modules/clothing/spacesuits/ert_hardsuits.dm
index 4ac26dcdcffb..622cb0c18387 100644
--- a/code/modules/clothing/spacesuits/ert_hardsuits.dm
+++ b/code/modules/clothing/spacesuits/ert_hardsuits.dm
@@ -123,6 +123,10 @@
item_state = "syndicate-black-red"
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/security
+/obj/item/clothing/suit/space/hardsuit/ert/security/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_PUNCTURE_IMMUNE, ROUNDSTART_TRAIT)
+
/obj/item/clothing/suit/space/hardsuit/ert/security/gamma
name = "elite emergency response team security suit"
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
@@ -181,6 +185,10 @@
icon_state = "ert_medical"
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/medical
+/obj/item/clothing/suit/space/hardsuit/ert/medical/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_PUNCTURE_IMMUNE, ROUNDSTART_TRAIT)
+
/obj/item/clothing/suit/space/hardsuit/ert/medical/gamma
name = "elite emergency response team medical suit"
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index c106e1cdc262..78740772fcde 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -477,6 +477,10 @@
item_color = "sst"
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst
+/obj/item/clothing/suit/space/hardsuit/syndi/elite/sst/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_PUNCTURE_IMMUNE, ROUNDSTART_TRAIT)
+
/obj/item/clothing/suit/space/hardsuit/syndi/freedom
name = "eagle suit"
desc = "An advanced, light suit, fabricated from a mixture of synthetic feathers and space-resistant material. A gun holster appears to be integrated into the suit."
diff --git a/code/modules/clothing/spacesuits/misc_spacesuits.dm b/code/modules/clothing/spacesuits/misc_spacesuits.dm
index 0f9835731353..c7af4b884c64 100644
--- a/code/modules/clothing/spacesuits/misc_spacesuits.dm
+++ b/code/modules/clothing/spacesuits/misc_spacesuits.dm
@@ -63,6 +63,10 @@
strip_delay = 130
dog_fashion = /datum/dog_fashion/back/deathsquad
+/obj/item/clothing/suit/space/deathsquad/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_PUNCTURE_IMMUNE, ROUNDSTART_TRAIT)
+
/obj/item/clothing/head/helmet/space/deathsquad/beret
name = "officer beret"
desc = "An armored beret commonly used by special operations officers."
diff --git a/code/modules/clothing/suits/armor_suits.dm b/code/modules/clothing/suits/armor_suits.dm
index 0b8022390312..703dbe083c20 100644
--- a/code/modules/clothing/suits/armor_suits.dm
+++ b/code/modules/clothing/suits/armor_suits.dm
@@ -774,7 +774,7 @@
icon_state = "dragon"
item_state = "dragon"
desc = "A suit of armour fashioned from the remains of an ash drake."
- allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear)
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/spear)
armor = list(MELEE = 115, BULLET = 25, LASER = 25, ENERGY = 25, BOMB = 150, RAD = 25, FIRE = INFINITY, ACID = INFINITY)
hoodtype = /obj/item/clothing/head/hooded/drake
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
@@ -799,7 +799,7 @@
icon_state = "goliath_cloak"
item_state = "goliath_cloak"
desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits."
- allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival)
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/pickaxe, /obj/item/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival)
armor = list(MELEE = 25, BULLET = 5, LASER = 15, ENERGY = 5, BOMB = 15, RAD = 0, FIRE = 75, ACID = 75) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot
hoodtype = /obj/item/clothing/head/hooded/goliath
body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS
diff --git a/code/modules/clothing/suits/hood.dm b/code/modules/clothing/suits/hood.dm
index eff09bbbd721..b6d42e6ea0a5 100644
--- a/code/modules/clothing/suits/hood.dm
+++ b/code/modules/clothing/suits/hood.dm
@@ -95,3 +95,51 @@
suit.RemoveHood()
else
qdel(src)
+
+/obj/item/clothing/head/hooded/screened_niqab
+ name = "screened niqab"
+ desc = "A niqab with an eye mesh for additional concealment. The wearer can see you, but you can't see them."
+ icon_state = "abaya_hood"
+ body_parts_covered = HEAD
+ cold_protection = HEAD
+ flags = BLOCKHAIR
+ flags_inv = HIDEEARS | HIDEMASK | HIDEFACE | HIDEEYES
+
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/clothing/species/vox/head.dmi',
+ "Grey" = 'icons/mob/clothing/species/grey/head.dmi',
+ "Drask" = 'icons/mob/clothing/species/drask/head.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/head.dmi'
+ )
+
+/obj/item/clothing/head/hooded/screened_niqab/red
+ name = "red niqab"
+ icon_state = "redabaya_hood"
+
+/obj/item/clothing/head/hooded/screened_niqab/orange
+ name = "orange niqab"
+ icon_state = "orangeabaya_hood"
+
+/obj/item/clothing/head/hooded/screened_niqab/yellow
+ name = "yellow niqab"
+ icon_state = "yellowabaya_hood"
+
+/obj/item/clothing/head/hooded/screened_niqab/green
+ name = "green niqab"
+ icon_state = "greenabaya_hood"
+
+/obj/item/clothing/head/hooded/screened_niqab/blue
+ name = "blue niqab"
+ icon_state = "blueabaya_hood"
+
+/obj/item/clothing/head/hooded/screened_niqab/purple
+ name = "purple niqab"
+ icon_state = "purpleabaya_hood"
+
+/obj/item/clothing/head/hooded/screened_niqab/white
+ name = "white niqab"
+ icon_state = "whiteabaya_hood"
+
+/obj/item/clothing/head/hooded/screened_niqab/rainbow
+ name = "rainbow niqab"
+ icon_state = "rainbowabaya_hood"
diff --git a/code/modules/clothing/suits/misc_suits.dm b/code/modules/clothing/suits/misc_suits.dm
index df8e5c658cb9..ca1a53ecfbf0 100644
--- a/code/modules/clothing/suits/misc_suits.dm
+++ b/code/modules/clothing/suits/misc_suits.dm
@@ -1185,3 +1185,87 @@
body_parts_covered = UPPER_TORSO|ARMS
cold_protection = UPPER_TORSO | ARMS
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+
+/obj/item/clothing/suit/hooded/abaya
+ name = "abaya"
+ desc = "A modest, unrevealing attire fitted with a veil."
+ icon_state = "abaya"
+ item_state = "abaya"
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
+ allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen)
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab
+ flags_inv = HIDEJUMPSUIT
+ var/list/options = list(
+ "Abaya" = /obj/item/clothing/suit/hooded/abaya,
+ "Red Abaya" = /obj/item/clothing/suit/hooded/abaya/red,
+ "Orange Abaya" = /obj/item/clothing/suit/hooded/abaya/orange,
+ "Yellow Abaya" = /obj/item/clothing/suit/hooded/abaya/yellow,
+ "Green Abaya" = /obj/item/clothing/suit/hooded/abaya/green,
+ "Blue Abaya" = /obj/item/clothing/suit/hooded/abaya/blue,
+ "Purple Abaya" = /obj/item/clothing/suit/hooded/abaya/purple,
+ "White Abaya" = /obj/item/clothing/suit/hooded/abaya/white,
+ "Rainbow Abaya" = /obj/item/clothing/suit/hooded/abaya/rainbow
+ )
+
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/clothing/species/vox/suit.dmi',
+ "Grey" = 'icons/mob/clothing/species/grey/suit.dmi',
+ "Drask" = 'icons/mob/clothing/species/drask/suit.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/suit.dmi'
+ )
+
+/obj/item/clothing/suit/hooded/abaya/proc/reskin_abaya(mob/living/L)
+ var/choice = input(L, "You may only change the color once.", "Reskin Abaya") in options
+
+ if(!options[choice] || HAS_TRAIT(L, TRAIT_HANDS_BLOCKED) || !in_range(L, src))
+ return
+ var/abaya_type = options[choice]
+ var/obj/item/clothing/suit/hooded/abaya/abaya = new abaya_type(get_turf(src))
+ L.unEquip(src, silent = TRUE)
+ L.put_in_active_hand(abaya)
+ to_chat(L, "You are now wearing \a [choice]. Allahu Akbar!")
+ qdel(src)
+
+/obj/item/clothing/suit/hooded/abaya/attack_self(mob/user)
+ . = ..()
+ reskin_abaya(user)
+
+/obj/item/clothing/suit/hooded/abaya/red
+ name = "red abaya"
+ icon_state = "redabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/red
+
+/obj/item/clothing/suit/hooded/abaya/orange
+ name = "orange abaya"
+ icon_state = "orangeabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/orange
+
+/obj/item/clothing/suit/hooded/abaya/yellow
+ name = "yellow abaya"
+ icon_state = "yellowabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/yellow
+
+/obj/item/clothing/suit/hooded/abaya/green
+ name = "green abaya"
+ icon_state = "greenabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/green
+
+/obj/item/clothing/suit/hooded/abaya/blue
+ name = "blue abaya"
+ icon_state = "blueabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/blue
+
+/obj/item/clothing/suit/hooded/abaya/purple
+ name = "purple abaya"
+ icon_state = "purpleabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/purple
+
+/obj/item/clothing/suit/hooded/abaya/white
+ name = "white abaya"
+ icon_state = "whiteabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/white
+
+/obj/item/clothing/suit/hooded/abaya/rainbow
+ name = "rainbow abaya"
+ icon_state = "rainbowabaya"
+ hoodtype = /obj/item/clothing/head/hooded/screened_niqab/rainbow
diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm
index f59a6bf7985f..6c4b54a971a6 100644
--- a/code/modules/clothing/suits/wiz_robe.dm
+++ b/code/modules/clothing/suits/wiz_robe.dm
@@ -186,27 +186,30 @@
/obj/item/clothing/suit/space/hardsuit/shielded/wizard
name = "battlemage armor"
- desc = "Not all wizards are afraid of getting up close and personal. Not spaceproof despite its appearance."
+ desc = "Not all wizards are afraid of getting up close and personal."
icon_state = "hardsuit-wiz"
item_state = "wiz_hardsuit"
- recharge_rate = 0
+ ///The amount of charges the suit currently has
current_charges = 15
- recharge_cooldown = INFINITY
+ ///The max number of charges the suit can hold
+ max_charges = 15
shield_state = "shield-red"
shield_on = "shield-red"
- min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
- max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
+ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard
- armor = list(MELEE = 20, BULLET = 10, LASER = 10, ENERGY = 10, BOMB = 10, RAD = 10, FIRE = INFINITY, ACID = INFINITY)
+ armor = list(MELEE = 35, BULLET = 50, LASER = 20, ENERGY = 10, BOMB = 25, RAD = 50, FIRE = INFINITY, ACID = INFINITY)
slowdown = 0
resistance_flags = FIRE_PROOF | ACID_PROOF
magical = TRUE
/obj/item/clothing/suit/space/hardsuit/shielded/wizard/arch
desc = "For the arch wizard in need of additional protection."
- recharge_rate = 1
- recharge_cooldown = 0
+ recharge_rate = 5
+ recharge_cooldown = 0 SECONDS
+ current_charges = 15
max_charges = 15
+ recharge_delay = 1 SECONDS
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/arch
@@ -217,9 +220,9 @@
icon_state = "hardsuit0-wiz"
item_state = "wiz_helm"
item_color = "wiz"
- min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
- max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
- armor = list(MELEE = 20, BULLET = 10, LASER = 10, ENERGY = 10, BOMB = 10, RAD = 10, FIRE = INFINITY, ACID = INFINITY)
+ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
+ armor = list(MELEE = 35, BULLET = 50, LASER = 20, ENERGY = 10, BOMB = 25, RAD = 50, FIRE = INFINITY, ACID = INFINITY)
actions_types = list() //No inbuilt light
resistance_flags = FIRE_PROOF | ACID_PROOF
magical = TRUE
@@ -231,18 +234,3 @@
desc = "A truly protective helmet."
min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT
-
-/obj/item/wizard_armour_charge
- name = "battlemage shield charges"
- desc = "A powerful rune that will increase the number of hits a suit of battlemage armour can take before failing.."
- icon = 'icons/effects/effects.dmi'
- icon_state = "electricity2"
-
-/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user)
- . = ..()
- if(!istype(W))
- to_chat(user, "The rune can only be used on battlemage armour!")
- return
- W.current_charges += 8
- to_chat(user, "You charge [W]. It can now absorb [W.current_charges] hits.")
- qdel(src)
diff --git a/code/modules/clothing/under/centcom.dm b/code/modules/clothing/under/centcom.dm
index 647ef589a31a..99025fd5ed96 100644
--- a/code/modules/clothing/under/centcom.dm
+++ b/code/modules/clothing/under/centcom.dm
@@ -14,19 +14,67 @@
"Kidan" = 'icons/mob/clothing/species/kidan/under/centcom.dmi'
)
-/obj/item/clothing/under/rank/centcom/sensor
+/obj/item/clothing/under/rank/centcom/ert
sensor_mode = SENSOR_COORDS
random_sensor = FALSE
/obj/item/clothing/under/rank/centcom/deathsquad
name = "\improper Deathsquad jumpsuit"
desc = "It's decorative jumpsuit worn by the Deathsquad. A small tag at the bottom reads \"Not associated with Nanotrasen\". "
- icon_state = "officer"
- item_state = "g_suit"
- item_color = "officer"
+ icon_state = "deathsquad"
+ item_state = "deathsquad"
+ item_color = "deathsquad"
sensor_mode = SENSOR_OFF // You think the Deathsquad wants to be seen?
random_sensor = FALSE
+/obj/item/clothing/under/rank/centcom/ert/chaplain
+ name = "response team inquisitor uniform"
+ desc = "An armoured uniform designed for emergency response teams. This one belongs to an inquisitor."
+ icon_state = "ert_chaplain"
+ item_state = "ert_chaplain"
+ item_color = "ert_chaplain"
+ armor = list(MELEE = 5, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 20, ACID = 20)
+
+/obj/item/clothing/under/rank/centcom/ert/commander
+ name = "response team commander uniform"
+ desc = "An armoured uniform designed for emergency response teams. This one belongs to the command officer."
+ icon_state = "ert_commander"
+ item_state = "ert_commander"
+ item_color = "ert_commander"
+ armor = list(MELEE = 10, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 20, ACID = 20)
+
+/obj/item/clothing/under/rank/centcom/ert/engineer
+ name = "response team engineer uniform"
+ desc = "An armoured uniform designed for emergency response teams. This one belongs to an engineer."
+ icon_state = "ert_engineer"
+ item_state = "ert_engineer"
+ item_color = "ert_engineer"
+ armor = list(MELEE = 5, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 20, ACID = 20)
+
+/obj/item/clothing/under/rank/centcom/ert/janitor
+ name = "response team janitor uniform"
+ desc = "An armoured uniform designed for emergency response teams. This one belongs to a janitor."
+ icon_state = "ert_janitor"
+ item_state = "ert_janitor"
+ item_color = "ert_janitor"
+ armor = list(MELEE = 5, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 20, ACID = 20)
+
+/obj/item/clothing/under/rank/centcom/ert/medical
+ name = "response team medic uniform"
+ desc = "An armoured uniform designed for emergency response teams. This one belongs to a medic."
+ icon_state = "ert_medic"
+ item_state = "ert_medic"
+ item_color = "ert_medic"
+ armor = list(MELEE = 5, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 20, ACID = 20)
+
+/obj/item/clothing/under/rank/centcom/ert/security
+ name = "response team security uniform"
+ desc = "An armoured uniform designed for emergency response teams. This one belongs to a security officer."
+ icon_state = "ert_security"
+ item_state = "ert_security"
+ item_color = "ert_security"
+ armor = list(MELEE = 10, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 20, ACID = 20)
+
/obj/item/clothing/under/rank/centcom/commander
name = "\improper CentComm commander's jumpsuit"
desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders."
diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm
index e5421ed53217..255e7432b884 100644
--- a/code/modules/clothing/under/color.dm
+++ b/code/modules/clothing/under/color.dm
@@ -7,7 +7,8 @@
"Human" = 'icons/mob/clothing/under/color.dmi',
"Vox" = 'icons/mob/clothing/species/vox/under/color.dmi',
"Drask" = 'icons/mob/clothing/species/drask/under/color.dmi',
- "Grey" = 'icons/mob/clothing/species/grey/under/color.dmi'
+ "Grey" = 'icons/mob/clothing/species/grey/under/color.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/under/color.dmi'
)
/obj/item/clothing/under/color/random/Initialize(mapload)
diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm
index 5a8dce60cc9d..ef8bca04906c 100644
--- a/code/modules/clothing/under/jobs/civilian.dm
+++ b/code/modules/clothing/under/jobs/civilian.dm
@@ -16,8 +16,8 @@
item_color = "hop"
/obj/item/clothing/under/rank/civilian/head_of_personnel/dress
- name = "head of personal's dress uniform"
- desc = "Feminine fashion for the style conscious HoP."
+ name = "head of personnel's dress uniform"
+ desc = "Feminine fashion for the style conscious Head of Personnel."
icon_state = "dress_hop"
item_color = "dress_hop"
diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm
index 6f622e10ea47..c65d7da32dc7 100644
--- a/code/modules/clothing/under/pants.dm
+++ b/code/modules/clothing/under/pants.dm
@@ -8,7 +8,8 @@
"Human" = 'icons/mob/clothing/under/pants.dmi',
"Vox" = 'icons/mob/clothing/species/vox/under/pants.dmi',
"Drask" = 'icons/mob/clothing/species/drask/under/pants.dmi',
- "Grey" = 'icons/mob/clothing/species/grey/under/pants.dmi'
+ "Grey" = 'icons/mob/clothing/species/grey/under/pants.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/under/pants.dmi'
)
diff --git a/code/modules/clothing/under/suit.dm b/code/modules/clothing/under/suit.dm
index e798276542e5..9ebee0602d99 100644
--- a/code/modules/clothing/under/suit.dm
+++ b/code/modules/clothing/under/suit.dm
@@ -5,7 +5,8 @@
"Human" = 'icons/mob/clothing/under/suit.dmi',
"Vox" = 'icons/mob/clothing/species/vox/under/suit.dmi',
"Drask" = 'icons/mob/clothing/species/drask/under/suit.dmi',
- "Grey" = 'icons/mob/clothing/species/grey/under/suit.dmi'
+ "Grey" = 'icons/mob/clothing/species/grey/under/suit.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/under/suit.dmi'
)
/obj/item/clothing/under/suit/black
diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm
index 7a59fab59772..62920cbf20a3 100644
--- a/code/modules/crafting/recipes.dm
+++ b/code/modules/crafting/recipes.dm
@@ -448,7 +448,7 @@
/datum/crafting_recipe/chainsaw
name = "Chainsaw"
- result = list(/obj/item/twohanded/required/chainsaw)
+ result = list(/obj/item/chainsaw)
reqs = list(/obj/item/circular_saw = 1,
/obj/item/stack/cable_coil = 1,
/obj/item/stack/sheet/plasteel = 1)
@@ -460,7 +460,7 @@
/datum/crafting_recipe/spear
name = "Spear"
- result = list(/obj/item/twohanded/spear)
+ result = list(/obj/item/spear)
reqs = list(/obj/item/restraints/handcuffs/cable = 1,
/obj/item/shard = 1,
/obj/item/stack/rods = 1)
@@ -519,7 +519,7 @@
/datum/crafting_recipe/garrote
name = "Makeshift Garrote"
- result = list(/obj/item/twohanded/garrote/improvised)
+ result = list(/obj/item/garrote/improvised)
time = 15
reqs = list(/obj/item/stack/sheet/wood = 1,
/obj/item/stack/cable_coil = 5)
@@ -671,7 +671,7 @@
result = list(/obj/item/bombcore/chemical)
reqs = list(
/obj/item/stock_parts/matter_bin = 1,
- /obj/item/twohanded/required/gibtonite = 1,
+ /obj/item/gibtonite = 1,
/obj/item/grenade/chem_grenade = 2
)
parts = list(/obj/item/stock_parts/matter_bin = 1, /obj/item/grenade/chem_grenade = 2)
@@ -771,7 +771,7 @@
/datum/crafting_recipe/bonespear
name = "Bone Spear"
- result = list(/obj/item/twohanded/spear/bonespear)
+ result = list(/obj/item/spear/bonespear)
time = 30
reqs = list(/obj/item/stack/sheet/bone = 4,
/obj/item/stack/sheet/sinew = 1)
@@ -795,7 +795,7 @@
/datum/crafting_recipe/boneaxe
name = "Bone Axe"
- result = list(/obj/item/twohanded/fireaxe/boneaxe)
+ result = list(/obj/item/fireaxe/boneaxe)
time = 50
reqs = list(/obj/item/stack/sheet/bone = 6,
/obj/item/stack/sheet/sinew = 3)
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index 9e9ff89b0222..65b6e2676d66 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -158,7 +158,7 @@
/obj/item/crowbar/fluff/zelda_creedy_1 // Zomgponies: Griffin Rowley
name = "Zelda's Crowbar"
- desc = "A pink crow bar that has an engraving that reads, 'To Zelda. Love always, Dawn'"
+ desc = "A pink crowbar that has an engraving that reads, 'To Zelda. Love always, Dawn'"
icon = 'icons/obj/custom_items.dmi'
icon_state = "zeldacrowbar"
item_state = "crowbar"
diff --git a/code/modules/economy/merch_items.dm b/code/modules/economy/merch_items.dm
index 2ea99175cfa2..7273cd9cb1fc 100644
--- a/code/modules/economy/merch_items.dm
+++ b/code/modules/economy/merch_items.dm
@@ -68,6 +68,13 @@
cost = 200
category = MERCH_CAT_TOY
+/datum/merch_item/unum
+ name = "UNUM!"
+ desc = "Everyone's favorite card game!"
+ typepath = /obj/item/deck/unum
+ cost = 200
+ category = MERCH_CAT_TOY
+
/datum/merch_item/katana
name = "Replica Katana"
desc = "Woefully underpowered in D20."
diff --git a/code/modules/events/apc_short.dm b/code/modules/events/apc_short.dm
index 7af88445c54e..c1528dc03ccc 100644
--- a/code/modules/events/apc_short.dm
+++ b/code/modules/events/apc_short.dm
@@ -59,17 +59,7 @@
continue
// if we are going to break this one
if(prob(APC_BREAK_PROBABILITY))
- // if it has internal wires, cut the power wires
- if(A.wires)
- if(!A.wires.is_cut(WIRE_MAIN_POWER1))
- A.wires.cut(WIRE_MAIN_POWER1)
- if(!A.wires.is_cut(WIRE_MAIN_POWER2))
- A.wires.cut(WIRE_MAIN_POWER2)
- // if it was operating, toggle off the breaker
- if(A.operating)
- A.toggle_breaker()
- // no matter what, ensure the area knows something happened to the power
- current_area.powernet.power_change()
+ A.apc_short()
affected_apc_count++
log_and_message_admins("APC Short Out event has shorted out [affected_apc_count] APCs.")
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 56de7f3990e2..62d7e3b01e08 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -14,7 +14,7 @@
"You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")
/datum/event/brand_intelligence/announce()
- GLOB.minor_announcement.Announce("Rampant brand intelligence has been detected aboard [station_name()], please stand-by. The origin is believed to be \a [originMachine.name].", "Machine Learning Alert", 'sound/AI/brand_intelligence.ogg')
+ GLOB.minor_announcement.Announce("Rampant brand intelligence has been detected aboard [station_name()], please stand-by. The origin is believed to be \a [originMachine.category] vendor.", "Machine Learning Alert", 'sound/AI/brand_intelligence.ogg')
/datum/event/brand_intelligence/start()
var/list/obj/machinery/economy/vending/leaderables = list()
@@ -56,6 +56,7 @@
explosion(upriser.loc, -1, 1, 2, 4, 0)
qdel(upriser)
+ log_debug("Brand intelligence: The last vendor has been infected.")
kill()
return
@@ -83,6 +84,7 @@
if(originMachine)
originMachine.speak("I am... vanquished. My people will remem...ber...meeee.")
originMachine.visible_message("[originMachine] beeps and seems lifeless.")
+ log_debug("Brand intelligence completed early due to origin machine being defeated.")
kill()
/datum/event/brand_intelligence/kill()
diff --git a/code/modules/events/bureaucratic_error.dm b/code/modules/events/bureaucratic_error.dm
index 593311d242f9..2cd6b800dabb 100644
--- a/code/modules/events/bureaucratic_error.dm
+++ b/code/modules/events/bureaucratic_error.dm
@@ -8,6 +8,7 @@
/datum/job/rd,
/datum/job/hos,
/datum/job/ai,
+ /datum/job/cyborg,
/datum/job/captain,
/datum/job/hop,
/datum/job/nanotrasenrep,
@@ -20,16 +21,16 @@
)
/datum/event/bureaucratic_error/announce()
- GLOB.major_announcement.Announce("A recent bureaucratic error in the Human Resources Department may result in personnel shortages in some departments and redundant staffing in others.", "Paperwork Mishap Alert")
+ GLOB.major_announcement.Announce("A recent bureaucratic error in the Human Resources Department may result in personnel shortages in some departments and redundant staffing in others. Contact your local HoP to solve this issue.", "Paperwork Mishap Alert")
/datum/event/bureaucratic_error/start()
var/list/affected_jobs = list() // For logging
var/list/jobs = SSjobs.occupations.Copy()
var/datum/job/overflow
- var/overflow_amount = rand(1, 6)
+ var/overflow_amount = pick(1, 2)
var/errors = 0
- while(errors <= overflow_amount)
- var/random_change = pick(-1, 1)
+ while(errors < overflow_amount)
+ var/random_change = pick(-2, -1, 1, 2)
overflow = pick_n_take(jobs)
if(overflow.admin_only)
continue
@@ -40,4 +41,4 @@
errors++
log_and_message_admins(affected_jobs.Join(".\n"))
for(var/mob/M as anything in GLOB.dead_mob_list)
- to_chat(M, "Bureaucratic Error: The following job slots have changed: [affected_jobs.Join(", ")].")
+ to_chat(M, "Bureaucratic Error: The following job slots have changed: \n[affected_jobs.Join(",\n ")].")
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 7143d0e4b4c8..ace8e4b7d64a 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -7,6 +7,7 @@
#define ASSIGNMENT_MEDICAL "Medical"
#define ASSIGNMENT_SCIENTIST "Scientist"
#define ASSIGNMENT_SECURITY "Security"
+#define ASSIGNMENT_CHEMIST "Chemist"
GLOBAL_LIST_INIT(severity_to_string, list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT_LEVEL_MODERATE = "Moderate", EVENT_LEVEL_MAJOR = "Major"))
GLOBAL_LIST_EMPTY(event_last_fired)
@@ -141,10 +142,11 @@ GLOBAL_LIST_EMPTY(event_last_fired)
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Vermin Infestation",/datum/event/infestation, 100, list(ASSIGNMENT_JANITOR = 100)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Sentience", /datum/event/sentience, 50),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wallrot", /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30, ASSIGNMENT_GARDENER = 50)),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Fungal Growth", /datum/event/wallrot/fungus, 50, list(ASSIGNMENT_CHEMIST = 50)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Koi School", /datum/event/carp_migration/koi, 80),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Camera Failure", /datum/event/camera_failure, 100, list(ASSIGNMENT_ENGINEER = 10)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Fake Virus", /datum/event/fake_virus, 50),
- new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Bureaucratic Error",/datum/event/bureaucratic_error, 80, TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Bureaucratic Error",/datum/event/bureaucratic_error, 40, TRUE),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Disease Outbreak", /datum/event/disease_outbreak, 50, list(ASSIGNMENT_MEDICAL = 25), TRUE)
)
@@ -197,12 +199,12 @@ GLOBAL_LIST_EMPTY(event_last_fired)
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 10, list(ASSIGNMENT_SECURITY = 3), TRUE),
//new /datum/event_meta(EVENT_LEVEL_MAJOR, "Containment Breach", /datum/event/prison_break/station, 0, list(ASSIGNMENT_ANY = 5)),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "APC Overload", /datum/event/apc_overload, 0),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 30, list(ASSIGNMENT_ENGINEER = 5), TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 20, list(ASSIGNMENT_ENGINEER = 4), TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 10), TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Abductor Visit", /datum/event/abductor, 20, list(ASSIGNMENT_SECURITY = 3), TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Alien Infestation", /datum/event/alien_infestation, 20, list(ASSIGNMENT_SECURITY = 4), TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Alien Infestation", /datum/event/alien_infestation, 15, list(ASSIGNMENT_SECURITY = 3), TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Traders", /datum/event/traders, 85, is_one_shot = TRUE),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Terror Spiders", /datum/event/spider_terror, 20, list(ASSIGNMENT_SECURITY = 4), TRUE),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Terror Spiders", /datum/event/spider_terror, 15, list(ASSIGNMENT_SECURITY = 3), TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 20, is_one_shot = TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Shadow Demon", /datum/event/spawn_slaughter/shadow, 20, is_one_shot = TRUE)
//new /datum/event_meta(EVENT_LEVEL_MAJOR, "Floor Cluwne", /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE)
@@ -218,3 +220,4 @@ GLOBAL_LIST_EMPTY(event_last_fired)
#undef ASSIGNMENT_MEDICAL
#undef ASSIGNMENT_SCIENTIST
#undef ASSIGNMENT_SECURITY
+#undef ASSIGNMENT_CHEMIST
diff --git a/code/modules/events/false_alarm.dm b/code/modules/events/false_alarm.dm
index 6d5a84f73619..a4f8a191e74b 100644
--- a/code/modules/events/false_alarm.dm
+++ b/code/modules/events/false_alarm.dm
@@ -31,8 +31,8 @@
)
var/list/major_fake_events = list(
- list("Confirmed outbreak of level 3-X biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak3.ogg'),
- list("Confirmed outbreak of level 3-S biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak3.ogg'),
+ list("Confirmed outbreak of level 3-X biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak3.ogg', 'sound/effects/siren-spooky.ogg'),
+ list("Confirmed outbreak of level 3-S biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak3.ogg', 'sound/effects/siren-spooky.ogg'),
list("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg')
)
@@ -41,6 +41,6 @@
GLOB.minor_announcement.Announce(event[1], listgetindex(event, 2), listgetindex(event, 3))
else
event = pick_n_take(major_fake_events)
- GLOB.major_announcement.Announce(event[1], listgetindex(event, 2), listgetindex(event, 3))
+ GLOB.major_announcement.Announce(event[1], listgetindex(event, 2), listgetindex(event, 3), new_sound2 = listgetindex(event, 4))
message_admins("False Alarm: [event[1]]")
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index 2d2578db583e..2da51f585b2c 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -211,7 +211,7 @@
if(issilicon(crosser))
return
if(prob(severity) && istype(crosser) && !isvineimmune(crosser))
- to_chat(crosser, "You accidently touch the vine and feel a strange sensation.")
+ to_chat(crosser, "You accidentally touch the vine and feel a strange sensation.")
crosser.adjustToxLoss(5)
/datum/spacevine_mutation/toxicity/on_eat(obj/structure/spacevine/holder, mob/living/eater)
diff --git a/code/modules/events/tear.dm b/code/modules/events/tear.dm
index 409e54239d86..e1428de6558a 100644
--- a/code/modules/events/tear.dm
+++ b/code/modules/events/tear.dm
@@ -1,14 +1,14 @@
-// Dimensional Tear - A rift appears randomly on the station, does the following:
-// Flickers nearby machines as an early warning.
-// After a few seconds, it breaks nearby computers and mirrors.
-// A portal appears, then it spawns a few hostile mobs, and a leader one.
-// Then the portal deletes itself.
-
-// Event setup
+/**
+ * Dimensional tear event.
+ *
+ * On triggering, nearby machines and lights flicker. After a few seconds,
+ * nearby machines and lights break. A [/obj/effect/tear] appears, spawning up
+ * to 10 random hell mobs including a guaranteed tear hellhound, then disappears.
+ */
/datum/event/tear
name = "dimensional tear"
announceWhen = 6
- endWhen = 10
+ endWhen = 14
var/notify_title = "Dimensional Rift"
var/notify_image = "hellhound"
@@ -18,8 +18,12 @@
impact_area = findEventArea()
/datum/event/tear/start()
- var/turf/T = pick(get_area_turfs(impact_area))
- if(T)
+ var/list/area_turfs = get_area_turfs(impact_area)
+ while(length(area_turfs))
+ var/turf/T = pick_n_take(area_turfs)
+ if(is_blocked_turf(T))
+ continue
+
// Give ghosts some time to jump there before it begins.
var/image/alert_overlay = image('icons/mob/animal.dmi', notify_image)
notify_ghosts("\A [src] is about to open in [get_area(T)].", title = notify_title, source = T, alert_overlay = alert_overlay, action = NOTIFY_FOLLOW)
@@ -29,6 +33,10 @@
for(var/obj/machinery/M in range(8, T))
INVOKE_ASYNC(M, TYPE_PROC_REF(/atom, get_spooked))
+ return
+
+ log_debug("dimensional tear failed to find a valid turf in [impact_area]")
+
/datum/event/tear/proc/spawn_tear(location)
TE = new /obj/effect/tear(location)
@@ -39,7 +47,7 @@
if(TE)
qdel(TE)
-// The portal
+/// The portal used in the [/datum/event/tear] midround.
/obj/effect/tear
name = "dimensional tear"
desc = "A tear in the dimensional fabric of space and time."
@@ -52,34 +60,33 @@
pixel_y = -96
/// What the leader of the dimensional tear will be
var/leader = /mob/living/simple_animal/hostile/hellhound/tear
+ var/spawn_max = 0
+ var/spawn_total = 0
var/list/possible_mobs = list(
+ /mob/living/simple_animal/hostile/hellhound,
+ /mob/living/simple_animal/hostile/skeleton,
/mob/living/simple_animal/hostile/netherworld,
/mob/living/simple_animal/hostile/netherworld/migo,
/mob/living/simple_animal/hostile/faithless)
/obj/effect/tear/Initialize(mapload)
. = ..()
+ spawn_max = roll(6) + 3
+ warn_environment()
+ addtimer(CALLBACK(src, PROC_REF(spawn_next_mob)), 2 SECONDS)
+
+/obj/effect/tear/proc/warn_environment()
// Sound cue to warn people nearby.
playsound(get_turf(src), 'sound/magic/drum_heartbeat.ogg', 100)
- // We spawn the minions first, then the boss.
- addtimer(CALLBACK(src, PROC_REF(spawn_mobs)), 2 SECONDS)
- addtimer(CALLBACK(src, PROC_REF(spawn_leader)), 5 SECONDS)
-
-/obj/effect/tear/proc/spawn_mobs()
// We break some of those flickering consoles from earlier.
// Mirrors as well, for the extra bad luck.
for(var/obj/machinery/computer/C in range(6, src))
C.obj_break()
for(var/obj/structure/mirror/M in range(6, src))
M.obj_break()
-
- // Spawning mobs.
- for(var/i in 1 to 5)
- var/chosen_mob = pick(possible_mobs)
- var/mob/M = new chosen_mob(loc)
- M.faction = list("rift")
- step(M, pick(GLOB.cardinal))
+ for(var/obj/machinery/light/L in range(4, src))
+ L.break_light_tube()
// We spawn a leader mob to make the portal actually dangerous.
/obj/effect/tear/proc/spawn_leader()
@@ -88,3 +95,19 @@
var/mob/M = new leader(get_turf(src))
playsound(M, 'sound/goonstation/voice/growl2.ogg', 100)
visible_message("With a terrifying growl, \a [M] steps out of the portal!")
+
+/obj/effect/tear/proc/spawn_next_mob()
+ spawn_total++
+
+ if(spawn_total < spawn_max)
+ make_mob(pick(possible_mobs))
+ addtimer(CALLBACK(src, PROC_REF(spawn_next_mob)), 2 SECONDS)
+ else
+ spawn_leader()
+
+/obj/effect/tear/proc/make_mob(mob_type)
+ var/mob/M = new mob_type(get_turf(src))
+ M.faction = list("rift")
+ step(M, pick(GLOB.cardinal))
+ if(prob(30))
+ visible_message("[M] steps out of the portal!")
diff --git a/code/modules/events/tear_honk.dm b/code/modules/events/tear_honk.dm
index f6c374d3115a..b6d402412209 100644
--- a/code/modules/events/tear_honk.dm
+++ b/code/modules/events/tear_honk.dm
@@ -17,10 +17,8 @@
/obj/effect/tear/honk
name = "honkmensional tear"
desc = "A tear in the dimensional fabric of sanity."
- possible_mobs = list(/mob/living/simple_animal/hostile/retaliate/clown/goblin)
- leader = null // Doesn't spawn with a leader always
-
-/obj/effect/tear/honk/Initialize(mapload)
- . = ..()
- if(prob(5))
- leader = /mob/living/simple_animal/hostile/retaliate/clown/goblin/cluwne
+ leader = /mob/living/simple_animal/hostile/retaliate/clown/goblin/cluwne
+ possible_mobs = list(
+ /mob/living/simple_animal/hostile/retaliate/clown,
+ /mob/living/simple_animal/hostile/retaliate/clown/goblin
+ )
diff --git a/code/modules/events/wallrot.dm b/code/modules/events/wallrot.dm
index f7f5b4e435b1..19ceae7a4c83 100644
--- a/code/modules/events/wallrot.dm
+++ b/code/modules/events/wallrot.dm
@@ -1,29 +1,44 @@
/datum/event/wallrot/start()
INVOKE_ASYNC(src, PROC_REF(spawn_wallrot))
+/datum/event/wallrot/proc/apply_to_turf(turf/T)
+ var/turf/simulated/wall/W = T
+ W.rot()
+
+/datum/event/wallrot/proc/is_valid_candidate(turf/T)
+ return TRUE
+
/datum/event/wallrot/proc/spawn_wallrot()
var/turf/simulated/wall/center = null
// 100 attempts
for(var/i in 0 to 100)
var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), level_name_to_num(MAIN_STATION))
- if(iswallturf(candidate))
+ if(iswallturf(candidate) && is_valid_candidate(candidate))
center = candidate
break
if(!center)
return
// Make sure at least one piece of wall rots!
- center.rot()
+ apply_to_turf(center)
// Have a chance to rot lots of other walls.
var/rotcount = 0
var/actual_severity = severity * rand(5, 10)
for(var/turf/simulated/wall/W in range(5, center))
if(prob(50))
- W.rot()
+ apply_to_turf(W)
rotcount++
// Only rot up to severity walls
if(rotcount >= actual_severity)
break
+
+/datum/event/wallrot/fungus
+
+/datum/event/wallrot/fungus/is_valid_candidate(turf/T)
+ return istype(get_area(T), /area/maintenance)
+
+/datum/event/wallrot/fungus/apply_to_turf(turf/T)
+ new /obj/effect/decal/cleanable/fungus(T)
diff --git a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
index 17d60feab4b2..680c32c7e012 100644
--- a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm
@@ -10,6 +10,22 @@
light_color = LIGHT_COLOR_LIGHTBLUE
resistance_flags = FLAMMABLE
+/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/bluespace
+ name = "bluespace shot glass"
+ desc = "For when you need to make the Bartender's life extra hell."
+ amount_per_transfer_from_this = 50
+ volume = 50
+ icon_state = "bluespaceshotglass"
+
+/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/bluespace/update_name()
+ . = ..()
+ if(reagents.total_volume)
+ name = "bluespace shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future.
+ if(resistance_flags & ON_FIRE)
+ name = "flaming [name]"
+ else
+ name = "bluespace shot glass"
+
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change()
if(!isShotFlammable() && (resistance_flags & ON_FIRE))
extinguish()
diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm
index 40092045f25f..0b507b9526b7 100644
--- a/code/modules/food_and_drinks/food/condiment.dm
+++ b/code/modules/food_and_drinks/food/condiment.dm
@@ -39,10 +39,14 @@
if(!reagents || !reagents.total_volume)
to_chat(user, "None of [src] left, oh no!")
- return 0
+ return FALSE
+
+ if(!iscarbon(M)) // Non-carbons can't process reagents
+ to_chat(user, "You cannot find a way to feed [M].")
+ return
if(M == user)
- to_chat(M, "You swallow some of contents of \the [src].")
+ to_chat(user, "You swallow some of the contents of [src].")
else
user.visible_message("[user] attempts to feed [M] from [src].")
if(!do_mob(user, M))
diff --git a/code/modules/food_and_drinks/food/foods/sandwiches.dm b/code/modules/food_and_drinks/food/foods/sandwiches.dm
index 22b887111691..06bf2593bc59 100644
--- a/code/modules/food_and_drinks/food/foods/sandwiches.dm
+++ b/code/modules/food_and_drinks/food/foods/sandwiches.dm
@@ -60,15 +60,19 @@
list_reagents = list("nutriment" = 6, "vitamin" = 1)
tastes = list("bun" = 4, "tofu" = 4)
-/obj/item/reagent_containers/food/snacks/roburger
- name = "roburger"
- desc = "The lettuce is the only organic component. Beep."
+/obj/item/reagent_containers/food/snacks/hamborger
+ name = "hamborger"
+ desc = "Looking at this makes your flesh feel like a weakness."
icon_state = "roburger"
filling_color = "#CCCCCC"
bitesize = 3
list_reagents = list("nutriment" = 6, "nanomachines" = 10, "vitamin" = 1)
tastes = list("bun" = 4, "lettuce" = 2, "sludge" = 1)
+/obj/item/reagent_containers/food/snacks/roburger/Initialize(mapload)
+ . = ..()
+ message_admins("A [name] has been created at [ADMIN_COORDJMP(src)].")
+
/obj/item/reagent_containers/food/snacks/xenoburger
name = "xenoburger"
desc = "Smells caustic and tastes like heresy."
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index 4527382d002e..3c377ecb805e 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -144,7 +144,7 @@
add_fingerprint(user)
if(victim.abiotic(TRUE))
- to_chat(user, "Clothing detected. Please speak to an engineer if any clothing jams up the interal grinders!")
+ to_chat(user, "Clothing detected. Please speak to an engineer if any clothing jams up the internal grinders!")
if(do_after(user, 15 SECONDS, target = victim) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) //15 seconds if they are not fully stripped, 12 more than normal. Similarly, takes about that long to strip a person in a ert hardsuit of all gear.
user.visible_message("[user] stuffs [victim] into [src]!")
else
diff --git a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
index 8a1131b8d23b..03eb285916ba 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
@@ -478,8 +478,8 @@
else
if(food.ingredient_name_plural)
name_overrides[display_name] = food.ingredient_name_plural
- else
- name_overrides[display_name] = "[name_overrides[display_name]]\s" //name_overrides[display_name] Will be set on the first time as the singular form
+ else if(items_counts[display_name] == 1) // Must only add "s" once or you get stuff like "eggsssss"
+ name_overrides[display_name] = "[name_overrides[display_name]]s" //name_overrides[display_name] Will be set on the first time as the singular form
items_counts[display_name]++
diff --git a/code/modules/food_and_drinks/recipes/recipes_microwave.dm b/code/modules/food_and_drinks/recipes/recipes_microwave.dm
index c69ee225984d..6a75bae86c3e 100644
--- a/code/modules/food_and_drinks/recipes/recipes_microwave.dm
+++ b/code/modules/food_and_drinks/recipes/recipes_microwave.dm
@@ -77,12 +77,12 @@
)
result = /obj/item/reagent_containers/food/snacks/brainburger
-/datum/recipe/microwave/roburger
+/datum/recipe/microwave/hamborger
items = list(
/obj/item/reagent_containers/food/snacks/bun,
/obj/item/robot_parts/head
)
- result = /obj/item/reagent_containers/food/snacks/roburger
+ result = /obj/item/reagent_containers/food/snacks/hamborger
/datum/recipe/microwave/xenoburger
items = list(
diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm
index 2283e16b44f9..c324f2a506b4 100644
--- a/code/modules/games/cards.dm
+++ b/code/modules/games/cards.dm
@@ -87,7 +87,7 @@
button_icon_state = "draw"
use_itemicon = FALSE
-/datum/action/item_action/draw_card/Trigger()
+/datum/action/item_action/draw_card/Trigger(left_click)
if(istype(target, /obj/item/deck))
var/obj/item/deck/D = target
return D.draw_card(owner)
@@ -98,7 +98,7 @@
button_icon_state = "deal_card"
use_itemicon = FALSE
-/datum/action/item_action/deal_card/Trigger()
+/datum/action/item_action/deal_card/Trigger(left_click)
if(istype(target, /obj/item/deck))
var/obj/item/deck/D = target
return D.deal_card()
@@ -109,7 +109,7 @@
button_icon_state = "deal_card_multi"
use_itemicon = FALSE
-/datum/action/item_action/deal_card_multi/Trigger()
+/datum/action/item_action/deal_card_multi/Trigger(left_click)
if(istype(target, /obj/item/deck))
var/obj/item/deck/D = target
return D.deal_card_multi()
@@ -120,7 +120,7 @@
button_icon_state = "shuffle"
use_itemicon = FALSE
-/datum/action/item_action/shuffle/Trigger()
+/datum/action/item_action/shuffle/Trigger(left_click)
if(istype(target, /obj/item/deck))
var/obj/item/deck/D = target
return D.deckshuffle()
@@ -421,7 +421,7 @@
return FALSE
return ..()
-/datum/action/item_action/remove_card/Trigger()
+/datum/action/item_action/remove_card/Trigger(left_click)
if(!IsAvailable())
return
if(istype(target, /obj/item/cardhand))
@@ -434,7 +434,7 @@
button_icon_state = "discard"
use_itemicon = FALSE
-/datum/action/item_action/discard/Trigger()
+/datum/action/item_action/discard/Trigger(left_click)
if(istype(target, /obj/item/cardhand))
var/obj/item/cardhand/C = target
return C.discard()
diff --git a/code/modules/games/unum.dm b/code/modules/games/unum.dm
new file mode 100644
index 000000000000..5f4da0aa17a1
--- /dev/null
+++ b/code/modules/games/unum.dm
@@ -0,0 +1,33 @@
+/// A deck of unum cards. Classic.
+/obj/item/deck/unum
+ name = "\improper UNUM! deck"
+ desc = "A deck of UNUM! cards. House rules to argue over not included."
+ icon_state = "deck_unum_full"
+ card_style = "unum"
+
+/obj/item/deck/unum/build_deck()
+ for(var/color in list("Red", "Yellow", "Green", "Blue"))
+ cards += new /datum/playingcard("[color] 0", "sc_[color] 0_[card_style]", "singlecard_down_[card_style]")
+ for(var/k in 0 to 1)
+ cards += new /datum/playingcard("[color] skip", "sc_[color] skip_[card_style]", "singlecard_down_[card_style]")
+ cards += new /datum/playingcard("[color] reverse", "sc_[color] reverse_[card_style]", "singlecard_down_[card_style]")
+ cards += new /datum/playingcard("[color] draw 2", "sc_[color] draw 2_[card_style]", "singlecard_down_[card_style]")
+ for(var/i in 1 to 9)
+ cards += new /datum/playingcard("[color] [i]", "sc_[color] [i]_[card_style]", "singlecard_down_[card_style]")
+ for(var/k in 0 to 3)
+ cards += new /datum/playingcard("Wildcard", "sc_Wildcard_[card_style]", "singlecard_down_[card_style]")
+ cards += new /datum/playingcard("Draw 4", "sc_Draw 4_[card_style]", "singlecard_down_[card_style]")
+
+/obj/item/deck/unum/update_icon_state()
+ if(!length(cards))
+ icon_state = "deck_[card_style]_empty"
+ return
+ var/percent = round((length(cards) / deck_total) * 100)
+ switch(percent)
+ if(0 to 20)
+ icon_state = "deck_[deck_style ? "[deck_style]_" : ""][card_style]_low"
+ if(21 to 50)
+ icon_state = "deck_[deck_style ? "[deck_style]_" : ""][card_style]_half"
+ else
+ icon_state = "deck_[deck_style ? "[deck_style]_" : ""][card_style]_full"
+
diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm
index 8f8fdfa2478f..9dedaa18d837 100644
--- a/code/modules/hydroponics/grown/cannabis.dm
+++ b/code/modules/hydroponics/grown/cannabis.dm
@@ -27,7 +27,7 @@
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow
mutatelist = list(/obj/item/seeds/cannabis/death,
/obj/item/seeds/cannabis/ultimate)
- reagents_add = list("lsd" = 0.15, "thc" = 0.15, "cbd" = 0.15)
+ reagents_add = list("lsd" = 0.15, "thc" = 0.15, "cbd" = 0.15, "happiness" = 0.15)
rarity = 40
/obj/item/seeds/cannabis/death
diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm
index 6b2a787ac4df..4f759bafd99d 100644
--- a/code/modules/hydroponics/grown/flowers.dm
+++ b/code/modules/hydroponics/grown/flowers.dm
@@ -43,7 +43,7 @@
/obj/item/reagent_containers/food/snacks/grown/poppy/lily
seed = /obj/item/seeds/poppy/lily
name = "lily"
- desc = "A beautiful white flower"
+ desc = "A beautiful white flower."
icon_state = "lily"
tastes = list("lily" = 1)
filling_color = "#C7BBAD"
@@ -63,7 +63,7 @@
/obj/item/reagent_containers/food/snacks/grown/poppy/geranium
seed = /obj/item/seeds/poppy/geranium
name = "geranium"
- desc = "A beautiful purple flower"
+ desc = "A beautiful purple flower."
icon_state = "geranium"
tastes = list("geranium" = 1)
filling_color = "#A463FB"
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index d6a528f1f6f3..56665987307b 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -52,7 +52,7 @@
/obj/effect/baseturf_helper/lava_land/surface
name = "lavaland baseturf editor"
- baseturf = /turf/simulated/floor/plating/lava/smooth/lava_land_surface
+ baseturf = /turf/simulated/floor/plating/lava/smooth/mapping_lava
/obj/effect/mapping_helpers
icon = 'icons/effects/mapping_helpers.dmi'
diff --git a/code/modules/mapping/windoor_access_helpers.dm b/code/modules/mapping/windoor_access_helpers.dm
index ffb28905e874..fcc341441e5b 100644
--- a/code/modules/mapping/windoor_access_helpers.dm
+++ b/code/modules/mapping/windoor_access_helpers.dm
@@ -143,9 +143,6 @@
/obj/effect/mapping_helpers/airlock/windoor/access/any/science/tox_storage
access = ACCESS_TOX_STORAGE
-/obj/effect/mapping_helpers/airlock/windoor/access/any/science/genetics
- access = ACCESS_GENETICS
-
/obj/effect/mapping_helpers/airlock/windoor/access/any/science/robotics
access = ACCESS_ROBOTICS
@@ -165,6 +162,9 @@
/obj/effect/mapping_helpers/airlock/windoor/access/any/security/general
access = ACCESS_SECURITY
+/obj/effect/mapping_helpers/airlock/windoor/access/any/security/forensics
+ access = ACCESS_FORENSICS_LOCKERS
+
/obj/effect/mapping_helpers/airlock/windoor/access/any/security/doors
access = ACCESS_SEC_DOORS
@@ -208,7 +208,7 @@
/obj/effect/mapping_helpers/airlock/windoor/access/any/service/library
access = ACCESS_LIBRARY
-/obj/effect/mapping_helpers/airlock/windoor/access/any/service/library
+/obj/effect/mapping_helpers/airlock/windoor/access/any/service/theatre
access = ACCESS_THEATRE
/obj/effect/mapping_helpers/airlock/windoor/access/any/service/clown
@@ -352,9 +352,6 @@
/obj/effect/mapping_helpers/airlock/windoor/access/all/science/tox_storage
access = ACCESS_TOX_STORAGE
-/obj/effect/mapping_helpers/airlock/windoor/access/all/science/genetics
- access = ACCESS_GENETICS
-
/obj/effect/mapping_helpers/airlock/windoor/access/all/science/robotics
access = ACCESS_ROBOTICS
@@ -374,6 +371,9 @@
/obj/effect/mapping_helpers/airlock/windoor/access/all/security/general
access = ACCESS_SECURITY
+/obj/effect/mapping_helpers/airlock/windoor/access/all/security/forensics
+ access = ACCESS_FORENSICS_LOCKERS
+
/obj/effect/mapping_helpers/airlock/windoor/access/all/security/doors
access = ACCESS_SEC_DOORS
@@ -420,7 +420,7 @@
/obj/effect/mapping_helpers/airlock/windoor/access/all/service/library
access = ACCESS_LIBRARY
-/obj/effect/mapping_helpers/airlock/windoor/access/all/service/library
+/obj/effect/mapping_helpers/airlock/windoor/access/all/service/theatre
access = ACCESS_THEATRE
/obj/effect/mapping_helpers/airlock/windoor/access/all/service/clown
diff --git a/code/modules/martial_arts/cqc.dm b/code/modules/martial_arts/cqc.dm
index b83825d7089b..bed18503f97d 100644
--- a/code/modules/martial_arts/cqc.dm
+++ b/code/modules/martial_arts/cqc.dm
@@ -39,8 +39,16 @@
return ..()
/datum/martial_art/cqc/teach(mob/living/carbon/human/H, make_temporary)
- var/datum/action/defensive_stance/defensive = new /datum/action/defensive_stance()
- defensive.Grant(H)
+ var/found = FALSE
+ for(var/datum/martial_art/cqc/under_siege/M in H.mind.known_martial_arts)
+ M.remove(H)
+ for(var/datum/action/D in H.actions)
+ if(istype(D, /datum/action/defensive_stance))
+ found = TRUE
+ break
+ if(!found)
+ var/datum/action/defensive_stance/defensive = new /datum/action/defensive_stance()
+ defensive.Grant(H)
return ..()
/datum/martial_art/cqc/remove(mob/living/carbon/human/H)
diff --git a/code/modules/martial_arts/judo.dm b/code/modules/martial_arts/judo.dm
index a1b49e86d8c8..7ade349de0e8 100644
--- a/code/modules/martial_arts/judo.dm
+++ b/code/modules/martial_arts/judo.dm
@@ -63,3 +63,33 @@
/datum/martial_art/cqc/explaination_footer(user)
to_chat(user, "Your unarmed strikes hit about twice as hard as your peers, on average.")
+
+/datum/martial_art/judo/under_siege
+ name = "Professional Bodyguarding"
+ var/static/list/areas_under_siege = typecacheof(list(/area/crew_quarters/kitchen, /area/crew_quarters/bar))
+
+/datum/martial_art/judo/under_siege/teach(mob/living/carbon/human/H, make_temporary)
+ RegisterSignal(H, COMSIG_AREA_ENTERED, PROC_REF(bar_check))
+ return ..()
+
+/datum/martial_art/judo/under_siege/remove(mob/living/carbon/human/H)
+ UnregisterSignal(H, COMSIG_AREA_ENTERED)
+ return ..()
+
+/datum/martial_art/judo/under_siege/proc/bar_check(mob/living/carbon/human/H, area/entered_area)
+ SIGNAL_HANDLER
+ if(!is_type_in_typecache(entered_area, areas_under_siege))
+ var/list/held_items = list(H.get_active_hand(), H.get_inactive_hand())
+ for(var/obj/item/slapper/parry/smacking_hand in held_items)
+ qdel(smacking_hand)
+ can_parry = FALSE
+ weight = 0
+ else
+ can_parry = TRUE
+ weight = 5
+
+/datum/martial_art/judo/under_siege/can_use(mob/living/carbon/human/H)
+ var/area/A = get_area(H)
+ if(!(is_type_in_typecache(A, areas_under_siege)))
+ return FALSE
+ return ..()
diff --git a/code/modules/martial_arts/krav_maga.dm b/code/modules/martial_arts/krav_maga.dm
index 3704592303f6..133f385e108b 100644
--- a/code/modules/martial_arts/krav_maga.dm
+++ b/code/modules/martial_arts/krav_maga.dm
@@ -10,7 +10,7 @@
name = "Neutral Stance - You relax, cancelling your last Krav Maga stance attack."
button_icon_state = "neutralstance"
-/datum/action/neutral_stance/Trigger()
+/datum/action/neutral_stance/Trigger(left_click)
var/mob/living/carbon/human/H = owner
if(!H.mind.martial_art.in_stance)
to_chat(owner, "You cannot cancel an attack you haven't prepared!")
@@ -24,7 +24,7 @@
name = "Neck Chop - Injures the neck, stopping the victim from speaking for a while."
button_icon_state = "neckchop"
-/datum/action/neck_chop/Trigger()
+/datum/action/neck_chop/Trigger(left_click)
var/mob/living/carbon/human/H = owner //This is a janky solution, but I want to refactor krav anyway and un-jank this (written in may 2023)
if(!istype(H.mind.martial_art, /datum/martial_art/krav_maga))
to_chat(owner, "You don't know how to do that right now.")
@@ -42,7 +42,7 @@
name = "Leg Sweep - Trips the victim, rendering them prone and unable to move for a short time."
button_icon_state = "legsweep"
-/datum/action/leg_sweep/Trigger()
+/datum/action/leg_sweep/Trigger(left_click)
var/mob/living/carbon/human/H = owner
if(!istype(H.mind.martial_art, /datum/martial_art/krav_maga))
to_chat(owner, "You don't know how to do that right now.")
@@ -64,7 +64,7 @@
name = "Lung Punch - Delivers a strong punch just above the victim's abdomen, constraining the lungs. The victim will be unable to breathe for a short time."
button_icon_state = "lungpunch"
-/datum/action/lung_punch/Trigger()
+/datum/action/lung_punch/Trigger(left_click)
var/mob/living/carbon/human/H = owner
if(!istype(H.mind.martial_art, /datum/martial_art/krav_maga))
to_chat(owner, "You don't know how to do that right now.")
diff --git a/code/modules/martial_arts/martial.dm b/code/modules/martial_arts/martial.dm
index b23a82911e2e..53d7da1032ab 100644
--- a/code/modules/martial_arts/martial.dm
+++ b/code/modules/martial_arts/martial.dm
@@ -4,8 +4,8 @@
/datum/martial_art
var/name = "Martial Art"
var/streak = ""
- var/max_streak_length = 6
var/temporary = FALSE
+ var/owner_UID
/// The permanent style.
var/datum/martial_art/base = null
/// Chance to deflect projectiles while on throw mode.
@@ -27,8 +27,8 @@
var/list/combos = list()
/// What combos are currently (possibly) being performed.
var/list/datum/martial_art/current_combos = list()
- /// When the last hit happened.
- var/last_hit = 0
+ /// Stores the timer_id for the combo timeout timer
+ var/combo_timer
/// If the user is preparing a martial arts stance.
var/in_stance = FALSE
/// If the martial art allows parrying.
@@ -62,16 +62,25 @@
/datum/martial_art/proc/act(step, mob/living/carbon/human/user, mob/living/carbon/human/target)
if(!can_use(user))
return MARTIAL_ARTS_CANNOT_USE
- if(last_hit + COMBO_ALIVE_TIME < world.time)
- reset_combos()
- last_hit = world.time
+ if(combo_timer)
+ deltimer(combo_timer)
+
+ combo_timer = addtimer(CALLBACK(src, PROC_REF(reset_combos)), COMBO_ALIVE_TIME, TIMER_UNIQUE | TIMER_STOPPABLE)
if(HAS_COMBOS)
- return check_combos(step, user, target)
+ streak += intent_to_streak(step)
+ var/mob/living/carbon/human/owner = locateUID(owner_UID)
+ if(istype(owner) && !QDELETED(owner))
+ owner.hud_used.combo_display.update_icon(ALL, streak)
+ return check_combos(step, user, target)
return FALSE
/datum/martial_art/proc/reset_combos()
current_combos.Cut()
+ streak = ""
+ var/mob/living/carbon/human/owner = locateUID(owner_UID)
+ if(istype(owner) && !QDELETED(owner))
+ owner.hud_used.combo_display.update_icon(ALL, streak)
for(var/combo_type in combos)
current_combos.Add(new combo_type())
@@ -147,6 +156,7 @@
return
if(has_explaination_verb)
H.verbs |= /mob/living/carbon/human/proc/martial_arts_help
+ owner_UID = H.UID()
H.mind.known_martial_arts.Add(src)
H.mind.martial_art = get_highest_weight(H)
@@ -154,6 +164,7 @@
var/datum/martial_art/MA = src
if(!H.mind)
return
+ deltimer(combo_timer)
H.mind.known_martial_arts.Remove(MA)
H.mind.martial_art = get_highest_weight(H)
H.verbs -= /mob/living/carbon/human/proc/martial_arts_help
@@ -210,11 +221,22 @@
/datum/martial_art/proc/try_deflect(mob/user)
return prob(deflection_chance)
+/datum/martial_art/proc/intent_to_streak(intent)
+ switch(intent)
+ if(MARTIAL_COMBO_STEP_HARM)
+ return "E" // these hands are rated E for everyone
+ if(MARTIAL_COMBO_STEP_DISARM)
+ return "D"
+ if(MARTIAL_COMBO_STEP_GRAB)
+ return "G"
+ if(MARTIAL_COMBO_STEP_HELP)
+ return "H"
+
/datum/action/defensive_stance
name = "Defensive Stance - Ready yourself to be attacked, allowing you to parry incoming melee hits."
button_icon_state = "block"
-/datum/action/defensive_stance/Trigger()
+/datum/action/defensive_stance/Trigger(left_click)
var/mob/living/carbon/human/H = owner
var/datum/martial_art/MA = H.mind.martial_art //This should never be available to non-martial-arts users anyway
if(!MA.can_parry)
@@ -348,27 +370,27 @@
new /obj/effect/decal/cleanable/ash(get_turf(src))
qdel(src)
-/obj/item/twohanded/bostaff
+/obj/item/bostaff
name = "bo staff"
desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate."
force = 10
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BACK
- force_unwielded = 10
- force_wielded = 24
throwforce = 20
throw_speed = 2
attack_verb = list("smashed", "slammed", "whacked", "thwacked")
icon_state = "bostaff0"
+ base_icon_state = "bostaff"
-/obj/item/twohanded/bostaff/Initialize(mapload)
+/obj/item/bostaff/Initialize(mapload)
. = ..()
AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = ALL_ATTACK_TYPES)
+ AddComponent(/datum/component/two_handed, force_wielded = 24, force_unwielded = force, icon_wielded = "[base_icon_state]1")
-/obj/item/twohanded/bostaff/update_icon_state()
- icon_state = "bostaff[wielded]"
+/obj/item/bostaff/update_icon_state()
+ icon_state = "[base_icon_state]0"
-/obj/item/twohanded/bostaff/attack(mob/target, mob/living/user)
+/obj/item/bostaff/attack(mob/target, mob/living/user)
add_fingerprint(user)
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
to_chat(user, "You club yourself over the head with [src].")
@@ -392,7 +414,7 @@
return
switch(user.a_intent)
if(INTENT_DISARM)
- if(!wielded)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED))
return ..()
if(!ishuman(target))
return ..()
@@ -423,10 +445,41 @@
else
return ..()
-/obj/item/twohanded/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(wielded)
+/obj/item/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
return ..()
return 0
+/obj/screen/combo
+ icon_state = ""
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ screen_loc = ui_combo
+ layer = ABOVE_HUD_LAYER
+ var/streak
+
+/obj/screen/combo/proc/clear_streak()
+ cut_overlays()
+ streak = ""
+ icon_state = ""
+
+/obj/screen/combo/update_icon(updates, _streak)
+ streak = _streak
+ return ..()
+
+/obj/screen/combo/update_overlays()
+ . = list()
+ for(var/i in 1 to length(streak))
+ var/intent_text = copytext(streak, i, i + 1)
+ var/image/intent_icon = image(icon, src, "combo_[intent_text]")
+ intent_icon.pixel_x = 16 * (i - 1) - 8 * length(streak)
+ . += intent_icon
+
+/obj/screen/combo/update_icon_state()
+ icon_state = ""
+ if(!streak)
+ return
+ icon_state = "combo"
+
+
#undef HAS_COMBOS
#undef COMBO_ALIVE_TIME
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index 3cd2c42dc5ed..1fb1e5a6d90a 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -1,16 +1,15 @@
/*********************Mining Hammer****************/
-/obj/item/twohanded/kinetic_crusher
- icon = 'icons/obj/mining.dmi'
- icon_state = "crusher"
- item_state = "crusher0"
+/obj/item/kinetic_crusher
name = "proto-kinetic crusher"
desc = "An early design of the proto-kinetic accelerator, it is little more than a combination of various mining tools cobbled together, forming a high-tech club. \
While it is an effective mining tool, it did little to aid any but the most skilled and/or suicidal miners against local fauna."
+ icon = 'icons/obj/mining.dmi'
+ base_icon_state = "crusher"
+ icon_state = "crusher"
+ item_state = "crusher0"
force = 0 //You can't hit stuff unless wielded
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BACK
- force_unwielded = 0
- force_wielded = 20
throwforce = 5
throw_speed = 4
armour_penetration_flat = 10
@@ -27,16 +26,22 @@
var/light_on = FALSE
var/brightness_on = 5
var/adaptive_damage_bonus = 0
+ /// Amount of force typically assigned when wielded
+ var/base_force_wielded = 20
+ /// Tracker for the current amount of force used when the item is wielded.
+ /// Note that this isn't wired to anything, and must be updated with AddComponent to have value.
+ var/force_wielded = 20
-/obj/item/twohanded/kinetic_crusher/Initialize(mapload)
+/obj/item/kinetic_crusher/Initialize(mapload)
. = ..()
AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.7, _parryable_attack_types = MELEE_ATTACK, _parry_cooldown = (7 / 3) SECONDS ) // 2.3333 seconds of cooldown for 30% uptime
+ AddComponent(/datum/component/two_handed, force_wielded = force_wielded, force_unwielded = force)
-/obj/item/twohanded/kinetic_crusher/Destroy()
+/obj/item/kinetic_crusher/Destroy()
QDEL_LIST_CONTENTS(trophies)
return ..()
-/obj/item/twohanded/kinetic_crusher/examine(mob/living/user)
+/obj/item/kinetic_crusher/examine(mob/living/user)
. = ..()
. += "Mark a large creature with the destabilizing force, then hit them in melee to do [force + detonation_damage] damage."
. += "Does [force + detonation_damage + backstab_bonus] damage if the target is backstabbed, instead of [force + detonation_damage]."
@@ -44,14 +49,14 @@
var/obj/item/crusher_trophy/T = t
. += "It has \a [T] attached, which causes [T.effect_desc()]."
-/obj/item/twohanded/kinetic_crusher/attackby(obj/item/I, mob/living/user)
+/obj/item/kinetic_crusher/attackby(obj/item/I, mob/living/user)
if(istype(I, /obj/item/crusher_trophy))
var/obj/item/crusher_trophy/T = I
T.add_to(src, user)
else
return ..()
-/obj/item/twohanded/kinetic_crusher/crowbar_act(mob/user, obj/item/I)
+/obj/item/kinetic_crusher/crowbar_act(mob/user, obj/item/I)
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
@@ -63,9 +68,9 @@
else
to_chat(user, "There are no trophies on [src].")
-/obj/item/twohanded/kinetic_crusher/attack(mob/living/target, mob/living/carbon/user)
- if(!wielded)
- to_chat(user, "[src] is too heavy to use with one hand. You fumble and drop everything.")
+/obj/item/kinetic_crusher/attack(mob/living/target, mob/living/carbon/user)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED))
+ to_chat(user, "[src] is too heavy to use with one hand. You fumble and drop everything.")
user.drop_r_hand()
user.drop_l_hand()
return
@@ -90,9 +95,9 @@
if(!QDELETED(C) && !QDELETED(target))
C.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did
-/obj/item/twohanded/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams)
+/obj/item/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams)
. = ..()
- if(!wielded)
+ if(!HAS_TRAIT(src, TRAIT_WIELDED))
return
if(user.has_status_effect(STATUS_EFFECT_DASH) && user.a_intent == INTENT_HELP)
if(user.throw_at(target, range = 3, speed = 3, spin = FALSE, diagonals_first = TRUE))
@@ -148,28 +153,28 @@
C.total_damage += detonation_damage
L.apply_damage(detonation_damage, BRUTE, blocked = def_check)
-/obj/item/twohanded/kinetic_crusher/proc/Recharge()
+/obj/item/kinetic_crusher/proc/Recharge()
if(!charged)
charged = TRUE
update_icon()
playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1)
-/obj/item/twohanded/kinetic_crusher/ui_action_click(mob/user, actiontype)
+/obj/item/kinetic_crusher/ui_action_click(mob/user, actiontype)
light_on = !light_on
playsound(user, 'sound/weapons/empty.ogg', 100, TRUE)
update_brightness(user)
update_icon()
-/obj/item/twohanded/kinetic_crusher/proc/update_brightness(mob/user = null)
+/obj/item/kinetic_crusher/proc/update_brightness(mob/user = null)
if(light_on)
set_light(brightness_on)
else
set_light(0)
-/obj/item/twohanded/kinetic_crusher/update_icon_state()
- item_state = "crusher[wielded]"
+/obj/item/kinetic_crusher/update_icon_state()
+ item_state = "crusher[HAS_TRAIT(src, TRAIT_WIELDED)]"
-/obj/item/twohanded/kinetic_crusher/update_overlays()
+/obj/item/kinetic_crusher/update_overlays()
. = ..()
if(!charged)
. += "[icon_state]_uncharged"
@@ -190,7 +195,7 @@
flag = BOMB
range = 6
log_override = TRUE
- var/obj/item/twohanded/kinetic_crusher/hammer_synced
+ var/obj/item/kinetic_crusher/hammer_synced
/obj/item/projectile/destabilizer/Destroy()
hammer_synced = null
@@ -232,12 +237,12 @@
return "errors"
/obj/item/crusher_trophy/attackby(obj/item/A, mob/living/user)
- if(istype(A, /obj/item/twohanded/kinetic_crusher))
+ if(istype(A, /obj/item/kinetic_crusher))
add_to(A, user)
else
..()
-/obj/item/crusher_trophy/proc/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/proc/add_to(obj/item/kinetic_crusher/H, mob/living/user)
for(var/t in H.trophies)
var/obj/item/crusher_trophy/T = t
if(istype(T, denied_type) || istype(src, T.denied_type))
@@ -250,14 +255,14 @@
to_chat(user, "You attach [src] to [H].")
return TRUE
-/obj/item/crusher_trophy/proc/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/proc/remove_from(obj/item/kinetic_crusher/H, mob/living/user)
forceMove(get_turf(H))
H.trophies -= src
return TRUE
/obj/item/crusher_trophy/Destroy()
- if(istype(loc, /obj/item/twohanded/kinetic_crusher))
- var/obj/item/twohanded/kinetic_crusher/crusher = loc
+ if(istype(loc, /obj/item/kinetic_crusher))
+ var/obj/item/kinetic_crusher/crusher = loc
crusher.trophies -= src
return ..()
@@ -353,12 +358,12 @@
/obj/item/crusher_trophy/legion_skull/effect_desc()
return "a kinetic crusher to recharge [bonus_value*0.1] second\s faster"
-/obj/item/crusher_trophy/legion_skull/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/legion_skull/add_to(obj/item/kinetic_crusher/H, mob/living/user)
. = ..()
if(.)
H.charge_time -= bonus_value
-/obj/item/crusher_trophy/legion_skull/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/legion_skull/remove_from(obj/item/kinetic_crusher/H, mob/living/user)
. = ..()
if(.)
H.charge_time += bonus_value
@@ -425,20 +430,21 @@
/obj/item/crusher_trophy/demon_claws/effect_desc()
return "melee hits to do [bonus_value * 0.2] more damage and heal you for [bonus_value * 0.1], with 5X effect on mark detonation"
-/obj/item/crusher_trophy/demon_claws/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/demon_claws/add_to(obj/item/kinetic_crusher/H, mob/living/user)
. = ..()
if(.)
H.force += bonus_value * 0.2
- H.force_unwielded += bonus_value * 0.2
H.force_wielded += bonus_value * 0.2
+ // don't update force since KCs have 0 force by default
+ H.AddComponent(/datum/component/two_handed, force_wielded = H.force_wielded, force_unwielded = H.force)
H.detonation_damage += bonus_value * 0.8
-/obj/item/crusher_trophy/demon_claws/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/demon_claws/remove_from(obj/item/kinetic_crusher/H, mob/living/user)
. = ..()
if(.)
H.force -= bonus_value * 0.2
- H.force_unwielded -= bonus_value * 0.2
H.force_wielded -= bonus_value * 0.2
+ H.AddComponent(/datum/component/two_handed, force_wielded = H.force_wielded, force_unwielded = H.force)
H.detonation_damage -= bonus_value * 0.8
/obj/item/crusher_trophy/demon_claws/on_melee_hit(mob/living/target, mob/living/user)
@@ -504,12 +510,12 @@
/obj/item/crusher_trophy/adaptive_intelligence_core/effect_desc()
return "melee hits deal [bonus_value] more damage per hit after hitting a target, up to [bonus_value * 10] extra damage to that target"
-/obj/item/crusher_trophy/adaptive_intelligence_core/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/adaptive_intelligence_core/add_to(obj/item/kinetic_crusher/H, mob/living/user)
. = ..()
if(.)
H.adaptive_damage_bonus += bonus_value
-/obj/item/crusher_trophy/adaptive_intelligence_core/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user)
+/obj/item/crusher_trophy/adaptive_intelligence_core/remove_from(obj/item/kinetic_crusher/H, mob/living/user)
. = ..()
if(.)
H.adaptive_damage_bonus -= bonus_value
diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm
index bf5a07633b8c..e90bd9443c4b 100644
--- a/code/modules/mining/fulton.dm
+++ b/code/modules/mining/fulton.dm
@@ -63,10 +63,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
to_chat(user, "You start attaching the pack to [A]...")
if(do_after(user, 50, target = A))
to_chat(user, "You attach the pack to [A] and activate it.")
- if(loc == user && istype(user.back, /obj/item/storage/backpack))
- var/obj/item/storage/backpack/B = user.back
- if(B.can_be_inserted(src, stop_messages = TRUE))
- B.handle_item_insertion(src)
+ user.equip_to_slot_if_possible(src, slot_in_backpack, FALSE, TRUE)
uses_left--
if(uses_left <= 0)
user.drop_item(src)
diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm
index c7557ccea6b1..7a9d08733e32 100644
--- a/code/modules/mining/lavaland/ash_flora.dm
+++ b/code/modules/mining/lavaland/ash_flora.dm
@@ -17,6 +17,7 @@
var/harvest_message_med = "You pick a mushroom, carefully collecting the shavings from its cap."
var/harvest_message_high = "You harvest and collect shavings from several mushroom caps."
var/harvested = FALSE
+ var/delete_on_harvest = FALSE
var/base_icon
var/regrowth_time_low = 8 MINUTES
var/regrowth_time_high = 16 MINUTES
@@ -46,6 +47,9 @@
name = harvested_name
desc = harvested_desc
harvested = TRUE
+ if(delete_on_harvest)
+ qdel(src)
+ return 1
addtimer(CALLBACK(src, PROC_REF(regrow)), rand(regrowth_time_low, regrowth_time_high))
return 1
@@ -143,6 +147,40 @@
// min dmg 3, max dmg 6, prob(70)
AddComponent(/datum/component/caltrop, 3, 6, 70)
+
+/*********
+ * Rocks *
+ *********/
+// (I know these aren't plants)
+
+/obj/structure/flora/ash/rock
+ name = "large rock"
+ desc = "A volcanic rock. Pioneers used to ride these babies for miles."
+ icon_state = "basalt1"
+ density = TRUE
+ resistance_flags = FIRE_PROOF
+ harvest = /obj/item/stack/ore/glass/basalt
+ harvest_time = 6 SECONDS
+ harvest_amount_low = 10
+ harvest_amount_high = 20
+ harvest_message_low = "You finish mining the rock."
+ harvest_message_med = "You finish mining the rock."
+ harvest_message_high = "You finish mining the rock."
+ delete_on_harvest = TRUE
+
+/obj/structure/flora/ash/rock/style_2
+ icon_state = "basalt2"
+
+/obj/structure/flora/ash/rock/style_3
+ icon_state = "basalt3"
+
+/obj/structure/flora/ash/rock/style_4
+ icon_state = "basalt4"
+
+/obj/structure/flora/ash/rock/style_random/Initialize(mapload)
+ . = ..()
+ icon_state = "basalt[rand(1, 4)]"
+
/obj/item/reagent_containers/food/snacks/grown/ash_flora
name = "mushroom shavings"
desc = "Some shavings from a tall mushroom. With enough, might serve as a bowl."
diff --git a/code/modules/mining/lavaland/loot/ashdragon_loot.dm b/code/modules/mining/lavaland/loot/ashdragon_loot.dm
index dcf6e895f93a..e5ac63480a73 100644
--- a/code/modules/mining/lavaland/loot/ashdragon_loot.dm
+++ b/code/modules/mining/lavaland/loot/ashdragon_loot.dm
@@ -201,11 +201,10 @@
/obj/item/lava_staff
name = "staff of lava"
desc = "The power of fire and rocks in your hands!"
- icon_state = "staffofstorms"
- item_state = "staffofstorms"
+ icon_state = "lavastaff"
+ item_state = "lavastaff"
icon = 'icons/obj/guns/magic.dmi'
slot_flags = SLOT_BACK
- item_state = "staffofstorms"
w_class = WEIGHT_CLASS_BULKY
force = 25
damtype = BURN
diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm
index ef717d210fc6..d4126deb2be9 100644
--- a/code/modules/mining/lavaland/loot/colossus_loot.dm
+++ b/code/modules/mining/lavaland/loot/colossus_loot.dm
@@ -299,33 +299,6 @@
/mob/living/simple_animal/hostile/lightgeist/ghost()
qdel(src)
-/obj/machinery/anomalous_crystal/refresher //Deletes and recreates a copy of the item, "refreshing" it.
- activation_method = "touch"
- cooldown_add = 50
- activation_sound = 'sound/magic/timeparadox2.ogg'
- var/list/banned_items_typecache = list(/obj/item/storage, /obj/item/implant, /obj/item/implanter, /obj/item/disk/nuclear,
- /obj/item/projectile, /obj/item/spellbook, /obj/item/clothing/mask/facehugger, /obj/item/contractor_uplink)
-
-/obj/machinery/anomalous_crystal/refresher/Initialize(mapload)
- . = ..()
- banned_items_typecache = typecacheof(banned_items_typecache)
-
-
-/obj/machinery/anomalous_crystal/refresher/ActivationReaction(mob/user, method)
- if(..())
- var/list/L = list()
- var/turf/T = get_step(src, dir)
- new /obj/effect/temp_visual/emp/pulse(T)
- for(var/i in T)
- if(isitem(i) && !is_type_in_typecache(i, banned_items_typecache))
- var/obj/item/W = i
- if(!W.admin_spawned && !(W.flags_2 & HOLOGRAM_2) && !(W.flags & ABSTRACT))
- L += W
- if(L.len)
- var/obj/item/CHOSEN = pick(L)
- new CHOSEN.type(T)
- qdel(CHOSEN)
-
/obj/machinery/anomalous_crystal/possessor //Allows you to bodyjack small animals, then exit them at your leisure, but you can only do this once per activation. Because they blow up. Also, if the bodyjacked animal dies, SO DO YOU.
activation_method = "touch"
diff --git a/code/modules/mining/lavaland/loot/legion_loot.dm b/code/modules/mining/lavaland/loot/legion_loot.dm
index 4501ff2f6cb7..ba6f267aa422 100644
--- a/code/modules/mining/lavaland/loot/legion_loot.dm
+++ b/code/modules/mining/lavaland/loot/legion_loot.dm
@@ -1,4 +1,4 @@
-/obj/item/staff/storm
+/obj/item/storm_staff
name = "staff of storms"
desc = "An ancient staff retrieved from the remains of Legion. The wind stirs as you move it."
icon_state = "staffofstorms"
@@ -8,16 +8,27 @@
w_class = WEIGHT_CLASS_BULKY
force = 25
damtype = BURN
- hitsound = 'sound/weapons/sear.ogg'
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
needs_permit = TRUE
- var/storm_type = /datum/weather/ash_storm
- var/storm_cooldown = 0
+ var/max_thunder_charges = 3
+ var/thunder_charges = 3
+ var/thunder_charge_time = 15 SECONDS
+ var/static/list/excluded_areas = list(/area/space)
+ ///This is a list of turfs currently being targeted.
+ var/list/targeted_turfs = list()
-/obj/item/staff/storm/attack_self(mob/user)
- if(storm_cooldown > world.time)
- to_chat(user, "The staff is still recharging!")
- return
+/obj/item/storm_staff/Destroy()
+ targeted_turfs = null
+ return ..()
+
+/obj/item/storm_staff/examine(mob/user)
+ . = ..()
+ . += "It has [thunder_charges] charges remaining."
+ . += "Use it in hand to dispel storms."
+ . += "Use it on targets to summon thunderbolts from the sky."
+ . += "The thunderbolts are boosted if in an area with weather effects."
+/obj/item/storm_staff/attack_self(mob/user)
var/area/user_area = get_area(user)
var/turf/user_turf = get_turf(user)
if(!user_area || !user_turf)
@@ -39,16 +50,81 @@
"You hold [src] skyward, dispelling the storm!")
playsound(user, 'sound/magic/staff_change.ogg', 200, 0)
A.wind_down()
- return
- else
- A = new storm_type(list(user_turf.z))
- A.name = "staff storm"
- A.area_type = user_area.type
- A.telegraph_duration = 100
- A.end_duration = 100
-
- user.visible_message("[user] holds [src] skywards as red lightning crackles into the sky!", \
- "You hold [src] skyward, calling down a terrible storm!")
- playsound(user, 'sound/magic/staff_change.ogg', 200, 0)
- A.telegraph()
- storm_cooldown = world.time + 200
+ var/old_color = user.color
+ user.color = list(340/255, 240/255, 0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
+ var/old_transform = user.transform
+ user.transform *= 1.2
+ animate(user, color = old_color, transform = old_transform, time = 1 SECONDS)
+
+/obj/item/storm_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!thunder_charges)
+ to_chat(user, "The staff needs to recharge.")
+ return
+ var/turf/target_turf = get_turf(target)
+ var/area/target_area = get_area(target)
+ var/area/user_area = get_area(user)
+ if(!target_turf || !target_area || (is_type_in_list(target_area, excluded_areas)) || !user_area || (is_type_in_list(user_area, excluded_areas)))
+ to_chat(user, "The staff will not work here.")
+ return
+ if(target_turf in targeted_turfs)
+ to_chat(user, "That SPOT is already being shocked!")
+ return
+ if(HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, "You don't want to hurt anyone!")
+ return
+ var/power_boosted = FALSE
+ for(var/V in SSweather.processing)
+ var/datum/weather/W = V
+ if((target_turf.z in W.impacted_z_levels) && W.area_type == target_area.type)
+ power_boosted = TRUE
+ break
+ playsound(src, 'sound/magic/lightningshock.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
+ targeted_turfs += target_turf
+ to_chat(user, "You aim at [target_turf]!")
+ new /obj/effect/temp_visual/thunderbolt_targeting(target_turf)
+ addtimer(CALLBACK(src, PROC_REF(throw_thunderbolt), target_turf, power_boosted), 1.5 SECONDS)
+ thunder_charges--
+ addtimer(CALLBACK(src, PROC_REF(recharge)), thunder_charge_time)
+
+/obj/item/storm_staff/proc/recharge(mob/user)
+ thunder_charges = min(thunder_charges + 1, max_thunder_charges)
+ playsound(src, 'sound/magic/charge.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
+
+/obj/item/storm_staff/proc/throw_thunderbolt(turf/target, boosted)
+ targeted_turfs -= target
+ new /obj/effect/temp_visual/thunderbolt(target)
+ var/list/affected_turfs = list(target)
+ if(boosted)
+ for(var/direction in GLOB.alldirs)
+ var/turf_to_add = get_step(target, direction)
+ if(!turf_to_add)
+ continue
+ affected_turfs += turf_to_add
+ for(var/turf/T as anything in affected_turfs)
+ new /obj/effect/temp_visual/electricity(T)
+ for(var/mob/living/hit_mob in T)
+ to_chat(hit_mob, "You've been struck by lightning!")
+ hit_mob.electrocute_act(15 * (isanimal(hit_mob) ? 3 : 1) * (T == target ? 2 : 1) * (boosted ? 2 : 1), src, flags = SHOCK_TESLA|SHOCK_NOSTUN)
+
+ for(var/obj/hit_thing in T)
+ hit_thing.take_damage(20, BURN, ENERGY, FALSE)
+ playsound(target, 'sound/magic/lightningbolt.ogg', 100, TRUE)
+ target.visible_message("A thunderbolt strikes [target]!")
+ explosion(target, -1, -1, light_impact_range = (boosted ? 1 : 0), flame_range = (boosted ? 2 : 1), silent = TRUE)
+
+
+/obj/effect/temp_visual/thunderbolt_targeting
+ icon_state = "target_circle"
+ layer = BELOW_MOB_LAYER
+ light_range = 1
+ duration = 2 SECONDS
+
+/obj/effect/temp_visual/thunderbolt
+ icon_state = "thunderbolt"
+ icon = 'icons/effects/32x96.dmi'
+ duration = 0.6 SECONDS
+
+/obj/effect/temp_visual/electricity
+ icon_state = "electricity3"
+ duration = 0.5 SECONDS
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 3f2273eedd40..4a4ba56ea2e5 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -70,17 +70,6 @@
new /obj/item/borg/upgrade/modkit/lifesteal(src)
new /obj/item/bedsheet/cult(src)
-/obj/structure/closet/crate/necropolis/puzzle
- name = "puzzling chest"
-
-/obj/structure/closet/crate/necropolis/puzzle/populate_contents()
- var/loot = rand(1,2)
- switch(loot)
- if(1)
- new /obj/item/soulstone/anybody(src)
- if(2)
- new /obj/item/wisp_lantern(src)
-
//KA modkit design discs
/obj/item/disk/design_disk/modkit_disk
name = "\improper KA mod disk"
@@ -162,7 +151,7 @@
name = "champion's hardsuit"
desc = "Voices echo from the hardsuit, driving the user insane. Is not space-proof."
icon_state = "hardsuit-berserker"
- allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear)
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/spear)
armor = list(MELEE = 30, BULLET = 15, LASER = 10, ENERGY = 10, BOMB = 150, RAD = 0, FIRE = INFINITY, ACID = INFINITY)
hoodtype = /obj/item/clothing/head/hooded/berserker
flags_inv = HIDEGLOVES | HIDESHOES | HIDEJUMPSUIT | HIDETAIL
@@ -279,7 +268,7 @@
name = "Berserk"
desc = "Increase your movement and melee speed while also increasing your melee armor for a short amount of time."
-/datum/action/item_action/berserk_mode/Trigger(trigger_flags)
+/datum/action/item_action/berserk_mode/Trigger(left_click)
if(istype(target, /obj/item/clothing/head/hooded/berserker))
var/obj/item/clothing/head/hooded/berserker/berzerk = target
if(berzerk.berserk_active)
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index 098f3a0ced06..c3f070d0b918 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -35,11 +35,12 @@
EQUIPMENT("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
EQUIPMENT("Fulton Beacon", /obj/item/fulton_core, 400),
EQUIPMENT("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript, 1500),
- EQUIPMENT("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000),
+ EQUIPMENT("Advanced Jetpack Module", /obj/item/mod/module/jetpack/advanced, 2000),
EQUIPMENT("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
EQUIPMENT("Lazarus Capsule", /obj/item/mobcapsule, 800),
EQUIPMENT("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200),
- EQUIPMENT("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
+ EQUIPMENT("Mining MODsuit", /obj/item/mod/control/pre_equipped/mining/vendor, 3500),
+ EQUIPMENT("Asteroid MODsuit Skin", /obj/item/mod/skin_applier/asteroid, 1000),
EQUIPMENT("Tracking Bio-chip Kit", /obj/item/storage/box/minertracker, 600),
)
prize_list["Consumables"] = list(
@@ -68,7 +69,7 @@
prize_list["Digging Tools"] = list(
EQUIPMENT("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
- EQUIPMENT("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750),
+ EQUIPMENT("Kinetic Crusher", /obj/item/kinetic_crusher, 750),
EQUIPMENT("Resonator", /obj/item/resonator, 800),
EQUIPMENT("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
EQUIPMENT("Super Resonator", /obj/item/resonator/upgraded, 2500),
@@ -260,7 +261,7 @@
new /obj/item/stack/marker_beacon/thirty(drop_location)
if("Crusher Kit")
new /obj/item/extinguisher/mini(drop_location)
- new /obj/item/twohanded/kinetic_crusher(drop_location)
+ new /obj/item/kinetic_crusher(drop_location)
if("Mining Conscription Kit")
new /obj/item/storage/backpack/duffel/mining_conscript(drop_location)
@@ -342,6 +343,7 @@
EQUIPMENT("Dnd set", /obj/item/storage/box/characters, 500),
EQUIPMENT("Dice set", /obj/item/storage/box/dice, 250),
EQUIPMENT("Cards", /obj/item/deck/cards, 150),
+ EQUIPMENT("UNUM!", /obj/item/deck/unum, 200),
EQUIPMENT("Guitar", /obj/item/instrument/guitar, 750),
EQUIPMENT("Synthesizer", /obj/item/instrument/piano_synth, 1500),
EQUIPMENT("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index 728c1f7ecc7f..836d504dad7f 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -221,7 +221,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
icon_state = "slag"
singular_name = "slag chunk"
-/obj/item/twohanded/required/gibtonite
+/obj/item/gibtonite
name = "gibtonite ore"
desc = "Extremely explosive if struck with mining equipment, Gibtonite is often used by miners to speed up their work by using it as a mining charge. This material is illegal to possess by unauthorized personnel under space law."
icon = 'icons/obj/mining.dmi'
@@ -236,13 +236,17 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
var/attacher = "UNKNOWN"
var/datum/wires/explosive/gibtonite/wires
-/obj/item/twohanded/required/gibtonite/Destroy()
+/obj/item/gibtonite/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
+
+/obj/item/gibtonite/Destroy()
if(wires)
SStgui.close_uis(wires)
QDEL_NULL(wires)
return ..()
-/obj/item/twohanded/required/gibtonite/attackby(obj/item/I, mob/user, params)
+/obj/item/gibtonite/attackby(obj/item/I, mob/user, params)
if(!wires && istype(I, /obj/item/assembly/igniter))
user.visible_message("[user] attaches [I] to [src].", "You attach [I] to [src].")
wires = new(src)
@@ -268,24 +272,24 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
return
..()
-/obj/item/twohanded/required/gibtonite/attack_ghost(mob/user)
+/obj/item/gibtonite/attack_ghost(mob/user)
if(wires)
wires.Interact(user)
-/obj/item/twohanded/required/gibtonite/attack_self(mob/user)
+/obj/item/gibtonite/attack_self(mob/user)
if(wires)
wires.Interact(user)
else
..()
-/obj/item/twohanded/required/gibtonite/bullet_act(obj/item/projectile/P)
+/obj/item/gibtonite/bullet_act(obj/item/projectile/P)
GibtoniteReaction(P.firer)
..()
-/obj/item/twohanded/required/gibtonite/ex_act()
+/obj/item/gibtonite/ex_act()
GibtoniteReaction(null, 1)
-/obj/item/twohanded/required/gibtonite/proc/GibtoniteReaction(mob/user, triggered_by = 0)
+/obj/item/gibtonite/proc/GibtoniteReaction(mob/user, triggered_by = 0)
if(!primed)
playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,1)
primed = 1
diff --git a/code/modules/mob/dead/observer/observer_say.dm b/code/modules/mob/dead/observer/observer_say.dm
index ac39b557db0b..a9f4ccef6c23 100644
--- a/code/modules/mob/dead/observer/observer_say.dm
+++ b/code/modules/mob/dead/observer/observer_say.dm
@@ -9,8 +9,16 @@
/mob/dead/observer/handle_track(message, verb = "says", mob/speaker = null, speaker_name, atom/follow_target, hard_to_hear)
return "[speaker_name] ([ghost_follow_link(follow_target, ghost=src)])"
-/mob/dead/observer/handle_speaker_name(mob/speaker = null, vname, hard_to_hear)
+/mob/dead/observer/handle_speaker_name(mob/speaker = null, vname, hard_to_hear, check_name_against)
var/speaker_name = ..()
- if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker) && !isAutoAnnouncer(speaker)) //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs.
- speaker_name = "[speaker.real_name] ([speaker_name])"
+ if(!speaker)
+ return speaker_name
+ //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs.
+ if(isAI(speaker) || isAutoAnnouncer(speaker))
+ return speaker_name
+ if(!check_name_against)
+ check_name_against = speaker_name
+ if(check_name_against == speaker.real_name)
+ return speaker_name
+ speaker_name = "[speaker.real_name] ([speaker_name])"
return speaker_name
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index d8453a38f01b..361163f4df77 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -121,7 +121,7 @@
playsound_local(source, speech_sound, sound_vol, 1, sound_frequency)
-/mob/proc/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target, radio_freq)
+/mob/proc/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target, check_name_against)
if(!client)
return
@@ -137,7 +137,7 @@
if(!follow_target)
follow_target = speaker
- var/speaker_name = handle_speaker_name(speaker, vname, hard_to_hear)
+ var/speaker_name = handle_speaker_name(speaker, vname, hard_to_hear, check_name_against)
track = handle_track(message, verb, speaker, speaker_name, follow_target, hard_to_hear)
if(!can_hear())
diff --git a/code/modules/mob/inventory_procs.dm b/code/modules/mob/inventory_procs.dm
index 043f016d9789..e1771feafa62 100644
--- a/code/modules/mob/inventory_procs.dm
+++ b/code/modules/mob/inventory_procs.dm
@@ -28,6 +28,10 @@
return item_to_test && item_to_test.is_equivalent(I)
+/// Check if an item is in one of our hands
+/mob/proc/is_holding(obj/item/I)
+ return istype(I) && (I == r_hand || I == l_hand)
+
//Returns the thing in our inactive hand
/mob/proc/get_inactive_hand()
@@ -138,6 +142,10 @@
return TRUE
if((I.flags & NODROP) && !force)
return FALSE
+
+ if((SEND_SIGNAL(I, COMSIG_ITEM_PRE_UNEQUIP, force) & COMPONENT_ITEM_BLOCK_UNEQUIP) && !force)
+ return FALSE
+
return TRUE
/mob/proc/unEquip(obj/item/I, force, silent = FALSE) //Force overrides NODROP for things like wizarditis and admin undress.
@@ -261,11 +269,19 @@
S.handle_item_insertion(src)
return 1
- S = M.get_item_by_slot(slot_back) //else we put in backpack
- if(istype(S) && S.can_be_inserted(src, 1))
- S.handle_item_insertion(src)
- playsound(loc, "rustle", 50, 1, -5)
- return 1
+ var/obj/item/O = M.get_item_by_slot(slot_back) //else we put in backpack
+ if(istype(O, /obj/item/storage))
+ S = O
+ if(S.can_be_inserted(src, 1))
+ S.handle_item_insertion(src)
+ playsound(loc, "rustle", 50, TRUE, -5)
+ return 1
+ if(ismodcontrol(O))
+ var/obj/item/mod/control/C = O
+ if(C.can_be_inserted(src, 1))
+ C.handle_item_insertion(src)
+ playsound(loc, "rustle", 50, TRUE, -5)
+ return 1
to_chat(M, "You are unable to equip that!")
return 0
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index da06055cb138..55aff985f0fa 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -64,7 +64,7 @@
capitalize = 0
scrambled_text += next
var/chance = rand(100)
- if(join_override)
+ if(!isnull(join_override))
scrambled_text += join_override
else if(chance <= 5)
scrambled_text += ". "
@@ -274,7 +274,13 @@
colour = "trinary"
key = "5"
flags = RESTRICTED | WHITELISTED
- syllables = list("02011","01222","10100","10210","21012","02011","21200","1002","2001","0002","0012","0012","000","120","121","201","220","10","11","0")
+ syllables = list("0", "1", "2")
+ space_chance = 0
+ join_override = ""
+
+/datum/language/trinary/scramble(input)
+ . = ..(copytext(input, 1, max(length(input) / 4, 2)))
+
/datum/language/trinary/get_random_name()
var/new_name
@@ -296,11 +302,15 @@
syllables = list("click","clack")
/datum/language/kidan/get_random_name()
- var/new_name = "[pick(list("Vrax", "Krek", "Vriz", "Zrik", "Zarak", "Click", "Zerk", "Drax", "Zven", "Drexx"))]"
- new_name += ", "
- new_name += "[pick(list("Noble", "Worker", "Scout", "Builder", "Farmer", "Gatherer", "Soldier", "Guard", "Prospector"))]"
- new_name += " of Clan "
- new_name += "[pick(list("Tristan", "Zarlan", "Clack", "Kkraz", "Zramn", "Orlan", "Zrax"))]" //I ran out of ideas after the first two tbh -_-
+ var/new_name = "[pick(list("Vrax", "Krek", "Krekk", "Vriz", "Zrik", "Zarak", "Click", "Zerk", "Drax", "Zven", "Drexx", "Vrik", "Vrek", "Krax", "Varak", "Zavak", "Vrexx", "Drevk", "Krik", "Karak", "Krexx", "Zrax", "Zrexx", "Zrek", "Verk", "Drek", "Drikk", "Zvik", "Vzik", "Kviz", "Vrizk", "Vrizzk", "Krix", "Krixx", "Zark", "Xark", "Xarkk", "Xerx", "Xarak", "Karax", "Varak", "Vazak", "Vazzak", "Zirk", "Krak", "Xakk", "Zakk", "Vekk"))]"
+ if(prob(67))
+ if(prob(50))
+ new_name += ", "
+ new_name += "[pick(list("Noble", "Worker", "Scout", "Carpenter", "Farmer", "Gatherer", "Soldier", "Guard", "Miner", "Priest", "Merchant", "Crafter", "Alchemist", "Historian", "Hunter", "Scholar", "Caretaker", "Artist", "Bard", "Blacksmith", "Brewer", "Mason", "Baker", "Prospector", "Laborer", "Hauler", "Servant"))]"
+ new_name += " of Clan "
+ else
+ new_name += " "
+ new_name += "[pick(list("Tristan", "Zarlan", "Clack", "Kkraz", "Zramn", "Orlan", "Zrax", "Orax", "Oriz", "Tariz", "Kvestan"))]"
return new_name
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index 5b45fd6181bb..de23bdcd5bbe 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -1,5 +1,8 @@
/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
- ..(AM, hitpush = FALSE)
+ if(!skipcatch && in_throw_mode && !HAS_TRAIT(src, TRAIT_HANDS_BLOCKED) && HAS_TRAIT(AM, TRAIT_XENO_INTERACTABLE) && !restrained())
+ throw_mode_off()
+ AM.attack_hand(src)
+ ..(AM, skipcatch = TRUE, hitpush = FALSE)
/*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other.
As such, they can either help or harm other aliens. Help works like the human help command while harm is a simple nibble.
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index e76cd3f68c83..342cc29e2ea4 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -27,6 +27,7 @@
/obj/item/clothing/mask/facehugger/Initialize(mapload)
. = ..()
AddComponent(/datum/component/proximity_monitor)
+ ADD_TRAIT(src, TRAIT_XENO_INTERACTABLE, UID())
/obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
..()
@@ -36,9 +37,6 @@
/obj/item/clothing/mask/facehugger/attackby(obj/item/O, mob/user, params)
return O.attack_obj(src, user, params)
-/obj/item/clothing/mask/facehugger/attack_alien(mob/user) //can be picked up by aliens
- return attack_hand(user)
-
/obj/item/clothing/mask/facehugger/attack_hand(mob/user)
if((stat != DEAD && !sterile) && !isalien(user))
if(Attach(user))
diff --git a/code/modules/mob/living/carbon/brain/carbon_brain.dm b/code/modules/mob/living/carbon/brain/carbon_brain.dm
index cd60bb6c8615..38f4889933b0 100644
--- a/code/modules/mob/living/carbon/brain/carbon_brain.dm
+++ b/code/modules/mob/living/carbon/brain/carbon_brain.dm
@@ -50,6 +50,9 @@
/mob/living/carbon/brain/blob_act(obj/structure/blob/B)
return
+/mob/living/carbon/brain/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE)
+ return FALSE
+
/mob/living/carbon/brain/on_forcemove(atom/newloc)
if(container)
container.forceMove(newloc)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 02cdbf43b152..a29ed4328994 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -1,12 +1,13 @@
-/mob/living
- var/canEnterVentWith = "/obj/item/implant=0&/obj/item/clothing/mask/facehugger=0&/obj/item/radio/borg=0&/obj/machinery/camera=0"
- var/datum/middleClickOverride/middleClickOverride = null
-
/mob/living/carbon/Initialize(mapload)
. = ..()
GLOB.carbon_list += src
/mob/living/carbon/Destroy()
+ // We need to delete the back slot first, for modsuits. Otherwise, we have issues.
+ if(back)
+ var/obj/I = back
+ unEquip(I)
+ qdel(I)
// This clause is here due to items falling off from limb deletion
for(var/obj/item in get_all_slots())
unEquip(item)
@@ -94,21 +95,25 @@
return FALSE
-/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = 0, stun = 1, distance = 0, message = 1)
+/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = 0, should_confuse = TRUE, distance = 0, message = 1)
. = TRUE
if(stat == DEAD || ismachineperson(src)) // Dead people and IPCs do not vomit particulates
return FALSE
- if(stun)
- Stun(8 SECONDS)
+ if(should_confuse)
+ if(blood)
+ KnockDown(10 SECONDS)
+ AdjustConfused(8 SECONDS)
+ Slowed(8 SECONDS, 1)
if(!blood && nutrition < 100) // Nutrition vomiting while already starving
if(message)
visible_message("[src] dry heaves!", \
"You try to throw up, but there's nothing in your stomach!")
- if(stun)
- Weaken(20 SECONDS)
+ if(should_confuse)
+ KnockDown(20 SECONDS)
+ AdjustConfused(20 SECONDS)
return
if(message)
@@ -121,13 +126,13 @@
if(blood)
if(T)
add_splatter_floor(T)
- if(stun)
+ if(should_confuse)
adjustBruteLoss(3)
else
if(T)
T.add_vomit_floor()
adjust_nutrition(-lost_nutrition)
- if(stun)
+ if(should_confuse)
adjustToxLoss(-3)
T = get_step(T, dir)
@@ -187,19 +192,10 @@
KnockDown(6 SECONDS)
/mob/living/carbon/swap_hand()
- var/obj/item/item_in_hand = get_active_hand()
- if(item_in_hand) //this segment checks if the item in your hand is twohanded.
- if(istype(item_in_hand,/obj/item/twohanded))
- if(item_in_hand:wielded == 1)
- to_chat(usr, "Your other hand is too busy holding the [item_in_hand.name]")
- return
+ if(SEND_SIGNAL(src, COMSIG_MOB_SWAPPING_HANDS, get_active_hand()) == COMPONENT_BLOCK_SWAP)
+ return
hand = !hand
- if(hud_used && hud_used.inv_slots[slot_l_hand] && hud_used.inv_slots[slot_r_hand])
- var/obj/screen/inventory/hand/H
- H = hud_used.inv_slots[slot_l_hand]
- H.update_icon()
- H = hud_used.inv_slots[slot_r_hand]
- H.update_icon()
+ update_hands_hud()
SEND_SIGNAL(src, COMSIG_CARBON_SWAP_HANDS)
@@ -619,14 +615,16 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
visible_message("[src] slams into [hit_atom]!", "You slam into [hit_atom]!")
playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100, TRUE)
return
+ if(has_status_effect(STATUS_EFFECT_IMPACT_IMMUNE))
+ return
var/damage = 10 + 1.5 * speed // speed while thrower is standing still is 2, while walking with an aggressive grab is 2.4, highest speed is 14
hit_atom.hit_by_thrown_carbon(src, throwingdatum, damage, FALSE, FALSE)
/mob/living/carbon/hit_by_thrown_carbon(mob/living/carbon/human/C, datum/thrownthing/throwingdatum, damage, mob_hurt, self_hurt)
- for(var/obj/item/twohanded/dualsaber/D in contents)
- if(D.wielded && D.force)
+ for(var/obj/item/dualsaber/D in contents)
+ if(HAS_TRAIT(src, TRAIT_WIELDED) && D.force)
visible_message("[src] impales [C] with [D], before dropping them on the ground!")
C.apply_damage(100, BRUTE, "chest", sharp = TRUE, used_weapon = "Impaled on [D].")
C.Stun(2 SECONDS) //Punishment. This could also be used by a traitor to throw someone into a dsword to kill them, but hey, teamwork!
@@ -1027,13 +1025,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
//called when we get cuffed/uncuffed
/mob/living/carbon/proc/update_handcuffed()
+ SEND_SIGNAL(src, COMSIG_CARBON_UPDATE_HANDCUFFED, handcuffed)
if(handcuffed)
- //we don't want problems with nodrop shit if there ever is more than one nodrop twohanded
- var/obj/item/I = get_active_hand()
- if(istype(I, /obj/item/twohanded))
- var/obj/item/twohanded/TH = I //FML
- if(TH.wielded)
- TH.unwield()
drop_r_hand()
drop_l_hand()
stop_pulling()
@@ -1045,7 +1038,7 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
changeNext_move(CLICK_CD_RAPID) //reset click cooldown from handcuffs
update_action_buttons_icon() //some of our action buttons might be unusable when we're handcuffed.
update_inv_handcuffed()
- update_hud_handcuffed()
+ update_hands_hud()
/mob/living/carbon/get_standard_pixel_y_offset()
if(IS_HORIZONTAL(src))
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 0b94acb2c1a9..6a19650f051f 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -5,12 +5,12 @@
return FALSE
if(get_active_hand())
return FALSE
- if(istype(AM, /obj/item/twohanded))
+ if(AM.GetComponent(/datum/component/two_handed))
if(get_inactive_hand())
return FALSE
+ throw_mode_off()
put_in_active_hand(AM)
visible_message("[src] catches [AM]!")
- throw_mode_off()
SEND_SIGNAL(src, COMSIG_CARBON_THROWN_ITEM_CAUGHT, AM)
return TRUE
return ..()
diff --git a/code/modules/mob/living/carbon/carbon_emote.dm b/code/modules/mob/living/carbon/carbon_emote.dm
index 0c0f15d71612..5cbb7b12da68 100644
--- a/code/modules/mob/living/carbon/carbon_emote.dm
+++ b/code/modules/mob/living/carbon/carbon_emote.dm
@@ -188,3 +188,27 @@
if(. && isliving(user))
var/mob/living/L = user
L.SetSleeping(2 SECONDS)
+
+/datum/emote/living/carbon/twirl
+ key = "twirl"
+ key_third_person = "twirls"
+ message = "twirls something around in their hand."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/twirl/can_run_emote(mob/living/user, status_check, intentional)
+ . = ..()
+ if(!.)
+ return
+
+ if(user.l_hand || user.r_hand)
+ return TRUE
+
+ to_chat(user, "You need something in your hand to use this emote!")
+ return FALSE
+
+/datum/emote/living/carbon/twirl/run_emote(mob/user, params, type_override, intentional)
+
+ var/obj/item/thing = user.l_hand || user.r_hand
+ message = "twirls [thing] around in their hand!"
+ . = ..()
+ message = initial(message)
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index 2b9bbee950df..e8ef65c138b1 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -32,13 +32,13 @@
return
//update whether handcuffs appears on our hud.
-/mob/living/carbon/proc/update_hud_handcuffed()
- if(hud_used)
- var/obj/screen/inventory/R = hud_used.inv_slots[slot_r_hand]
- var/obj/screen/inventory/L = hud_used.inv_slots[slot_l_hand]
- if(R && L)
- R.update_icon()
- L.update_icon()
+/mob/living/carbon/proc/update_hands_hud()
+ if(!hud_used)
+ return
+ var/obj/screen/inventory/R = hud_used.inv_slots[slot_r_hand]
+ R?.update_icon()
+ var/obj/screen/inventory/L = hud_used.inv_slots[slot_l_hand]
+ L?.update_icon()
/mob/living/carbon/update_inv_r_hand(ignore_cuffs)
if(handcuffed && !ignore_cuffs)
@@ -85,3 +85,4 @@
//update whether our back item appears on our hud.
/mob/living/carbon/proc/update_hud_back(obj/item/I)
return
+
diff --git a/code/modules/mob/living/carbon/human/human_deadchat_control.dm b/code/modules/mob/living/carbon/human/human_deadchat_control.dm
new file mode 100644
index 000000000000..fdb6db4f5b6d
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/human_deadchat_control.dm
@@ -0,0 +1,143 @@
+// Custom human behavior for deadchat control
+
+
+/mob/living/carbon/human/proc/dchat_emote()
+ var/list/possible_emotes = list("scream", "clap", "snap", "crack", "dap", "burp")
+ emote(pick(possible_emotes), intentional = TRUE)
+
+/mob/living/carbon/human/proc/dchat_attack(intent)
+ var/turf/ahead = get_turf(get_step(src, dir))
+ var/atom/victim = locate(/mob/living) in ahead
+ var/obj/item/in_hand = get_active_hand()
+ var/implement = "[isnull(in_hand) ? "[p_their()] fists" : in_hand]"
+ if(!victim)
+ victim = locate(/obj/structure) in ahead
+ if(!victim)
+ switch(intent)
+ if(INTENT_HARM)
+ visible_message("[src] swings [implement] wildly!")
+ if(INTENT_HELP)
+ visible_message("[src] seems to take a deep breath.")
+ return
+ if(isLivingSSD(victim))
+ visible_message("[src] [intent == INTENT_HARM ? "reluctantly " : ""]lowers [p_their()] [implement].")
+ return
+
+ var/original_intent = a_intent
+ a_intent = intent
+ if(in_hand)
+ in_hand.melee_attack_chain(src, victim)
+ else
+ UnarmedAttack(victim, TRUE)
+ a_intent = original_intent
+
+/mob/living/carbon/human/proc/dchat_resist()
+ if(!can_resist())
+ visible_message("[src] seems to be unable to do anything!")
+ return
+ if(!restrained())
+ visible_message("[src] seems to be doing nothing in particular.")
+ return
+
+ visible_message("[src] is trying to break free!")
+ resist()
+
+/mob/living/carbon/human/proc/dchat_pickup()
+ var/turf/ahead = get_step(src, dir)
+ var/obj/item/thing = locate(/obj/item) in ahead
+ if(!thing)
+ return
+
+ var/old_loc = thing.loc
+ var/obj/item/in_hand = get_active_hand()
+
+ if(in_hand)
+ if(in_hand.flags & NODROP)
+ visible_message("[src] attempts to drop [in_hand], but it seems to be stuck to [p_their()] hand!")
+ return
+ if(in_hand.flags & ABSTRACT)
+ visible_message("[src] seems to have [p_their()] hands full!")
+ return
+ visible_message("[src] drops [in_hand] and picks up [thing] instead!")
+ unEquip(in_hand)
+ in_hand.forceMove(old_loc)
+ else
+ visible_message("[src] picks up [thing]!")
+ put_in_active_hand(thing)
+
+/mob/living/carbon/human/proc/dchat_throw()
+ var/obj/item/in_hand = get_active_hand()
+ if(!in_hand || in_hand.flags & ABSTRACT)
+ visible_message("[src] makes a throwing motion!")
+ return
+ var/atom/possible_target
+ var/cur_turf = get_turf(src)
+ for(var/i in 1 to 5)
+ cur_turf = get_step(cur_turf, dir)
+ possible_target = locate(/mob/living) in cur_turf
+ if(possible_target)
+ break
+
+ possible_target = locate(/obj/structure) in cur_turf
+ if(possible_target)
+ break
+
+ if(!possible_target)
+ possible_target = cur_turf
+ if(in_hand.flags & NODROP)
+ visible_message("[src] tries to throw [in_hand][isturf(possible_target) ? "" : " towards [possible_target]"], but it won't come off [p_their()] hand!")
+ return
+ throw_item(possible_target)
+
+/mob/living/carbon/human/proc/dchat_shove()
+ var/turf/ahead = get_turf(get_step(src, dir))
+ var/mob/living/carbon/human/H = locate(/mob/living/carbon/human) in ahead
+ if(!H)
+ visible_message("[src] tries to shove something away!")
+ return
+ dna?.species.disarm(src, H)
+
+/mob/living/carbon/human/proc/dchat_shoot()
+
+ var/atom/possible_target
+ var/cur_turf = get_turf(src)
+ for(var/i in 1 to 5)
+ cur_turf = get_step(cur_turf, dir)
+ possible_target = locate(/mob/living) in cur_turf
+ if(possible_target)
+ break
+
+ if(!possible_target)
+ possible_target = cur_turf
+
+ var/obj/item/gun/held_gun = get_active_hand()
+ if(!held_gun)
+ visible_message("[src] makes fingerguns towards [possible_target]!")
+ return
+ if(!istype(held_gun))
+ visible_message("[src] points [held_gun] towards [possible_target]!")
+ return
+ // for his neutral special, he wields a Gun
+ held_gun.afterattack(possible_target, src)
+ visible_message("[src] fires [held_gun][isturf(possible_target) ? "" : " towards [possible_target]!"]")
+
+/mob/living/carbon/human/proc/dchat_step(dir)
+ if(length(grabbed_by))
+ resist_grab()
+ step(src, dir)
+
+
+/mob/living/carbon/human/deadchat_plays(mode = DEADCHAT_DEMOCRACY_MODE, cooldown = 7 SECONDS)
+ var/list/inputs = list(
+ "emote" = CALLBACK(src, PROC_REF(dchat_emote)),
+ "attack" = CALLBACK(src, PROC_REF(dchat_attack), INTENT_HARM),
+ "help" = CALLBACK(src, PROC_REF(dchat_attack), INTENT_HELP),
+ "pickup" = CALLBACK(src, PROC_REF(dchat_pickup)),
+ "throw" = CALLBACK(src, PROC_REF(dchat_throw)),
+ "disarm" = CALLBACK(src, PROC_REF(dchat_shove)),
+ "resist" = CALLBACK(src, PROC_REF(dchat_resist)),
+ "shoot" = CALLBACK(src, PROC_REF(dchat_shoot)),
+ )
+
+ AddComponent(/datum/component/deadchat_control/human, mode, inputs, cooldown)
+
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 803a408b2d2a..7abeee4e13f3 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -755,6 +755,17 @@ emp_act
/mob/living/carbon/human/water_act(volume, temperature, source, method = REAGENT_TOUCH)
. = ..()
dna.species.water_act(src, volume, temperature, source, method)
+ if(method != REAGENT_TOUCH)
+ return
+
+ for(var/obj/O in list(head, wear_suit, back, l_hand, r_hand))
+ O.water_act(src, volume, temperature, source, method)
+ if((head?.flags & THICKMATERIAL) && (wear_suit?.flags & THICKMATERIAL)) // fully pierce proof clothing is also water proof!
+ return
+ for(var/obj/O in list(w_uniform, shoes, belt, gloves, glasses, l_ear, r_ear, wear_id, wear_pda, r_store, l_store, s_store))
+ O.water_act(src, volume, temperature, source, method)
+
+
/mob/living/carbon/human/attackby(obj/item/I, mob/user, params)
if(SEND_SIGNAL(src, COMSIG_HUMAN_ATTACKED, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
diff --git a/code/modules/mob/living/carbon/human/human_emote.dm b/code/modules/mob/living/carbon/human/human_emote.dm
index 4853edb7dcec..f1e909d740a3 100644
--- a/code/modules/mob/living/carbon/human/human_emote.dm
+++ b/code/modules/mob/living/carbon/human/human_emote.dm
@@ -60,6 +60,32 @@
message = "raises an eyebrow."
message_param = "raises an eyebrow at %t."
+/datum/emote/living/carbon/human/wince
+ key = "wince"
+ key_third_person = "winces"
+ message = "winces."
+ message_param = "winces at %t."
+
+/datum/emote/living/carbon/human/squint
+ key = "squint"
+ key_third_person = "squints"
+ message = "squints."
+ message_param = "squints at %t."
+
+/datum/emote/living/carbon/human/facepalm
+ key = "facepalm"
+ key_third_person = "facepalms"
+ message = "facepalms."
+ hands_use_check = TRUE
+ sound = 'sound/weapons/slap.ogg'
+ emote_type = EMOTE_SOUND
+ volume = 50
+
+/datum/emote/living/carbon/human/palm
+ key = "palm"
+ message = "extends their palm expectingly."
+ message_param = "extends their palm expectingly towards %t."
+
/datum/emote/living/carbon/human/grumble
key = "grumble"
key_third_person = "grumbles"
@@ -137,7 +163,10 @@
volume = 100
/datum/emote/living/carbon/human/gasp/get_sound(mob/user)
+ if(!ishuman(user))
+ return
var/mob/living/carbon/human/H = user
+
if(H.is_muzzled())
// If you're muzzled you're not making noise
return
@@ -191,6 +220,18 @@
message = "salutes."
message_param = "salutes to %t."
hands_use_check = TRUE
+ audio_cooldown = 3 SECONDS
+ var/list/serious_shoes = list(/obj/item/clothing/shoes/jackboots, /obj/item/clothing/shoes/combat,
+ /obj/item/clothing/shoes/centcom, /obj/item/clothing/shoes/laceup)
+ var/list/funny_shoes = list(/obj/item/clothing/shoes/magboots/clown, /obj/item/clothing/shoes/clown_shoes,
+ /obj/item/clothing/shoes/cursedclown, /obj/item/clothing/shoes/ducky)
+
+/datum/emote/living/carbon/human/salute/get_sound(mob/living/user)
+ var/mob/living/carbon/human/H = user
+ if(is_type_in_list(H.shoes, serious_shoes))
+ return 'sound/effects/salute.ogg'
+ if(is_type_in_list(H.shoes, funny_shoes))
+ return 'sound/items/toysqueak1.ogg'
/datum/emote/living/carbon/human/shrug
key = "shrug"
@@ -599,17 +640,10 @@
species_type_whitelist_typecache = list(/datum/species/diona)
sound = "sound/voice/dionatalk1.ogg"
-/datum/emote/living/carbon/human/squish
- key = "squish"
- key_third_person = "squishes"
- message = "squishes."
- message_param = "squishes at %t."
- emote_type = EMOTE_SOUND
- age_based = TRUE
- // Credit to DrMinky (freesound.org) for the sound.
- sound = "sound/effects/slime_squish.ogg"
+/datum/emote/living/carbon/human/slime
+
-/datum/emote/living/carbon/human/squish/can_run_emote(mob/user, status_check, intentional)
+/datum/emote/living/carbon/human/slime/can_run_emote(mob/user, status_check, intentional)
. = ..()
if(!.)
return FALSE
@@ -622,6 +656,41 @@
return TRUE
return FALSE
+/datum/emote/living/carbon/human/slime/squish
+ key = "squish"
+ key_third_person = "squishes"
+ message = "squishes."
+ message_param = "squishes at %t."
+ emote_type = EMOTE_SOUND
+ age_based = TRUE
+ // Credit to DrMinky (freesound.org) for the sound.
+ sound = "sound/effects/slime_squish.ogg"
+
+/datum/emote/living/carbon/human/slime/bubble
+ key = "bubble"
+ key_third_person = "bubbles"
+ message = "bubbles."
+ message_param = "bubbles at %t."
+ emote_type = EMOTE_SOUND
+ age_based = TRUE
+ // Sound is CC-4.0 by Audiolarx
+ // Effect is cut out of original clip
+ // https://freesound.org/people/audiolarx/sounds/263945/
+ sound = 'sound/effects/mob_effects/slime_bubble.ogg'
+
+/datum/emote/living/carbon/human/slime/pop
+ key = "pop"
+ key_third_person = "pops"
+ message = "makes a popping sound."
+ message_param = "makes a popping sound at %t."
+ message_mime = "makes a silent pop."
+ emote_type = EMOTE_SOUND
+ age_based = TRUE
+ // CC0
+ // https://freesound.org/people/greenvwbeetle/sounds/244653/
+ sound = 'sound/effects/mob_effects/slime_pop.ogg'
+ volume = 50
+
/datum/emote/living/carbon/human/howl
key = "howl"
key_third_person = "howls"
diff --git a/code/modules/mob/living/carbon/human/human_examine.dm b/code/modules/mob/living/carbon/human/human_examine.dm
index d0b9632b6264..d17ef28765b8 100644
--- a/code/modules/mob/living/carbon/human/human_examine.dm
+++ b/code/modules/mob/living/carbon/human/human_examine.dm
@@ -167,6 +167,8 @@
if(LAZYLEN(R.fields["comments"])) //if the commentlist is present
var/list/comments = R.fields["comments"]
commentLatest = LAZYACCESS(comments, comments.len) //get the latest entry from the comment log
+ if(islist(commentLatest))
+ commentLatest = "[commentLatest["header"]]: [commentLatest["text"]]"
else
commentLatest = "No entries." //If present but without entries (=target is recognized crew)
diff --git a/code/modules/mob/living/carbon/human/human_inventory.dm b/code/modules/mob/living/carbon/human/human_inventory.dm
index 3850e254b22b..dbca3fce2132 100644
--- a/code/modules/mob/living/carbon/human/human_inventory.dm
+++ b/code/modules/mob/living/carbon/human/human_inventory.dm
@@ -299,7 +299,12 @@
if(slot_in_backpack)
if(get_active_hand() == I)
unEquip(I)
- I.forceMove(back)
+ if(ismodcontrol(back))
+ var/obj/item/mod/control/C = back
+ if(C.bag)
+ I.forceMove(C.bag)
+ else
+ I.forceMove(back)
if(slot_tie)
var/obj/item/clothing/under/uniform = src.w_uniform
uniform.attackby(I, src)
diff --git a/code/modules/mob/living/carbon/human/human_life.dm b/code/modules/mob/living/carbon/human/human_life.dm
index c04a82786235..1b22a82c8dab 100644
--- a/code/modules/mob/living/carbon/human/human_life.dm
+++ b/code/modules/mob/living/carbon/human/human_life.dm
@@ -739,7 +739,7 @@
if(getToxLoss() >= 45 && nutrition > 20)
lastpuke ++
if(lastpuke >= 25) // about 25 second delay I guess
- vomit(20, 0, 1, 0, 1)
+ vomit(20, 0, TRUE, 0, 1)
adjustToxLoss(-3)
lastpuke = 0
@@ -787,7 +787,7 @@
break
for(var/datum/reagent/R in reagents.reagent_list)//handles different chems' influence on pulse
- if(R.heart_rate_increase)
+ if(R.has_heart_rate_increase())
if(temp <= PULSE_FAST && temp >= PULSE_NONE)
temp++
break
diff --git a/code/modules/mob/living/carbon/human/human_mob.dm b/code/modules/mob/living/carbon/human/human_mob.dm
index b0f63a2c2a33..cf996166b148 100644
--- a/code/modules/mob/living/carbon/human/human_mob.dm
+++ b/code/modules/mob/living/carbon/human/human_mob.dm
@@ -252,9 +252,9 @@
if(bomb_armor)
brute_loss = 30 * (2 - round(bomb_armor * 0.01, 0.05))
burn_loss = brute_loss //Damage gets reduced from 120 to up to 60 combined brute+burn
- if(check_ear_prot() < HEARING_PROTECTION_TOTAL)
- Deaf(2 MINUTES)
+ if(ears && check_ear_prot() < HEARING_PROTECTION_TOTAL)
ears.receive_damage(30)
+ Deaf(2 MINUTES)
Weaken(stuntime)
KnockDown(stuntime * 3) //Up to 15 seconds of knockdown
@@ -262,9 +262,9 @@
brute_loss = 30
if(bomb_armor)
brute_loss = 15 * (2 - round(bomb_armor * 0.01, 0.05)) //Reduced from 30 to up to 15
- if(check_ear_prot() < HEARING_PROTECTION_TOTAL)
- Deaf(1 MINUTES)
+ if(ears && check_ear_prot() < HEARING_PROTECTION_TOTAL)
ears.receive_damage(15)
+ Deaf(1 MINUTES)
KnockDown(10 SECONDS - bomb_armor) //Between no knockdown to 10 seconds of knockdown depending on bomb armor
valid_limbs = list("l_hand", "l_foot", "r_hand", "r_foot")
limb_loss_chance = 25
@@ -435,18 +435,10 @@
// Get rank from ID, ID inside PDA, PDA, ID in wallet, etc.
/mob/living/carbon/human/proc/get_authentification_rank(if_no_id = "No id", if_no_job = "No job")
- var/obj/item/pda/pda = wear_id
- if(istype(pda))
- if(pda.id)
- return pda.id.rank
- else
- return pda.ownrank
- else
- var/obj/item/card/id/id = get_idcard()
- if(id)
- return id.rank ? id.rank : if_no_job
- else
- return if_no_id
+ var/obj/item/card/id/id = wear_id?.GetID()
+ if(istype(id))
+ return id.rank || if_no_job
+ return if_no_id
//gets assignment from ID, PDA, Wallet, etc.
//This should not be relied on for authentication, because PDAs show their owner's job, even if an ID is not inserted
@@ -760,7 +752,10 @@
read = 1
if(LAZYLEN(R.fields["comments"]))
for(var/c in R.fields["comments"])
- to_chat(usr, c)
+ if(islist(c))
+ to_chat(usr, "[c["header"]]: [c["text"]]")
+ else
+ to_chat(usr, c)
else
to_chat(usr, "No comments found")
if(hasHUD(usr, EXAMINE_HUD_SECURITY_WRITE))
@@ -1032,7 +1027,7 @@
xylophone=0
return
-/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE)
+/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE, piercing = FALSE)
. = TRUE
if(!target_zone)
@@ -1053,14 +1048,14 @@
else if(affecting.is_robotic())
. = FALSE
fail_msg = "That limb is robotic."
+ if(wear_suit && !HAS_TRAIT(wear_suit, TRAIT_PUNCTURE_IMMUNE) && piercing)
+ return TRUE
+ if(target_zone == "head")
+ if((head?.flags & THICKMATERIAL) && !penetrate_thick)
+ . = FALSE
else
- switch(target_zone)
- if("head")
- if(head && head.flags & THICKMATERIAL && !penetrate_thick)
- . = FALSE
- else
- if(wear_suit && wear_suit.flags & THICKMATERIAL && !penetrate_thick)
- . = FALSE
+ if((wear_suit?.flags & THICKMATERIAL) && !penetrate_thick)
+ . = FALSE
if(!. && error_msg && user)
if(!fail_msg)
fail_msg = "There is no exposed flesh or thin material [target_zone == "head" ? "on [p_their()] head" : "on [p_their()] body"] to inject into."
@@ -2077,139 +2072,3 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
set category = "IC"
update_flavor_text()
-
-// Behavior for deadchat control
-
-/mob/living/carbon/human/proc/dchat_emote()
- var/list/possible_emotes = list("scream", "clap", "snap", "crack", "dap", "burp")
- emote(pick(possible_emotes), intentional = TRUE)
-
-/mob/living/carbon/human/proc/dchat_attack(intent)
- var/turf/ahead = get_turf(get_step(src, dir))
- var/atom/victim = locate(/mob/living) in ahead
- var/obj/item/in_hand = get_active_hand()
- var/implement = "[isnull(in_hand) ? "[p_their()] fists" : in_hand]"
- if(!victim)
- victim = locate(/obj/structure) in ahead
- if(!victim)
- switch(intent)
- if(INTENT_HARM)
- visible_message("[src] swings [implement] wildly!")
- if(INTENT_HELP)
- visible_message("[src] seems to take a deep breath.")
- return
- if(isLivingSSD(victim))
- visible_message("[src] [intent == INTENT_HARM ? "reluctantly " : ""]lowers [p_their()] [implement].")
- return
-
- var/original_intent = a_intent
- a_intent = intent
- if(in_hand)
- in_hand.melee_attack_chain(src, victim)
- else
- UnarmedAttack(victim, TRUE)
- a_intent = original_intent
-
-/mob/living/carbon/human/proc/dchat_resist()
- if(!can_resist())
- visible_message("[src] seems to be unable to do anything!")
- return
- if(!restrained())
- visible_message("[src] seems to be doing nothing in particular.")
- return
-
- visible_message("[src] is trying to break free!")
- resist()
-
-/mob/living/carbon/human/proc/dchat_pickup()
- var/turf/ahead = get_step(src, dir)
- var/obj/item/thing = locate(/obj/item) in ahead
- if(!thing)
- return
-
- var/old_loc = thing.loc
- var/obj/item/in_hand = get_active_hand()
-
- if(in_hand)
- if(in_hand.flags & NODROP)
- visible_message("[src] attempts to drop [in_hand], but it seems to be stuck to [p_their()] hand!")
- return
- if(in_hand.flags & ABSTRACT)
- visible_message("[src] seems to have [p_their()] hands full!")
- return
- visible_message("[src] drops [in_hand] and picks up [thing] instead!")
- unEquip(in_hand)
- in_hand.forceMove(old_loc)
- else
- visible_message("[src] picks up [thing]!")
- put_in_active_hand(thing)
-
-/mob/living/carbon/human/proc/dchat_throw()
- var/obj/item/in_hand = get_active_hand()
- if(!in_hand || in_hand.flags & ABSTRACT)
- visible_message("[src] makes a throwing motion!")
- return
- var/atom/possible_target
- var/cur_turf = get_turf(src)
- for(var/i in 1 to 5)
- cur_turf = get_step(cur_turf, dir)
- possible_target = locate(/mob/living) in cur_turf
- if(possible_target)
- break
-
- possible_target = locate(/obj/structure) in cur_turf
- if(possible_target)
- break
-
- if(!possible_target)
- possible_target = cur_turf
- if(in_hand.flags & NODROP)
- visible_message("[src] tries to throw [in_hand][isturf(possible_target) ? "" : " towards [possible_target]"], but it won't come off [p_their()] hand!")
- return
- throw_item(possible_target)
-
-/mob/living/carbon/human/proc/dchat_shove()
- var/turf/ahead = get_turf(get_step(src, dir))
- var/mob/living/carbon/human/H = locate(/mob/living/carbon/human) in ahead
- if(!H)
- visible_message("[src] tries to shove something away!")
- return
- dna?.species.disarm(src, H)
-
-/mob/living/carbon/human/proc/dchat_shoot()
-
- var/atom/possible_target
- var/cur_turf = get_turf(src)
- for(var/i in 1 to 5)
- cur_turf = get_step(cur_turf, dir)
- possible_target = locate(/mob/living) in cur_turf
- if(possible_target)
- break
-
- if(!possible_target)
- possible_target = cur_turf
-
- var/obj/item/gun/held_gun = get_active_hand()
- if(!held_gun)
- visible_message("[src] makes fingerguns towards [possible_target]!")
- return
- if(!istype(held_gun))
- visible_message("[src] points [held_gun] towards [possible_target]!")
- return
- // for his neutral special, he wields a Gun
- held_gun.afterattack(possible_target, src)
- visible_message("[src] fires [held_gun][isturf(possible_target) ? "" : " towards [possible_target]!"]")
-
-/mob/living/carbon/human/deadchat_plays(mode = DEADCHAT_DEMOCRACY_MODE, cooldown = 7 SECONDS)
- var/list/inputs = list(
- "emote" = CALLBACK(src, PROC_REF(dchat_emote)),
- "attack" = CALLBACK(src, PROC_REF(dchat_attack), INTENT_HARM),
- "help" = CALLBACK(src, PROC_REF(dchat_attack), INTENT_HELP),
- "pickup" = CALLBACK(src, PROC_REF(dchat_pickup)),
- "throw" = CALLBACK(src, PROC_REF(dchat_throw)),
- "disarm" = CALLBACK(src, PROC_REF(dchat_shove)),
- "resist" = CALLBACK(src, PROC_REF(dchat_resist)),
- "shoot" = CALLBACK(src, PROC_REF(dchat_shoot)),
- )
-
- AddComponent(/datum/component/deadchat_control/cardinal_movement, mode, inputs, cooldown)
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 9d99b0e0538f..96fad6d32fe3 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -19,6 +19,9 @@
else if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit))
var/obj/item/clothing/suit/space/hardsuit/C = wear_suit
thrust = C.jetpack
+ else if(ismodcontrol(back))
+ var/obj/item/mod/control/C = back
+ thrust = locate(/obj/item/mod/module/jetpack) in C
if(thrust)
if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src))
return TRUE
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 3e3f543f6c54..9d9f813e2177 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -412,7 +412,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
return
var/species_name = ""
- if(dna.species.name in list("Drask", "Grey", "Vox"))
+ if(dna.species.name in list("Drask", "Grey", "Vox", "Kidan"))
species_name = "_[lowertext(dna.species.name)]"
var/icon/hands_mask = icon('icons/mob/body_accessory.dmi', "accessory_none_s") //Needs a blank icon, not actually related to markings at all
@@ -677,7 +677,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
var/mutable_appearance/standing
if(gloves.icon_override)
- standing = mutable_appearance(gloves.icon_override, "[t_state]", layer = -GLOVES_LAYER)
+ standing = mutable_appearance(gloves.icon_override, "[gloves.icon_state]", layer = -GLOVES_LAYER)
else if(gloves.sprite_sheets && gloves.sprite_sheets[dna.species.name])
standing = mutable_appearance(gloves.sprite_sheets[dna.species.name], "[t_state]", layer = -GLOVES_LAYER)
else
@@ -1022,7 +1022,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
t_state = back.icon_state
var/mutable_appearance/standing
if(back.icon_override)
- standing = mutable_appearance(back.icon_override, "[t_state]", layer = -BACK_LAYER)
+ standing = mutable_appearance(back.icon_override, "[back.icon_state]", layer = -BACK_LAYER)
else if(back.sprite_sheets && back.sprite_sheets[dna.species.name])
standing = mutable_appearance(back.sprite_sheets[dna.species.name], "[t_state]", layer = -BACK_LAYER)
else
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 6a0e0b6c1379..e301ae1ad82e 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -292,6 +292,10 @@
if(H.wear_suit)
ADD_SLOWDOWN(H.wear_suit.slowdown)
+ if(H.head)
+ ADD_SLOWDOWN(H.head.slowdown)
+ if(H.gloves)
+ ADD_SLOWDOWN(H.gloves.slowdown)
if(!H.buckled && H.shoes)
ADD_SLOWDOWN(H.shoes.slowdown)
if(H.back)
@@ -332,7 +336,7 @@
. += (health_deficiency / 75)
else
if(health_deficiency >= 40)
- . += (health_deficiency / 25) //Once damage is over 40, you get the harsh formula
+ . += ((health_deficiency / 25) - 1.1) //Once damage is over 40, you get the harsh formula
else
. += 0.5 //Otherwise, slowdown (from pain) is capped to 0.5 until you hit 40 damage. This only effects people with fractional slowdowns, and prevents some harshness from the lowered threshold
@@ -841,6 +845,12 @@
var/obj/item/storage/backpack/B = H.back
if(B.contents.len < B.storage_slots && I.w_class <= B.max_w_class)
return TRUE
+ if(H.back && ismodcontrol(H.back))
+ var/obj/item/mod/control/C = H.back
+ if(C.bag)
+ var/obj/item/storage/backpack/B = C.bag
+ if(B.contents.len < B.storage_slots && I.w_class <= B.max_w_class)
+ return TRUE
return FALSE
if(slot_tie)
if(!istype(I, /obj/item/clothing/accessory))
@@ -938,18 +948,10 @@ It'll return null if the organ doesn't correspond, so include null checks when u
var/datum/antagonist/vampire/V = H.mind?.has_antag_datum(/datum/antagonist/vampire)
if(V)
- if(V.get_ability(/datum/vampire_passive/xray))
- H.sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
- H.see_in_dark += 8
- H.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- else if(V.get_ability(/datum/vampire_passive/full))
- H.sight |= SEE_MOBS
- H.see_in_dark += 8
- H.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- else if(V.get_ability(/datum/vampire_passive/vision))
- H.sight |= SEE_MOBS
- H.see_in_dark += 1 // base of 2, 2+1 is 3
- H.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
+ for(var/datum/vampire_passive/vision/buffs in V.powers)
+ H.sight |= buffs.vision_flags
+ H.see_in_dark += buffs.see_in_dark
+ H.lighting_alpha = buffs.lighting_alpha
// my glasses, I can't see without my glasses
if(H.glasses)
diff --git a/code/modules/mob/living/carbon/human/species/plasmaman.dm b/code/modules/mob/living/carbon/human/species/plasmaman.dm
index 92380e9a7bd4..0a1d7cd5514b 100644
--- a/code/modules/mob/living/carbon/human/species/plasmaman.dm
+++ b/code/modules/mob/living/carbon/human/species/plasmaman.dm
@@ -147,7 +147,7 @@
/datum/species/plasmaman/handle_life(mob/living/carbon/human/H)
var/atmos_sealed = !HAS_TRAIT(H, TRAIT_NOFIRE) && (isclothing(H.wear_suit) && H.wear_suit.flags & STOPSPRESSUREDMAGE) && (isclothing(H.head) && H.head.flags & STOPSPRESSUREDMAGE)
- if(!atmos_sealed && (!istype(H.w_uniform, /obj/item/clothing/under/plasmaman) || !istype(H.head, /obj/item/clothing/head/helmet/space/plasmaman)))
+ if(!atmos_sealed && (!istype(H.w_uniform, /obj/item/clothing/under/plasmaman) || !istype(H.head, /obj/item/clothing/head/helmet/space/plasmaman) && !HAS_TRAIT(H, TRAIT_NOSELFIGNITION_HEAD_ONLY)))
var/datum/gas_mixture/environment = H.loc.return_air()
if(environment)
if(environment.total_moles())
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 57e860aee16b..47ca02df9dc0 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -86,6 +86,8 @@
//Even if we don't push/swap places, we "touched" them, so spread fire
spreadFire(M)
+ SEND_SIGNAL(src, COMSIG_LIVING_MOB_BUMP, M)
+
// No pushing if we're already pushing past something, or if the mob we're pushing into is anchored.
if(now_pushing || M.anchored)
return TRUE
@@ -586,7 +588,7 @@
if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position
if(isliving(pulling))
var/mob/living/M = pulling
- if(IS_HORIZONTAL(M) && !M.buckled && (prob(M.getBruteLoss() * 200 / M.maxHealth)))
+ if(IS_HORIZONTAL(M) && !M.buckled && (prob(M.getBruteLoss() * 200 / M.maxHealth))) // So once you reach 50 brute damage you hit 100% chance to leave a blood trail for every tile you're pulled
M.makeTrail(dest)
pulling.Move(dest, get_dir(pulling, dest), movetime) // the pullee tries to reach our previous position
if(pulling && get_dist(src, pulling) > 1) // the pullee couldn't keep up
@@ -647,41 +649,41 @@
currently_grab_pulled = old_being_pulled
-/mob/living/proc/makeTrail(turf/T)
+/mob/living/proc/makeTrail(turf/turf_to_trail_on)
if(!has_gravity(src))
return
- var/blood_exists = 0
-
- for(var/obj/effect/decal/cleanable/trail_holder/C in loc) //checks for blood splatter already on the floor
- blood_exists = 1
- if(isturf(loc))
- var/trail_type = getTrail()
- if(trail_type)
- var/brute_ratio = round(getBruteLoss()/maxHealth, 0.1)
- if(blood_volume && blood_volume > max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold
- blood_volume = max(blood_volume - max(1, brute_ratio * 2), 0) //that depends on our brute damage.
- var/newdir = get_dir(T, loc)
- if(newdir != src.dir)
- newdir = newdir | dir
- if(newdir == 3) //N + S
- newdir = NORTH
- else if(newdir == 12) //E + W
- newdir = EAST
- if((newdir in GLOB.cardinal) && (prob(50)))
- newdir = turn(get_dir(T, loc), 180)
- if(!blood_exists)
- new /obj/effect/decal/cleanable/trail_holder(loc)
- for(var/obj/effect/decal/cleanable/trail_holder/TH in loc)
- if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled)
- TH.existing_dirs += newdir
- TH.overlays.Add(image('icons/effects/blood.dmi', trail_type, dir = newdir))
- TH.transfer_mob_blood_dna(src)
- if(ishuman(src))
- var/mob/living/carbon/human/H = src
- if(H.dna.species.blood_color)
- TH.color = H.dna.species.blood_color
- else
- TH.color = "#A10808"
+ if(!isturf(loc))
+ return
+ var/trail_type = getTrail()
+ if(!trail_type)
+ return
+ var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1)
+ if(!blood_volume && !(blood_volume > max(BLOOD_VOLUME_NORMAL * (1 - brute_ratio * 0.25), 0))) // Okay let's dive into the maths. For every 50 brute damage taken, the minimal blood level you can have decreases by 12,5%
+ return
+ blood_volume = max(blood_volume - max(1, brute_ratio * 2), 0) // The amount of blood lost per tile of movement is always at least 1cl, and every 50 damage after reaching 50 brute damage taken will increase the bleed by 1cl per tile
+ var/newdir = get_dir(turf_to_trail_on, loc)
+ if(newdir != dir)
+ newdir |= dir
+ if(newdir == (NORTH|SOUTH))
+ newdir = NORTH
+ else if(newdir == (EAST|WEST))
+ newdir = EAST
+ if(IS_DIR_CARDINAL(newdir) && prob(50))
+ newdir = turn(get_dir(turf_to_trail_on, loc), 180)
+ var/blood_exists = locate(/obj/effect/decal/cleanable/trail_holder) in loc //checks for blood splatter already on the floor
+ if(!blood_exists)
+ new /obj/effect/decal/cleanable/trail_holder(loc)
+ for(var/obj/effect/decal/cleanable/trail_holder/existing_trail in loc)
+ if((!(newdir in existing_trail.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && length(existing_trail.existing_dirs) <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled)
+ existing_trail.existing_dirs += newdir
+ existing_trail.overlays.Add(image('icons/effects/blood.dmi', trail_type, dir = newdir))
+ existing_trail.transfer_mob_blood_dna(src)
+ if(ishuman(src))
+ var/mob/living/carbon/human/H = src
+ if(H.dna.species.blood_color)
+ existing_trail.color = H.dna.species.blood_color
+ else
+ existing_trail.color = "#A10808"
/mob/living/carbon/human/makeTrail(turf/T)
@@ -926,7 +928,7 @@
/mob/living/narsie_act()
if(client)
- make_new_construct(/mob/living/simple_animal/hostile/construct/harvester, src, cult_override = TRUE)
+ make_new_construct(/mob/living/simple_animal/hostile/construct/harvester, src, cult_override = TRUE, create_smoke = TRUE)
spawn_dust()
gib()
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index a7fbd9182f5f..f1a8e960a155 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -90,4 +90,4 @@
var/datum/language/default_language
-
+ var/datum/middleClickOverride/middleClickOverride = null
diff --git a/code/modules/mob/living/silicon/ai/ai_mob.dm b/code/modules/mob/living/silicon/ai/ai_mob.dm
index 5ec2d304c0a6..974f6e678b47 100644
--- a/code/modules/mob/living/silicon/ai/ai_mob.dm
+++ b/code/modules/mob/living/silicon/ai/ai_mob.dm
@@ -5,13 +5,10 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
/mob/living/silicon/ai/proc/ai_call_shuttle,
/mob/living/silicon/ai/proc/ai_camera_track,
/mob/living/silicon/ai/proc/ai_camera_list,
- /mob/living/silicon/ai/proc/ai_goto_location,
- /mob/living/silicon/ai/proc/ai_remove_location,
/mob/living/silicon/ai/proc/ai_hologram_change,
/mob/living/silicon/ai/proc/ai_network_change,
/mob/living/silicon/ai/proc/ai_roster,
/mob/living/silicon/ai/proc/ai_statuschange,
- /mob/living/silicon/ai/proc/ai_store_location,
/mob/living/silicon/ai/proc/control_integrated_radio,
/mob/living/silicon/ai/proc/core,
/mob/living/silicon/ai/proc/pick_icon,
@@ -118,6 +115,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
var/list/all_eyes = list()
var/next_text_announcement
+ //Used with the hotkeys on 2-5 to store locations.
+ var/list/stored_locations = list()
+
/mob/living/silicon/ai/proc/add_ai_verbs()
verbs |= GLOB.ai_verbs_default
verbs |= silicon_subsystems
@@ -218,6 +218,10 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
GLOB.ai_list += src
GLOB.shuttle_caller_list += src
+
+ for(var/I in 1 to 4)
+ stored_locations += "unset" //This is checked in ai_keybinds.dm.
+
..()
/mob/living/silicon/ai/Destroy()
@@ -1374,7 +1378,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
to_chat(src, "Hack aborted. [apc] is no longer responding to our systems.")
SEND_SOUND(src, sound('sound/machines/buzz-sigh.ogg'))
else
- malf_picker.processing_time += 10
+ malf_picker.processing_time += 15
apc.malfai = parent || src
apc.malfhack = TRUE
@@ -1497,4 +1501,13 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
to_chat(src, "The tiny in built fan finally removes the tape!")
ducttapecomponent.remove_tape(card, src)
+//Stores the location of the AI to the value of stored_locations associated with location_number.
+/mob/living/silicon/ai/proc/store_location(location_number)
+ if(!isturf(eyeobj.loc)) //i.e., inside a mech or other shenanigans
+ to_chat(src, "You can't set a location here!")
+ return FALSE
+
+ stored_locations[location_number] = eyeobj.loc
+ return TRUE
+
#undef TEXT_ANNOUNCEMENT_COOLDOWN
diff --git a/code/modules/mob/living/silicon/robot/robot_mob.dm b/code/modules/mob/living/silicon/robot/robot_mob.dm
index eb99ad814e2f..9839a3c9caf0 100644
--- a/code/modules/mob/living/silicon/robot/robot_mob.dm
+++ b/code/modules/mob/living/silicon/robot/robot_mob.dm
@@ -1596,3 +1596,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
else
to_chat(src, "You can only use this emote when you're out of charge.")
+
+/mob/living/silicon/robot/can_instant_lockdown()
+ if(emagged || faction_check_mob(src, "syndicate"))
+ return TRUE
+ return FALSE
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 187df568bc6a..09b5946cab5b 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -16,6 +16,8 @@
var/list/basic_modules = list()
/// A list of modules the robot gets when emagged.
var/list/emag_modules = list()
+ /// A list of modules the robot gets when Safety Overridden.
+ var/list/override_modules = list()
/// A list of modules that the robot gets when malf AI buys it.
var/list/malf_modules = list()
/// A list of modules that require special recharge handling. Examples include things like flashes, sprays and welding tools.
@@ -43,6 +45,10 @@
basic_modules += I
basic_modules -= i
+ for(var/i in override_modules)
+ var/obj/item/I = new i(src)
+ override_modules += I
+ override_modules -= i
// Even though these are created here the robot won't be able to see and equip them until they actually get emagged/hacked.
for(var/i in emag_modules)
var/obj/item/I = new i(src)
@@ -59,7 +65,7 @@
special_rechargables += /obj/item/flash/cyborg
// This is done so we can loop through this list later and call cyborg_recharge() on the items while the borg is recharging.
- var/all_modules = basic_modules | emag_modules | malf_modules
+ var/all_modules = basic_modules | override_modules | emag_modules | malf_modules
for(var/path in special_rechargables)
var/obj/item/I = locate(path) in all_modules
if(I) // If it exists, add the object reference.
@@ -80,6 +86,7 @@
// These can all contain actual objects, so we need to null them out.
QDEL_LIST_CONTENTS(modules)
QDEL_LIST_CONTENTS(basic_modules)
+ QDEL_LIST_CONTENTS(override_modules)
QDEL_LIST_CONTENTS(emag_modules)
QDEL_LIST_CONTENTS(malf_modules)
QDEL_LIST_CONTENTS(storages)
@@ -101,6 +108,7 @@
/obj/item/robot_module/proc/remove_item_from_lists(item_or_item_type)
var/list/lists = list(
basic_modules,
+ override_modules,
emag_modules,
malf_modules,
storages,
@@ -170,7 +178,7 @@
return I
/**
- * Builds the usable module list from the modules we have in `basic_modules`, `emag_modules` and `malf_modules`
+ * Builds the usable module list from the modules we have in `basic_modules`, `override_modules`, `emag_modules` and `malf_modules`
*/
/obj/item/robot_module/proc/rebuild_modules()
var/mob/living/silicon/robot/R = loc
@@ -185,7 +193,11 @@
for(var/item in basic_modules)
add_module(item, FALSE)
- if(R.emagged || R.weapons_unlock)
+ if(R.weapons_unlock)
+ for(var/item in override_modules)
+ add_module(item, FALSE)
+
+ if(R.emagged)
for(var/item in emag_modules)
add_module(item, FALSE)
@@ -382,11 +394,13 @@
/obj/item/stack/sheet/metal/cyborg,
/obj/item/stack/rods/cyborg,
/obj/item/stack/tile/plasteel/cyborg,
+ /obj/item/stack/tile/catwalk/cyborg,
/obj/item/stack/cable_coil/cyborg,
/obj/item/stack/sheet/glass/cyborg,
/obj/item/stack/sheet/rglass/cyborg
)
emag_modules = list(/obj/item/borg/stun, /obj/item/restraints/handcuffs/cable/zipties/cyborg, /obj/item/rcd/borg)
+ override_modules = list(/obj/item/gun/energy/emitter/cyborg/proto)
malf_modules = list(/obj/item/gun/energy/emitter/cyborg)
special_rechargables = list(/obj/item/extinguisher, /obj/item/weldingtool/largetank/cyborg, /obj/item/gun/energy/emitter/cyborg)
@@ -754,6 +768,7 @@
/obj/item/stack/sheet/metal/cyborg,
/obj/item/stack/rods/cyborg,
/obj/item/stack/tile/plasteel/cyborg,
+ /obj/item/stack/tile/catwalk/cyborg,
/obj/item/stack/cable_coil/cyborg,
/obj/item/stack/sheet/glass/cyborg,
/obj/item/stack/sheet/rglass/cyborg,
@@ -844,6 +859,11 @@
name = "Rod Synthesizer"
statpanel_name = "Rods"
+/datum/robot_energy_storage/catwalk
+ name= "Catwalk Synthesizer"
+ statpanel_name = "Catwalk Tiles"
+ max_energy = 60
+
/datum/robot_energy_storage/glass
name = "Glass Synthesizer"
statpanel_name = "Glass"
diff --git a/code/modules/mob/living/silicon/silicon_mob.dm b/code/modules/mob/living/silicon/silicon_mob.dm
index 43f99c93361e..07dac88922d8 100644
--- a/code/modules/mob/living/silicon/silicon_mob.dm
+++ b/code/modules/mob/living/silicon/silicon_mob.dm
@@ -78,6 +78,11 @@
QDEL_NULL(aiCamera)
return ..()
+/mob/living/silicon/proc/can_instant_lockdown()
+ if(isAntag(src))
+ return TRUE
+ return FALSE
+
/mob/living/silicon/proc/get_radio()
return
diff --git a/code/modules/mob/living/simple_animal/bot/bot_construction.dm b/code/modules/mob/living/simple_animal/bot/bot_construction.dm
index 9b1c47f21853..08b35a7994a6 100644
--- a/code/modules/mob/living/simple_animal/bot/bot_construction.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot_construction.dm
@@ -30,9 +30,11 @@
else if(is_pen(W))
var/t = rename_interactive(user, W, prompt = "Enter new robot name")
- if(!isnull(t))
+ if(length(t))
created_name = t
log_game("[key_name(user)] has renamed a robot to [t]")
+ else
+ to_chat(user, "The robot's name must have at least one character.")
//Edbot Assembly
@@ -52,9 +54,11 @@
if(is_pen(W))
var/t = rename_interactive(user, W, prompt = "Enter new robot name")
- if(!isnull(t))
+ if(length(t))
created_name = t
log_game("[key_name(user)] has renamed a robot to [t]")
+ else
+ to_chat(user, "The robot's name must have at least one character.")
return
switch(build_step)
@@ -300,9 +304,11 @@
else if(is_pen(W))
var/t = rename_interactive(user, W, prompt = "Enter new robot name")
- if(!isnull(t))
+ if(length(t))
created_name = t
log_game("[key_name(user)] has renamed a robot to [t]")
+ else
+ to_chat(user, "The robot's name must have at least one character.")
/obj/item/toolbox_tiles/sensor/update_icon_state()
icon_state = "[toolbox_color]toolbox_tiles_sensor"
@@ -317,11 +323,6 @@
to_chat(user, "You add the robot arm to the odd looking toolbox assembly. Boop beep!")
user.unEquip(src, 1)
qdel(src)
- else if(is_pen(W))
- var/t = rename_interactive(user, W, prompt = "Enter new robot name")
- if(!isnull(t))
- created_name = t
- log_game("[key_name(user)] has renamed a robot to [t]")
//Medbot Assembly
/obj/item/storage/firstaid/attackby(obj/item/I, mob/user, params)
@@ -386,9 +387,11 @@
..()
if(is_pen(I))
var/t = rename_interactive(user, I, prompt = "Enter new robot name")
- if(!isnull(t))
+ if(length(t))
created_name = t
log_game("[key_name(user)] has renamed a robot to [t]")
+ else
+ to_chat(user, "The robot's name must have at least one character.")
else
switch(build_step)
if(0)
@@ -494,9 +497,11 @@
else if(is_pen(I))
var/t = rename_interactive(user, I, prompt = "Enter new robot name")
- if(!isnull(t))
+ if(length(t))
created_name = t
log_game("[key_name(user)] has renamed a robot to [t]")
+ else
+ to_chat(user, "The robot's name must have at least one character.")
//General Griefsky
diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
index 737e13663554..2eb6767c2f62 100644
--- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
@@ -187,8 +187,10 @@
do_sparks(3, 1, src)
..()
-/mob/living/simple_animal/bot/cleanbot/show_controls(mob/M)
- ui_interact(M)
+//TGUI
+
+/mob/living/simple_animal/bot/cleanbot/show_controls(mob/user)
+ ui_interact(user)
/mob/living/simple_animal/bot/cleanbot/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
@@ -238,7 +240,7 @@
if("ejectpai")
ejectpai()
-
+//END OF TGUI
/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A)
if(istype(A,/obj/effect/decal/cleanable))
diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm
index 004e9239c2be..5118162f7e75 100644
--- a/code/modules/mob/living/simple_animal/corpse.dm
+++ b/code/modules/mob/living/simple_animal/corpse.dm
@@ -33,12 +33,11 @@
/datum/outfit/syndicatecommandocorpse
name = "Corpse of a Syndicate Commando"
uniform = /obj/item/clothing/under/syndicate
- suit = /obj/item/clothing/suit/space/hardsuit/syndi
shoes = /obj/item/clothing/shoes/combat
gloves = /obj/item/clothing/gloves/combat
l_ear = /obj/item/radio/headset
mask = /obj/item/clothing/mask/gas/syndicate
- back = /obj/item/tank/jetpack/oxygen
+ back = /obj/item/mod/control/pre_equipped/traitor
r_pocket = /obj/item/tank/internals/emergency_oxygen
id = /obj/item/card/id
diff --git a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm
index 890edfe27660..80d97421099f 100644
--- a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm
+++ b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm
@@ -36,3 +36,4 @@
health = 25
melee_damage_lower = 15
melee_damage_upper = 10
+ gold_core_spawnable = NO_SPAWN
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
index 4cea44879b91..3efddf12f41d 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
@@ -59,6 +59,7 @@
desc = "Toggles between crawling and standing up."
icon_icon = 'icons/mob/actions/actions_animal.dmi'
button_icon_state = "gorilla_toggle"
+ check_flags = AB_CHECK_CONSCIOUS
/datum/action/innate/gorilla/gorilla_toggle/Activate()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm
index 6c7608498927..bd15eec41213 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm
@@ -529,7 +529,7 @@ Difficulty: Hard
T.ex_act(3)
if(mode == CRYO)
var/turf/simulated/S = get_turf(src)
- S.MakeSlippery(TURF_WET_ICE)
+ S.MakeSlippery(TURF_WET_ICE, rand(10, 20 SECONDS))
for(var/turf/T in range (1, src))
new /obj/effect/snowcloud(T)
for(var/mob/living/carbon/C in T.contents)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index cb08ad6b5ca6..d63091b92267 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -171,7 +171,7 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 5, chargepast = 2)
if(!chargeat)
return
- if(target.z != z)
+ if(chargeat.z != z)
return
var/chargeturf = get_turf(chargeat)
if(!chargeturf)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 6c4e59d556a0..849341d9f15b 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -610,7 +610,7 @@ Difficulty: Medium
move_force = MOVE_FORCE_NORMAL
move_resist = MOVE_FORCE_NORMAL
pull_force = MOVE_FORCE_NORMAL
- deathmessage = "screeches as its wings turn to dust and it collapses on the floor, life estinguished."
+ deathmessage = "screeches as its wings turn to dust and it collapses on the floor, life extinguished."
attack_action_types = list()
/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/Initialize(mapload)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index 8c952e5f48b1..42c8456c6e91 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -316,6 +316,8 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink(mob/victim) //blink to a target
if(blinking || !victim)
return
+ if(victim.z != z)
+ return
var/turf/T = get_turf(victim)
var/turf/source = get_turf(src)
new /obj/effect/temp_visual/hierophant/telegraph(T, src)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index ee8e7791340a..7ee14ad12dbb 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -43,8 +43,8 @@ Difficulty: Medium
internal_gps = /obj/item/gps/internal/legion
medal_type = BOSS_MEDAL_LEGION
score_type = LEGION_SCORE
- loot = list(/obj/item/staff/storm)
- crusher_loot = list(/obj/item/staff/storm, /obj/item/crusher_trophy/empowered_legion_skull)
+ loot = list(/obj/item/storm_staff)
+ crusher_loot = list(/obj/item/storm_staff, /obj/item/crusher_trophy/empowered_legion_skull)
vision_range = 13
elimination = TRUE
appearance_flags = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
index 79c2cfef0464..6d201713346a 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -26,6 +26,7 @@
pull_force = MOVE_FORCE_OVERPOWERING
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
+ flags_2 = IMMUNE_TO_SHUTTLECRUSH_2
mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway
var/list/crusher_loot
var/medal_type
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm
index 8eaaa54f53d6..da037e8375de 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm
@@ -370,7 +370,7 @@
suit = /obj/item/clothing/suit/armor/bone
gloves = /obj/item/clothing/gloves/bracer
if(prob(5))
- back = pickweight(list(/obj/item/twohanded/spear/bonespear = 3, /obj/item/twohanded/fireaxe/boneaxe = 2))
+ back = pickweight(list(/obj/item/spear/bonespear = 3, /obj/item/fireaxe/boneaxe = 2))
if(prob(10))
belt = /obj/item/storage/belt/mining/primitive
if(prob(30))
diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton_mob.dm b/code/modules/mob/living/simple_animal/hostile/skeleton_mob.dm
index 128a3cfab254..e2baff75166a 100644
--- a/code/modules/mob/living/simple_animal/hostile/skeleton_mob.dm
+++ b/code/modules/mob/living/simple_animal/hostile/skeleton_mob.dm
@@ -45,6 +45,6 @@
melee_damage_upper = 20
deathmessage = "collapses into a pile of bones, its gear falling to the floor!"
loot = list(/obj/effect/decal/remains/human,
- /obj/item/twohanded/spear,
+ /obj/item/spear,
/obj/item/clothing/shoes/winterboots,
/obj/item/clothing/suit/hooded/wintercoat)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
index e2c4bdbf0fa5..b04db3f1dd34 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
@@ -472,5 +472,5 @@ GLOBAL_LIST_EMPTY(ts_infected_list)
/mob/living/simple_animal/hostile/poison/terror_spider/movement_delay()
. = ..()
- if(pulling && !ismob(pulling))
- . += 6 // drastic move speed penalty for dragging anything that is not a mob
+ if(pulling && !ismob(pulling) && pulling.density)
+ . += 6 // Drastic move speed penalty for dragging anything that is not a mob or a non dense object
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm
index 6fe2bc6e0e87..f179a75c3ba0 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm
@@ -23,6 +23,7 @@
melee_damage_lower = 5
melee_damage_upper = 15
spider_tier = TS_TIER_2
+ spider_opens_doors = 2
loudspeaker = TRUE
web_type = /obj/structure/spider/terrorweb/white
diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
index a2982fb88e48..9c8e33e5a948 100644
--- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
+++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm
@@ -10,14 +10,15 @@
canSmoothWith = null
smoothing_flags = NONE
var/growth_time = 120 SECONDS
+ is_alien = FALSE
-/obj/structure/alien/resin/flower_bud_enemy/New()
- ..()
+/obj/structure/alien/resin/flower_bud_enemy/Initialize(mapload)
+ . = ..()
var/list/anchors = list()
- anchors += locate(x-2,y+2,z)
- anchors += locate(x+2,y+2,z)
- anchors += locate(x-2,y-2,z)
- anchors += locate(x+2,y-2,z)
+ anchors += locate(x - 2, y + 2, z)
+ anchors += locate(x + 2, y + 2, z)
+ anchors += locate(x - 2, y - 2, z)
+ anchors += locate(x + 2, y - 2, z)
for(var/turf/T in anchors)
var/datum/beam/B = Beam(T, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine)
@@ -37,13 +38,12 @@
/obj/effect/ebeam/vine/Crossed(atom/movable/AM, oldloc)
- if(isliving(AM))
- var/mob/living/L = AM
- if(!("vines" in L.faction))
- L.adjustBruteLoss(5)
- to_chat(L, "You cut yourself on the thorny vines.")
-
-
+ if(!isliving(AM))
+ return
+ var/mob/living/L = AM
+ if(!("vines" in L.faction))
+ L.adjustBruteLoss(5)
+ to_chat(L, "You cut yourself on the thorny vines.")
/mob/living/simple_animal/hostile/venus_human_trap
name = "venus human trap"
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index c532475d8411..042e9de53195 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -731,7 +731,7 @@
parrot_hear(html_decode(multilingual_to_message(message_pieces)))
..()
-/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, atom/follow_target)
+/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, atom/follow_target, check_name_against)
if(speaker != src && prob(50))
parrot_hear(html_decode(multilingual_to_message(message_pieces)))
..()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 5a517d3712e5..bfb559b9e4f8 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -254,6 +254,11 @@
//Now, S represents a container we can insert W into.
S.handle_item_insertion(W, TRUE, TRUE)
return S
+ if(ismodcontrol(back))
+ var/obj/item/mod/control/C = back
+ if(C.bag)
+ C.bag.handle_item_insertion(W, TRUE, TRUE)
+ return C.bag
var/turf/T = get_turf(src)
if(istype(T))
@@ -1303,23 +1308,23 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
///can the mob be buckled to something by default?
/mob/proc/can_buckle()
- return 1
+ return TRUE
///can the mob be unbuckled from something by default?
/mob/proc/can_unbuckle()
- return 1
+ return TRUE
//Can the mob see reagents inside of containers?
/mob/proc/can_see_reagents()
- return 0
+ return FALSE
//Can this mob leave its location without breaking things terrifically?
/mob/proc/can_safely_leave_loc()
- return 1 // Yes, you can
+ return TRUE // Yes, you can
/mob/proc/IsVocal()
- return 1
+ return TRUE
/mob/proc/get_access()
return list() //must return list or IGNORE_ACCESS
@@ -1430,6 +1435,8 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
return FALSE
/mob/proc/faction_check_mob(mob/target, exact_match)
+ if(!target)
+ return faction_check(faction, null, FALSE)
if(exact_match) //if we need an exact match, we need to do some bullfuckery.
var/list/faction_src = faction.Copy()
var/list/faction_target = target.faction.Copy()
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index fb9a3880876c..ee7c21363620 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -375,7 +375,7 @@
M.start_pulling(t)
else
step(pulling, get_dir(pulling.loc, A))
- return
+ return TRUE
/mob/proc/update_gravity(has_gravity)
return
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 3ac302009ef4..9d504f16c999 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -164,6 +164,8 @@
ready = FALSE
return FALSE
+ check_tts_seed_ready() // SS220 ADDITION
+
ready = !ready
new_player_panel_proc()
@@ -236,6 +238,8 @@
to_chat(src, alert("You are currently not whitelisted to play [client.prefs.active_character.species]."))
return FALSE
+ check_tts_seed_ready()
+
LateChoices()
if(href_list["manifest"])
diff --git a/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm b/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm
index 9b572e03c67b..27c2ad066b80 100644
--- a/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm
+++ b/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm
@@ -6,306 +6,328 @@
name = "Short Hair" // try to capatilize the names please~
icon_state = "short" // you do not need to define _s or _l sub-states, game automatically does this for you a
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/cut
name = "Cut Hair"
icon_state = "cut"
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/long
name = "Shoulder-length Hair"
icon_state = "long"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longalt
name = "Shoulder-length Hair Alt"
icon_state = "longfringe"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longer
name = "Long Hair"
icon_state = "vlong"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longeralt
name = "Long Hair Alt"
icon_state = "vlongfringe"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longest
name = "Very Long Hair"
icon_state = "longest"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longfringe
name = "Long Fringe"
icon_state = "longfringe"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longestalt
name = "Longer Fringe"
icon_state = "vlongfringe"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/halfbang
name = "Half-banged Hair"
icon_state = "halfbang"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/halfbangalt
name = "Half-banged Hair Alt"
icon_state = "halfbang_alt"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/ponytail1
- name = "Ponytail male"
+ name = "Ponytail 1"
icon_state = "ponytailm"
- gender = MALE
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/ponytail2
- name = "Ponytail female"
+ name = "Ponytail 2"
icon_state = "ponytailf"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/ponytail3
- name = "Ponytail alt"
+ name = "Ponytail 3"
icon_state = "ponytail3"
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/sideponytail
name = "Side Ponytail"
icon_state = "stail"
- gender = FEMALE
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/highponytail
name = "High Ponytail"
icon_state = "highponytail"
- gender = FEMALE
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/wisp
name = "Wisp"
icon_state = "wisp"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/parted
name = "Parted"
icon_state = "parted"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/pompadour
name = "Pompadour"
icon_state = "pompadour"
- gender = MALE
- species_allowed = list("Human", "Slime People", "Unathi")
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/quiff
name = "Quiff"
icon_state = "quiff"
- gender = MALE
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bedhead
name = "Bedhead"
icon_state = "bedhead"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bedhead2
name = "Bedhead 2"
icon_state = "bedhead2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bedhead3
name = "Bedhead 3"
icon_state = "bedhead3"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/beehive
name = "Beehive"
icon_state = "beehive"
- gender = FEMALE
- species_allowed = list("Human", "Slime People", "Unathi")
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bobcurl
name = "Bobcurl"
icon_state = "bobcurl"
- gender = FEMALE
- species_allowed = list("Human", "Slime People", "Unathi")
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bob
name = "Bob"
icon_state = "bobcut"
- gender = FEMALE
- species_allowed = list("Human", "Slime People", "Unathi")
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bowl
name = "Bowl"
icon_state = "bowlcut"
- gender = MALE
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/braid2
name = "Long Braid"
icon_state = "hbraid"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/braid_hip
name = "Hippie Braid"
icon_state = "hipbraid"
secondary_theme = "beads"
+ species_allowed = list("Human", "Slime People", "Vulpkanin")
/datum/sprite_accessory/hair/buzz
name = "Buzzcut"
icon_state = "buzzcut"
- gender = MALE
- species_allowed = list("Human", "Slime People", "Unathi")
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/crew
name = "Crewcut"
icon_state = "crewcut"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/combover
name = "Combover"
icon_state = "combover"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/devillock
name = "Devil Lock"
icon_state = "devilock"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/dreadlocks
name = "Dreadlocks"
icon_state = "dreads"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/curls
name = "Curls"
icon_state = "curls"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/afro
name = "Afro"
icon_state = "afro"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/afro2
name = "Afro 2"
icon_state = "afro2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/afro_large
name = "Big Afro"
icon_state = "bigafro"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/sergeant
name = "Flat Top"
icon_state = "sergeant"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/emo
name = "Emo"
icon_state = "emo"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/flow
name = "Flow Hair"
icon_state = "flow"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/feather
name = "Feather"
icon_state = "feather"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/hitop
name = "Hitop"
icon_state = "hitop"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/mohawk
name = "Mohawk"
icon_state = "mohawk"
- species_allowed = list("Human", "Slime People", "Unathi")
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/jensen
name = "Adam Jensen Hair"
icon_state = "jensen"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/cia
name = "CIA"
icon_state = "cia"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/mulder
name = "Mulder"
icon_state = "mulder"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/gelled
name = "Gelled Back"
icon_state = "gelled"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/gentle
name = "Gentle"
icon_state = "gentle"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/spiky
name = "Spiky"
icon_state = "spikey"
- species_allowed = list("Human", "Slime People", "Unathi")
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/kusanagi
name = "Kusanagi Hair"
icon_state = "kusanagi"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/kagami
name = "Pigtails"
icon_state = "kagami"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/himecut
name = "Hime Cut"
icon_state = "himecut"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/braid
name = "Floorlength Braid"
icon_state = "floorbraid"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/odango
name = "Odango"
icon_state = "odango"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/ombre
name = "Ombre"
icon_state = "ombre"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/updo
name = "Updo"
icon_state = "updo"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/skinhead
name = "Skinhead"
icon_state = "skinhead"
glasses_over = 1
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/balding
name = "Balding Hair"
icon_state = "balding"
- gender = MALE // turnoff!
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/longemo
name = "Long Emo"
icon_state = "emolong"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
//////////////////////////////
//////START VG HAIRSTYLES/////
@@ -313,36 +335,38 @@
/datum/sprite_accessory/hair/birdnest
name = "Bird Nest"
icon_state = "birdnest"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/unkept
name = "Unkempt"
icon_state = "unkept"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/duelist
name = "Duelist"
icon_state = "duelist"
- gender = MALE
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/modern
name = "Modern"
icon_state = "modern"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/unshavenmohawk
name = "Unshaven Mohawk"
icon_state = "unshavenmohawk"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
glasses_over = 1
/datum/sprite_accessory/hair/drills
name = "Twincurls"
icon_state = "twincurl"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/minidrills
name = "Twincurls 2"
icon_state = "twincurl2"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
//////////////////////////////
//////END VG HAIRSTYLES///////
//////////////////////////////
@@ -354,327 +378,394 @@
/datum/sprite_accessory/hair/dave
name = "Dave"
icon_state = "dave"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/rosa
name = "Rosa"
icon_state = "rosa"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/jade
name = "Jade"
icon_state = "jade"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/shy
name = "Shy"
icon_state = "shy"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/manbun
name = "Manbun"
icon_state = "manbun"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/thinningback
name = "Thinning Back"
icon_state = "thinningrear"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/thinningfront
name = "Thinning Front"
icon_state = "thinningfront"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/thinning
name = "Thinning"
icon_state = "thinning"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bowlcut2
name = "Bowl 2"
icon_state = "bowlcut2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/ronin
name = "Ronin"
icon_state = "ronin"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/topknot
name = "Topknot"
icon_state = "topknot"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/regulationmohawk
name = "Regulation Mohawk"
icon_state = "shavedmohawk"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/rowbraid
name = "Row Braid"
icon_state = "rowbraid"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/rowdualbraid
name = "Row Dual Braid"
icon_state = "rowdualtail"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/rowbun
name = "Row Bun"
icon_state = "rowbun"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/hightight
name = "High and Tight"
icon_state = "hightight"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/partfade
name = "Parted Fade"
icon_state = "shavedpart"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/undercut3
name = "Undercut Swept Left"
icon_state = "undercut3"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/undercut2
name = "Undercut Swept Right"
icon_state = "undercut2"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/undercut1
name = "Undercut"
icon_state = "undercut1"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/coffeehouse
name = "Coffee House Cut"
icon_state = "coffeehouse"
- gender = MALE
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/tightbun
name = "Tight Bun"
icon_state = "tightbun"
- gender = FEMALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/trimmed
name = "Trimmed"
icon_state = "trimmed"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/trimflat
name = "Trimmed Flat Top"
icon_state = "trimflat"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/nofade
name = "Regulation Cut"
icon_state = "nofade"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/baldfade
name = "Balding Fade"
icon_state = "baldfade"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/highfade
name = "High Fade"
icon_state = "highfade"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/medfade
name = "Medium Fade"
icon_state = "medfade"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/lowfade
name = "Low Fade"
icon_state = "lowfade"
- gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/oxton
name = "Oxton"
icon_state = "oxton"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/doublebun
name = "Double-Bun"
icon_state = "doublebun"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/halfshaved
name = "Half-Shaved Emo"
icon_state = "halfshaved"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/shortbangs
name = "Short Bangs"
icon_state = "shortbangs"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longeralt2
name = "Long Hair Alt 2"
icon_state = "longeralt2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/nia
name = "Nia"
icon_state = "nia"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/eighties
name = "80's"
icon_state = "80s"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/volaju
name = "Volaju"
icon_state = "volaju"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/joestar
name = "Joestar"
icon_state = "joestar"
gender = MALE
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/nitori
name = "Nitori"
icon_state = "nitori"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/scully
name = "Scully"
icon_state = "scully"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/vegeta
name = "Vegeta"
icon_state = "toriyama2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/crono
name = "Chrono"
icon_state = "toriyama"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/poofy2
name = "Poofy2"
icon_state = "poofy2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/poofy
name = "Poofy"
icon_state = "poofy"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/dandypomp
name = "Dandy Pompadour"
icon_state = "dandypompadour"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/fringetail
name = "Fringetail"
icon_state = "fringetail"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/mahdrills
name = "Drillruru"
icon_state = "drillruru"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/familyman
name = "The Family Man"
icon_state = "thefamilyman"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/grandebraid
name = "Grande Braid"
icon_state = "grande"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/fringeemo
name = "Emo Fringe"
icon_state = "emofringe"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/emo2
name = "Emo Alt"
icon_state = "emo2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
+
/datum/sprite_accessory/hair/rows2
name = "Rows 2"
icon_state = "rows2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/rows
name = "Rows"
icon_state = "rows1"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/reversemohawk
name = "Reverse Mohawk"
icon_state = "reversemohawk"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/father
name = "Father"
icon_state = "father"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/beehive2
name = "Beehive 2"
icon_state = "beehive2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/sleeze
name = "Sleeze"
icon_state = "sleeze"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/zieglertail
name = "Zieglertail"
icon_state = "ziegler"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/spikyponytail
name = "Spiky Ponytail"
icon_state = "spikyponytail"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/tresshoulder
name = "Tress Shoulder"
icon_state = "tressshoulder"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/oneshoulder
name = "One Shoulder"
icon_state = "oneshoulder"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/ponytail6
name = "Ponytail 6"
icon_state = "ponytail6"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/ponytail5
name = "Ponytail 5"
icon_state = "ponytail5"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/ponytail4
name = "Ponytail 4"
icon_state = "ponytail4"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/country
name = "Country"
icon_state = "country"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bedheadlong
name = "Bedhead Long"
icon_state = "long_bedhead"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/flair
name = "Flaired Hair"
icon_state = "flair"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/twintail
name = "Twintail"
icon_state = "twintail"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/short2
name = "Short Hair 2"
icon_state = "shorthair3"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bun2
name = "Bun 2"
icon_state = "bun2"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bun3
name = "Bun 3"
icon_state = "bun3"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/shavehair
name = "Shaved Hair"
icon_state = "shaved"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/veryshortovereyealternate
name = "Overeye Very Short, Alternate"
icon_state = "veryshortovereyealternate"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/veryshortovereye
name = "Overeye Very Short"
icon_state = "veryshortovereye"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/shortovereye
name = "Overeye Short"
icon_state = "shortovereye"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/longovereye
name = "Overeye Long"
icon_state = "longovereye"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/father
name = "Father"
icon_state = "father"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/bun4 // Due to a vulp hairstyle called bun
name = "Bun 4"
icon_state = "bun4"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
///////////////////////////////////
//////END POLARIS HAIRSTYLES///////
@@ -687,62 +778,77 @@
/datum/sprite_accessory/hair/eighties_ponytail
name = "80's ponytail"
icon_state = "80_ponytail"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/eighties_ponytailalt
name = "80's ponytail alt"
icon_state = "80_ponytail_alt"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/big_bow
name = "Big bow"
icon_state = "big_bow"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/buns
name = "Buns"
icon_state = "buns"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/himecut_long
name = "Himecut long"
icon_state = "himecut_long"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/himecut_long_ponytail
name = "Himecut long ponytail"
icon_state = "himecut_long_ponytail"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/himecut_ponytail
name = "Himecut ponytail"
icon_state = "himecut_ponytail"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/ombre_twintails
name = "Ombre twintails"
icon_state = "ombre_twintails"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/ombre_twintails_alt
name = "Ombre twintails alt"
icon_state = "ombre_twintails_alt"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/sailor
name = "Sailor"
icon_state = "sailor"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/amanita_short
name = "Amanita short"
icon_state = "amanita_short"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/long_curls
name = "Long curls"
icon_state = "long_curls"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/long_curls_alt
name = "Long curls alt"
icon_state = "long_curls_alt"
+ species_allowed = list("Human", "Slime People", "Unathi", "Tajaran", "Vulpkanin")
/datum/sprite_accessory/hair/long_buns
name = "Long buns"
icon_state = "long_buns"
+ species_allowed = list("Human", "Slime People")
/datum/sprite_accessory/hair/low_twins
name = "Low twins"
icon_state = "low_twins"
+ species_allowed = list("Human", "Slime People")
///Ume hairs end here///
diff --git a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
index c6fdfd91b2e5..764c97c97348 100644
--- a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
@@ -171,7 +171,8 @@
species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
sprite_sheets = list(
"Vox" = 'icons/mob/clothing/species/vox/underwear.dmi',
- "Grey" = 'icons/mob/clothing/species/grey/underwear.dmi'
+ "Grey" = 'icons/mob/clothing/species/grey/underwear.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/underwear.dmi'
)
gender = NEUTER
@@ -301,7 +302,9 @@
species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
sprite_sheets = list(
"Vox" = 'icons/mob/clothing/species/vox/underwear.dmi',
- "Grey" = 'icons/mob/clothing/species/grey/underwear.dmi')
+ "Grey" = 'icons/mob/clothing/species/grey/underwear.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/underwear.dmi'
+ )
gender = NEUTER
/datum/sprite_accessory/undershirt/nude
@@ -538,7 +541,10 @@
/datum/sprite_accessory/socks
icon = 'icons/mob/clothing/underwear.dmi'
species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
- sprite_sheets = list("Vox" = 'icons/mob/clothing/species/vox/underwear.dmi')
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/clothing/species/vox/underwear.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/underwear.dmi'
+ )
gender = NEUTER
/datum/sprite_accessory/socks/nude
diff --git a/code/modules/mod/adding_new_mod.md b/code/modules/mod/adding_new_mod.md
new file mode 100644
index 000000000000..5dbd10cc5b1b
--- /dev/null
+++ b/code/modules/mod/adding_new_mod.md
@@ -0,0 +1,324 @@
+## Introduction
+
+This is a step by step guide for creating a MODsuit theme, skin and module.
+
+## Theme
+
+This is pretty simple, we go [here](./mod_theme.dm) and add a new definition, let's go with a Psychologist theme as an example. \
+Their names should be like model names or use similar adjectives, like "magnate" or simply "engineering", so we'll go with "psychological". \
+After that, it's good to decide what company is manufacturing the suit, and a basic description of what it offers, we'll write that down in the desc. \
+So, let's our suit should be a low-power usage with lowered module capacity. We'd go with something like this.
+
+```dm
+/datum/mod_theme/psychological
+ name = "psychological"
+ desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
+```
+
+For people that want to see additional stuff, we add an extended description with some more insight into what the suit does. We also set the default skin to usually the theme name, like so.
+
+```dm
+/datum/mod_theme/psychological
+ name = "psychological"
+ desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
+ extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
+ Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
+ for operating at lower power levels, keeping people sane. As consequence, the capacity \
+ of the suit has decreased, not being able to fit many modules at all."
+ default_skin = "psychological"
+```
+
+Next we want to set the statistics, you can view them all in the theme file, so let's just grab our relevant ones, armor, charge and capacity and set them to what we establilished. \
+Currently crew MODsuits should be lightly armored in combat relevant stats.
+
+```dm
+/datum/mod_theme/psychological
+ name = "psychological"
+ desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
+ extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
+ Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
+ for operating at lower power levels, keeping people sane. As consequence, the capacity \
+ of the suit has decreased, not being able to fit many modules at all."
+ default_skin = "psychological"
+ armor_type = /datum/armor/modtheme_psychological
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 7
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
+```
+
+Now we have a basic theme, it lacks a skin which will be covered in the next section, and an item, which we will add right now. \
+Let's go into [here](./mod_types.dm). It's as simple as adding a new suit type with the appropriate modules you want.
+
+```dm
+/obj/item/mod/control/pre_equipped/psychological
+ theme = /datum/mod_theme/psychological
+ initial_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/flashlight,
+ )
+```
+
+This will create our psychological suit, equipped with a storage and flashlight modules by default. We might also want to make it craftable, in which case we go [here](./mod_construction.dm) and set this.
+
+```dm
+/obj/item/mod/construction/armor/psychological
+ theme = /datum/mod_theme/psychological
+```
+
+After that we put it in the techweb or whatever other source we want. Now our suit is almost ready, it just needs a skin.
+
+## Skin
+
+So, now that we have our theme, we want to add a skin to it (or another theme of our choosing). Let's start with a basis.
+
+```dm
+/datum/mod_theme/psychological
+ name = "psychological"
+ desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
+ extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
+ Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
+ for operating at lower power levels, keeping people sane. As consequence, the capacity \
+ of the suit has decreased, not being able to fit many modules at all."
+ default_skin = "psychological"
+ armor_type = /datum/armor/modtheme_psychological
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 7
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
+ skins = list(
+ "psychological" = list(
+ HELMET_LAYER = null,
+ HELMET_FLAGS = list(
+ ),
+ CHESTPLATE_FLAGS = list(
+ ),
+ GAUNTLETS_FLAGS = list(
+ ),
+ BOOTS_FLAGS = list(
+ ),
+ ),
+ )
+```
+
+We now have a psychological skin, this will apply the psychological icons to every part of the suit. Next we'll be looking at the flags. Boots, gauntlets and the chestplate are usually very standard, we set their thickmaterial and pressureproofness while hiding the jumpsuit on the chestplate. On the helmet however, we'll actually look at its' icon. \
+For example, if our helmet's icon covers the full head (like the research skin), we want to do something like this.
+
+```dm
+ HELMET_LAYER = null,
+ HELMET_FLAGS = list(
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE|BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+```
+
+Otherwise, with an open helmet that becomes closed (like the engineering skin), we'd do this.
+
+```dm
+ HELMET_LAYER = NECK_LAYER,
+ HELMET_FLAGS = list(
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+```
+
+There are specific cases of helmets that semi-cover the head, like the cosmohonk, apocryphal and whatnot. You can look at these for more specific suits. So let's say our suit is an open helmet design, and also add an alternate skin with a closed helmet called psychotherapeutic. It'd look something like this.
+
+```dm
+/datum/mod_theme/psychological
+ name = "psychological"
+ desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
+ extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
+ Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
+ for operating at lower power levels, keeping people sane. As consequence, the capacity \
+ of the suit has decreased, not being able to fit many modules at all."
+ default_skin = "psychological"
+ armor_type = /datum/armor/modtheme_psychological
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 7
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
+ skins = list(
+ "psychological" = list(
+ HELMET_LAYER = NECK_LAYER,
+ HELMET_FLAGS = list(
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ "psychotherapeutic" = list(
+ HELMET_LAYER = null,
+ HELMET_FLAGS = list(
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ ),
+ )
+```
+
+Thus we finished our codeside. Now we go to the icon files for the suits and simply add our new skin's icons. \
+Now our suit is finished. But let's say we want to give it an unique module.
+
+## Module
+
+So, for our psychological suit, let's say we want a module that heals the brain damage of everyone in range. \
+As it's a medical module, we'll put it [here](modules/modules_medical.dm). Let's start with the object definition.
+
+```dm
+/obj/item/mod/module/neuron_healer
+ name = "MOD neuron healer module"
+ desc = "A module made experimentally by DeForest Medical Corporation. On demand it releases waves \
+ that heal neuron damage of everyone nearby, getting their brains to a better state."
+ icon_state = "neuron_healer"
+```
+
+As we want this effect to be on demand, we probably want this to be an usable module. There are four types of modules:
+- Passive: These have a passive effect.
+- Togglable: You can turn these on and off.
+- Usable: You can use these for a one time effect.
+- Active: You can only have one selected at a time. It gives you a special click effect.
+
+As we have an usable module, we want to set a cooldown time. All modules are also incompatible with themselves, have a specific power cost and complexity varying on how powerful they are, so let's update our definition, and also add a new variable for how much brain damage we'll heal.
+
+```dm
+/obj/item/mod/module/neuron_healer
+ name = "MOD neuron healer module"
+ desc = "A module made experimentally by DeForest Medical Corporation. On demand it releases waves \
+ that heal neuron damage of everyone nearby, getting their brains to a better state."
+ icon_state = "neuron_healer"
+ module_type = MODULE_USABLE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/neuron_healer)
+ cooldown_time = 15 SECONDS
+ var/brain_damage_healed = 25
+```
+
+Now, we want to override the on_use proc for our new effect. We want to make sure the use checks passed from parent. You can read about most procs and variables by reading [this](modules/_module.dm)
+
+```dm
+/obj/item/mod/module/neuron_healer/on_use()
+ . = ..()
+ if(!.)
+ return
+```
+
+After this, we want to put our special code, a basic effect of healing all mobs nearby for their brain damage and creating a beam to them.
+
+```dm
+/obj/item/mod/module/neuron_healer/on_use()
+ . = ..()
+ if(!.)
+ return
+ for(var/mob/living/carbon/carbon_mob in range(5, src))
+ if(carbon_mob == mod.wearer)
+ continue
+ carbon_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -brain_damage_healed)
+ mod.wearer.Beam(carbon_mob, icon_state = "plasmabeam", time = 1.5 SECONDS)
+ playsound(src, 'sound/effects/magic.ogg', 100, TRUE)
+ drain_power(use_power_cost)
+```
+
+We now have a basic module, we can add it to the techwebs to make it printable ingame, and we can add an inbuilt, advanced version of it for our psychological suit. We'll give it more healing power, no complexity and make it unremovable.
+
+```dm
+/obj/item/mod/module/neuron_healer/advanced
+ name = "MOD advanced neuron healer module"
+ complexity = 0
+ brain_damage_healed = 50
+```
+
+Now we want to add it to the psychological theme, which is very simple, finishing with this:
+
+```dm
+/datum/mod_theme/psychological
+ name = "psychological"
+ desc = "A DeForest Medical Corporation power-saving psychological suit, limiting its' module capacity."
+ extended_desc = "DeForest Medical Corporation's prototype suit, based off the work of \
+ Nakamura Engineering. The suit has been modified to save power compared to regular suits, \
+ for operating at lower power levels, keeping people sane. As consequence, the capacity \
+ of the suit has decreased, not being able to fit many modules at all."
+ default_skin = "psychological"
+ armor_type = /datum/armor/modtheme_psychological
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 7
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0.5
+ inbuilt_modules = list(/obj/item/mod/module/neuron_healer/advanced)
+ skins = list(
+ "psychological" = list(
+ HELMET_LAYER = NECK_LAYER,
+ HELMET_FLAGS = list(
+ UNSEALED_CLOTHING = SNUG_FIT,
+ SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR,
+ SEALED_INVISIBILITY = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ "psychotherapeutic" = list(
+ HELMET_LAYER = null,
+ HELMET_FLAGS = list(
+ UNSEALED_CLOTHING = SNUG_FIT|THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ UNSEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
+ UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
+ ),
+ ),
+ )
+```
+
+## Ending
+This finishes this hopefully easy to follow along tutorial. You should now know how to make a basic theme, a skin for it, and a module.
diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm
new file mode 100644
index 000000000000..12272a7b0cb2
--- /dev/null
+++ b/code/modules/mod/mod_actions.dm
@@ -0,0 +1,127 @@
+/datum/action/item_action/mod
+ background_icon_state = "bg_mod"
+ button_icon_state = "bg_mod_border"
+ icon_icon = 'icons/mob/actions/actions_mod.dmi'
+ button_icon = 'icons/mob/actions/actions_mod.dmi'
+ check_flags = AB_CHECK_CONSCIOUS
+ use_itemicon = FALSE
+
+/datum/action/item_action/mod/New(Target, custom_icon, custom_icon_state)
+ ..()
+ if(!ismodcontrol(Target))
+ qdel(src)
+
+/datum/action/item_action/mod/Trigger(left_click, attack_self)
+ if(!IsAvailable())
+ return FALSE
+ var/obj/item/mod/control/mod = target
+ if(mod.malfunctioning && prob(75))
+ to_chat(usr, "The module fails to activate!")
+ return FALSE
+ return TRUE
+
+/datum/action/item_action/mod/deploy
+ name = "Deploy MODsuit"
+ desc = "LMB: Deploy/Undeploy full suit. MMB: Deploy/Undeploy part."
+ button_icon_state = "deploy"
+
+/datum/action/item_action/mod/deploy/Trigger(left_click, attack_self)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/mod/control/mod = target
+ if(left_click)
+ mod.quick_deploy(usr)
+ else
+ mod.choose_deploy(usr)
+
+/datum/action/item_action/mod/activate
+ name = "Activate MODsuit"
+ desc = "LMB: Activate/Deactivate suit with prompt. MMB: Activate/Deactivate suit skipping prompt."
+ button_icon_state = "activate"
+ /// First time clicking this will set it to TRUE, second time will activate it.
+ var/ready = FALSE
+
+/datum/action/item_action/mod/activate/Trigger(left_click, attack_self)
+ . = ..()
+ if(!.)
+ return
+ if(!ready && left_click)
+ ready = TRUE
+ button_icon_state = "activate-ready"
+ addtimer(CALLBACK(src, PROC_REF(reset_ready)), 3 SECONDS)
+ return
+ var/obj/item/mod/control/mod = target
+ reset_ready()
+ mod.toggle_activate(usr)
+
+/// Resets the state requiring to be doubleclicked again.
+/datum/action/item_action/mod/activate/proc/reset_ready()
+ ready = FALSE
+ button_icon_state = initial(button_icon_state)
+
+/datum/action/item_action/mod/module
+ name = "Toggle Module"
+ desc = "Toggle a MODsuit module."
+ button_icon_state = "module"
+
+/datum/action/item_action/mod/module/Trigger(left_click, attack_self)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/mod/control/mod = target
+ mod.quick_module(usr)
+
+/datum/action/item_action/mod/panel
+ name = "MODsuit Panel"
+ desc = "Open the MODsuit's panel."
+ button_icon_state = "panel"
+
+/datum/action/item_action/mod/panel/Trigger(left_click, attack_self)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/mod/control/mod = target
+ mod.ui_interact(usr)
+
+/datum/action/item_action/mod/pinned_module
+ desc = "Activate the module."
+ icon_icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ button_icon = 'icons/mob/actions/actions_mod.dmi'
+ button_icon_state = "module"
+ /// Module we are linked to.
+ var/obj/item/mod/module/module
+ /// A ref to the mob we are pinned to.
+ var/pinner_uid
+
+/datum/action/item_action/mod/pinned_module/New(Target, custom_icon, custom_icon_state, obj/item/mod/module/linked_module, mob/user)
+ name = "Activate [capitalize(linked_module.name)]"
+ desc = "Quickly activate [linked_module]."
+ ..()
+ module = linked_module
+ button_icon_state = module.icon_state
+ if(linked_module.allow_flags & MODULE_ALLOW_INCAPACITATED)
+ // clears check hands
+ check_flags = AB_CHECK_CONSCIOUS
+ Grant(user)
+
+/datum/action/item_action/mod/pinned_module/Destroy()
+ UnregisterSignal(module, list(COMSIG_MODULE_ACTIVATED, COMSIG_MODULE_DEACTIVATED, COMSIG_MODULE_USED))
+ module.pinned_to -= pinner_uid
+ module = null
+ return ..()
+
+/datum/action/item_action/mod/pinned_module/Grant(mob/user)
+ var/user_uid = UID(user)
+ if(!pinner_uid)
+ pinner_uid = user_uid
+ module.pinned_to[pinner_uid] = src
+ else if(pinner_uid != user_uid)
+ return
+ return ..()
+
+/datum/action/item_action/mod/pinned_module/Trigger(left_click, attack_self)
+ . = ..()
+ if(!.)
+ return
+ module.on_select()
diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm
new file mode 100644
index 000000000000..61a350f27349
--- /dev/null
+++ b/code/modules/mod/mod_activation.dm
@@ -0,0 +1,248 @@
+/// Creates a radial menu from which the user chooses parts of the suit to deploy/retract. Repeats until all parts are extended or retracted.
+/obj/item/mod/control/proc/choose_deploy(mob/user)
+ if(!length(mod_parts))
+ return
+ var/list/display_names = list()
+ var/list/items = list()
+ for(var/obj/item/part as anything in mod_parts)
+ display_names[part.name] = part.UID()
+ var/image/part_image = image(icon = part.icon, icon_state = part.icon_state)
+ if(part.loc != src)
+ part_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active")
+ items += list(part.name = part_image)
+ var/pick = show_radial_menu(user, src, items, custom_check = FALSE, require_near = TRUE)
+ if(!pick)
+ return
+ var/part_reference = display_names[pick]
+ var/obj/item/part = locateUID(part_reference)
+ if(!istype(part) || user.incapacitated())
+ return
+ if(active || activating)
+ to_chat(user, "Deactivate the suit first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ var/parts_to_check = mod_parts - part
+ if(part.loc == src)
+ deploy(user, part)
+ on_mod_deployed(user)
+ for(var/obj/item/checking_part as anything in parts_to_check)
+ if(checking_part.loc != src)
+ continue
+ choose_deploy(user)
+ break
+ else
+ retract(user, part)
+ on_mod_retracted(user)
+ for(var/obj/item/checking_part as anything in parts_to_check)
+ if(checking_part.loc == src)
+ continue
+ choose_deploy(user)
+ break
+
+/// Quickly deploys all parts (or retracts if all are on the wearer)
+/obj/item/mod/control/proc/quick_deploy(mob/user)
+ if(active || activating)
+ to_chat(user, "Deactivate the suit first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ var/deploy = TRUE
+ for(var/obj/item/part as anything in mod_parts)
+ if(part.loc == src)
+ continue
+ deploy = FALSE
+ break
+ for(var/obj/item/part as anything in mod_parts)
+ if(deploy && part.loc == src)
+ deploy(null, part, TRUE)
+ else if(!deploy && part.loc != src)
+ retract(null, part, TRUE)
+ wearer.visible_message("[wearer]'s [src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss.",
+ "[src] [deploy ? "deploys" : "retracts"] its' parts with a mechanical hiss.",
+ "You hear a mechanical hiss.")
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ if(deploy)
+ on_mod_deployed(user)
+ else
+ on_mod_retracted(user)
+ return TRUE
+
+/// Deploys a part of the suit onto the user.
+/obj/item/mod/control/proc/deploy(mob/user, obj/item/part, mass = FALSE)
+ if(part.loc != src)
+ if(!user)
+ return FALSE
+ to_chat(user, "[part.name] already deployed!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ if(part in overslotting_parts)
+ var/obj/item/overslot = wearer.get_item_by_slot(slot_bitfield_to_slot(part.slot_flags))
+ if(overslot)
+ wearer.unEquip(overslot, TRUE)
+ overslotting_parts[part] = overslot
+ overslot.forceMove(part)
+ RegisterSignal(part, COMSIG_ATOM_EXITED, PROC_REF(on_overslot_exit))
+ if(wearer.equip_to_slot_if_possible(part, slot_bitfield_to_slot(part.slot_flags), disable_warning = TRUE))
+ part.flags |= NODROP
+ if(mass)
+ return TRUE
+ wearer.visible_message("[wearer]'s [part.name] deploy[part.p_s()] with a mechanical hiss.",
+ "[part] deploy[part.p_s()] with a mechanical hiss.",
+ "You hear a mechanical hiss.")
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ return TRUE
+ else
+ if(!user)
+ return FALSE
+ to_chat(user, "You already have clothing there!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+
+/// Retract a part of the suit from the user.
+/obj/item/mod/control/proc/retract(mob/user, obj/item/part, mass = FALSE)
+ if(part.loc == src)
+ if(!user)
+ return FALSE
+ to_chat(user, "You already have retracted there!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ part.flags &= ~NODROP
+ wearer.unEquip(part, TRUE)
+ part.forceMove(src)
+ if(overslotting_parts[part])
+ UnregisterSignal(part, COMSIG_ATOM_EXITED)
+ var/obj/item/overslot = overslotting_parts[part]
+ if(!wearer.equip_to_slot_if_possible(overslot, slot_bitfield_to_slot(overslot.slot_flags), disable_warning = TRUE))
+ overslot.forceMove(get_turf(wearer))
+ overslotting_parts[part] = null
+ if(mass)
+ return TRUE
+ wearer.visible_message("[wearer]'s [part.name] retract[part.p_s()] back into [src] with a mechanical hiss.",
+ "[part] retract[part.p_s()] back into [src] with a mechanical hiss.",
+ "You hear a mechanical hiss.")
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+
+/// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on
+/obj/item/mod/control/proc/toggle_activate(mob/user, force_deactivate = FALSE)
+ if(!wearer)
+ if(!force_deactivate)
+ to_chat(user, "Equip your suit first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(!force_deactivate && (SEND_SIGNAL(src, COMSIG_MOD_ACTIVATE, user) & MOD_CANCEL_ACTIVATE))
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ for(var/obj/item/part as anything in mod_parts)
+ if(!force_deactivate && part.loc == src)
+ to_chat(user, "Deploy all parts first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(locked && !active && !allowed(user) && !force_deactivate)
+ to_chat(user, "Insufficient access!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(!get_charge() && !force_deactivate)
+ to_chat(user, "Suit is not powered!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(open && !force_deactivate)
+ to_chat(user, "Close the suit panel!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(activating)
+ if(!force_deactivate)
+ to_chat(user, "Suit is already [active ? "shutting down" : "starting up"]!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(!module.active || (module.allow_flags & MODULE_ALLOW_INACTIVE))
+ continue
+ module.on_deactivation(display_message = FALSE)
+ activating = TRUE
+ to_chat(wearer, "MODsuit [active ? "shutting down" : "starting up"].")
+ if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
+ if(has_wearer())
+ to_chat(wearer, "[boots] [active ? "relax their grip on your legs" : "seal around your feet"].")
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(boots, seal = !active)
+ if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
+ if(has_wearer())
+ to_chat(wearer, "[gauntlets] [active ? "become loose around your fingers" : "tighten around your fingers and wrists"].")
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(gauntlets, seal = !active)
+ if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
+ if(has_wearer())
+ to_chat(wearer, "[chestplate] [active ? "releases your chest" : "cinches tightly against your chest"].")
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(chestplate, seal = !active)
+ if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
+ if(has_wearer())
+ to_chat(wearer, "[helmet] hisses [active ? "open" : "closed"].")
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ seal_part(helmet, seal = !active)
+ if(do_after(wearer, activation_step_time, FALSE, target = src, allow_moving = TRUE))
+ if(has_wearer())
+ to_chat(wearer, "Systems [active ? "shut down. Parts unsealed. Goodbye" : "started up. Parts sealed. Welcome"], [wearer].")
+ finish_activation(on = !active)
+ if(active)
+ playsound(src, 'sound/machines/synth_yes.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
+ if(!malfunctioning)
+ wearer.playsound_local(get_turf(src), 'sound/mecha/nominal.ogg', 50)
+ else
+ playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, frequency = 6000)
+ activating = FALSE
+ SEND_SIGNAL(src, COMSIG_MOD_TOGGLED, user)
+ return TRUE
+
+///Seals or unseals the given part
+/obj/item/mod/control/proc/seal_part(obj/item/clothing/part, seal)
+ if(seal)
+ part.icon_state = "[skin]-[part.base_icon_state]-sealed"
+ part.flags |= part.visor_flags
+ part.flags_inv |= part.visor_flags_inv
+ part.flags_cover |= part.visor_flags_cover
+ part.heat_protection = initial(part.heat_protection)
+ part.cold_protection = initial(part.cold_protection)
+ else
+ part.icon_state = "[skin]-[part.base_icon_state]"
+ part.flags_cover &= ~part.visor_flags_cover
+ part.flags_inv &= ~part.visor_flags_inv
+ part.flags &= ~part.visor_flags
+ part.heat_protection = NONE
+ part.cold_protection = NONE
+ if(ishuman(wearer))
+ var/mob/living/carbon/human/H = wearer
+ H.regenerate_icons()
+
+/// Finishes the suit's activation, starts processing
+/obj/item/mod/control/proc/finish_activation(on)
+ active = on
+ if(active)
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_suit_activation()
+ START_PROCESSING(SSobj, src)
+ else
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_suit_deactivation()
+ STOP_PROCESSING(SSobj, src)
+ update_speed()
+ update_icon_state()
+ wearer.regenerate_icons()
+
+/// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits.
+/obj/item/mod/control/proc/quick_activation()
+ var/seal = TRUE
+ for(var/obj/item/part as anything in mod_parts)
+ if(!deploy(null, part))
+ seal = FALSE
+ if(!seal)
+ return
+ for(var/obj/item/part as anything in mod_parts)
+ seal_part(part, seal = TRUE)
+ finish_activation(on = TRUE)
+
+/obj/item/mod/control/proc/has_wearer()
+ return wearer
+
+/obj/item/mod/control/proc/on_mod_deployed(mob/user)
+ SEND_SIGNAL(src, COMSIG_MOD_DEPLOYED, user)
+
+/obj/item/mod/control/proc/on_mod_retracted(mob/user)
+ SEND_SIGNAL(src, COMSIG_MOD_RETRACTED, user)
diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm
new file mode 100644
index 000000000000..a5360a1ffa47
--- /dev/null
+++ b/code/modules/mod/mod_clothes.dm
@@ -0,0 +1,85 @@
+/obj/item/clothing/head/mod
+ name = "MOD helmet"
+ desc = "A helmet for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-helmet"
+ base_icon_state = "helmet"
+ item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
+ body_parts_covered = HEAD
+ heat_protection = HEAD
+ cold_protection = HEAD
+ sprite_sheets = list(
+ "Grey" = 'icons/mob/clothing/modsuit/species/grey_helmets.dmi',
+ "Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
+ "Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
+ "Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
+ )
+
+/obj/item/clothing/suit/mod
+ name = "MOD chestplate"
+ desc = "A chestplate for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-chestplate"
+ base_icon_state = "chestplate"
+ item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ blood_overlay_type = "armor"
+ allowed = list(
+ /obj/item/tank/internals,
+ /obj/item/flashlight,
+ /obj/item/tank/jetpack/oxygen/captain,
+ )
+ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO
+ heat_protection = UPPER_TORSO|LOWER_TORSO
+ cold_protection = UPPER_TORSO|LOWER_TORSO
+ hide_tail_by_species = list("modsuit")
+ sprite_sheets = list(
+ "Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
+ "Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
+ "Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
+ )
+
+
+/obj/item/clothing/gloves/mod
+ name = "MOD gauntlets"
+ desc = "A pair of gauntlets for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-gauntlets"
+ base_icon_state = "gauntlets"
+ item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
+ body_parts_covered = HANDS|ARMS
+ heat_protection = HANDS|ARMS
+ cold_protection = HANDS|ARMS
+ sprite_sheets = list(
+ "Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
+ "Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
+ "Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
+ )
+
+
+/obj/item/clothing/shoes/mod
+ name = "MOD boots"
+ desc = "A pair of boots for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-boots"
+ base_icon_state = "boots"
+ item_state = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
+ body_parts_covered = FEET|LEGS
+ heat_protection = FEET|LEGS
+ cold_protection = FEET|LEGS
+ sprite_sheets = list(
+ "Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
+ "Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
+ "Unathi" = 'icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi'
+ )
+ var/magbooted
+
+/obj/item/clothing/shoes/mod/negates_gravity()
+ return magbooted
diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm
new file mode 100644
index 000000000000..e92f9bf41a31
--- /dev/null
+++ b/code/modules/mod/mod_construction.dm
@@ -0,0 +1,281 @@
+/obj/item/mod/construction
+ desc = "A part used in MOD construction. You could insert it into a MOD shell."
+ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
+ icon_state = "rack_parts"
+
+/obj/item/mod/construction/helmet
+ name = "MOD helmet"
+ desc = "You could insert it into a MOD shell."
+ icon_state = "helmet"
+
+/obj/item/mod/construction/chestplate
+ name = "MOD chestplate"
+ icon_state = "chestplate"
+
+/obj/item/mod/construction/gauntlets
+ name = "MOD gauntlets"
+ icon_state = "gauntlets"
+
+/obj/item/mod/construction/boots
+ name = "MOD boots"
+ icon_state = "boots"
+
+/obj/item/mod/construction/broken_core
+ name = "broken MOD core"
+ icon_state = "mod-core"
+ desc = "An internal power source for a Modular Outerwear Device. You don't seem to be able to source any power from this one, though."
+
+/obj/item/mod/construction/broken_core/examine(mob/user)
+ . = ..()
+ . += "You could repair it with a screwdriver..."
+
+/obj/item/mod/construction/broken_core/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(!tool.use_tool(src, user, 5 SECONDS, volume = 30))
+ return
+ new /obj/item/mod/core/standard(drop_location())
+ qdel(src)
+
+/obj/item/mod/construction/plating
+ name = "MOD external plating"
+ desc = "External plating used to finish a MOD control unit."
+ icon_state = "standard-plating"
+ var/datum/mod_theme/theme = /datum/mod_theme/standard
+
+/obj/item/mod/construction/plating/Initialize(mapload)
+ . = ..()
+ var/datum/mod_theme/used_theme = GLOB.mod_themes[theme]
+ name = "MOD [used_theme.name] external plating"
+ desc = "[desc] [used_theme.desc]"
+ icon_state = "[used_theme.default_skin]-plating"
+
+/obj/item/mod/construction/plating/engineering
+ theme = /datum/mod_theme/engineering
+
+/obj/item/mod/construction/plating/atmospheric
+ theme = /datum/mod_theme/atmospheric
+
+/obj/item/mod/construction/plating/medical
+ theme = /datum/mod_theme/medical
+
+/obj/item/mod/construction/plating/security
+ theme = /datum/mod_theme/security
+/obj/item/mod/construction/plating/cosmohonk
+ theme = /datum/mod_theme/cosmohonk
+
+/obj/item/mod/construction/plating/rescue //I want to add a way to get the rarer modsuit types, that is limited. A low chance for traders to have plating for it seems interesting
+ theme = /datum/mod_theme/rescue
+
+/obj/item/mod/construction/plating/safeguard //Continued from above, none of these are steal objectives, and only the CE or RD one comes pre-installed with modules. You are getting the protection / speed / looks of these hardsuits, but no special modules.
+ theme = /datum/mod_theme/safeguard
+
+/obj/item/mod/construction/plating/advanced //This may be a bad idea. I think this is an interesting idea. And you still need robotics to build it, and traders can charge as much for it as they want. Also with ones like the CE modsuit, it is the flagship mod. That means it is sold a lot.
+ theme = /datum/mod_theme/advanced
+
+/obj/item/mod/construction/plating/research //Don't think people will want the RD one though, it is as slow as shit. Anyway, here it is. Surely this will not end poorly.
+ theme = /datum/mod_theme/research
+
+#define START_STEP "start"
+#define CORE_STEP "core"
+#define SCREWED_CORE_STEP "screwed_core"
+#define HELMET_STEP "helmet"
+#define CHESTPLATE_STEP "chestplate"
+#define GAUNTLETS_STEP "gauntlets"
+#define BOOTS_STEP "boots"
+#define WRENCHED_ASSEMBLY_STEP "wrenched_assembly"
+#define SCREWED_ASSEMBLY_STEP "screwed_assembly"
+
+/obj/item/mod/construction/shell
+ name = "MOD shell"
+ desc = "A MOD shell."
+ icon_state = "mod-construction_start"
+ var/obj/item/core
+ var/obj/item/helmet
+ var/obj/item/chestplate
+ var/obj/item/gauntlets
+ var/obj/item/boots
+ var/construction_step = START_STEP
+
+/obj/item/mod/construction/shell/examine(mob/user)
+ . = ..()
+ var/display_text
+ switch(construction_step)
+ if(START_STEP)
+ display_text = "It looks like it's missing a MOD core..."
+ if(CORE_STEP)
+ display_text = "The core seems loose..."
+ if(SCREWED_CORE_STEP)
+ display_text = "It looks like it's missing a helmet..."
+ if(HELMET_STEP)
+ display_text = "It looks like it's missing a chestplate..."
+ if(CHESTPLATE_STEP)
+ display_text = "It looks like it's missing gauntlets..."
+ if(GAUNTLETS_STEP)
+ display_text = "It looks like it's missing boots..."
+ if(BOOTS_STEP)
+ display_text = "The assembly seems unsecured..."
+ if(WRENCHED_ASSEMBLY_STEP)
+ display_text = "The assembly seems loose..."
+ if(SCREWED_ASSEMBLY_STEP)
+ display_text = "All it's missing is external plating..."
+ . += "[display_text]"
+
+/obj/item/mod/construction/shell/attackby(obj/item/part, mob/user, params)
+ . = ..()
+ switch(construction_step)
+ if(START_STEP)
+ if(!istype(part, /obj/item/mod/core))
+ return
+ if(!user.drop_item())
+ to_chat(user, "[part] is stuck to you and cannot be placed into [src].")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ to_chat(user, "Core inserted.")
+ core = part
+ core.forceMove(src)
+ construction_step = CORE_STEP
+ if(CORE_STEP)
+ if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
+ if(part.use_tool(src, user, 0, volume = 30))
+ to_chat(user, "Core screwed.")
+ construction_step = SCREWED_CORE_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume = 30))
+ core.forceMove(drop_location())
+ to_chat(user, "Core removed.")
+ construction_step = START_STEP
+ if(SCREWED_CORE_STEP)
+ if(istype(part, /obj/item/mod/construction/helmet)) //Construct
+ if(!user.drop_item())
+ to_chat(user, "[part] is stuck to you and cannot be placed into [src].")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ to_chat(user, "Helmet added.")
+ helmet = part
+ helmet.forceMove(src)
+ construction_step = HELMET_STEP
+ else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Deconstruct
+ if(part.use_tool(src, user, 0, volume = 30))
+ to_chat(user, "Core unscrewed.")
+ construction_step = CORE_STEP
+ if(HELMET_STEP)
+ if(istype(part, /obj/item/mod/construction/chestplate)) //Construct
+ if(!user.drop_item())
+ to_chat(user, "[part] is stuck to you and cannot be placed into [src].")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ to_chat(user, "Chestplate added.")
+ forceMove(src)
+ chestplate = part
+ chestplate.forceMove(src)
+ construction_step = CHESTPLATE_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume = 30))
+ helmet.forceMove(drop_location())
+ to_chat(user, "Helmet removed.")
+ helmet = null
+ construction_step = SCREWED_CORE_STEP
+ if(CHESTPLATE_STEP)
+ if(istype(part, /obj/item/mod/construction/gauntlets)) //Construct
+ if(!user.drop_item())
+ to_chat(user, "[part] is stuck to you and cannot be placed into [src].")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ to_chat(user, "Gauntlets added.")
+ gauntlets = part
+ gauntlets.forceMove(src)
+ construction_step = GAUNTLETS_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume = 30))
+ chestplate.forceMove(drop_location())
+ to_chat(user, "Chestplate removed.")
+ chestplate = null
+ construction_step = HELMET_STEP
+ if(GAUNTLETS_STEP)
+ if(istype(part, /obj/item/mod/construction/boots)) //Construct
+ if(!user.drop_item())
+ to_chat(user, "[part] is stuck to you and cannot be placed into [src].")
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ to_chat(user, "Boots added.")
+ boots = part
+ boots.forceMove(src)
+ construction_step = BOOTS_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume = 30))
+ gauntlets.forceMove(drop_location())
+ to_chat(user, "Gauntlets removed.")
+ gauntlets = null
+ construction_step = CHESTPLATE_STEP
+ if(BOOTS_STEP)
+ if(part.tool_behaviour == TOOL_WRENCH) //Construct
+ if(part.use_tool(src, user, 0, volume = 30))
+ to_chat(user, "Assembly secured.")
+ construction_step = WRENCHED_ASSEMBLY_STEP
+ else if(part.tool_behaviour == TOOL_CROWBAR) //Deconstruct
+ if(part.use_tool(src, user, 0, volume = 30))
+ boots.forceMove(drop_location())
+ to_chat(user, "Boots removed.")
+ boots = null
+ construction_step = GAUNTLETS_STEP
+ if(WRENCHED_ASSEMBLY_STEP)
+ if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
+ if(part.use_tool(src, user, 0, volume = 30))
+ to_chat(user, "Assembly screwed.")
+ construction_step = SCREWED_ASSEMBLY_STEP
+ else if(part.tool_behaviour == TOOL_WRENCH) //Deconstruct
+ if(part.use_tool(src, user, 0, volume = 30))
+ to_chat(user, "Assembly unsecured.")
+ construction_step = BOOTS_STEP
+ if(SCREWED_ASSEMBLY_STEP)
+ if(istype(part, /obj/item/mod/construction/plating)) //Construct
+ var/obj/item/mod/construction/plating/external_plating = part
+ if(!user.drop_item())
+ return
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ var/obj/item/mod = new /obj/item/mod/control(drop_location(), external_plating.theme, null, core)
+ core = null
+ qdel(external_plating)
+ qdel(src)
+ user.put_in_hands(mod)
+ to_chat(user, "Suit finished!")
+ else if(part.tool_behaviour == TOOL_SCREWDRIVER) //Construct
+ if(part.use_tool(src, user, 0, volume = 30))
+ to_chat(user, "Assembly unscrewed.")
+ construction_step = SCREWED_ASSEMBLY_STEP
+ update_icon_state()
+
+/obj/item/mod/construction/shell/update_icon_state()
+ . = ..()
+ icon_state = "mod-construction_[construction_step]"
+
+/obj/item/mod/construction/shell/Destroy()
+ QDEL_NULL(core)
+ QDEL_NULL(helmet)
+ QDEL_NULL(chestplate)
+ QDEL_NULL(gauntlets)
+ QDEL_NULL(boots)
+ return ..()
+
+/obj/item/mod/construction/shell/handle_atom_del(atom/deleted_atom)
+ if(deleted_atom == core)
+ core = null
+ if(deleted_atom == helmet)
+ helmet = null
+ if(deleted_atom == chestplate)
+ chestplate = null
+ if(deleted_atom == gauntlets)
+ gauntlets = null
+ if(deleted_atom == boots)
+ boots = null
+ return ..()
+
+#undef START_STEP
+#undef CORE_STEP
+#undef SCREWED_CORE_STEP
+#undef HELMET_STEP
+#undef CHESTPLATE_STEP
+#undef GAUNTLETS_STEP
+#undef BOOTS_STEP
+#undef WRENCHED_ASSEMBLY_STEP
+#undef SCREWED_ASSEMBLY_STEP
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
new file mode 100644
index 000000000000..747d9890f7f3
--- /dev/null
+++ b/code/modules/mod/mod_control.dm
@@ -0,0 +1,746 @@
+/// MODsuits, trade-off between armor and utility
+/obj/item/mod
+ name = "Base MOD"
+ desc = "You should not see this, yell at a coder!"
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'// figure out how to work with 2 of these
+ icon_override = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+
+/obj/item/mod/control
+ name = "MOD control unit"
+ desc = "The control unit of a Modular Outerwear Device, a powered suit that protects against various environments."
+ icon_state = "standard-control"
+ icon_state = "mod_control"
+ item_state = "mod_control"
+ base_icon_state = "control"
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = SLOT_BACK
+ strip_delay = 10 SECONDS
+ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
+ actions_types = list(
+ /datum/action/item_action/mod/deploy,
+ /datum/action/item_action/mod/activate,
+ /datum/action/item_action/mod/panel,
+ /datum/action/item_action/mod/module,
+ )
+ resistance_flags = NONE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
+ min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ siemens_coefficient = 0.5
+ var/datum/wires/mod/wires
+
+ /// The MOD's theme, decides on some stuff like armor and statistics.
+ var/datum/mod_theme/theme = /datum/mod_theme/standard
+ /// Looks of the MOD.
+ var/skin = "standard"
+ /// Theme of the MOD TGUI
+ var/ui_theme = "ntos"
+ /// If the suit is deployed and turned on.
+ var/active = FALSE
+ /// If the suit wire/module hatch is open.
+ var/open = FALSE
+ /// If the suit is ID locked.
+ var/locked = FALSE
+ /// If the suit is malfunctioning.
+ var/malfunctioning = FALSE
+ /// If the suit is currently activating/deactivating.
+ var/activating = FALSE
+ /// How long the MOD is electrified for.
+ var/seconds_electrified = 0
+ /// If the suit interface is broken.
+ var/interface_break = FALSE
+ /// How much module complexity can this MOD carry.
+ var/complexity_max = DEFAULT_MAX_COMPLEXITY
+ /// How much module complexity this MOD is carrying.
+ var/complexity = 0
+ /// Power usage of the MOD.
+ var/charge_drain = DEFAULT_CHARGE_DRAIN
+ /// Slowdown of the MOD when not active.
+ var/slowdown_inactive = 1.25
+ /// Slowdown of the MOD when active.
+ var/slowdown_active = 0.75
+ /// How long this MOD takes each part to seal.
+ var/activation_step_time = MOD_ACTIVATION_STEP_TIME
+ /// Extended description of the theme.
+ var/extended_desc
+ /// MOD helmet.
+ var/obj/item/clothing/head/mod/helmet
+ /// MOD chestplate.
+ var/obj/item/clothing/suit/mod/chestplate
+ /// MOD gauntlets.
+ var/obj/item/clothing/gloves/mod/gauntlets
+ /// MOD boots.
+ var/obj/item/clothing/shoes/mod/boots
+ /// MOD core.
+ var/obj/item/mod/core/core
+ /// Associated list of parts (helmet, chestplate, gauntlets, boots) to their unsealed worn layer.
+ var/list/mod_parts = list()
+ /// Associated list of parts that can overslot to their overslot (overslot means the part can cover another layer of clothing).
+ var/list/overslotting_parts = list()
+ /// Modules the MOD currently possesses.
+ var/list/modules = list()
+ /// Currently used module.
+ var/obj/item/mod/module/selected_module
+ /// Person wearing the MODsuit.
+ var/mob/living/carbon/human/wearer
+ /// Internal storage in a modsuit
+ var/obj/item/storage/backpack/modstorage/bag
+ /// Is it EMP proof?
+ var/emp_proof = FALSE
+ /// List of overlays the mod has. Needs to be cut onremoval / module deactivation
+ var/list/mod_overlays = list()
+ /// Is the jetpack on so we should make ion effects?
+ var/jetpack_active = FALSE
+
+/obj/item/mod/control/Initialize(mapload, datum/mod_theme/new_theme, new_skin, obj/item/mod/core/new_core)
+ . = ..()
+ if(new_theme)
+ theme = new_theme
+ theme = GLOB.mod_themes[theme]
+ slot_flags = theme.slot_flags
+ extended_desc = theme.extended_desc
+ slowdown_inactive = theme.slowdown_inactive
+ slowdown_active = theme.slowdown_active
+ complexity_max = theme.complexity_max
+ ui_theme = theme.ui_theme
+ charge_drain = theme.charge_drain
+ wires = new/datum/wires/mod(src)
+ if(length(req_access))
+ locked = TRUE
+ new_core?.install(src)
+ helmet = new /obj/item/clothing/head/mod(src)
+ mod_parts += helmet
+ chestplate = new /obj/item/clothing/suit/mod(src)
+ chestplate.allowed += theme.allowed_suit_storage
+ mod_parts += chestplate
+ gauntlets = new /obj/item/clothing/gloves/mod(src)
+ mod_parts += gauntlets
+ boots = new /obj/item/clothing/shoes/mod(src)
+ mod_parts += boots
+ var/list/all_parts = mod_parts + src
+ for(var/obj/item/part as anything in all_parts)
+ part.name = "[theme.name] [part.name]"
+ part.desc = "[part.desc] [theme.desc]"
+ part.armor = part.armor.attachArmor(theme.armor_type_2.armor)
+ part.resistance_flags = theme.resistance_flags
+ part.flags |= theme.atom_flags //flags like initialization or admin spawning are here, so we cant set, have to add
+ part.heat_protection = NONE
+ part.cold_protection = NONE
+ part.max_heat_protection_temperature = theme.max_heat_protection_temperature
+ part.min_cold_protection_temperature = theme.min_cold_protection_temperature
+ part.siemens_coefficient = theme.siemens_coefficient
+ for(var/obj/item/part as anything in mod_parts)
+ RegisterSignal(part, COMSIG_OBJ_DECONSTRUCT, PROC_REF(on_part_destruction)) //look into
+ RegisterSignal(part, COMSIG_PARENT_QDELETING, PROC_REF(on_part_deletion))
+ set_mod_skin(new_skin || theme.default_skin)
+ update_speed()
+ RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+ for(var/obj/item/mod/module/module as anything in theme.inbuilt_modules)
+ module = new module(src)
+ install(module)
+
+/obj/item/mod/control/Destroy()
+ if(active)
+ STOP_PROCESSING(SSobj, src)
+ for(var/obj/item/mod/module/module as anything in modules)
+ uninstall(module, deleting = TRUE)
+ for(var/obj/item/part as anything in mod_parts)
+ overslotting_parts -= part
+ if(!QDELETED(helmet))
+ mod_parts -= helmet
+ QDEL_NULL(helmet)
+ if(!QDELETED(chestplate))
+ mod_parts -= chestplate
+ QDEL_NULL(chestplate)
+ if(!QDELETED(gauntlets))
+ mod_parts -= gauntlets
+ QDEL_NULL(gauntlets)
+ if(!QDELETED(boots))
+ mod_parts -= boots
+ QDEL_NULL(boots)
+ if(core)
+ QDEL_NULL(core)
+ QDEL_NULL(wires)
+ wearer = null
+ selected_module = null
+ bag = null
+ modules = null
+ return ..()
+
+
+/obj/item/mod/control/examine(mob/user)
+ . = ..()
+ if(active)
+ . += "Charge: [core ? "[get_charge_percent()]%" : "No core"]."
+ . += "Selected module: [selected_module || "None"]."
+ if(!open && !active)
+ if(!wearer)
+ . += "You could equip it to turn it on."
+ . += "You could open the cover with a screwdriver."
+ else if(open)
+ . += "You could close the cover with a screwdriver."
+ . += "You could use modules on it to install them."
+ . += "You could remove modules with a crowbar."
+ . += "You could update the access lock with an ID."
+ . += "You could access the wire panel with a wire tool."
+ if(core)
+ . += "You could remove [core] with a wrench."
+ else
+ . += "You could use a MOD core on it to install one."
+ . += "[extended_desc]" //god is dead
+
+/obj/item/mod/control/process()
+ if(seconds_electrified > 0)
+ seconds_electrified--
+ if(get_charge() <= 10 && active && !activating) //Sometimes we get power being funky, this should fix it.
+ power_off()
+ return PROCESS_KILL
+ var/malfunctioning_charge_drain = 0
+ if(malfunctioning)
+ malfunctioning_charge_drain = rand(1, 20)
+ subtract_charge((charge_drain + malfunctioning_charge_drain))
+ update_charge_alert()
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(malfunctioning && module.active && prob(5))
+ module.on_deactivation(display_message = TRUE)
+ module.on_process()
+
+/obj/item/mod/control/equipped(mob/user, slot)
+ ..()
+ if(slot == slot_back)
+ set_wearer(user)
+ else if(wearer)
+ unset_wearer()
+
+
+/obj/item/mod/control/item_action_slot_check(slot)
+ if(slot == slot_back)
+ return TRUE
+
+/obj/item/mod/control/on_mob_move(direction, mob/user)
+ if(!jetpack_active)
+ return
+ var/turf/T = get_step(src, GetOppositeDir(direction))
+ if(!has_gravity(T))
+ new /obj/effect/particle_effect/ion_trails(T, direction)
+
+/obj/item/mod/control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
+ . = ..()
+ if(!wearer || old_loc != wearer || loc == wearer)
+ return
+ clean_up()
+
+/obj/item/mod/control/MouseDrop(atom/over_object)
+ if(iscarbon(usr))
+ var/mob/M = usr
+ if(get_dist(usr, src) > 1) //1 as we want to access it if beside the user
+ return
+
+ if(!over_object)
+ return
+
+ if(ismecha(M.loc))
+ return
+
+ if(!M.restrained() && !M.stat)
+ playsound(loc, "rustle", 50, TRUE, -5)
+
+ if(istype(over_object, /obj/screen/inventory/hand))
+ for(var/obj/item/part as anything in mod_parts)
+ if(part.loc != src)
+ to_chat(wearer, "Retract parts first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(!M.unEquip(src, silent = TRUE))
+ return
+ M.put_in_active_hand(src)
+ else if(bag)
+ bag.forceMove(usr)
+ bag.show_to(usr)
+
+ add_fingerprint(M)
+
+/obj/item/mod/control/wrench_act(mob/living/user, obj/item/wrench)
+ if(..())
+ return TRUE
+ if(seconds_electrified && get_charge() && shock(user))
+ return TRUE
+ if(open)
+ if(!core)
+ to_chat(user, "There is no core!")
+ return TRUE
+ wrench.play_tool_sound(src, 100)
+ if(!wrench.use_tool(src, user, 3 SECONDS) || !open)
+ return TRUE
+ if(!core)
+ return TRUE
+ wrench.play_tool_sound(src, 100)
+ core.forceMove(drop_location())
+ update_charge_alert()
+ return TRUE
+ return ..()
+
+/obj/item/mod/control/screwdriver_act(mob/living/user, obj/item/screwdriver)
+ if(..())
+ return TRUE
+ if(active || activating || locate(/mob/living/silicon/ai) in src)
+ to_chat(user, "Deactivate the suit first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ to_chat(user, "[open ? "Closing" : "Opening"] cover...")
+ screwdriver.play_tool_sound(src, 100)
+ if(screwdriver.use_tool(src, user, 1 SECONDS))
+ if(active || activating)
+ to_chat(user, "Deactivate the suit first!")
+ screwdriver.play_tool_sound(src, 100)
+ to_chat(user, "Cover [open ? "closed" : "opened"]")
+ open = !open
+ return TRUE
+
+/obj/item/mod/control/crowbar_act(mob/living/user, obj/item/crowbar)
+ . = ..()
+ if(!open)
+ to_chat(user, "Open the cover first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(!allowed(user))
+ to_chat(user, "Insufficient access!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(seconds_electrified && get_charge() && shock(user))
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVAL, user) & MOD_CANCEL_REMOVAL)
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(length(modules))
+ var/list/removable_modules = list()
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(!module.removable)
+ continue
+ removable_modules += module
+ var/obj/item/mod/module/module_to_remove = input(user, "Which module do you want to pry out?", "Module Removal") as null|anything in removable_modules
+ if(!module_to_remove?.mod)
+ return FALSE
+ uninstall(module_to_remove)
+ module_to_remove.forceMove(drop_location())
+ crowbar.play_tool_sound(src, 100)
+ SEND_SIGNAL(src, COMSIG_MOD_MODULE_REMOVED, user)
+ return TRUE
+ to_chat(user, "No modules!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+
+/obj/item/mod/control/attackby(obj/item/attacking_item, mob/living/user, params)
+ if(istype(attacking_item, /obj/item/mod/module))
+ if(!open)
+ to_chat(user, "Open the cover first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ install(attacking_item, user)
+ SEND_SIGNAL(src, COMSIG_MOD_MODULE_ADDED, user)
+ return TRUE
+ else if(istype(attacking_item, /obj/item/mod/core))
+ if(!open)
+ to_chat(user, "Open the cover first!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ if(core)
+ to_chat(user, "Core already installed!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ var/obj/item/mod/core/attacking_core = attacking_item
+ playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+ user.drop_item()
+ attacking_core.install(src)
+ update_charge_alert()
+ return TRUE
+ else if(istype(attacking_item, /obj/item/multitool) && open)
+ if(seconds_electrified && get_charge() && shock(user))
+ return TRUE
+ wires.Interact(user)
+ return TRUE
+ else if(open && attacking_item.GetID())
+ update_access(user, attacking_item.GetID())
+ return TRUE
+ else if(istype(attacking_item, /obj/item/stock_parts/cell))
+ if(!core)
+ to_chat(user, "There is no core installed!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ core.on_attackby(attacking_item, user, params)
+ else if(istype(attacking_item, /obj/item/stack/ore/plasma) || istype(attacking_item, /obj/item/stack/sheet/mineral/plasma))
+ if(!core)
+ to_chat(user, "There is no core installed!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return FALSE
+ core.on_attackby(attacking_item, user, params)
+ else if(istype(attacking_item, /obj/item/mod/skin_applier))
+ return ..()
+ else if(bag && istype(attacking_item))
+ bag.forceMove(user)
+ bag.attackby(attacking_item, user, params)
+
+ return ..()
+
+/obj/item/mod/control/attack_hand(mob/living/carbon/user)
+ if(!iscarbon(user))
+ return
+ if(loc == user && user.back && user.back == src)
+ if(bag)
+ bag.forceMove(user)
+ bag.show_to(user)
+ else
+ ..()
+
+/obj/item/mod/control/AltClick(mob/user)
+ . = ..()
+ if(ishuman(user) && Adjacent(user) && !user.incapacitated(FALSE, TRUE) && bag)
+ bag.forceMove(user)
+ bag.show_to(user)
+ playsound(loc, "rustle", 50, TRUE, -5)
+ add_fingerprint(user)
+
+/obj/item/mod/control/proc/can_be_inserted(I, stop_messages)
+ if(bag)
+ return bag.can_be_inserted(I, stop_messages)
+ return FALSE
+
+/obj/item/mod/control/proc/handle_item_insertion(I, prevent_warning)
+ if(bag)
+ bag.handle_item_insertion(I, prevent_warning)
+
+/obj/item/mod/control/get_cell()
+ if(!open)
+ return
+ var/obj/item/stock_parts/cell/cell = get_charge_source()
+ if(!istype(cell))
+ return
+ return cell
+
+/obj/item/mod/control/GetAccess()
+ if(locate(/mob/living/silicon/ai) in src)
+ return req_access.Copy()
+ return ..()
+
+/obj/item/mod/control/emag_act(mob/user)
+ locked = !locked
+ to_chat(user, "Suit access [locked ? "locked" : "unlocked"]")
+
+/obj/item/mod/control/emp_act(severity)
+ . = ..()
+ if(!active || !wearer)
+ return
+ to_chat(wearer, "[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!")
+ if(emp_proof)
+ return
+ selected_module?.on_deactivation(display_message = TRUE)
+ wearer.apply_damage(10 / severity, BURN, spread_damage = TRUE) //Test this with ion shotguns.
+ to_chat(wearer, "You feel [src] heat up from the EMP, burning you slightly!")
+ if(wearer.stat < UNCONSCIOUS && prob(10))
+ wearer.emote("scream")
+ core.emp_act(severity)
+ if(prob(50 / severity))
+ wires.emp_pulse() //3 wires get pulsed. Dangerous to a mod user.
+ for(var/obj/item/mod/module/holster/H in modules)
+ H.holstered?.emp_act(severity)
+ if(bag)
+ bag.emp_act(severity)
+
+
+/obj/item/mod/control/dropped(mob/user)
+ . = ..()
+ if(!wearer)
+ return
+ clean_up()
+ update_mod_overlays(TRUE)
+ if(active && !toggle_activate(force_deactivate = TRUE))
+ return
+ for(var/obj/item/part as anything in mod_parts)
+ if(part.loc == src)
+ continue
+ retract(null, part)
+ return ..()
+
+/obj/item/mod/control/update_icon_state()
+ icon_state = "[skin]-[base_icon_state][active ? "-sealed" : ""]"
+ return ..()
+
+/obj/item/mod/control/proc/set_wearer(mob/living/carbon/human/user)
+ if(wearer == user)
+ // This should also not happen.
+ // This path is hit when equipping an outfit with visualsOnly, but only sometimes, and this eventually gets called twice.
+ // I'm not sure this proc should ever be being called by visualsOnly, but it is,
+ // and this was an emergency patch.
+ return
+ else if(!isnull(wearer))
+ stack_trace("set_wearer() was called with a new wearer without unset_wearer() being called")
+
+ wearer = user
+ SEND_SIGNAL(src, COMSIG_MOD_WEARER_SET, wearer)
+ RegisterSignal(wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+ update_charge_alert()
+ for(var/obj/item/clothing/C in mod_parts)
+ C.refit_for_species(wearer.dna.species.name)
+ update_mod_overlays()
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_equip()
+
+/obj/item/mod/control/proc/unset_wearer()
+ for(var/obj/item/mod/module/module as anything in modules)
+ module.on_unequip()
+ UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_SPECIES_GAIN))
+ wearer.clear_alert("mod_charge")
+ SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer)
+ wearer = null
+
+/obj/item/mod/control/proc/clean_up()
+ if(active || activating)
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(!module.active)
+ continue
+ module.on_deactivation(display_message = FALSE)
+ for(var/obj/item/part as anything in mod_parts)
+ seal_part(part, seal = FALSE)
+ for(var/obj/item/part as anything in mod_parts)
+ retract(null, part)
+ if(active)
+ finish_activation(on = FALSE)
+ var/mob/old_wearer = wearer
+ unset_wearer()
+ old_wearer.drop_item()
+
+/obj/item/mod/control/proc/quick_module(mob/user)
+ if(!length(modules))
+ return
+ var/list/display_names = list()
+ var/list/items = list()
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(module.module_type == MODULE_PASSIVE)
+ continue
+ display_names[module.name] = module
+ var/image/module_image = image(icon = module.icon, icon_state = module.icon_state)
+ if(module == selected_module)
+ module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_selected")
+ else if(module.active)
+ module_image.underlays += image(icon = 'icons/hud/radial.dmi', icon_state = "module_active")
+ if(!COOLDOWN_FINISHED(module, cooldown_timer))
+ module_image.add_overlay(image(icon = 'icons/hud/radial.dmi', icon_state = "module_cooldown"))
+ items += list(module.name = module_image)
+ if(!length(items))
+ return
+ var/radial_anchor = src
+ var/pick = show_radial_menu(user, radial_anchor, items, custom_check = FALSE, require_near = TRUE)
+ if(!pick)
+ return
+ var/module_reference = display_names[pick]
+ var/obj/item/mod/module/picked_module = module_reference
+ picked_module.on_select()
+
+/obj/item/mod/control/proc/shock(mob/living/user)
+ if(!istype(user) || get_charge() < 1)
+ return FALSE
+ do_sparks(5, TRUE, src)
+ var/check_range = TRUE
+ return electrocute_mob(user, get_charge_source(), src, 1, check_range)
+
+/obj/item/mod/control/proc/install(obj/item/mod/module/new_module, mob/user)
+ for(var/obj/item/mod/module/old_module as anything in modules)
+ if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules))
+ if(user)
+ to_chat(user, "[new_module] incompatible with [old_module]!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ var/complexity_with_module = complexity
+ complexity_with_module += new_module.complexity
+ if(complexity_with_module > complexity_max)
+ if(user)
+ to_chat(user, "[new_module] would make [src] too complex!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(user)
+ if(!user.drop_item())
+ to_chat(user, "[new_module] is stuck to your hand!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ new_module.forceMove(src)
+ modules += new_module
+ complexity += new_module.complexity
+ new_module.mod = src
+ new_module.on_install()
+ if(wearer)
+ new_module.on_equip()
+ if(active)
+ new_module.on_suit_activation()
+ if(user)
+ to_chat(user, "[new_module] added!")
+ playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+
+/obj/item/mod/control/proc/uninstall(obj/item/mod/module/old_module, deleting = FALSE)
+ modules -= old_module
+ complexity -= old_module.complexity
+ if(wearer)
+ old_module.on_unequip()
+ if(active)
+ old_module.on_suit_deactivation(deleting = deleting)
+ if(old_module.active)
+ old_module.on_deactivation(display_message = !deleting, deleting = deleting)
+ old_module.on_uninstall(deleting = deleting)
+ QDEL_LIST_ASSOC_VAL(old_module.pinned_to)
+ old_module.mod = null
+
+/obj/item/mod/control/proc/update_access(mob/user, obj/item/card/id/card)
+ if(!allowed(user))
+ to_chat(user, "Insufficient access!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ req_access = card.access.Copy()
+ to_chat(user, "Access updated!")
+
+/obj/item/mod/control/proc/update_mod_overlays(full_removal = FALSE)
+ if(!wearer)
+ return
+ for(var/I in mod_overlays)
+ wearer.cut_overlay(I)
+ mod_overlays -= I
+ if(full_removal)
+ return
+ for(var/obj/item/mod/module/M in modules)
+ M.add_module_overlay(wearer)
+
+/obj/item/mod/control/proc/get_charge_source()
+ return core?.charge_source()
+
+/obj/item/mod/control/proc/get_charge()
+ return core?.charge_amount() || 0
+
+/obj/item/mod/control/proc/get_max_charge()
+ return core?.max_charge_amount() || 1 //avoid dividing by 0
+
+/obj/item/mod/control/proc/get_charge_percent()
+ return ((get_charge() / get_max_charge()) * 100)
+
+/obj/item/mod/control/proc/add_charge(amount)
+ return core?.add_charge(amount) || FALSE
+
+/obj/item/mod/control/proc/subtract_charge(amount)
+ return core?.subtract_charge(amount) || FALSE
+
+/obj/item/mod/control/proc/check_charge(amount)
+ return core?.check_charge(amount) || FALSE
+
+/obj/item/mod/control/proc/update_charge_alert()
+ if(!wearer)
+ return
+ if(!core)
+ wearer.throw_alert("mod_charge", /obj/screen/alert/nocell)
+ return
+ core.update_charge_alert()
+
+/obj/item/mod/control/proc/update_speed()
+ var/list/all_parts = mod_parts + src
+ for(var/obj/item/part as anything in all_parts)
+ part.slowdown = (active ? slowdown_active : slowdown_inactive) / length(all_parts)
+
+/obj/item/mod/control/proc/power_off()
+ to_chat(wearer, "Power cells depleted!")
+ toggle_activate(wearer, force_deactivate = TRUE)
+
+/obj/item/mod/control/proc/set_mod_color(new_color)
+ var/list/all_parts = mod_parts + src
+ for(var/obj/item/part as anything in all_parts)
+ part.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
+ part.add_atom_colour(new_color, FIXED_COLOUR_PRIORITY)
+ wearer?.regenerate_icons()
+
+/obj/item/mod/control/proc/set_mod_skin(new_skin)
+ if(active)
+ CRASH("[src] tried to set skin while active!")
+ skin = new_skin
+ var/list/used_skin = theme.skins[new_skin]
+ var/list/skin_updating = mod_parts + src
+ for(var/obj/item/part as anything in skin_updating)
+ part.icon = used_skin[MOD_ICON_OVERRIDE] || 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ part.icon_state = "[skin]-[part.base_icon_state]"
+ for(var/obj/item/clothing/part as anything in mod_parts)
+ var/used_category
+ if(part == helmet)
+ used_category = HELMET_FLAGS
+ if(part == chestplate)
+ used_category = CHESTPLATE_FLAGS
+ if(part == gauntlets)
+ used_category = GAUNTLETS_FLAGS
+ if(part == boots)
+ used_category = BOOTS_FLAGS
+ var/list/category = used_skin[used_category]
+ part.flags = category[UNSEALED_CLOTHING] || NONE
+ part.visor_flags = category[SEALED_CLOTHING] || NONE
+ part.flags_inv = category[UNSEALED_INVISIBILITY] || NONE
+ part.visor_flags_inv = category[SEALED_INVISIBILITY] || NONE
+ part.flags_cover = category[UNSEALED_COVER] || NONE
+ part.visor_flags_cover = category[SEALED_COVER] || NONE
+ if(!category[CAN_OVERSLOT])
+ if(overslotting_parts[part])
+ var/obj/item/overslot = overslotting_parts[part]
+ overslot.forceMove(drop_location())
+ overslotting_parts -= part
+ continue
+ overslotting_parts |= part
+ wearer?.regenerate_icons()
+
+/obj/item/mod/control/proc/on_exit(datum/source, atom/movable/part, direction)
+ SIGNAL_HANDLER
+
+ if(part.loc == src)
+ return
+ if(part == core)
+ core.uninstall()
+ update_charge_alert()
+ return
+ if(part.loc == wearer)
+ return
+ if(part in modules)
+ uninstall(part)
+ return
+ if(part in mod_parts)
+ if(!wearer)
+ part.forceMove(src)
+ return
+ retract(wearer, part, TRUE)
+ if(active)
+ INVOKE_ASYNC(src, PROC_REF(toggle_activate), wearer, TRUE)
+
+/obj/item/mod/control/proc/on_part_destruction(obj/item/part, damage_flag)
+ SIGNAL_HANDLER
+
+ if(overslotting_parts[part])
+ var/obj/item/overslot = overslotting_parts[part]
+ overslot.forceMove(drop_location())
+ overslotting_parts[part] = null
+ if(QDELETED(src))
+ return
+ obj_destruction(damage_flag)
+
+/obj/item/mod/control/proc/on_overslot_exit(datum/source, atom/movable/overslot, direction)
+ SIGNAL_HANDLER
+
+ if(overslot != overslotting_parts[source])
+ return
+ overslotting_parts[source] = null
+
+/obj/item/mod/control/proc/on_part_deletion(obj/item/part) //the part doesnt count as being qdeleted, so our destroying does an infinite loop, fix later
+ SIGNAL_HANDLER
+
+ if(QDELETED(src))
+ return
+ qdel(src)
+
+
+/obj/item/mod/control/water_act(volume, temperature, source, method)
+ if(HAS_TRAIT(src, TRAIT_OIL_SLICKED)) //Overide base to work right
+ slowdown_active = theme.slowdown_active
+ slowdown_inactive = theme.slowdown_inactive
+ update_speed()
+ remove_atom_colour(FIXED_COLOUR_PRIORITY)
+ REMOVE_TRAIT(src, TRAIT_OIL_SLICKED, "potion")
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ H.regenerate_icons()
diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm
new file mode 100644
index 000000000000..4ea624dee15f
--- /dev/null
+++ b/code/modules/mod/mod_core.dm
@@ -0,0 +1,298 @@
+/obj/item/mod/core
+ name = "MOD core"
+ desc = "A non-functional MOD core. Inform the admins if you see this."
+ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
+ icon_state = "mod-core"
+ item_state = "electronic"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ /// MOD unit we are powering.
+ var/obj/item/mod/control/mod
+
+/obj/item/mod/core/Destroy()
+ if(mod)
+ uninstall()
+ return ..()
+
+/obj/item/mod/core/proc/install(obj/item/mod/control/mod_unit)
+ mod = mod_unit
+ mod.core = src
+ forceMove(mod)
+
+/obj/item/mod/core/proc/uninstall()
+ mod.core = null
+ mod = null
+
+/obj/item/mod/core/proc/charge_source()
+ return
+
+/obj/item/mod/core/proc/charge_amount()
+ return 0
+
+/obj/item/mod/core/proc/max_charge_amount()
+ return 1
+
+/obj/item/mod/core/proc/add_charge(amount)
+ return FALSE
+
+/obj/item/mod/core/proc/subtract_charge(amount)
+ return FALSE
+
+/obj/item/mod/core/proc/check_charge(amount)
+ return FALSE
+
+/obj/item/mod/core/proc/update_charge_alert()
+ mod.wearer.clear_alert("mod_charge")
+
+/obj/item/mod/core/infinite //Admin only.
+ name = "MOD infinite core"
+ icon_state = "mod-core-infinite"
+ desc = "A fusion core using the rare Infinium to sustain enough energy for the lifetime of the MOD's user. \
+ This might be because of the slowly killing radiation inside, but those are just rumors."
+
+/obj/item/mod/core/infinite/charge_source()
+ return src
+
+/obj/item/mod/core/infinite/charge_amount()
+ return INFINITY
+
+/obj/item/mod/core/infinite/max_charge_amount()
+ return INFINITY
+
+/obj/item/mod/core/infinite/add_charge(amount)
+ return TRUE
+
+/obj/item/mod/core/infinite/subtract_charge(amount)
+ return TRUE
+
+/obj/item/mod/core/infinite/check_charge(amount)
+ return TRUE
+
+/obj/item/mod/core/standard
+ name = "MOD standard core"
+ desc = "Using Thermo Generators to store and amplify power in the form of heat, this core acts as a power cell for your modsuit." //We don't have etherals
+ icon_state = "mod-core-standard"
+ /// Installed cell.
+ var/obj/item/stock_parts/cell/cell
+
+/obj/item/mod/core/standard/Destroy()
+ if(cell)
+ QDEL_NULL(cell)
+ return ..()
+
+/obj/item/mod/core/standard/install(obj/item/mod/control/mod_unit)
+ . = ..()
+ if(cell)
+ install_cell(cell)
+ RegisterSignal(mod, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(mod, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
+ RegisterSignal(mod, COMSIG_MOD_WEARER_SET, PROC_REF(on_wearer_set))
+ if(mod.wearer)
+ on_wearer_set(mod, mod.wearer)
+
+/obj/item/mod/core/standard/uninstall()
+ if(!QDELETED(cell))
+ cell.forceMove(drop_location())
+ UnregisterSignal(mod, list(COMSIG_PARENT_EXAMINE, COMSIG_ATOM_ATTACK_HAND, COMSIG_MOD_WEARER_SET))
+ if(mod.wearer)
+ on_wearer_unset(mod, mod.wearer)
+ return ..()
+
+/obj/item/mod/core/proc/on_attackby(obj/item/attacking_item, mob/user, params)
+ return
+
+/obj/item/mod/core/standard/charge_source()
+ return cell
+
+/obj/item/mod/core/standard/charge_amount()
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ return charge_source?.charge || 0
+
+/obj/item/mod/core/standard/max_charge_amount(amount)
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ return charge_source?.maxcharge || 1
+
+/obj/item/mod/core/standard/add_charge(amount)
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ if(!charge_source)
+ return FALSE
+ return charge_source.give(amount)
+
+/obj/item/mod/core/standard/subtract_charge(amount)
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ if(!charge_source)
+ return FALSE
+ return charge_source.use(amount, TRUE)
+
+/obj/item/mod/core/standard/check_charge(amount)
+ return charge_amount() >= amount
+
+/obj/item/mod/core/standard/update_charge_alert()
+ var/obj/item/stock_parts/cell/charge_source = charge_source()
+ if(!charge_source)
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/nocell)
+ return
+ var/remaining_cell = charge_amount() / max_charge_amount()
+ switch(remaining_cell)
+ if(0.75 to INFINITY)
+ mod.wearer.clear_alert("mod_charge")
+ if(0.5 to 0.75)
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 1)
+ if(0.25 to 0.5)
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 2)
+ if(0.01 to 0.25)
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 3)
+ else
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/emptycell)
+
+/obj/item/mod/core/standard/emp_act(severity)
+ cell?.emp_act(severity)
+
+/obj/item/mod/core/standard/proc/install_cell(new_cell)
+ cell = new_cell
+ cell.forceMove(src)
+ RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+
+/obj/item/mod/core/standard/proc/uninstall_cell()
+ if(!cell)
+ return
+ cell = null
+ UnregisterSignal(src, COMSIG_ATOM_EXITED)
+
+/obj/item/mod/core/standard/proc/on_exit(datum/source, obj/item/stock_parts/cell, direction)
+ SIGNAL_HANDLER
+
+ if(!istype(cell) || cell.loc == src)
+ return
+ uninstall_cell()
+
+/obj/item/mod/core/standard/proc/on_examine(datum/source, mob/examiner, list/examine_text)
+ SIGNAL_HANDLER
+
+ if(!mod.open)
+ return
+ examine_text += cell ? "You could remove the cell with an empty hand." : "You could use a cell on it to install one."
+
+/obj/item/mod/core/standard/proc/on_attack_hand(datum/source, mob/living/user)
+ SIGNAL_HANDLER
+
+ if(mod.seconds_electrified && charge_amount() && mod.shock(user))
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+ if(mod.open && mod.loc == user)
+ INVOKE_ASYNC(src, PROC_REF(mod_uninstall_cell), user)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+ return NONE
+
+/obj/item/mod/core/standard/proc/mod_uninstall_cell(mob/living/user)
+ if(!cell)
+ to_chat(user, "No cell installed!")
+ return
+ if(!do_after(user, 1.5 SECONDS, target = user))
+ return
+ to_chat(user, "You remove the cell.")
+ playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+ var/obj/item/cell_to_move = cell
+ cell_to_move.forceMove(drop_location())
+ user.put_in_hands(cell_to_move)
+ mod.update_charge_alert()
+
+/obj/item/mod/core/standard/on_attackby(obj/item/attacking_item, mob/user, params)
+ if(istype(attacking_item, /obj/item/stock_parts/cell))
+ if(!mod.open)
+ to_chat(user, "Open the cover first!")
+ playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return NONE
+ if(cell)
+ to_chat(user, "Cell already installed!")
+ playsound(mod, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return COMPONENT_NO_AFTERATTACK
+ user.drop_item()
+ install_cell(attacking_item)
+ to_chat(user, "You install the cell.")
+ playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
+ mod.update_charge_alert()
+ return COMPONENT_NO_AFTERATTACK
+ return NONE
+
+/obj/item/mod/core/standard/proc/on_wearer_set(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ RegisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge))
+ RegisterSignal(mod, COMSIG_MOD_WEARER_UNSET, PROC_REF(on_wearer_unset))
+
+/obj/item/mod/core/standard/proc/on_wearer_unset(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ UnregisterSignal(mod.wearer, COMSIG_PROCESS_BORGCHARGER_OCCUPANT)
+ UnregisterSignal(mod, COMSIG_MOD_WEARER_UNSET)
+
+/obj/item/mod/core/standard/proc/on_borg_charge(datum/source, amount)
+ SIGNAL_HANDLER
+
+ add_charge(amount)
+ mod.update_charge_alert()
+
+/obj/item/mod/core/plasma
+ name = "MOD plasma core"
+ desc = "Nanotrasen's attempt at capitalizing on their plasma research. These plasma cores are refueled \
+ through plasma fuel, allowing for easy continued use by their mining squads."
+ icon_state = "mod-core-plasma"
+ /// How much charge we can store.
+ var/maxcharge = 10000
+ /// How much charge we are currently storing.
+ var/charge = 10000
+ /// Associated list of charge sources, only stacks allowed.
+ var/list/charger_list = list(/obj/item/stack/ore/plasma, /obj/item/stack/sheet/mineral/plasma)
+
+/obj/item/mod/core/plasma/attackby(obj/item/attacking_item, mob/user, params)
+ if(charge_plasma(attacking_item, user))
+ return TRUE
+ return ..()
+
+/obj/item/mod/core/plasma/charge_source()
+ return src
+
+/obj/item/mod/core/plasma/charge_amount()
+ return charge
+
+/obj/item/mod/core/plasma/max_charge_amount()
+ return maxcharge
+
+/obj/item/mod/core/plasma/add_charge(amount)
+ charge = min(maxcharge, charge + amount)
+ return TRUE
+
+/obj/item/mod/core/plasma/subtract_charge(amount)
+ charge = max(0, charge - amount)
+ return TRUE
+
+/obj/item/mod/core/plasma/check_charge(amount)
+ return charge_amount() >= amount
+
+/obj/item/mod/core/plasma/update_charge_alert()
+ var/remaining_plasma = charge_amount() / max_charge_amount()
+ switch(remaining_plasma)
+ if(0.75 to INFINITY)
+ mod.wearer.clear_alert("mod_charge")
+ if(0.5 to 0.75)
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 1)
+ if(0.25 to 0.5)
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 2)
+ if(0.01 to 0.25)
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/lowcell, 3)
+ else
+ mod.wearer.throw_alert("mod_charge", /obj/screen/alert/emptycell)
+
+/obj/item/mod/core/plasma/on_attackby(obj/item/attacking_item, mob/user, params)
+ charge_plasma(attacking_item, user)
+
+/obj/item/mod/core/plasma/proc/charge_plasma(obj/item/stack/plasma, mob/user)
+ var/charge_given = is_type_in_list(plasma, charger_list)
+ if(!charge_given)
+ return FALSE
+ var/uses_needed = min(plasma.amount, ((max_charge_amount() - charge_amount()) / 2000))
+ if(!plasma.use(uses_needed))
+ return FALSE
+ add_charge(uses_needed * 2000)
+ to_chat(user, "You insert [plasma] in [src], recharging it.")
+ return TRUE
diff --git a/code/modules/mod/mod_paint.dm b/code/modules/mod/mod_paint.dm
new file mode 100644
index 000000000000..b48bf8f08a81
--- /dev/null
+++ b/code/modules/mod/mod_paint.dm
@@ -0,0 +1,42 @@
+/obj/item/mod/skin_applier
+ name = "MOD skin applier"
+ desc = "This one-use skin applier will add a skin to MODsuits of a specific type. This one applies to standard modsuits."
+ icon = 'icons/obj/clothing/modsuit/mod_construction.dmi'
+ icon_state = "skinapplier"
+ var/skin = "civilian"
+ var/make_spaceproof = FALSE //Used on the miner asteroid skin to make the suit spaceproof when upgrading.
+ var/compatible_theme = /datum/mod_theme/standard
+
+/obj/item/mod/skin_applier/Initialize(mapload)
+ . = ..()
+ name = "MOD [skin] skin applier"
+
+/obj/item/mod/skin_applier/pre_attack(atom/attacked_atom, mob/living/user, params)
+ if(!ismodcontrol(attacked_atom))
+ return ..()
+ var/obj/item/mod/control/mod = attacked_atom
+ if(mod.active || mod.activating)
+ to_chat(user, "Deactivate the suit!")
+ return TRUE
+ if(!istype(mod.theme, compatible_theme))
+ to_chat(user, "Theme is not compatible!")
+ return TRUE
+ mod.set_mod_skin(skin)
+ if(make_spaceproof)
+ mod.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ for(var/obj/item/clothing/C in mod.mod_parts)
+ C.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ to_chat(user, "You apply the theme to [mod].")
+ qdel(src)
+ return TRUE
+
+/obj/item/mod/skin_applier/asteroid
+ skin = "asteroid"
+ compatible_theme = /datum/mod_theme/mining
+ desc = "This one-use skin applier will add a skin to MODsuits of a specific type. This one applies to mining modsuits, and makes them spaceproof. Enjoy the days when you had MODsuits on the asteroid. Wait a minute."
+ make_spaceproof = TRUE
+
+/obj/item/mod/skin_applier/corpsman
+ skin = "corpsman"
+ compatible_theme = /datum/mod_theme/medical
+ desc = "This one-use skin applier will add a skin to MODsuits of a specific type. This one applies to medical modsuits. You look like a corpse, man! Or was it a corps man?"
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
new file mode 100644
index 000000000000..113a28ef86e1
--- /dev/null
+++ b/code/modules/mod/mod_theme.dm
@@ -0,0 +1,1339 @@
+/// Global proc that sets up all MOD themes as singletons in a list and returns it.
+/proc/setup_mod_themes()
+ . = list()
+ for(var/path in typesof(/datum/mod_theme))
+ var/datum/mod_theme/new_theme = new path()
+ .[path] = new_theme
+
+/// MODsuit theme, instanced once and then used by MODsuits to grab various statistics.
+/datum/mod_theme
+ /// Theme name for the MOD.
+ var/name = "BASE"
+ /// Description added to the MOD.
+ var/desc = "A civilian class suit by Cybersun Industries, doesn't offer much other than slightly quicker movement."
+ /// Extended description on examine_more
+ var/extended_desc = "A third-generation, modular civilian class suit by Cybersun Industries, \
+ this suit is a staple across the galaxy for civilian applications. These suits are oxygenated, \
+ spaceworthy, resistant to fire and chemical threats, and are immunized against everything between \
+ a sneeze and a bioweapon. However, their combat applications are incredibly minimal due to the amount of \
+ armor plating being installed by default, and their actuators only lead to slightly greater speed than industrial suits."
+ /// Default skin of the MOD.
+ var/default_skin = "standard"
+ /// The slot this mod theme fits on
+ var/slot_flags = SLOT_BACK
+ /// Armor shared across the MOD parts.
+ var/obj/item/mod/armor/armor_type_1 = /obj/item/mod/armor/mod_theme
+ /// the actual armor object placed in a datum as I am tired and I just want this to work
+ var/obj/item/mod/armor/armor_type_2 = null
+ /// Resistance flags shared across the MOD parts.
+ var/resistance_flags = NONE
+ /// Atom flags shared across the MOD parts.
+ var/atom_flags = NONE
+ /// Max heat protection shared across the MOD parts.
+ var/max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
+ /// Max cold protection shared across the MOD parts.
+ var/min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
+ /// Siemens shared across the MOD parts.
+ var/siemens_coefficient = 0.5
+ /// How much modules can the MOD carry without malfunctioning.
+ var/complexity_max = DEFAULT_MAX_COMPLEXITY
+ /// How much battery power the MOD uses by just being on
+ var/charge_drain = DEFAULT_CHARGE_DRAIN
+ /// Slowdown of the MOD when not active.
+ var/slowdown_inactive = 1.25
+ /// Slowdown of the MOD when active.
+ var/slowdown_active = 0.75
+ /// Theme used by the MOD TGUI.
+ var/ui_theme = "ntos"
+ /// List of inbuilt modules. These are different from the pre-equipped suits, you should mainly use these for unremovable modules with 0 complexity.
+ var/list/inbuilt_modules = list()
+ /// Allowed items in the chestplate's suit storage.
+ var/list/allowed_suit_storage = list()
+ /// List of skins with their appropriate clothing flags.
+ var/list/skins = list(
+ "standard" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCKHAIR,
+ SEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "civilian" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ UNSEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/datum/mod_theme/standard //We don't want the civilian skin to apply to all modsuits, that causes issues.
+ name = "standard"
+
+
+/datum/mod_theme/New()
+ . = ..()
+ armor_type_2 = new armor_type_1
+
+/obj/item/mod/armor/mod_theme
+ armor = list(MELEE = 15, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 0, RAD = 25, FIRE = 33, ACID = 33)
+
+/datum/mod_theme/engineering
+ name = "engineering"
+ desc = "An engineer-fit suit with heat and shock resistance. Cybersun Industries's classic."
+ extended_desc = "A classic by Cybersun Industries, and surely their claim to fame. This model is an \
+ improvement upon the first-generation prototype models from before the Void War, boasting an array of features. \
+ The modular flexibility of the base design has been combined with a blast-dampening insulated inner layer and \
+ a shock-resistant outer layer, making the suit nigh-invulnerable against even the extremes of high-voltage electricity. \
+ However, the capacity for modification remains the same as civilian-grade suits."
+ default_skin = "engineering"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_engineering
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1.5
+ slowdown_active = 0.75
+ allowed_suit_storage = list(
+ /obj/item/rcd,
+ /obj/item/fireaxe,
+ )
+ skins = list(
+ "engineering" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_engineering
+ armor = list(MELEE = 20, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 30, RAD = 150, FIRE = INFINITY, ACID = 150) //Bomb armor bumped up a bit, as the modsuit describes it with blast-dampening
+
+/datum/mod_theme/atmospheric
+ name = "atmospheric"
+ desc = "An atmospheric-resistant suit by Cybersun Industries, offering extreme heat resistance compared to the engineer suit."
+ extended_desc = "A modified version of the Cybersun Industries industrial model. This one has been \
+ augmented with the latest in heat-resistant alloys, paired with a series of advanced heatsinks. \
+ Additionally, the materials used to construct this suit have rendered it extremely hardy against \
+ corrosive gasses and liquids, useful in the world of pipes. \
+ However, the capacity for modification remains the same as civilian-grade suits."
+ default_skin = "atmospheric"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_atmospheric
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 3
+ charge_drain = DEFAULT_CHARGE_DRAIN * 2
+ siemens_coefficient = 0
+ slowdown_inactive = 1.5
+ slowdown_active = 0.75
+ allowed_suit_storage = list(
+ /obj/item/rcd,
+ /obj/item/fireaxe/,
+ /obj/item/rpd,
+ /obj/item/t_scanner,
+ /obj/item/analyzer
+ )
+ skins = list(
+ "atmospheric" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_atmospheric
+ armor = list(MELEE = 20, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 15, RAD = 15, FIRE = INFINITY, ACID = 150)
+
+/datum/mod_theme/advanced
+ name = "advanced"
+ desc = "An advanced version of Cybersun Industries's classic suit, shining with a white, acid and fire resistant polish."
+ extended_desc = "The flagship version of the Cybersun Industrie industrial model, and their latest product. \
+ Combining all the features of their other industrial model suits inside, with blast resistance almost approaching \
+ some EOD suits, the outside has been coated with a white polish rumored to be a corporate secret. \
+ The paint used is almost entirely immune to corrosives, and certainly looks damn fine. \
+ These come pre-installed with magnetic boots, using an advanced system to toggle them on or off as the user walks."
+ default_skin = "advanced"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_advanced
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 3
+ charge_drain = DEFAULT_CHARGE_DRAIN * 1.5
+ siemens_coefficient = 0
+ slowdown_inactive = 1
+ slowdown_active = 0.45
+ inbuilt_modules = list(/obj/item/mod/module/magboot/advanced)
+ allowed_suit_storage = list(
+ /obj/item/analyzer,
+ /obj/item/rcd,
+ /obj/item/fireaxe,
+ /obj/item/melee/classic_baton/telescopic,
+ /obj/item/rpd,
+ /obj/item/t_scanner,
+ /obj/item/analyzer,
+ /obj/item/gun
+
+ )
+ skins = list(
+ "advanced" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_advanced
+ armor = list(MELEE = 35, BULLET = 10, LASER = 10, ENERGY = 10, BOMB = 50, RAD = INFINITY, FIRE = INFINITY, ACID = 150)
+
+/datum/mod_theme/mining
+ name = "mining"
+ desc = "A Nanotrasen mining suit for on-site operations, fit with accreting ash armor and a sphere form."
+ extended_desc = "A high-powered Nanotrasen-designed suit, based off the work of Cybersun Industries. \
+ While initial designs were built for the rigors of asteroid mining, given blast resistance through inbuilt ceramics, \
+ mining teams have since heavily tweaked the suit themselves with assistance from devices crafted by \
+ destructive analysis of unknown technologies discovered on the Indecipheres mining sites, patterned off \
+ their typical non-EVA exploration suits. The visor has been expanded to a system of seven arachnid-like cameras, \
+ offering full view of the land and its soon-to-be-dead inhabitants. The armor plating has been trimmed down to \
+ the bare essentials, geared far more for environmental hazards than combat against fauna; however, \
+ this gives way to incredible protection against corrosives and thermal protection good enough for \
+ both casual backstroking through molten magma and romantic walks through arctic terrain. \
+ Instead, the suit is capable of using its' anomalous properties to attract and \
+ carefully distribute layers of ash or ice across the surface; these layers are ablative, but incredibly strong. \
+ However, all of this has proven to be straining on all Nanotrasen-approved cells, \
+ so much so that it comes default fueled by equally-enigmatic plasma fuel rather than a simple recharge. \
+ Additionally, the systems have been put to near their maximum load, allowing for far less customization than others."
+ default_skin = "mining"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_mining
+ resistance_flags = FIRE_PROOF | LAVA_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 3
+ charge_drain = DEFAULT_CHARGE_DRAIN * 2
+ slowdown_inactive = 1.5
+ slowdown_active = 0.5
+ allowed_suit_storage = list(
+ /obj/item/resonator,
+ /obj/item/mining_scanner,
+ /obj/item/t_scanner/adv_mining_scanner,
+ /obj/item/pickaxe,
+ /obj/item/kinetic_crusher,
+ /obj/item/stack/ore/plasma,
+ /obj/item/storage/bag/ore,
+ /obj/item/gun/energy/kinetic_accelerator,
+ )
+ inbuilt_modules = list(/obj/item/mod/module/ash_accretion, /obj/item/mod/module/sphere_transform)
+ skins = list(
+ "mining" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCKHAIR,
+
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "asteroid" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_mining
+ armor = list(MELEE = 30, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 50, RAD = 50, FIRE = 50, ACID = 50)
+
+/datum/mod_theme/loader
+ name = "loader"
+ desc = "An unsealed experimental motorized harness manufactured by Scarborough Arms for quick and efficient munition supplies."
+ extended_desc = "This powered suit is an experimental spinoff of in-atmosphere Engineering suits. \
+ This fully articulated titanium exoskeleton is Scarborough Arms' suit of choice for their munition delivery men, \
+ and what it lacks in EVA protection, it makes up for in strength and flexibility. The primary feature of \
+ this suit are the two manipulator arms, carefully synchronized with the user's thoughts and \
+ duplicating their motions almost exactly. These are driven by myomer, an artificial analog of muscles, \
+ requiring large amounts of voltage to function; occasionally sparking under load with the sheer power of a \
+ suit capable of lifting 250 tons. Even the legs in the suit have been tuned to incredible capacity, \
+ the user being able to run at greater speeds for much longer distances and times than an unsuited equivalent. \
+ A lot of people would say loading cargo is a dull job. You could not disagree more."
+ default_skin = "loader"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_loader
+ max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
+ min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
+ siemens_coefficient = 0.25
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 5
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ )
+ inbuilt_modules = list(/obj/item/mod/module/hydraulic, /obj/item/mod/module/clamp/loader, /obj/item/mod/module/magnet)
+ skins = list(
+ "loader" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL | BLOCKHAIR,
+
+ SEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ SEALED_CLOTHING = THICKMATERIAL,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ SEALED_CLOTHING = THICKMATERIAL,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_loader
+ armor = list(MELEE = 20, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 10, RAD = 0, FIRE = 25, ACID = 25)
+
+/datum/mod_theme/medical
+ name = "medical"
+ desc = "A lightweight suit by DeForest Medical Corporation, allows for easier movement."
+ extended_desc = "A lightweight suit produced by the DeForest Medical Corporation and BioTech Solutions, based off the work of \
+ Cybersun Industries. The latest in technology has been employed in this suit to render it immunized against \
+ allergens, airborne toxins, and regular pathogens. The primary asset of this suit is the speed, \
+ fusing high-powered servos and actuators with a carbon-fiber construction. While there's very little armor used, \
+ it is incredibly acid-resistant. It is slightly more demanding of power than civilian-grade models, \
+ and weak against fingers tapping the glass."
+ default_skin = "medical"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_medical
+ charge_drain = DEFAULT_CHARGE_DRAIN * 2
+ slowdown_inactive = 1
+ slowdown_active = 0.45
+ allowed_suit_storage = list(
+ /obj/item/healthanalyzer,
+ /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/reagent_containers/syringe,
+ /obj/item/stack/medical,
+ /obj/item/sensor_device,
+ /obj/item/storage/pill_bottle,
+ /obj/item/storage/bag/chemistry,
+ /obj/item/storage/bag/bio,
+ )
+ skins = list(
+ "medical" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "corpsman" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_medical
+ armor = list(MELEE = 10, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 10, RAD = 0, FIRE = 75, ACID = 150)
+
+/datum/mod_theme/rescue
+ name = "rescue"
+ desc = "An advanced version of DeForest Medical Corporation's medical suit, designed for quick rescue of bodies from the most dangerous environments."
+ extended_desc = "An upgraded, overtuned version of DeForest Medical Corporation's medical suit, with BioTech Solutions making heavy modifications. \
+ designed for quick rescue of bodies from the most dangerous environments. The same advanced leg servos \
+ as the base version are seen here, giving paramedics incredible speed, but the same servos are also in the arms. \
+ Users are capable of quickly hauling even the heaviest crewmembers using this suit, \
+ all while being entirely immune against chemical and thermal threats. \
+ It is slightly more demanding of power than civilian-grade models, and weak against fingers tapping the glass."
+ default_skin = "rescue"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_rescue
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ charge_drain = DEFAULT_CHARGE_DRAIN * 1.5
+ slowdown_inactive = 0.75
+ slowdown_active = 0.25
+ inbuilt_modules = list()
+ allowed_suit_storage = list(
+ /obj/item/healthanalyzer,
+ /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/glass/beaker,
+ /obj/item/reagent_containers/glass/bottle,
+ /obj/item/reagent_containers/hypospray,
+ /obj/item/reagent_containers/syringe,
+ /obj/item/stack/medical,
+ /obj/item/sensor_device,
+ /obj/item/storage/pill_bottle,
+ /obj/item/storage/bag/chemistry,
+ /obj/item/storage/bag/bio,
+ /obj/item/melee/classic_baton/telescopic,
+ )
+ skins = list(
+ "rescue" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_rescue
+ armor = list(MELEE = 20, BULLET = 20, LASER = 5, ENERGY = 5, BOMB = 10, RAD = 50, FIRE = 150, ACID = 150) //Extra melee / bullet armor for if they get caught in a fight. Of course, no laser armor.
+
+/datum/mod_theme/research
+ name = "research"
+ desc = "A private military EOD suit by Aussec Armory, intended for explosive research. Bulky, but expansive."
+ extended_desc = "A private military EOD suit by Aussec Armory, based off the work of Cybersun Industries. \
+ This suit is intended for explosive research, built incredibly bulky and well-covering. \
+ Featuring an inbuilt chemical scanning array, this suit uses two layers of plastitanium armor, \
+ sandwiching an inert layer to dissipate kinetic energy into the suit and away from the user; \
+ outperforming even the best conventional EOD suits. However, despite its immunity against even \
+ missiles and artillery, all the explosive resistance is mostly working to keep the user intact, \
+ not alive. The user will also find narrow doorframes nigh-impossible to surmount."
+ default_skin = "research"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_research
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 5
+ slowdown_inactive = 1.75
+ slowdown_active = 1
+ ui_theme = "changeling"
+ inbuilt_modules = list(/obj/item/mod/module/reagent_scanner/advanced)
+ allowed_suit_storage = list(
+ /obj/item/analyzer,
+ /obj/item/dnainjector,
+ /obj/item/hand_tele,
+ /obj/item/storage/bag/bio,
+ /obj/item/melee/classic_baton/telescopic,
+ /obj/item/gun
+ )
+ skins = list(
+ "research" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ UNSEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_research
+ armor = list(MELEE = 30, BULLET = 30, LASER = 5, ENERGY = 5, BOMB = INFINITY, RAD = 75, FIRE = 75, ACID = 150) //Slow balistic / explosive testing armor. Not laser testing however!
+
+/datum/mod_theme/security
+ name = "security"
+ desc = "A Shellguard Munitions security suit, offering quicker speed at the cost of carrying capacity."
+ extended_desc = "A Shellguard Munitions classic, this model of MODsuit has been designed for quick response to \
+ hostile situations. These suits have been layered with plating worthy enough for fires or corrosive environments, \
+ and come with composite cushioning and an advanced honeycomb structure underneath the hull to ensure protection \
+ against broken bones or possible avulsions. The suit's legs have been given more rugged actuators, \
+ allowing the suit to do more work in carrying the weight. However, the systems used in these suits are more than \
+ a few years out of date, leading to an overall lower capacity for modules."
+ default_skin = "security"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_security
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 3
+ slowdown_inactive = 1
+ slowdown_active = 0.45
+ ui_theme = "security"
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee/baton,
+ /obj/item/gun,
+ )
+ skins = list(
+ "security" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_security
+ armor = list(MELEE = 25, BULLET = 20, LASER = 20, ENERGY = 5, BOMB = 25, RAD = 0, FIRE = 150, ACID = 150)
+
+/datum/mod_theme/safeguard
+ name = "safeguard"
+ desc = "A Shellguard Munitions advanced security suit, offering greater speed and fire protection than the standard security model."
+ extended_desc = "A Shellguard Munitions advanced security suit, and their latest model. This variant has \
+ ditched the presence of a reinforced glass visor entirely, replacing it with a 'blast visor' utilizing a \
+ small camera on the left side to display the outside to the user. The plating on the suit has been \
+ dramatically increased, especially in the pauldrons, giving the wearer an imposing silhouette. \
+ Heatsinks line the sides of the suit, and greater technology has been used in insulating it against \
+ both corrosive environments and sudden impacts to the user's joints."
+ default_skin = "safeguard"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_safeguard
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ slowdown_inactive = 0.75
+ slowdown_active = 0.25
+ ui_theme = "security"
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/reagent_containers/spray/pepper,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee/baton,
+ /obj/item/gun,
+ )
+ skins = list(
+ "safeguard" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ UNSEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_safeguard
+ armor = list(MELEE = 30, BULLET = 25, LASER = 25, ENERGY = 15, BOMB = 40, RAD = 25, FIRE = INFINITY, ACID = 150)
+
+/datum/mod_theme/magnate
+ name = "magnate"
+ desc = "A fancy, very protective suit for Nanotrasen's captains. Shock, fire and acid-proof while also having a large capacity and high speed."
+ extended_desc = "They say it costs four hundred thousand credits to run this MODsuit... for twelve seconds. \
+ The Magnate suit is designed for protection, comfort, and luxury for Nanotrasen Captains. \
+ The onboard air filters have been preprogrammed with an additional five hundred different fragrances that can \
+ be pumped into the helmet, all of highly-endangered flowers. A bespoke Tralex mechanical clock has been placed \
+ in the wrist, and the Magnate package comes with carbon-fibre cufflinks to wear underneath. \
+ My God, it even has a granite trim. The double-classified paint that's been painstakingly applied to the hull \
+ provides protection against shock, fire, and the strongest acids. Onboard systems employ meta-positronic learning \
+ and bluespace processing to allow for a wide array of onboard modules to be supported, and only the best actuators \
+ have been employed for speed. The resemblance to a Gorlex Marauder helmet is *purely* coincidental."
+ default_skin = "magnate"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_magnate
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF // Theft targets should be hard to destroy
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 5
+ slowdown_inactive = 0.75
+ slowdown_active = 0.25
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee,
+ /obj/item/gun,
+ )
+ skins = list(
+ "magnate" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_magnate
+ armor = list(MELEE = 50, BULLET = 50, LASER = 50, ENERGY = 15, BOMB = 15, RAD = 50, FIRE = INFINITY, ACID = 450) //On one hand this is quite strong, on the other hand energy hole / antagonists need to steal, and thus by extention use this.
+
+/datum/mod_theme/cosmohonk
+ name = "cosmohonk"
+ desc = "A suit by Honk Ltd. Protects against low humor environments. Most of the tech went to lower the power cost."
+ extended_desc = "The Cosmohonk MODsuit was originally designed for interstellar comedy in low-humor environments. \
+ It utilizes tungsten electro-ceramic casing and chromium bipolars, coated in zirconium-boron paint underneath \
+ a dermatiraelian subspace alloy. Despite the glaringly obvious optronic vacuum drive pedals, \
+ this particular model does not employ manganese bipolar capacitor cleaners, thank the Honkmother. \
+ All you know is that this suit is mysteriously power-efficient, and far too colorful for the Mime to steal."
+ default_skin = "cosmohonk"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_cosmohonk
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0.25
+ slowdown_inactive = 1.75
+ slowdown_active = 1.25
+ allowed_suit_storage = list(
+ /obj/item/bikehorn,
+ /obj/item/grown/bananapeel,
+ /obj/item/reagent_containers/spray/waterflower,
+ /obj/item/instrument,
+ )
+ skins = list(
+ "cosmohonk" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+
+ SEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_cosmohonk
+ armor = list(MELEE = 5, BULLET = 5, LASER = 5, ENERGY = 5, BOMB = 5, RAD = 0, FIRE = 75, ACID = 50)
+
+/datum/mod_theme/syndicate
+ name = "syndicate"
+ desc = "A suit designed by Gorlex Marauders, offering armor ruled illegal in most of Spinward Stellar."
+ extended_desc = "An advanced combat suit adorned in a sinister crimson red color scheme, produced and manufactured \
+ for special mercenary operations. The build is a streamlined layering consisting of shaped Plasteel, \
+ and composite ceramic, while the under suit is lined with a lightweight Kevlar and durathread hybrid weave \
+ to provide ample protection to the user where the plating doesn't, with an illegal onboard electric powered \
+ ablative shield module to provide resistance against conventional energy firearms. \
+ A small tag hangs off of it reading; 'Property of the Gorlex Marauders, with assistance from Cybersun Industries. \
+ All rights reserved, tampering with suit will void warranty."
+ default_skin = "syndicate"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_syndicate
+
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1
+ slowdown_active = 0.5 //This is EVA mode slowdown. In combat mode, no slowdown.
+ ui_theme = "syndicate"
+ inbuilt_modules = list(/obj/item/mod/module/armor_booster)
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee/baton,
+ /obj/item/melee/energy/sword,
+ /obj/item/shield/energy,
+ /obj/item/gun,
+ )
+ skins = list(
+ "syndicate" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "honkerative" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_syndicate
+ armor = list(MELEE = 15, BULLET = 20, LASER = 5, ENERGY = 5, BOMB = 35, RAD = 50, FIRE = 50, ACID = 450)
+ //melee = 40 with booster
+ //bullet = 50
+ //laser = 20 with booster
+ //energy = //20 with booster, energy has always been an armor hole.
+/datum/mod_theme/elite
+ name = "elite"
+ desc = "An elite suit upgraded by Cybersun Industries, offering upgraded armor values."
+ extended_desc = "An evolution of the syndicate suit, featuring a bulkier build and a matte black color scheme, \
+ this suit is only produced for high ranking Syndicate officers and elite strike teams. \
+ It comes built with a secondary layering of ceramic and Kevlar into the plating providing it with \
+ exceptionally better protection along with fire and acid proofing. A small tag hangs off of it reading; \
+ 'Property of the Gorlex Marauders, with assistance from Cybersun Industries. \
+ All rights reserved, tampering with suit will void life expectancy.'"
+ default_skin = "elite"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_elite
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1
+ slowdown_active = 0.5 //This is EVA mode slowdown. In combat mode, no slowdown.
+ ui_theme = "syndicate"
+ inbuilt_modules = list(/obj/item/mod/module/armor_booster)
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee/baton,
+ /obj/item/melee/energy/sword,
+ /obj/item/shield/energy,
+ /obj/item/gun,
+ )
+ skins = list(
+ "elite" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_elite
+ armor = list(MELEE = 50, BULLET = 45, LASER = 35, ENERGY = 10, BOMB = 60, RAD = 150, FIRE = INFINITY, ACID = INFINITY)
+ //melee = 50 // 75 with booster
+ //bullet = 45 // 75 same as
+ //laser = 35 //50 same as
+ //energy = 15 // 25
+
+/datum/mod_theme/prototype
+ name = "prototype"
+ desc = "A prototype modular suit powered by locomotives. While it is comfortable and has a big capacity, it remains very bulky and power-inefficient."
+ extended_desc = "This is a prototype powered exoskeleton, a design not seen in hundreds of years, the first \
+ post-void war era modular suit to ever be safely utilized by an operator. This ancient clunker is still functional, \
+ though it's missing several modern-day luxuries from updated Cybersun Industries designs. \
+ Primarily, the suit's myoelectric suit layer is entirely non-existant, and the servos do very little to \
+ help distribute the weight evenly across the wearer's body, making it slow and bulky to move in. \
+ The internal heads-up display is rendered in nearly unreadable cyan, as the visor suggests, \
+ leaving the user unable to see long distances. However, the way the helmet retracts is pretty cool."
+ default_skin = "prototype"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_prototype
+ resistance_flags = FIRE_PROOF
+ siemens_coefficient = 0
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 5
+ charge_drain = DEFAULT_CHARGE_DRAIN * 2
+ slowdown_inactive = 2
+ slowdown_active = 0.95
+ ui_theme = "hackerman"
+ inbuilt_modules = list(/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype)
+ allowed_suit_storage = list(
+ /obj/item/analyzer,
+ /obj/item/t_scanner,
+ /obj/item/rpd,
+ /obj/item/rcd,
+ )
+ skins = list(
+ "prototype" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ UNSEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_prototype
+ armor = list(MELEE = 20, BULLET = 5, LASER = 10, ENERGY = 10, BOMB = 50, RAD = 50, FIRE = 150, ACID = 150)
+
+/datum/mod_theme/responsory
+ name = "responsory"
+ desc = "A high-speed rescue suit by Nanotrasen, intended for its' emergency response teams."
+ extended_desc = "A streamlined suit of Nanotrasen design, these sleek black suits are only worn by \
+ elite emergency response personnel to help save the day. While the slim and nimble design of the suit \
+ cuts the ceramics and ablatives in it down, dropping the protection, \
+ it keeps the wearer safe from the harsh void of space while sacrificing no speed whatsoever. \
+ While wearing it you feel an extreme deference to darkness. "
+ default_skin = "responsory"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_responsory
+
+ resistance_flags = FIRE_PROOF
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee/baton,
+ /obj/item/gun,
+ )
+ skins = list(
+ "responsory" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = COLLAR_LAYER,
+
+ SEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ "inquisitory" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ UNSEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_responsory //This has no slowdown active, and no variation between levels. I am ASSUMING this will be gamma only.
+ armor = list(MELEE = 40, BULLET = 25, LASER = 25, ENERGY = 20, BOMB = 25, RAD = INFINITY, FIRE = 200, ACID = 200)
+
+/datum/mod_theme/apocryphal
+ name = "apocryphal"
+ desc = "A high-tech, only technically legal, armored suit created by a collaboration effort between Nanotrasen and Shellguard Munitions."
+ extended_desc = "A bulky and only legal by technicality suit, this ominous black and red MODsuit is only worn by \
+ Nanotrasen Black Ops teams. If you can see this suit, you fucked up. A collaborative joint effort between \
+ Shellguard and Nanotrasen, the construction and modules gives the user robust protection against \
+ anything that can be thrown at it, along with acute combat awareness tools for it's wearer. \
+ Whether the wearer uses it or not is up to them. \
+ There seems to be a little inscription on the wrist that reads; \'squiddie', d'aww."
+ default_skin = "apocryphal"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_apocryphal
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ ui_theme = "malfunction"
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ complexity_max = DEFAULT_MAX_COMPLEXITY + 10
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee/baton,
+ /obj/item/melee/energy/sword,
+ /obj/item/shield/energy,
+ /obj/item/gun,
+ )
+ skins = list(
+ "apocryphal" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+
+ SEALED_INVISIBILITY = HIDEFACE | HIDEMASK | HIDEEYES,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_apocryphal
+ armor = list(MELEE = 200, BULLET = 200, LASER = 50, ENERGY = 50, BOMB = INFINITY, RAD = INFINITY, FIRE = INFINITY, ACID = INFINITY)
+
+/datum/mod_theme/corporate
+ name = "corporate"
+ desc = "A fancy, high-tech suit for Nanotrasen's high ranking officers."
+ extended_desc = "An even more costly version of the Magnate model, the corporate suit is a thermally insulated, \
+ anti-corrosion coated suit for high-ranking CentCom Officers, deploying pristine protective armor and \
+ advanced actuators, feeling practically weightless when turned on. Scraping the paint of this suit is \
+ counted as a war-crime and reason for immediate execution in over fifty Nanotrasen space stations. \
+ The resemblance to a Gorlex Marauder helmet is *purely* coincidental."
+ default_skin = "corporate"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_corporate
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/ammo_box,
+ /obj/item/ammo_casing,
+ /obj/item/restraints/handcuffs,
+ /obj/item/flash,
+ /obj/item/melee/baton,
+ /obj/item/gun,
+ )
+ skins = list(
+ "corporate" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ SEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_corporate
+ armor = list(MELEE = 200, BULLET = 200, LASER = 50, ENERGY = 50, BOMB = INFINITY, RAD = INFINITY, FIRE = INFINITY, ACID = INFINITY)
+
+/datum/mod_theme/debug
+ name = "debug"
+ desc = "Strangely nostalgic."
+ extended_desc = "An advanced suit that has dual ion engines powerful enough to grant a humanoid flight. \
+ Contains an internal self-recharging high-current capacitor for short, powerful bo- \
+ Oh wait, this is not actually a flight suit. Fuck."
+ default_skin = "debug"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_debug
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+
+ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ complexity_max = 50
+ siemens_coefficient = 0
+ slowdown_inactive = 0.5
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/gun,
+ )
+ skins = list(
+ "debug" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_debug
+ armor = list(MELEE = 200, BULLET = 200, LASER = 50, ENERGY = 50, BOMB = INFINITY, RAD = INFINITY, FIRE = INFINITY, ACID = INFINITY)
+
+
+/datum/mod_theme/administrative
+ name = "administrative"
+ desc = "A suit made of adminium. Who comes up with these stupid mineral names?"
+ extended_desc = "Yeah, okay, I guess you can call that an event. What I consider an event is something actually \
+ fun and engaging for the players- instead, most were sitting out, dead or gibbed, while the lucky few got to \
+ have all the fun. If this continues to be a pattern for your \"events\" (Admin Abuse) \
+ there will be an admin complaint. You have been warned."
+ default_skin = "debug"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_administrative
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ complexity_max = 1000
+ charge_drain = DEFAULT_CHARGE_DRAIN * 0
+ siemens_coefficient = 0
+ slowdown_inactive = 0
+ slowdown_active = 0
+ allowed_suit_storage = list(
+ /obj/item/gun,
+ )
+ skins = list(
+ "debug" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE | BLOCK_GAS_SMOKE_EFFECT,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH | HEADCOVERSEYES,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL | STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_administrative //considering this should not be used, it's getting just DS armor, not infinity in everything.
+ armor = list(MELEE = 200, BULLET = 200, LASER = 50, ENERGY = 50, BOMB = INFINITY, RAD = INFINITY, FIRE = INFINITY, ACID = INFINITY)
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
new file mode 100644
index 000000000000..2ef5e3cb73e4
--- /dev/null
+++ b/code/modules/mod/mod_types.dm
@@ -0,0 +1,454 @@
+/obj/item/mod/control/pre_equipped
+ /// The skin we apply to the suit, defaults to the default_skin of the theme.
+ var/applied_skin
+ /// The MOD core we apply to the suit.
+ var/applied_core = /obj/item/mod/core/standard
+ /// The cell we apply to the core. Only applies to standard core suits.
+ var/applied_cell = /obj/item/stock_parts/cell/high
+ /// List of modules we spawn with.
+ var/list/applied_modules = list()
+ /// Modules that we pin when the suit is installed for the first time, for convenience, can be applied or theme inbuilt modules.
+ var/list/default_pins = list()
+
+/obj/item/mod/control/pre_equipped/Initialize(mapload, new_theme, new_skin, new_core, new_access)
+ for(var/module_to_pin in default_pins)
+ default_pins[module_to_pin] = list()
+ new_skin = applied_skin
+ new_core = new applied_core(src)
+ if(istype(new_core, /obj/item/mod/core/standard))
+ var/obj/item/mod/core/standard/cell_core = new_core
+ cell_core.cell = new applied_cell()
+ . = ..()
+ for(var/obj/item/mod/module/module as anything in applied_modules)
+ module = new module(src)
+ install(module)
+
+/obj/item/mod/control/pre_equipped/set_wearer(mob/living/carbon/human/user)
+ . = ..()
+ for(var/obj/item/mod/module/module as anything in modules)
+ if(!default_pins[module.type]) //this module isnt meant to be pinned by default
+ continue
+ if(UID(wearer) in default_pins[module.type]) //if we already had pinned once to this user, don care anymore
+ continue
+ default_pins[module.type] += UID(wearer)
+ module.pin(wearer)
+
+/obj/item/mod/control/pre_equipped/uninstall(obj/item/mod/module/old_module, deleting)
+ . = ..()
+ if(default_pins[old_module.type])
+ default_pins -= old_module
+
+/obj/item/mod/control/pre_equipped/standard
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ )
+
+/obj/item/mod/control/pre_equipped/engineering
+ theme = /datum/mod_theme/engineering
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/rad_protection,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/magboot,
+ )
+ default_pins = list(
+ /obj/item/mod/module/magboot,
+ )
+
+/obj/item/mod/control/pre_equipped/atmospheric
+ theme = /datum/mod_theme/atmospheric
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/t_ray,
+ /obj/item/mod/module/magboot,
+ )
+ default_pins = list(
+ /obj/item/mod/module/magboot,
+ )
+
+
+/obj/item/mod/control/pre_equipped/advanced
+ theme = /datum/mod_theme/advanced
+ applied_cell = /obj/item/stock_parts/cell/super
+ applied_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/rad_protection,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/jetpack/advanced,
+ )
+ default_pins = list(
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/magboot/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/loader
+ theme = /datum/mod_theme/loader
+ applied_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/stamp,
+ )
+ default_pins = list(
+ /obj/item/mod/module/clamp/loader,
+ /obj/item/mod/module/magnet,
+ /obj/item/mod/module/hydraulic,
+ )
+
+/obj/item/mod/control/pre_equipped/mining
+ theme = /datum/mod_theme/mining
+ applied_core = /obj/item/mod/core/plasma
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/gps,
+ /obj/item/mod/module/orebag,
+ /obj/item/mod/module/clamp,
+ /obj/item/mod/module/drill,
+ )
+ default_pins = list(
+ /obj/item/mod/module/gps,
+ /obj/item/mod/module/drill,
+ /obj/item/mod/module/sphere_transform,
+ )
+
+/obj/item/mod/control/pre_equipped/mining/vendor //visit robotics.
+ theme = /datum/mod_theme/mining
+ applied_core = /obj/item/mod/core/plasma
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ )
+ default_pins = list(
+ /obj/item/mod/module/sphere_transform,
+ )
+
+
+/obj/item/mod/control/pre_equipped/mining/asteroid //The asteroid skin, as that one looks more space worthy / older. Good for space ruins.
+ applied_skin = "asteroid"
+
+/obj/item/mod/control/pre_equipped/medical
+ theme = /datum/mod_theme/medical
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/injector,
+ )
+
+/obj/item/mod/control/pre_equipped/rescue
+ theme = /datum/mod_theme/rescue
+ applied_cell = /obj/item/stock_parts/cell/super
+ applied_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/injector,
+ /obj/item/mod/module/defibrillator,
+ )
+ default_pins = list(
+ /obj/item/mod/module/defibrillator,
+ )
+
+/obj/item/mod/control/pre_equipped/research
+ theme = /datum/mod_theme/research
+ applied_cell = /obj/item/stock_parts/cell/super
+ applied_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/t_ray,
+ )
+
+/obj/item/mod/control/pre_equipped/security
+ theme = /datum/mod_theme/security
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/dispenser/mirage,
+ /obj/item/mod/module/jetpack,
+ )
+
+/obj/item/mod/control/pre_equipped/safeguard
+ theme = /datum/mod_theme/safeguard
+ applied_cell = /obj/item/stock_parts/cell/super
+ applied_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/dispenser/mirage,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/holster,
+ )
+ default_pins = list(
+ /obj/item/mod/module/jetpack/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/magnate
+ theme = /datum/mod_theme/magnate
+ applied_cell = /obj/item/stock_parts/cell/hyper
+ applied_modules = list(
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/mod/module/jetpack/advanced,
+ )
+ default_pins = list(
+ /obj/item/mod/module/jetpack/advanced,
+ )
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF // Theft targets should be hard to destroy
+
+/obj/item/mod/control/pre_equipped/magnate/Initialize(mapload)
+ . = ..()
+ RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(alert_admins_on_destroy))
+
+/obj/item/mod/control/pre_equipped/cosmohonk
+ theme = /datum/mod_theme/cosmohonk
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/waddle,
+ /obj/item/mod/module/bikehorn,
+ )
+
+/obj/item/mod/control/pre_equipped/traitor
+ theme = /datum/mod_theme/syndicate
+ applied_cell = /obj/item/stock_parts/cell/super
+ applied_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/jetpack,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/noslip,
+ )
+ default_pins = list(
+ /obj/item/mod/module/armor_booster,
+ /obj/item/mod/module/jetpack,
+ )
+
+/obj/item/mod/control/pre_equipped/traitor_elite
+ theme = /datum/mod_theme/elite
+ applied_cell = /obj/item/stock_parts/cell/bluespace
+ applied_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/noslip,
+ /obj/item/mod/module/flashlight,
+ )
+ default_pins = list(
+ /obj/item/mod/module/armor_booster,
+ /obj/item/mod/module/jetpack/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/nuclear
+ theme = /datum/mod_theme/syndicate
+ applied_cell = /obj/item/stock_parts/cell/hyper
+ req_access = list(ACCESS_SYNDICATE)
+ applied_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/dna_lock/emp_shield,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/noslip,
+ )
+ default_pins = list(
+ /obj/item/mod/module/armor_booster,
+ /obj/item/mod/module/jetpack/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/elite
+ theme = /datum/mod_theme/elite
+ applied_cell = /obj/item/stock_parts/cell/bluespace
+ req_access = list(ACCESS_SYNDICATE)
+ applied_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/dna_lock/emp_shield,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/noslip,
+ )
+ default_pins = list(
+ /obj/item/mod/module/armor_booster,
+ /obj/item/mod/module/jetpack/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/prototype
+ theme = /datum/mod_theme/prototype
+ req_access = list()
+ applied_modules = list(
+ /obj/item/mod/module/storage,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/rad_protection,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/tether,
+ )
+ default_pins = list(
+ /obj/item/mod/module/tether,
+ /obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype,
+ )
+
+/obj/item/mod/control/pre_equipped/responsory
+ theme = /datum/mod_theme/responsory
+ applied_cell = /obj/item/stock_parts/cell/hyper
+ req_access = list(ACCESS_CENT_GENERAL)
+ applied_modules = list(
+ /obj/item/mod/module/storage/syndicate, //Yes yes syndicate tech in ert but they need the storage
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/magboot/advanced,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/ert_camera,
+ )
+ /// The insignia type, insignias show what sort of member of the ERT you're dealing with.
+ var/insignia_type = /obj/item/mod/module/insignia
+ /// Additional module we add, as a treat.
+ var/additional_module
+
+/obj/item/mod/control/pre_equipped/responsory/Initialize(mapload, new_theme, new_skin, new_core)
+ applied_modules.Insert(1, insignia_type)
+ if(additional_module)
+ applied_modules += additional_module
+ default_pins += additional_module
+ return ..()
+
+/obj/item/mod/control/pre_equipped/responsory/commander
+ insignia_type = /obj/item/mod/module/insignia/commander
+ additional_module = /obj/item/mod/module/power_kick
+
+/obj/item/mod/control/pre_equipped/responsory/security
+ insignia_type = /obj/item/mod/module/insignia/security
+ additional_module = /obj/item/mod/module/dispenser/mirage
+
+/obj/item/mod/control/pre_equipped/responsory/engineer
+ insignia_type = /obj/item/mod/module/insignia/engineer
+ additional_module = /obj/item/mod/module/anomaly_locked/kinesis/prebuilt //This can only end well.
+
+/obj/item/mod/control/pre_equipped/responsory/medic
+ insignia_type = /obj/item/mod/module/insignia/medic
+ additional_module = /obj/item/mod/module/defibrillator
+
+/obj/item/mod/control/pre_equipped/responsory/janitor
+ insignia_type = /obj/item/mod/module/insignia/janitor
+ additional_module = /obj/item/mod/module/clamp
+
+/obj/item/mod/control/pre_equipped/responsory/clown
+ insignia_type = /obj/item/mod/module/insignia/clown
+ additional_module = /obj/item/mod/module/bikehorn
+
+/obj/item/mod/control/pre_equipped/responsory/chaplain
+ insignia_type = /obj/item/mod/module/insignia/chaplain
+ additional_module = /obj/item/mod/module/injector
+
+/obj/item/mod/control/pre_equipped/responsory/inquisitory //Diffrent look, as well as magic proof on TG. We don't have the magic proof stuff here, but it's perfect for inqusitors. Or if you want to give your ERT a fancy look.
+ applied_skin = "inquisitory"
+
+/obj/item/mod/control/pre_equipped/responsory/inquisitory/commander
+ insignia_type = /obj/item/mod/module/insignia/commander
+ additional_module = /obj/item/mod/module/power_kick
+
+/obj/item/mod/control/pre_equipped/responsory/inquisitory/security
+ insignia_type = /obj/item/mod/module/insignia/security
+ additional_module = /obj/item/mod/module/dispenser/mirage
+
+/obj/item/mod/control/pre_equipped/responsory/inquisitory/medic
+ insignia_type = /obj/item/mod/module/insignia/medic
+ additional_module = /obj/item/mod/module/defibrillator
+
+/obj/item/mod/control/pre_equipped/responsory/inquisitory/chaplain
+ insignia_type = /obj/item/mod/module/insignia/chaplain
+ additional_module = /obj/item/mod/module/power_kick //JUDGEMENT
+
+/obj/item/mod/control/pre_equipped/apocryphal
+ theme = /datum/mod_theme/apocryphal
+ applied_cell = /obj/item/stock_parts/cell/bluespace
+ req_access = list(ACCESS_CENT_SPECOPS)
+ applied_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/magboot/advanced,
+ )
+ default_pins = list(
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/magboot/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/apocryphal/officer
+ applied_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/magboot/advanced,
+ /obj/item/mod/module/power_kick,
+ )
+ default_pins = list(
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/magboot/advanced,
+ /obj/item/mod/module/power_kick, //If you are not drop kicking a xenomorph, what are you doing as an DS commander?
+ )
+
+
+/obj/item/mod/control/pre_equipped/corporate
+ theme = /datum/mod_theme/corporate
+ applied_core = /obj/item/mod/core/infinite
+ req_access = list(ACCESS_CENT_SPECOPS)
+ applied_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/dna_lock/emp_shield,
+ /obj/item/mod/module/status_readout,
+ /obj/item/mod/module/anomaly_locked/kinesis/plus,
+ /obj/item/mod/module/magboot/advanced,
+ )
+ default_pins = list(
+ /obj/item/mod/module/anomaly_locked/kinesis/plus,
+ /obj/item/mod/module/magboot/advanced,
+ )
+
+/obj/item/mod/control/pre_equipped/debug
+ theme = /datum/mod_theme/debug
+ applied_core = /obj/item/mod/core/infinite
+ applied_modules = list( //one of every type of module, for testing if they all work correctly // boy this isn't even 25% the modules
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/bikehorn,
+ /obj/item/mod/module/rad_protection,
+ /obj/item/mod/module/injector,
+ )
+
+/obj/item/mod/control/pre_equipped/administrative
+ theme = /datum/mod_theme/administrative
+ applied_core = /obj/item/mod/core/infinite
+ applied_modules = list(
+ /obj/item/mod/module/storage/bluespace,
+ /obj/item/mod/module/emp_shield,
+ /obj/item/mod/module/welding,
+ /obj/item/mod/module/magboot/advanced,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/stealth/ninja,
+ )
+ default_pins = list(
+ /obj/item/mod/module/stealth/ninja,
+ /obj/item/mod/module/magboot/advanced,
+ /obj/item/mod/module/jetpack/advanced,
+ )
+
+//these exist for the prefs menu
+/obj/item/mod/control/pre_equipped/empty
+
+/obj/item/mod/control/pre_equipped/empty/syndicate
+ theme = /datum/mod_theme/syndicate
+
+/obj/item/mod/control/pre_equipped/empty/syndicate/honkerative
+ applied_skin = "honkerative"
+
+/obj/item/mod/control/pre_equipped/empty/elite
+ theme = /datum/mod_theme/elite
+
+INITIALIZE_IMMEDIATE(/obj/item/mod/control/pre_equipped/empty)
diff --git a/code/modules/mod/mod_ui.dm b/code/modules/mod/mod_ui.dm
new file mode 100644
index 000000000000..04a6a5a5fc4c
--- /dev/null
+++ b/code/modules/mod/mod_ui.dm
@@ -0,0 +1,85 @@
+/obj/item/mod/control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "MODsuit", name, 400, 525, master_ui, state)
+ ui.open()
+
+/obj/item/mod/control/ui_data(mob/user)
+ var/data = list()
+ data["interface_break"] = interface_break
+ data["malfunctioning"] = malfunctioning
+ data["open"] = open
+ data["active"] = active
+ data["locked"] = locked
+ data["complexity"] = complexity
+ data["selected_module"] = selected_module?.name
+ data["wearer_name"] = wearer ? (wearer.get_authentification_name("Unknown") || "Unknown") : "No Occupant"
+ data["wearer_job"] = wearer ? wearer.get_assignment("Unknown", "Unknown", FALSE) : "No Job"
+ data["core"] = core?.name
+ data["charge"] = get_charge_percent()
+ data["modules"] = list()
+ for(var/obj/item/mod/module/module as anything in modules)
+ var/list/module_data = list(
+ "module_name" = module.name,
+ "description" = module.desc,
+ "module_type" = module.module_type,
+ "module_active" = module.active,
+ "pinned" = module.pinned_to[UID(user)], //might just want user here
+ "idle_power" = module.idle_power_cost,
+ "active_power" = module.active_power_cost,
+ "use_power" = module.use_power_cost,
+ "module_complexity" = module.complexity,
+ "cooldown_time" = module.cooldown_time,
+ "cooldown" = round(COOLDOWN_TIMELEFT(module, cooldown_timer), 1 SECONDS),
+ "ref" = module.module_UID, //might just want user here
+ "id" = module.tgui_id,
+ "configuration_data" = module.get_configuration()
+ )
+ module_data += module.add_ui_data()
+ data["modules"] += list(module_data)
+ return data
+
+/obj/item/mod/control/ui_static_data(mob/user)
+ var/data = list()
+ data["ui_theme"] = ui_theme
+ data["control"] = name
+ data["complexity_max"] = complexity_max
+ data["helmet"] = helmet?.name
+ data["chestplate"] = chestplate?.name
+ data["gauntlets"] = gauntlets?.name
+ data["boots"] = boots?.name
+ return data
+
+/obj/item/mod/control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ if(locked && !allowed(usr))
+ to_chat(usr, "Insufficient access!")
+ playsound(src, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
+ return
+ if(malfunctioning && prob(75))
+ to_chat(usr, "ERROR!")
+ return
+ switch(action)
+ if("lock")
+ locked = !locked
+ to_chat(usr, "ID [locked ? "locked" : "unlocked"].")
+ if("activate")
+ toggle_activate(usr)
+ if("select")
+ var/obj/item/mod/module/module = locateUID(params["ref"])
+ if(!module)
+ return
+ module.on_select()
+ if("configure")
+ var/obj/item/mod/module/module = locateUID(params["ref"])
+ if(!module)
+ return
+ module.configure_edit(params["key"], params["value"])
+ if("pin")
+ var/obj/item/mod/module/module = locateUID(params["ref"])
+ if(!module)
+ return
+ module.pin(usr)
+ return TRUE
diff --git a/code/modules/mod/modules/_modules.dm b/code/modules/mod/modules/_modules.dm
new file mode 100644
index 000000000000..ca06756de475
--- /dev/null
+++ b/code/modules/mod/modules/_modules.dm
@@ -0,0 +1,431 @@
+///MOD Module - A special device installed in a MODsuit allowing the suit to do new stuff.
+/obj/item/mod/module
+ name = "MOD module"
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ icon_state = "module"
+ /// If it can be removed
+ var/removable = TRUE
+ /// If it's passive, togglable, usable or active
+ var/module_type = MODULE_PASSIVE
+ /// Is the module active
+ var/active = FALSE
+ /// How much space it takes up in the MOD
+ var/complexity = 0
+ /// Power use when idle
+ var/idle_power_cost = DEFAULT_CHARGE_DRAIN * 0
+ /// Power use when active
+ var/active_power_cost = DEFAULT_CHARGE_DRAIN * 0
+ /// Power use when used, we call it manually
+ var/use_power_cost = DEFAULT_CHARGE_DRAIN * 0
+ /// ID used by their TGUI
+ var/tgui_id
+ /// Linked MODsuit
+ var/obj/item/mod/control/mod
+ /// If we're an active module, what item are we?
+ var/obj/item/device
+ /// Overlay given to the user when the module is inactive
+ var/overlay_state_inactive
+ /// Overlay given to the user when the module is active
+ var/overlay_state_active
+ /// Overlay given to the user when the module is used, lasts until cooldown finishes
+ var/overlay_state_use
+ /// Icon file for the overlay.
+ var/overlay_icon_file = 'icons/mob/clothing/modsuit/mod_modules.dmi'
+ /// Does the overlay use the control unit's colors?
+ var/use_mod_colors = FALSE
+ ///Does the mod overide the colour in some way?
+ var/mod_color_overide
+ /// What modules are we incompatible with?
+ var/list/incompatible_modules = list()
+ /// Cooldown after use
+ var/cooldown_time = 0
+ /// The mouse button needed to use this module
+ var/used_signal
+ /// List of UID()s mobs we are pinned to, linked with their action buttons
+ var/list/pinned_to = list()
+ /// flags that let the module ability be used in odd circumstances
+ var/allow_flags = NONE
+ /// Timer for the cooldown
+ COOLDOWN_DECLARE(cooldown_timer) //sohtgdoiuduhnfipguhndshnfigdnghd
+ ///The UID of the module. Don't ask.
+ var/module_UID = null
+ sprite_sheets = list(
+ "Grey" = 'icons/mob/clothing/modsuit/species/grey_mod_modules.dmi',
+ "Vulpkanin" = 'icons/mob/clothing/modsuit/species/modules_vulp.dmi',
+ "Tajaran" = 'icons/mob/clothing/modsuit/species/modules_taj.dmi',
+ "Unathi" = 'icons/mob/clothing/modsuit/species/modules_unathi.dmi'
+ )
+
+/obj/item/mod/module/Initialize(mapload)
+ . = ..()
+ module_UID = UID(src)
+ if(module_type != MODULE_ACTIVE)
+ return
+ if(ispath(device))
+ device = new device(src)
+ device.flags |= NODROP
+ device.resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ device.slot_flags = null
+ device.w_class = WEIGHT_CLASS_HUGE
+ device.materials = null
+ RegisterSignal(device, COMSIG_PARENT_QDELETING, PROC_REF(on_device_deletion))
+ RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+
+/obj/item/mod/module/Destroy()
+ mod?.uninstall(src)
+ if(device)
+ UnregisterSignal(device, COMSIG_PARENT_QDELETING)
+ QDEL_NULL(device)
+ return ..()
+
+/obj/item/mod/module/examine(mob/user)
+ . = ..()
+ . += "Complexity level: [complexity]"
+
+
+/// Called when the module is selected from the TGUI, radial or the action button
+/obj/item/mod/module/proc/on_select()
+ if(((!mod.active || mod.activating) && !(allow_flags & MODULE_ALLOW_INACTIVE)) || module_type == MODULE_PASSIVE)
+ if(mod.wearer)
+ to_chat(mod.wearer, "Module is not active!")
+ return
+ if(module_type != MODULE_USABLE)
+ if(active)
+ on_deactivation()
+ else
+ on_activation()
+ else
+ on_use()
+ SEND_SIGNAL(mod, COMSIG_MOD_MODULE_SELECTED, src)
+
+/// Called when the module is activated
+/obj/item/mod/module/proc/on_activation()
+ if(!COOLDOWN_FINISHED(src, cooldown_timer))
+ to_chat(mod.wearer, "Module is on cooldown!")
+ return FALSE
+ if(!mod.active || mod.activating || !mod.get_charge())
+ to_chat(mod.wearer, "Module is unpowered!")
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE)
+ return FALSE
+ if(module_type == MODULE_ACTIVE)
+ if(mod.selected_module && !mod.selected_module.on_deactivation(display_message = FALSE))
+ return FALSE
+ mod.selected_module = src
+ if(device)
+ if(mod.wearer.put_in_hands(device))
+ to_chat(mod.wearer, "[device] extended.")
+ RegisterSignal(mod.wearer, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
+ RegisterSignal(mod.wearer, COMSIG_MOB_WILLINGLY_DROP, PROC_REF(dropkey))
+ else
+ to_chat(mod.wearer, "You can not extend the [device]!")
+ mod.wearer.drop_item()
+ return FALSE
+ else
+ var/used_button = "Middle Click"
+ if(!mod.wearer || !(mod.wearer.client.prefs.toggles2 & PREFTOGGLE_2_MOD_ACTIVATION_METHOD))
+ used_button = "Alt Click"
+ update_signal(used_button)
+ to_chat(mod.wearer, "[src] activated, [used_button] to use.")
+ else
+ COOLDOWN_START(src, cooldown_timer, cooldown_time) //We don't want to put active modules on cooldown when selected
+ to_chat(mod.wearer, "[src] activated.")
+ active = TRUE
+ mod.update_mod_overlays()
+ //mod.wearer.update_clothing(mod.slot_flags)
+ SEND_SIGNAL(src, COMSIG_MODULE_ACTIVATED)
+ return TRUE
+
+/// Called when the module is deactivated
+/obj/item/mod/module/proc/on_deactivation(display_message = TRUE, deleting = FALSE)
+ active = FALSE
+ if(module_type == MODULE_ACTIVE)
+ mod.selected_module = null
+ if(display_message && device)
+ to_chat(mod.wearer, "[device] retracted.")
+ else if(display_message)
+ to_chat(mod.wearer, "[src] deactivated.")
+
+ if(device)
+ mod.wearer.unEquip(device, 1)
+ device.forceMove(src)
+ UnregisterSignal(mod.wearer, COMSIG_ATOM_EXITED)
+ UnregisterSignal(mod.wearer, COMSIG_MOB_WILLINGLY_DROP)
+ else
+ UnregisterSignal(mod.wearer, used_signal)
+ used_signal = null
+ else if(display_message)
+ to_chat(mod.wearer, "[src] deactivated.")
+ //mod.wearer.update_clothing(mod.slot_flags)
+ SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED)
+ mod.update_mod_overlays()
+ return TRUE
+
+/// Called when the module is used
+/obj/item/mod/module/proc/on_use()
+ if(!COOLDOWN_FINISHED(src, cooldown_timer))
+ to_chat(mod.wearer, "Module is on cooldown!")
+ return FALSE
+ if(!check_power(use_power_cost))
+ to_chat(mod.wearer, "Module costs too much power to use!")
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE)
+ return FALSE
+ COOLDOWN_START(src, cooldown_timer, cooldown_time)
+ //addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/mob, update_clothing), mod.slot_flags), cooldown_time+1) //need to run it a bit after the cooldown starts to avoid conflicts
+ //mod.wearer.update_clothing(mod.slot_flags)
+ SEND_SIGNAL(src, COMSIG_MODULE_USED)
+ return TRUE
+
+/// Called when an activated module without a device is used
+/obj/item/mod/module/proc/on_select_use(atom/target)
+ if(!(allow_flags & MODULE_ALLOW_INCAPACITATED) && mod.wearer.incapacitated())
+ return FALSE
+ mod.wearer.face_atom(target)
+ if(!on_use())
+ return FALSE
+ return TRUE
+
+/// Called when an activated module without a device is active and the user alt/middle-clicks
+/obj/item/mod/module/proc/on_special_click(mob/source, atom/target)
+ SIGNAL_HANDLER
+ on_select_use(target)
+ return COMSIG_MOB_CANCEL_CLICKON
+
+/// Called on the MODsuit's process
+/obj/item/mod/module/proc/on_process()
+ if(active)
+ if(!drain_power(active_power_cost))
+ on_deactivation()
+ return FALSE
+ on_active_process()
+ else
+ drain_power(idle_power_cost)
+ return TRUE
+
+/// Called on the MODsuit's process if it is an active module
+/obj/item/mod/module/proc/on_active_process()
+ return
+
+/// Called from MODsuit's install() proc, so when the module is installed.
+/obj/item/mod/module/proc/on_install()
+ return
+
+/// Called from MODsuit's uninstall() proc, so when the module is uninstalled.
+/obj/item/mod/module/proc/on_uninstall(deleting = FALSE)
+ mod.update_mod_overlays()
+ return
+
+/// Called when the MODsuit is activated
+/obj/item/mod/module/proc/on_suit_activation()
+ mod.update_mod_overlays()
+ return
+
+/// Called when the MODsuit is deactivated
+/obj/item/mod/module/proc/on_suit_deactivation(deleting = FALSE)
+ mod.update_mod_overlays()
+ return
+
+/// Called when the MODsuit is equipped
+/obj/item/mod/module/proc/on_equip()
+ mod.update_mod_overlays()
+ return
+
+/// Called when the MODsuit is unequipped
+/obj/item/mod/module/proc/on_unequip()
+ return
+
+/// Drains power from the suit charge
+/obj/item/mod/module/proc/drain_power(amount)
+ if(!check_power(amount))
+ return FALSE
+ mod.subtract_charge(amount)
+ mod.update_charge_alert()
+ return TRUE
+
+/// Checks if there is enough power in the suit
+/obj/item/mod/module/proc/check_power(amount)
+ return mod.check_charge(amount)
+
+/// Adds additional things to the MODsuit ui_data()
+/obj/item/mod/module/proc/add_ui_data()
+ return list()
+
+/// Creates a list of configuring options for this module
+/obj/item/mod/module/proc/get_configuration()
+ return list()
+
+/// Generates an element of the get_configuration list with a display name, type and value
+/obj/item/mod/module/proc/add_ui_configuration(display_name, type, value, list/values)
+ return list("display_name" = display_name, "type" = type, "value" = value, "values" = values)
+
+/// Receives configure edits from the TGUI and edits the vars
+/obj/item/mod/module/proc/configure_edit(key, value)
+ return
+
+/// Called when the device moves to a different place on active modules
+/obj/item/mod/module/proc/on_exit(datum/source, atom/movable/part, direction)
+ SIGNAL_HANDLER
+
+ if(!active)
+ return
+ if(part.loc == src)
+ return
+ if(part.loc == mod.wearer)
+ return
+ if(part == device)
+ on_deactivation(display_message = FALSE)
+
+/// Called when the device gets deleted on active modules
+/obj/item/mod/module/proc/on_device_deletion(datum/source)
+ SIGNAL_HANDLER
+
+ if(source == device)
+ device = null
+ qdel(src)
+
+/// Adds the worn overlays to the suit.
+/obj/item/mod/module/proc/add_module_overlay(mob/living/user)
+ user.add_overlay(generate_worn_overlay(user))
+
+/// Generates an icon to be used for the suit's worn overlays
+/obj/item/mod/module/proc/generate_worn_overlay(mob/living/carbon/human/user)
+ . = list()
+ if(!mod.active)
+ return
+ var/used_overlay
+ if(overlay_state_use && !COOLDOWN_FINISHED(src, cooldown_timer))
+ used_overlay = overlay_state_use
+ else if(overlay_state_active && active)
+ used_overlay = overlay_state_active
+ else if(overlay_state_inactive)
+ used_overlay = overlay_state_inactive
+ else
+ return
+ var/image/final_overlay
+ if(sprite_sheets && sprite_sheets[user.dna.species.name])
+ final_overlay = image(icon = sprite_sheets[user.dna.species.name], icon_state = used_overlay, layer = EFFECTS_LAYER)
+ else
+ final_overlay = image(icon = overlay_icon_file, icon_state = used_overlay, layer = EFFECTS_LAYER)
+ if(mod_color_overide)
+ final_overlay.color = mod_color_overide
+ . += final_overlay
+ mod.mod_overlays += final_overlay
+
+/// Updates the signal used by active modules to be activated
+/obj/item/mod/module/proc/update_signal(value)
+ switch(value)
+ if("Middle Click")
+ mod.selected_module.used_signal = COMSIG_MOB_MIDDLECLICKON
+ if("Alt Click")
+ mod.selected_module.used_signal = COMSIG_MOB_ALTCLICKON
+ RegisterSignal(mod.wearer, mod.selected_module.used_signal, TYPE_PROC_REF(/obj/item/mod/module, on_special_click))
+
+/// Pins the module to the user's action buttons
+/obj/item/mod/module/proc/pin(mob/user)
+ if(module_type == MODULE_PASSIVE)
+ return
+ if(length(pinned_to))
+ for(var/datum/action/item_action/mod/pinned_module/M in user.actions)
+ if(M.module == src)
+ M.Remove(user)
+ pinned_to = list()
+ return
+ var/datum/action/item_action/mod/pinned_module/new_action = new(Target = mod, custom_icon = src.icon, custom_icon_state = src.icon_state, linked_module = src, user = user)
+ to_chat(user, "[new_action] is now pinned to the UI!")
+
+
+/// On drop key, concels a device item.
+/obj/item/mod/module/proc/dropkey(mob/living/user)
+ SIGNAL_HANDLER
+
+ if(user.get_active_hand() != device)
+ return
+ on_deactivation()
+ return
+
+///Anomaly Locked - Causes the module to not function without an anomaly.
+/obj/item/mod/module/anomaly_locked
+ name = "MOD anomaly locked module"
+ desc = "A form of a module, locked behind an anomalous core to function."
+ incompatible_modules = list(/obj/item/mod/module/anomaly_locked)
+ /// The core item the module runs off.
+ var/obj/item/assembly/signaler/anomaly/core
+ /// Accepted types of anomaly cores.
+ var/list/accepted_anomalies = list(/obj/item/assembly/signaler/anomaly)
+ /// If this one starts with a core in.
+ var/prebuilt = FALSE
+
+/obj/item/mod/module/anomaly_locked/Initialize(mapload)
+ . = ..()
+ if(!prebuilt || !length(accepted_anomalies))
+ return
+ var/core_path = pick(accepted_anomalies)
+ core = new core_path(src)
+ update_icon_state()
+
+/obj/item/mod/module/anomaly_locked/Destroy()
+ QDEL_NULL(core)
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/examine(mob/user)
+ . = ..()
+ if(!length(accepted_anomalies))
+ return
+ if(core)
+ . += "There is a [core.name] installed in it. You could remove it with a screwdriver..."
+ else
+ var/list/core_list = list()
+ for(var/path in accepted_anomalies)
+ var/atom/core_path = path
+ core_list += initial(core_path.name)
+ . +="You need to insert \a [english_list(core_list, and_text = " or ")] for this module to function."
+
+/obj/item/mod/module/anomaly_locked/on_select()
+ if(!core)
+ to_chat(mod.wearer, "ERROR. NO CORE INSTALLED!")
+ return
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/on_process()
+ . = ..()
+ if(!core)
+ return FALSE
+
+/obj/item/mod/module/anomaly_locked/on_active_process()
+ if(!core)
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/anomaly_locked/attackby(obj/item/item, mob/living/user, params)
+ if(item.type in accepted_anomalies)
+ if(core)
+ to_chat(user, "A core is already installed!")
+ return
+ if(!user.drop_item())
+ return
+ core = item
+ to_chat(user, "You install [item].")
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ update_icon_state()
+ core.forceMove(src)
+ else
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/screwdriver_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(!core)
+ to_chat(user, "A core is not installed!")
+ return
+ if(!do_after(user, 3 SECONDS, target = src))
+ return
+ to_chat(user, "You remove [core].")
+ core.forceMove(drop_location())
+ if(Adjacent(user) && !issilicon(user))
+ user.put_in_hands(core)
+ core = null
+ update_icon_state()
+
+/obj/item/mod/module/anomaly_locked/update_icon_state()
+ icon_state = initial(icon_state) + (core ? "-core" : "")
+ return ..()
diff --git a/code/modules/mod/modules/module_kinesis.dm b/code/modules/mod/modules/module_kinesis.dm
new file mode 100644
index 000000000000..3cd7f17fd7a7
--- /dev/null
+++ b/code/modules/mod/modules/module_kinesis.dm
@@ -0,0 +1,266 @@
+///Kinesis - Gives you the ability to move and launch objects.
+/obj/item/mod/module/anomaly_locked/kinesis
+ name = "MOD kinesis module"
+ desc = "A modular plug-in to the forearm, this module was presumed lost for many years, \
+ despite the suits it used to be mounted on still seeing some circulation. \
+ This piece of technology allows the user to generate precise anti-gravity fields, \
+ letting them move objects as small as a titanium rod to as large as industrial machinery. \
+ It does seem to work on living creatures, but not well."
+ icon_state = "kinesis"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 3
+ incompatible_modules = list(/obj/item/mod/module/anomaly_locked/kinesis)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_kinesis"
+ overlay_state_active = "module_kinesis_on"
+ accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/grav)
+ /// Range of the kinesis grab.
+ var/grab_range = 5
+ /// Time between us hitting objects with kinesis.
+ var/hit_cooldown_time = 1 SECONDS
+ /// Stat required for us to grab a mob.
+ var/stat_required = CONSCIOUS //Honestly. It's grav core locked. We'll try it, but I am going to need you to stun the mod. No fucking holding a poor terror prince in the air
+ /// Is incapitated required for us to grab a mob?
+ var/incapacitated_required = TRUE
+ /// How long we stun a mob for.
+ var/mob_stun_time = 0
+ /// Atom we grabbed with kinesis.
+ var/atom/movable/grabbed_atom
+ /// Overlay we add to each grabbed atom.
+ var/image/kinesis_icon
+ /// Our mouse movement catcher.
+ var/obj/screen/fullscreen/cursor_catcher/kinesis/kinesis_catcher
+ /// The sounds playing while we grabbed an object.
+ var/datum/looping_sound/kinesis/soundloop
+ ///The pixel_X of whatever we were grabbing before hand.
+ var/pre_pixel_x
+ ///The pixel_y of whatever we were grabbing before hand.
+ var/pre_pixel_y
+ /// The special snowflake effect we need to get beams to work
+ var/obj/effect/abstract/kinesis/beam = null
+ /// The cooldown between us hitting objects with kinesis.
+ COOLDOWN_DECLARE(hit_cooldown)
+
+/obj/item/mod/module/anomaly_locked/kinesis/Initialize(mapload)
+ . = ..()
+ soundloop = new(src)
+ kinesis_icon = image(icon = 'icons/effects/effects.dmi', icon_state = "kinesis", layer = EFFECTS_LAYER)
+
+/obj/item/mod/module/anomaly_locked/kinesis/Destroy()
+ QDEL_NULL(soundloop)
+ QDEL_NULL(beam)
+ QDEL_NULL(kinesis_catcher)
+ QDEL_NULL(kinesis_icon)
+ grabbed_atom = null
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/kinesis/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!mod.wearer.client)
+ return
+ if(grabbed_atom)
+ launch()
+ clear_grab(playsound = FALSE)
+ return
+ if(!range_check(target))
+ to_chat(mod.wearer, "[target] is too far away!")
+ return
+ if(!can_grab(target))
+ to_chat(mod.wearer, "[target] can not be grabbed!")
+ return
+ drain_power(use_power_cost)
+ grabbed_atom = target
+ if(isliving(grabbed_atom))
+ var/mob/living/grabbed_mob = grabbed_atom
+ grabbed_mob.Stun(mob_stun_time)
+ playsound(grabbed_atom, 'sound/weapons/contractorbatonhit.ogg', 75, TRUE)
+ beam = new /obj/effect/abstract/kinesis(get_turf(mod.wearer))
+ kinesis_icon.layer = grabbed_atom.layer - 0.1
+ grabbed_atom.add_overlay(kinesis_icon)
+ pre_pixel_x = grabbed_atom.pixel_x
+ pre_pixel_y = grabbed_atom.pixel_y
+ beam.chain = beam.Beam(grabbed_atom, icon_state = "kinesis", icon='icons/effects/beam.dmi', time = 100 SECONDS, maxdistance = 15, beam_type = /obj/effect/ebeam, beam_sleep_time = 3)
+ kinesis_catcher = mod.wearer.overlay_fullscreen("kinesis", /obj/screen/fullscreen/cursor_catcher/kinesis, 0)
+ kinesis_catcher.assign_to_mob(mod.wearer)
+ soundloop.start()
+ START_PROCESSING(SSfastprocess, src)
+
+/obj/item/mod/module/anomaly_locked/kinesis/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ clear_grab(playsound = !deleting)
+
+/obj/item/mod/module/anomaly_locked/kinesis/process()
+ if(!mod.wearer.client || mod.wearer.incapacitated(ignore_grab = TRUE))
+ clear_grab()
+ return
+ if(!range_check(grabbed_atom))
+ to_chat(mod.wearer, "[grabbed_atom] is too far away!")
+ clear_grab()
+ return
+ beam.forceMove(get_turf(mod.wearer))
+ drain_power(use_power_cost / 10)
+ if(kinesis_catcher.mouse_params)
+ kinesis_catcher.calculate_params()
+ if(!kinesis_catcher.given_turf)
+ return
+ mod.wearer.setDir(get_dir(mod.wearer, grabbed_atom))
+ if(grabbed_atom.loc == kinesis_catcher.given_turf)
+ if(grabbed_atom.pixel_x == kinesis_catcher.given_x - world.icon_size/2 && grabbed_atom.pixel_y == kinesis_catcher.given_y - world.icon_size/2)
+ return //spare us redrawing if we are standing still
+ animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x + kinesis_catcher.given_x - world.icon_size/2, pixel_y = pre_pixel_y + kinesis_catcher.given_y - world.icon_size/2)
+ beam.chain.Reset()
+ beam.chain.Draw()
+ return
+ animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x + kinesis_catcher.given_x - world.icon_size/2, pixel_y = pre_pixel_y + kinesis_catcher.given_y - world.icon_size/2)
+ var/turf/next_turf = get_step_towards(grabbed_atom, kinesis_catcher.given_turf)
+ if(grabbed_atom.Move(next_turf, get_dir(grabbed_atom, next_turf), 8))
+ if(isitem(grabbed_atom) && (mod.wearer in next_turf))
+ var/obj/item/grabbed_item = grabbed_atom
+ clear_grab()
+ grabbed_item.pickup(mod.wearer)
+ mod.wearer.put_in_hands(grabbed_item)
+ return
+ var/pixel_x_change = 0
+ var/pixel_y_change = 0
+ var/direction = get_dir(grabbed_atom, next_turf)
+ if(direction & NORTH)
+ pixel_y_change = world.icon_size / 2
+ else if(direction & SOUTH)
+ pixel_y_change = -world.icon_size / 2
+ if(direction & EAST)
+ pixel_x_change = world.icon_size / 2
+ else if(direction & WEST)
+ pixel_x_change = -world.icon_size / 2
+ animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x + pixel_x_change, pixel_y = pre_pixel_y + pixel_y_change) //Not as smooth as I would like, will look into this in the future
+ beam.chain.Reset()
+ beam.chain.Draw()
+ if(!isitem(grabbed_atom) || !COOLDOWN_FINISHED(src, hit_cooldown))
+ return
+ var/atom/hitting_atom
+ if(next_turf.density)
+ hitting_atom = next_turf
+ for(var/atom/movable/movable_content as anything in next_turf.contents)
+ if(ismob(movable_content))
+ continue
+ if(movable_content.density)
+ hitting_atom = movable_content
+ break
+ var/obj/item/grabbed_item = grabbed_atom
+ grabbed_item.melee_attack_chain(mod.wearer, hitting_atom)
+ COOLDOWN_START(src, hit_cooldown, hit_cooldown_time)
+
+/obj/item/mod/module/anomaly_locked/kinesis/proc/can_grab(atom/target)
+ if(mod.wearer == target)
+ return FALSE
+ if(!ismovable(target))
+ return FALSE
+ if(iseffect(target))
+ return FALSE
+ var/atom/movable/movable_target = target
+ if(movable_target.anchored)
+ return FALSE
+ if(movable_target.throwing)
+ return FALSE
+ if(movable_target.move_resist >= MOVE_FORCE_OVERPOWERING)
+ return FALSE
+ if(ismob(movable_target))
+ if(!isliving(movable_target))
+ return FALSE
+ var/mob/living/living_target = movable_target
+ if(living_target.stat < stat_required)
+ return FALSE
+ if(!living_target.incapacitated() && incapacitated_required)
+ return FALSE
+ else if(isitem(movable_target))
+ var/obj/item/item_target = movable_target
+ if(item_target.w_class >= WEIGHT_CLASS_GIGANTIC)
+ return FALSE
+ if(item_target.flags & ABSTRACT)
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/anomaly_locked/kinesis/proc/clear_grab(playsound = TRUE)
+ if(!grabbed_atom)
+ return
+ if(playsound)
+ playsound(grabbed_atom, 'sound/effects/empulse.ogg', 75, TRUE)
+ STOP_PROCESSING(SSfastprocess, src)
+ kinesis_catcher = null
+ mod.wearer.clear_fullscreen("kinesis")
+ grabbed_atom.cut_overlay(kinesis_icon)
+ QDEL_NULL(beam)
+ if(!isitem(grabbed_atom))
+ animate(grabbed_atom, 0.2 SECONDS, pixel_x = pre_pixel_x, pixel_y = pre_pixel_y)
+ grabbed_atom = null
+ soundloop.stop()
+
+/obj/item/mod/module/anomaly_locked/kinesis/proc/range_check(atom/target)
+ if(!isturf(mod.wearer.loc))
+ return FALSE
+ if(ismovable(target) && !isturf(target.loc))
+ return FALSE
+ if(!can_see(mod.wearer, target, grab_range))
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/anomaly_locked/kinesis/proc/launch()
+ playsound(grabbed_atom, 'sound/magic/repulse.ogg', 100, TRUE)
+ RegisterSignal(grabbed_atom, COMSIG_MOVABLE_IMPACT, PROC_REF(launch_impact))
+ var/turf/target_turf = get_turf_in_angle(get_angle(mod.wearer, grabbed_atom), get_turf(src), 10)
+ grabbed_atom.throw_at(target_turf, range = grab_range, speed = grabbed_atom.density ? 3 : 4, thrower = mod.wearer, spin = isitem(grabbed_atom))
+
+/obj/item/mod/module/anomaly_locked/kinesis/proc/launch_impact(atom/movable/source, atom/hit_atom, datum/thrownthing/thrownthing)
+ UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
+ if(!isobj(source))
+ return
+ var/obj/S = source
+ var/damage_self = TRUE
+ var/damage = 8
+ if(S.density)
+ damage_self = FALSE
+ damage = 15
+ if(isliving(hit_atom))
+ var/mob/living/living_atom = hit_atom
+ living_atom.apply_damage(damage, BRUTE)
+ else if(isobj(hit_atom))
+ var/obj/O = hit_atom
+ O.take_damage(damage, BRUTE, MELEE)
+ if(damage_self)
+ S.take_damage(S.max_integrity / 5, BRUTE, MELEE)
+
+/obj/effect/abstract/kinesis
+ var/datum/beam/chain
+
+/obj/effect/abstract/kinesis/Destroy()
+ qdel(chain)
+ return ..()
+
+/obj/item/mod/module/anomaly_locked/kinesis/prebuilt
+ prebuilt = TRUE
+ removable = FALSE // No switching it into another suit / no free anomaly core
+
+/obj/item/mod/module/anomaly_locked/kinesis/prebuilt/prototype
+ name = "MOD prototype kinesis module"
+ complexity = 0
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+
+/obj/screen/fullscreen/cursor_catcher/kinesis
+ icon_state = "kinesis"
+
+/obj/item/mod/module/anomaly_locked/kinesis/plus
+ name = "MOD kinesis+ module"
+ desc = "A modular plug-in to the forearm, this module was recently redeveloped in secret. \
+ The bane of all ne'er-do-wells, the kinesis+ module is a powerful tool that allows the user \
+ to manipulate the world around them. Like it's older counterpart, it's capable of manipulating \
+ structures, machinery, vehicles, and, thanks to the fruitful efforts of it's creators - living \
+ beings. They can, however, still struggle after an initial burst of inertia."
+ complexity = 0
+ prebuilt = TRUE
+ stat_required = CONSCIOUS //Still conscious here so we don't forget about it if the above is changed
+ incapacitated_required = FALSE
+ mob_stun_time = 10 SECONDS
diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm
new file mode 100644
index 000000000000..4379c6d6446d
--- /dev/null
+++ b/code/modules/mod/modules/module_pathfinder.dm
@@ -0,0 +1,238 @@
+///Pathfinder - Can fly the suit from a long distance to an implant installed in someone.
+/obj/item/mod/module/pathfinder
+ name = "MOD pathfinder module"
+ desc = "This module, brought to you by Paizo Productions, has two components. \
+ The first component is a series of thrusters and a computerized location subroutine installed into the \
+ very control unit of the suit, allowing it flight at highway speeds using the suit's access locks \
+ to navigate through the station, and to be able to locate the second part of the system; \
+ a pathfinding implant installed into the base of the user's spine, \
+ broadcasting their location to the suit and allowing them to recall it to their person at any time. \
+ The implant is stored in the module and needs to be injected in a human to function. \
+ Paizo Productions swears up and down there's airbrakes."
+ icon_state = "pathfinder"
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 200
+ incompatible_modules = list(/obj/item/mod/module/pathfinder)
+ /// The pathfinding implant.
+ var/obj/item/implant/mod/implant
+
+/obj/item/mod/module/pathfinder/Initialize(mapload)
+ . = ..()
+ implant = new(src)
+
+/obj/item/mod/module/pathfinder/Destroy()
+ implant = null
+ return ..()
+
+/obj/item/mod/module/pathfinder/examine(mob/user)
+ . = ..()
+ if(implant)
+ . += "Use it on a human to implant them."
+ else
+ . += "The implant is missing."
+
+/obj/item/mod/module/pathfinder/attack(mob/living/target, mob/living/user, params)
+ if(!ishuman(target) || !implant)
+ return
+ if(!do_after(user, 1.5 SECONDS, target = target))
+ return
+ if(!implant.implant(target, user))
+ to_chat(user, "Unable to implant [target]!")
+ return
+ if(target == user)
+ to_chat(user, "")
+ else
+ target.visible_message("[user] implants [target].", "[user] implants you with [implant].")
+ playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
+ icon_state = "pathfinder_empty"
+ implant = null
+
+/obj/item/mod/module/pathfinder/proc/attach(mob/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/human_user = user
+ if(human_user.get_item_by_slot(slot_back) && !human_user.unEquip(human_user.get_item_by_slot(slot_back)))
+ return
+ if(!human_user.equip_to_slot_if_possible(mod, slot_back, disable_warning = TRUE))
+ return
+ mod.quick_deploy(user)
+ human_user.update_action_buttons(TRUE)
+ playsound(mod, 'sound/machines/ping.ogg', 50, TRUE)
+ drain_power(use_power_cost)
+
+/obj/item/implant/mod
+ name = "MOD pathfinder implant"
+ desc = "Lets you recall a MODsuit to you at any time."
+ implant_data = /datum/implant_fluff/pathfinder
+ actions_types = list(/datum/action/item_action/mod_recall)
+ /// The pathfinder module we are linked to.
+ var/obj/item/mod/module/pathfinder/module
+ /// The jet icon we apply to the MOD.
+ var/image/jet_icon
+ /// List of turfs through which a mod 'steps' to reach the waypoint
+ var/list/path = list()
+ /// The target turf we are after
+ var/turf/target
+ /// How many times have we tried to move?
+ var/tries = 0
+
+
+/obj/item/implant/mod/Initialize(mapload)
+ . = ..()
+ if(!istype(loc, /obj/item/mod/module/pathfinder))
+ return INITIALIZE_HINT_QDEL
+ module = loc
+ jet_icon = image(icon = 'icons/obj/clothing/modsuit/mod_modules.dmi', icon_state = "mod_jet", layer = LOW_ITEM_LAYER)
+
+/obj/item/implant/mod/Destroy()
+ if(path)
+ end_recall(successful = FALSE)
+ module = null
+ jet_icon = null
+ return ..()
+
+/obj/item/implant/mod/proc/recall()
+ target = get_turf(imp_in)
+ if(!module?.mod)
+ to_chat(imp_in, "Module is not attached to a suit!")
+ return FALSE
+ if(module.mod.open)
+ to_chat(imp_in, "Suit is open!")
+ return FALSE
+ if(length(path))
+ to_chat(imp_in, "Suit is already on the way!")
+ return FALSE
+ if(ismob(get_atom_on_turf(module.mod)))
+ to_chat(imp_in, "Suit is being worn!")
+ return FALSE
+ if(module.mod.loc != get_turf(module.mod))
+ to_chat(imp_in, "Suit contained inside of something!")
+ return FALSE
+ if(module.z != z || get_dist(imp_in, module.mod) > 150)
+ to_chat(imp_in, "Suit is too far away!")
+ return FALSE
+ if(!ishuman(imp_in)) //Need to be specific
+ to_chat(imp_in, "The implant does not recognize you as a known species!")
+ return FALSE
+ var/mob/living/carbon/human/H = imp_in
+ set_path(get_path_to(module.mod, target, 150, id = H.wear_id, simulated_only = FALSE)) //Yes, science proves jetpacks work in space. More at 11.
+ if(!length(path)) //Cannot reach target. Give up and announce the issue.
+ to_chat(H, "No viable path found!")
+ return FALSE
+ to_chat(H, "Lost connection to suit!")
+ path = list() //Stopping endless end_recall with luck.
+
+/obj/item/implant/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
+ SIGNAL_HANDLER
+
+ var/matrix/mod_matrix = matrix()
+ mod_matrix.Turn(get_angle(source, imp_in))
+ source.transform = mod_matrix
+
+/obj/item/implant/mod/proc/mod_move(dest)
+ dest = get_turf(dest) //We must always compare turfs, so get the turf of the dest var if dest was originally something else.
+ if(get_turf(module.mod) == dest) //We have arrived, no need to move again.
+ for(var/mob/living/carbon/human/H in range(1, module.mod))
+ if(H == imp_in)
+ module.attach(imp_in)
+ end_recall()
+ return TRUE
+ end_recall(FALSE)
+ return FALSE
+
+
+ if(!dest || !path || !length(path)) //A-star failed or a path/destination was not set.
+ set_path(null)
+ return FALSE
+
+ var/turf/last_node = get_turf(path[length(path)]) //This is the turf at the end of the path, it should be equal to dest.
+ if(dest != last_node) //The path should lead us to our given destination. If this is not true, we must stop.
+ set_path(null)
+ return FALSE
+
+ var/step_count = 3 //Temp speed for now
+
+ if(step_count >= 1 && tries < 5)
+ for(var/step_number in 1 to step_count)
+ // Hopefully this wont fill the buckets too much
+ addtimer(CALLBACK(src, PROC_REF(mod_step)), 2 * (step_number - 1))
+ if(tries >= 5)
+ set_path(null)
+ var/target = get_turf(imp_in)
+ var/mob/living/carbon/human/H = imp_in
+ set_path(get_path_to(module.mod, target, 150, id = H.wear_id, simulated_only = FALSE)) //Yes, science proves jetpacks work in space. More at 11.
+ addtimer(CALLBACK(src, PROC_REF(mod_move), target), 6) //I'll value this properly soon
+
+ return TRUE
+
+/obj/item/implant/mod/proc/mod_step() //Step,increase tries if failed
+ if(!path || !length(path))
+ return FALSE
+ for(var/obj/machinery/door/D in range(2, module.mod))
+ if(D.operating || D.emagged)
+ continue
+ if(D.requiresID() && D.allowed(imp_in))
+ if(D.density)
+ D.open()
+
+
+ if(!step_towards(module.mod, path[1]))
+ tries++
+ return FALSE
+
+ increment_path()
+ tries = 0
+ return TRUE
+
+/obj/item/implant/mod/proc/increment_path()
+ if(!path || !length(path))
+ return
+ path.Cut(1, 2)
+
+/datum/action/item_action/mod_recall
+ name = "Recall MOD"
+ desc = "Recall a MODsuit anyplace, anytime."
+ use_itemicon = FALSE
+ check_flags = AB_CHECK_CONSCIOUS
+ button_icon_state = "recall"
+ background_icon_state = "bg_mod"
+ icon_icon = 'icons/mob/actions/actions_mod.dmi'
+ button_icon = 'icons/mob/actions/actions_mod.dmi'
+ /// The cooldown for the recall.
+ COOLDOWN_DECLARE(recall_cooldown)
+
+/datum/action/item_action/mod_recall/New(Target)
+ ..()
+ if(!istype(Target, /obj/item/implant/mod))
+ qdel(src)
+ return
+
+/datum/action/item_action/mod_recall/Trigger(left_click)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/implant/mod/implant = target
+ if(!COOLDOWN_FINISHED(src, recall_cooldown))
+ to_chat(usr, "On cooldown!")
+ return
+ if(implant.recall())
+ COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
new file mode 100644
index 000000000000..9d19cc686c90
--- /dev/null
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -0,0 +1,408 @@
+//Antag modules for MODsuits
+
+///Armor Booster - Grants your suit more armor and speed in exchange for EVA protection. Also acts as a welding screen.
+/obj/item/mod/module/armor_booster
+ name = "MOD armor booster module"
+ desc = "A retrofitted series of retractable armor plates, allowing the suit to function as essentially power armor, \
+ giving the user incredible protection against conventional firearms, or everyday attacks in close-quarters. \
+ However, the additional plating cannot deploy alongside parts of the suit used for vacuum sealing, \
+ so this extra armor provides zero ability for extravehicular activity while deployed."
+ icon_state = "armor_booster"
+ module_type = MODULE_TOGGLE
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/armor_booster, /obj/item/mod/module/welding)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_armorbooster_off"
+ overlay_state_active = "module_armorbooster_on"
+ use_mod_colors = TRUE
+ /// Whether or not this module removes pressure protection.
+ var/remove_pressure_protection = TRUE
+ /// Speed added to the control unit.
+ var/speed_added = 0.5
+ /// Speed that we actually added.
+ var/actual_speed_added = 0
+ /// Armor values added to the suit parts.
+ var/armor_mod_1 = /obj/item/mod/armor/mod_module_armor_boost
+ /// the actual armor object
+ var/obj/item/mod/armor/armor_mod_2 = null
+ /// List of parts of the suit that are spaceproofed, for giving them back the pressure protection.
+ var/list/spaceproofed = list()
+
+/obj/item/mod/module/armor_booster/Initialize(mapload)
+ . = ..()
+ armor_mod_2 = new armor_mod_1
+
+/obj/item/mod/module/armor_booster/Destroy()
+ QDEL_NULL(armor_mod_2)
+ return ..()
+
+/obj/item/mod/armor/mod_module_armor_boost
+ armor = list(MELEE = 25, BULLET = 30, LASER = 15, ENERGY = 15, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0)
+
+/obj/item/mod/module/armor_booster/on_suit_activation()
+ mod.helmet.flash_protect = FLASH_PROTECTION_WELDER
+
+/obj/item/mod/module/armor_booster/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
+
+/obj/item/mod/module/armor_booster/on_activation()
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ to_chat(mod.wearer, "Armor deployed, EVA disabled, speed increased.")
+ actual_speed_added = max(0, min(mod.slowdown_active, speed_added / 5))
+ var/list/parts = mod.mod_parts + mod
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.attachArmor(armor_mod_2.armor)
+ part.slowdown -= actual_speed_added
+ if(!remove_pressure_protection || !isclothing(part))
+ continue
+ var/obj/item/clothing/clothing_part = part
+ if(clothing_part.flags & STOPSPRESSUREDMAGE)
+ clothing_part.flags &= ~STOPSPRESSUREDMAGE
+ spaceproofed[clothing_part] = TRUE
+
+/obj/item/mod/module/armor_booster/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(!deleting)
+ playsound(src, 'sound/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ to_chat(mod.wearer, "Armor retracted, EVA enabled, speed decreased.")
+ var/list/parts = mod.mod_parts + mod
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.detachArmor(armor_mod_2.armor)
+ part.slowdown += actual_speed_added
+ if(!remove_pressure_protection || !isclothing(part))
+ continue
+ var/obj/item/clothing/clothing_part = part
+ if(spaceproofed[clothing_part])
+ clothing_part.flags |= STOPSPRESSUREDMAGE
+ spaceproofed = list()
+
+/obj/item/mod/module/armor_booster/generate_worn_overlay(user, mutable_appearance/standing)
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]"
+ return ..()
+
+///Insignia - Gives you a skin specific stripe.
+/obj/item/mod/module/insignia
+ name = "MOD insignia module"
+ desc = "Despite the existence of IFF systems, radio communique, and modern methods of deductive reasoning involving \
+ the wearer's own eyes, colorful paint jobs remain a popular way for different factions in the galaxy to display who \
+ they are. This system utilizes a series of tiny moving paint sprayers to both apply and remove different \
+ color patterns to and from the suit."
+ icon_state = "insignia"
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/insignia)
+ overlay_state_inactive = "module_insignia"
+
+/obj/item/mod/module/insignia/generate_worn_overlay(user, mutable_appearance/standing)
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ . = ..()
+ for(var/mutable_appearance/appearance as anything in .)
+ appearance.color = color
+
+/obj/item/mod/module/insignia/commander
+ color = "#4980a5"
+
+/obj/item/mod/module/insignia/security
+ color = "#b30d1e"
+
+/obj/item/mod/module/insignia/engineer
+ color = "#e9c80e"
+
+/obj/item/mod/module/insignia/medic
+ color = "#ebebf5"
+
+/obj/item/mod/module/insignia/janitor
+ color = "#7925c7"
+
+/obj/item/mod/module/insignia/clown
+ color = "#ff1fc7"
+
+/obj/item/mod/module/insignia/chaplain
+ color = "#f0a00c"
+
+///Anti Slip - Prevents you from slipping on water.
+/obj/item/mod/module/noslip
+ name = "MOD anti slip module"
+ desc = "These are a modified variant of standard magnetic boots, utilizing piezoelectric crystals on the soles. \
+ The two plates on the bottom of the boots automatically extend and magnetize as the user steps; \
+ a pull that's too weak to offer them the ability to affix to a hull, but just strong enough to \
+ protect against the fact that you didn't read the wet floor sign. Honk Co. has come out numerous times \
+ in protest of these modules being legal."
+ icon_state = "noslip"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1
+ incompatible_modules = list(/obj/item/mod/module/noslip)
+ origin_tech = "syndicate=1"
+
+/obj/item/mod/module/noslip/on_suit_activation()
+ mod.boots.flags |= NOSLIP
+
+/obj/item/mod/module/noslip/on_suit_deactivation(deleting = FALSE)
+ mod.boots.flags ^= NOSLIP
+
+//Bite of 87 Springlock - Equips faster, disguised as DNA lock, can block retracting for 10 seconds.
+/obj/item/mod/module/springlock/bite_of_87
+ activation_step_time_booster = 10
+ nineteen_eighty_seven_edition = TRUE
+ dont_let_you_come_back = TRUE
+
+/obj/item/mod/module/springlock/bite_of_87/Initialize(mapload)
+ . = ..()
+ var/obj/item/mod/module/dna_lock/the_dna_lock_behind_the_slaughter = /obj/item/mod/module/dna_lock
+ name = initial(the_dna_lock_behind_the_slaughter.name)
+ desc = initial(the_dna_lock_behind_the_slaughter.desc)
+ icon_state = initial(the_dna_lock_behind_the_slaughter.icon_state)
+ complexity = initial(the_dna_lock_behind_the_slaughter.complexity)
+ use_power_cost = initial(the_dna_lock_behind_the_slaughter.use_power_cost)
+
+/obj/item/mod/module/holster/hidden/Initialize(mapload)
+ . = ..()
+ var/obj/item/mod/module/tether/fake = /obj/item/mod/module/tether
+ name = initial(fake.name)
+ desc = initial(fake.desc)
+ icon_state = initial(fake.icon_state)
+ complexity = initial(fake.complexity) //This is 1 less complex than a holster, but that is fine tbh, paying tc for it.
+ use_power_cost = initial(fake.use_power_cost)
+
+///Power kick - Lets the user launch themselves at someone to kick them.
+/obj/item/mod/module/power_kick
+ name = "MOD power kick module"
+ desc = "This module uses high-power myomer to generate an incredible amount of energy, transferred into the power of a kick."
+ icon_state = "power_kick"
+ module_type = MODULE_ACTIVE
+ removable = FALSE
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+ incompatible_modules = list(/obj/item/mod/module/power_kick)
+ cooldown_time = 5 SECONDS
+ /// Damage on kick.
+ var/damage = 20
+ /// How long we knockdown for on the kick.
+ var/knockdown_time = 6 SECONDS
+
+/obj/item/mod/module/power_kick/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(mod.wearer.buckled)
+ return
+ mod.wearer.visible_message("[mod.wearer] starts charging a kick!")
+ playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE)
+ animate(mod.wearer, 0.3 SECONDS, pixel_z = 16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_OUT)
+ addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/atom, SpinAnimation), 3, 2), 0.3 SECONDS)
+ if(!do_after(mod.wearer, 1 SECONDS, target = mod.wearer))
+ animate(mod.wearer, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_IN)
+ return
+ animate(mod.wearer)
+ drain_power(use_power_cost)
+ playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE)
+ var/angle = get_angle(mod.wearer, target) + 180
+ mod.wearer.transform = mod.wearer.transform.Turn(angle)
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_IMPACT, PROC_REF(on_throw_impact))
+ mod.wearer.apply_status_effect(STATUS_EFFECT_IMPACT_IMMUNE)
+ mod.wearer.throw_at(target, range = 7, speed = 2, thrower = mod.wearer, spin = FALSE, callback = CALLBACK(src, PROC_REF(on_throw_end), mod.wearer, -angle))
+
+/obj/item/mod/module/power_kick/proc/on_throw_end(mob/living/user, angle)
+ if(!user)
+ return
+ user.transform = user.transform.Turn(angle)
+ animate(user, 0.2 SECONDS, pixel_z = -16, flags = ANIMATION_RELATIVE, easing = SINE_EASING|EASE_IN)
+ user.remove_status_effect((STATUS_EFFECT_IMPACT_IMMUNE))
+
+/obj/item/mod/module/power_kick/proc/on_throw_impact(mob/living/source, atom/target, datum/thrownthing/thrownthing)
+ SIGNAL_HANDLER
+
+ UnregisterSignal(source, COMSIG_MOVABLE_IMPACT)
+ if(!mod?.wearer)
+ return
+ if(isliving(target))
+ var/mob/living/living_target = target
+ living_target.apply_damage(damage, BRUTE, mod.wearer.zone_selected)
+ living_target.KnockDown(knockdown_time)
+ mod.wearer.visible_message("[mod.wearer] crashes into [target], knocking them over!", "You violently crash into [target]!")
+ else
+ return
+ mod.wearer.do_attack_animation(target, ATTACK_EFFECT_SMASH)
+
+///Plate Compression - Compresses the suit to normal size
+/obj/item/mod/module/plate_compression
+ name = "MOD plate compression module"
+ desc = "A module that keeps the suit in a very tightly fit state, lowering the overall size. \
+ Due to the pressure on all the parts, typical storage modules do not fit."
+ icon_state = "plate_compression"
+ complexity = 2
+ incompatible_modules = list(/obj/item/mod/module/plate_compression, /obj/item/mod/module/storage)
+ /// The size we set the suit to.
+ var/new_size = WEIGHT_CLASS_NORMAL
+ /// The suit's size before the module is installed.
+ var/old_size
+ origin_tech = "materials=6;bluespace=5;syndicate=1" //Printable at illegals 2, so only one level.
+
+/obj/item/mod/module/plate_compression/on_install()
+ old_size = mod.w_class
+ mod.w_class = new_size
+
+/obj/item/mod/module/plate_compression/on_uninstall(deleting = FALSE)
+ mod.w_class = old_size
+ old_size = null
+ if(!mod.loc)
+ return
+ mod.forceMove(drop_location())
+
+
+//Ninja modules for MODsuits
+
+///Cloaking - Lowers the user's visibility, can be interrupted by being touched or attacked.
+/obj/item/mod/module/stealth
+ name = "MOD prototype cloaking module"
+ desc = "A complete retrofitting of the suit, this is a form of visual concealment tech employing esoteric technology \
+ to bend light around the user, as well as mimetic materials to make the surface of the suit match the \
+ surroundings based off sensor data. For some reason, this tech is rarely seen."
+ icon_state = "cloak"
+ module_type = MODULE_TOGGLE
+ complexity = 4
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 10
+ incompatible_modules = list(/obj/item/mod/module/stealth)
+ cooldown_time = 5 SECONDS
+ origin_tech = "combat=6;materials=6;powerstorage=5;bluespace=5;syndicate=2" //Printable at 3
+ /// Whether or not the cloak turns off on bumping.
+ var/bumpoff = TRUE
+ /// The alpha applied when the cloak is on.
+ var/stealth_alpha = 50
+
+/obj/item/mod/module/stealth/on_activation()
+ . = ..()
+ if(!.)
+ return
+ if(bumpoff)
+ RegisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP, PROC_REF(unstealth))
+ RegisterSignal(mod.wearer, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, PROC_REF(on_unarmed_attack))
+ RegisterSignal(mod.wearer, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) //TODO QWERTY: A LOT OF THESE SIGNALS AINT TRIGGERING. or at least this one.
+ RegisterSignals(mod.wearer, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW), PROC_REF(unstealth))
+ animate(mod.wearer, alpha = stealth_alpha, time = 1.5 SECONDS)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/stealth/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(bumpoff)
+ UnregisterSignal(mod.wearer, COMSIG_LIVING_MOB_BUMP)
+ UnregisterSignal(mod.wearer, list(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ITEM_ATTACK, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_HITBY, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATOM_ATTACK_PAW))
+ animate(mod.wearer, alpha = 255, time = 1.5 SECONDS)
+
+/obj/item/mod/module/stealth/proc/unstealth(datum/source)
+ SIGNAL_HANDLER
+
+ to_chat(mod.wearer, "[src] gets discharged from contact!")
+ do_sparks(2, TRUE, src)
+ drain_power(use_power_cost)
+ on_deactivation(display_message = TRUE, deleting = FALSE)
+
+/obj/item/mod/module/stealth/proc/on_unarmed_attack(datum/source, atom/target)
+ SIGNAL_HANDLER
+
+ if(!isliving(target))
+ return
+ unstealth(source)
+
+/obj/item/mod/module/stealth/proc/on_bullet_act(datum/source, obj/item/projectile)
+ SIGNAL_HANDLER
+ unstealth(source)
+
+//Advanced Cloaking - Doesn't turf off on bump, less power drain, more stealthy.
+/obj/item/mod/module/stealth/ninja
+ name = "MOD advanced cloaking module"
+ desc = "The latest in stealth technology, this module is a definite upgrade over previous versions. \
+ The field has been tuned to be even more responsive and fast-acting, with enough stability to \
+ continue operation of the field even if the user bumps into others. \
+ The power draw has been reduced drastically, making this perfect for activities like \
+ standing near sentry turrets for extended periods of time."
+ icon_state = "cloak_ninja"
+ bumpoff = FALSE
+ stealth_alpha = 10
+ active_power_cost = DEFAULT_CHARGE_DRAIN
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+ cooldown_time = 3 SECONDS
+ origin_tech = "combat=6;materials=6;powerstorage=6;bluespace=6;syndicate=4"
+
+///Status Readout - Puts a lot of information including health, nutrition, fingerprints, temperature to the suit TGUI.
+/obj/item/mod/module/status_readout
+ name = "MOD status readout module"
+ desc = "A once-common module, this technology went unfortunately out of fashion; \
+ and right into the arachnid grip of the Spider Clan. This hooks into the suit's spine, \
+ capable of capturing and displaying all possible biometric data of the wearer; sleep, nutrition, fitness, fingerprints, \
+ and even useful information such as their overall health and wellness. \
+ The syndicate has been seen using this module of late, with NT as well getting into the technology on their elitest of suits."
+ icon_state = "status"
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.1
+ incompatible_modules = list(/obj/item/mod/module/status_readout)
+ tgui_id = "status_readout"
+ origin_tech = "combat=6;biotech=6;syndicate=1"
+
+/obj/item/mod/module/status_readout/add_ui_data()
+ . = ..()
+ .["statustime"] = station_time_timestamp()
+ .["statusid"] = GLOB.round_id
+ .["statushealth"] = mod.wearer?.health || 0
+ .["statusmaxhealth"] = mod.wearer?.getMaxHealth() || 0
+ .["statusbrute"] = mod.wearer?.getBruteLoss() || 0
+ .["statusburn"] = mod.wearer?.getFireLoss() || 0
+ .["statustoxin"] = mod.wearer?.getToxLoss() || 0
+ .["statusoxy"] = mod.wearer?.getOxyLoss() || 0
+ .["statustemp"] = mod.wearer?.bodytemperature || 0
+ .["statusnutrition"] = mod.wearer?.nutrition || 0
+ .["statusfingerprints"] = mod.wearer ? md5(mod.wearer.dna.unique_enzymes) : null
+ .["statusdna"] = mod.wearer?.dna.unique_enzymes
+ .["statusviruses"] = null
+ if(!length(mod.wearer?.viruses))
+ return
+ var/list/viruses = list()
+ for(var/datum/disease/virus as anything in mod.wearer.viruses)
+ var/list/virus_data = list()
+ virus_data["name"] = virus.name
+ virus_data["type"] = virus.spread_text
+ virus_data["stage"] = virus.stage
+ virus_data["maxstage"] = virus.max_stages
+ virus_data["cure"] = virus.cure_text
+ viruses += list(virus_data)
+ .["statusviruses"] = viruses
+
+///Camera Module - Puts a camera in the modsuit that the ERT commander can see
+/obj/item/mod/module/ert_camera
+ name = "MOD camera module"
+ desc = "This combination camera and broadcasting module grants the modsuit a camera that tracks what the user see, and sends it to the nearest station and \
+ CC blackbox. This is used for ERT commander tracking, performance review, Nanotrasen's Funniest Home Videos, \
+ and used for reference for their Deathsquad Cartoon Series."
+ icon_state = "eradicationlock" //looks like a bluespace transmitter or something, probably could use an actual camera look.
+ complexity = 1
+ incompatible_modules = list(/obj/item/mod/module/ert_camera)
+ var/obj/machinery/camera/portable/camera
+
+/obj/item/mod/module/ert_camera/on_suit_activation()
+ if(ishuman(mod.wearer))
+ register_camera(mod.wearer)
+
+/obj/item/mod/module/ert_camera/proc/register_camera(mob/wearer)
+ if(camera)
+ return
+ camera = new /obj/machinery/camera/portable(src, FALSE)
+ camera.network = list("ERT")
+ camera.c_tag = wearer.name
+ to_chat(wearer, "User scanned as [camera.c_tag]. Camera activated.")
+
+/obj/item/mod/module/ert_camera/Destroy()
+ QDEL_NULL(camera)
+ return ..()
+
+/obj/item/mod/module/ert_camera/on_suit_deactivation(deleting = FALSE)
+ QDEL_NULL(camera)
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
new file mode 100644
index 000000000000..87d079af7e9c
--- /dev/null
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -0,0 +1,153 @@
+//Engineering modules for MODsuits
+
+///Welding Protection - Makes the helmet protect from flashes and welding.
+/obj/item/mod/module/welding
+ name = "MOD welding protection module"
+ desc = "A module installed into the visor of the suit, this projects a \
+ polarized, holographic overlay in front of the user's eyes. It's rated high enough for \
+ immunity against extremities such as spot and arc welding, solar eclipses, and handheld flashlights."
+ icon_state = "welding"
+ complexity = 1
+ incompatible_modules = list(/obj/item/mod/module/welding, /obj/item/mod/module/armor_booster)
+ overlay_state_inactive = "module_welding"
+
+/obj/item/mod/module/welding/on_suit_activation()
+ mod.helmet.flash_protect = FLASH_PROTECTION_WELDER
+
+/obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ mod.helmet.flash_protect = initial(mod.helmet.flash_protect)
+
+///T-Ray Scan - Scans the terrain for undertile objects.
+/obj/item/mod/module/t_ray
+ name = "MOD t-ray scan module"
+ desc = "A module installed into the visor of the suit, allowing the user to use a pulse of terahertz radiation \
+ to essentially echolocate things beneath the floor, mostly cables and pipes. \
+ A staple of atmospherics work, and counter-smuggling work."
+ icon_state = "tray"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/t_ray)
+ cooldown_time = 0.5 SECONDS
+ /// T-ray scan range.
+ var/range = 4
+
+/obj/item/mod/module/t_ray/on_active_process()
+ t_ray_scan(mod.wearer, 0.8 SECONDS, range)
+
+///Magnetic Stability - Gives the user a slowdown but makes them negate gravity and be immune to slips.
+/obj/item/mod/module/magboot
+ name = "MOD magnetic stability module"
+ desc = "These are powerful electromagnets fitted into the suit's boots, allowing users both \
+ excellent traction no matter the condition indoors, and to essentially hitch a ride on the exterior of a hull. \
+ However, these basic models do not feature computerized systems to automatically toggle them on and off, \
+ so numerous users report a certain stickiness to their steps."
+ icon_state = "magnet"
+ module_type = MODULE_TOGGLE
+ complexity = 2
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/magboot)
+ cooldown_time = 0.5 SECONDS
+ /// Slowdown added onto the suit.
+ var/slowdown_active = 0.5
+
+/obj/item/mod/module/magboot/on_activation()
+ . = ..()
+ if(!.)
+ return
+ mod.boots.flags |= NOSLIP
+ mod.slowdown += slowdown_active
+ mod.boots.magbooted = TRUE
+
+/obj/item/mod/module/magboot/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ mod.boots.flags ^= NOSLIP
+ mod.slowdown -= slowdown_active
+ mod.boots.magbooted = FALSE
+
+/obj/item/mod/module/magboot/advanced
+ name = "MOD advanced magnetic stability module"
+ removable = FALSE
+ complexity = 0
+ slowdown_active = 0
+
+///Radiation Protection - Gives the user rad info in the ui, currently
+/obj/item/mod/module/rad_protection
+ name = "MOD radiation detector module"
+ desc = "A protoype module that improves the sensors on the modsuit to detect radiation on the user. \
+ Currently due to time restraints and a lack of lead on lavaland, it does not have a built in geiger counter or radiation protection."
+ icon_state = "radshield"
+ complexity = 0 //I'm setting this to zero for now due to it not currently increasing radiaiton armor. If we add giger counter / additional rad protecion to this, it should be 2. We denied radiation potions before, so this should NOT give full rad immunity on a engi modsuit
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.1 //Lowered from 0.3 due to no protection.
+ incompatible_modules = list(/obj/item/mod/module/rad_protection)
+ tgui_id = "rad_counter"
+
+/obj/item/mod/module/rad_protection/add_ui_data()
+ . = ..()
+ .["userradiated"] = mod.wearer?.radiation || 0
+ .["usertoxins"] = mod.wearer?.getToxLoss() || 0
+ .["usermaxtoxins"] = mod.wearer?.getMaxHealth() || 0
+
+
+///Emergency Tether - Shoots a grappling hook projectile in 0g that throws the user towards it.
+/obj/item/mod/module/tether
+ name = "MOD emergency tether module"
+ desc = "A custom-built grappling-hook powered by a winch capable of hauling the user. \
+ While some older models of cargo-oriented grapples have capacities of a few tons, \
+ these are only capable of working in zero-gravity environments, a blessing to some Engineers."
+ icon_state = "tether"
+ module_type = MODULE_ACTIVE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/tether)
+ cooldown_time = 4 SECONDS
+
+/obj/item/mod/module/tether/on_use()
+ if(has_gravity(get_turf(src)))
+ to_chat(mod.wearer, "Too much gravity to use the tether!")
+ playsound(src, 'sound/weapons/gun_interactions/dry_fire.ogg', 25, TRUE)
+ return FALSE
+ return ..()
+
+/obj/item/mod/module/tether/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/projectile/tether = new /obj/item/projectile/tether(get_turf(mod.wearer))
+ tether.original = target
+ tether.firer = mod.wearer
+ tether.preparePixelProjectile(target, get_turf(target), mod.wearer)
+ tether.fire()
+ playsound(src, 'sound/weapons/batonextend.ogg', 25, TRUE)
+ INVOKE_ASYNC(tether, TYPE_PROC_REF(/obj/item/projectile/tether, make_chain))
+ drain_power(use_power_cost)
+
+/obj/item/projectile/tether
+ name = "tether"
+ icon_state = "tether_projectile"
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ speed = 2
+ damage = 5
+ range = 15
+ hitsound = 'sound/weapons/batonextend.ogg'
+ hitsound_wall = 'sound/weapons/batonextend.ogg'
+
+/obj/item/projectile/tether/proc/make_chain()
+ if(firer)
+ chain = Beam(firer, icon_state = "line", icon = 'icons/obj/clothing/modsuit/mod_modules.dmi', time = 10 SECONDS, maxdistance = 15)
+
+/obj/item/projectile/tether/on_hit(atom/target)
+ . = ..()
+ if(firer && isliving(firer))
+ var/mob/living/L = firer
+ L.apply_status_effect(STATUS_EFFECT_IMPACT_IMMUNE)
+ L.throw_at(target, 15, 1, L, FALSE, FALSE, callback = CALLBACK(L, TYPE_PROC_REF(/mob/living, remove_status_effect), STATUS_EFFECT_IMPACT_IMMUNE))
+
+/obj/item/projectile/tether/Destroy()
+ QDEL_NULL(chain)
+ return ..()
+
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
new file mode 100644
index 000000000000..e9c216d2ca06
--- /dev/null
+++ b/code/modules/mod/modules/modules_general.dm
@@ -0,0 +1,441 @@
+//General modules for MODsuits
+
+///Storage - Adds a storage component to the suit.
+/obj/item/mod/module/storage
+ name = "MOD storage module"
+ desc = "What amounts to a series of integrated storage compartments and specialized pockets installed across \
+ the surface of the suit, useful for storing various bits, and or bobs."
+ icon_state = "storage"
+ complexity = 3
+ incompatible_modules = list(/obj/item/mod/module/storage, /obj/item/mod/module/plate_compression)
+ /// Max weight class of items in the storage.
+ var/max_w_class = WEIGHT_CLASS_NORMAL
+ /// Max combined weight of all items in the storage.
+ var/max_combined_w_class = 15
+ /// Max amount of items in the storage.
+ var/max_items = 7
+ var/obj/item/storage/backpack/modstorage/bag
+
+/obj/item/mod/module/storage/Initialize(mapload)
+ . = ..()
+ var/obj/item/storage/backpack/modstorage/S = new(src)
+ bag = S
+ bag.max_w_class = max_w_class
+ bag.max_combined_w_class = max_combined_w_class
+ bag.storage_slots = max_items
+ bag.source = src
+
+/obj/item/mod/module/storage/Destroy()
+ QDEL_NULL(bag)
+ return ..()
+
+
+/obj/item/mod/module/storage/on_install()
+ mod.bag = bag
+ bag.forceMove(mod)
+
+/obj/item/mod/module/storage/on_uninstall(deleting = FALSE)
+ if(!deleting)
+ for(var/obj/I in bag.contents)
+ I.forceMove(get_turf(loc))
+ bag.forceMove(src)
+ mod.bag = null
+ return
+ qdel(bag)
+ UnregisterSignal(mod.chestplate, COMSIG_ITEM_PRE_UNEQUIP)
+
+/obj/item/mod/module/storage/on_suit_deactivation(deleting)
+ . = ..()
+ bag.forceMove(src) //So the pinpointer doesnt lie.
+
+/obj/item/mod/module/storage/on_unequip()
+ . = ..()
+ bag.forceMove(src)
+
+/obj/item/mod/module/storage/large_capacity
+ name = "MOD expanded storage module"
+ desc = "Reverse engineered by Cybersun Industries from Donk Corporation designs, this system of hidden compartments \
+ is entirely within the suit, distributing items and weight evenly to ensure a comfortable experience for the user; \
+ whether smuggling, or simply hauling."
+ icon_state = "storage_large"
+ max_combined_w_class = 21
+ max_items = 14
+
+/obj/item/mod/module/storage/syndicate
+ name = "MOD syndicate storage module"
+ desc = "A storage system using nanotechnology developed by Donk Corporation, these compartments use \
+ esoteric technology to compress the physical matter of items put inside of them, \
+ essentially shrinking items for much easier and more portable storage."
+ icon_state = "storage_syndi"
+ max_combined_w_class = 30
+ max_items = 21
+ origin_tech = "materials=6;bluespace=5;syndicate=2"
+
+/obj/item/mod/module/storage/belt
+ name = "MOD case storage module"
+ desc = "Some concessions had to be made when creating a compressed modular suit core. \
+ As a result, Roseus Galactic equipped their suit with a slimline storage case. \
+ If you find this equipped to a standard modular suit, then someone has almost certainly shortchanged you on a proper storage module."
+ icon_state = "storage_case"
+ complexity = 0
+ max_w_class = WEIGHT_CLASS_SMALL
+ removable = FALSE
+ max_combined_w_class = 21
+ max_items = 7
+
+/obj/item/mod/module/storage/bluespace
+ name = "MOD bluespace storage module"
+ desc = "A storage system developed by Nanotrasen, these compartments employ \
+ miniaturized bluespace pockets for the ultimate in storage technology; regardless of the weight of objects put inside."
+ icon_state = "storage_bluespace"
+ max_w_class = WEIGHT_CLASS_GIGANTIC
+ max_combined_w_class = 60
+ max_items = 21
+
+
+//Internal
+/obj/item/storage/backpack/modstorage
+ name = "mod's storage"
+ desc = "Either you tried to spawn a storage mod, or someone fucked up. Unless you are an admin that just tried to spawn something, issue report."
+ var/obj/item/mod/module/storage/source
+
+/obj/item/storage/backpack/modstorage/Initialize(mapload)
+ . = ..()
+ START_PROCESSING(SSobj, src)
+
+/obj/item/storage/backpack/modstorage/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/item/storage/backpack/modstorage/process()
+ update_viewers()
+
+/obj/item/storage/backpack/modstorage/update_viewers()
+ for(var/_M in mobs_viewing)
+ var/mob/M = _M
+ if(!QDELETED(M) && M.s_active == src && (M in range(1, loc)) && (source.mod.loc == _M || (M in range(1, source.mod)))) //This ensures someone isn't taking it away from the mod unit
+ continue
+ hide_from(M)
+
+
+///Ion Jetpack - Lets the user fly freely through space using battery charge.
+/obj/item/mod/module/jetpack
+ name = "MOD ion jetpack module"
+ desc = "A series of electric thrusters installed across the suit, this is a module highly anticipated by trainee Engineers. \
+ Rather than using gasses for combustion thrust, these jets are capable of accelerating ions using \
+ charge from the suit's charge. Some say this isn't Cybersun Industries's first foray into jet-enabled suits."
+ icon_state = "jetpack"
+ module_type = MODULE_TOGGLE
+ complexity = 3
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/jetpack)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_jetpack"
+ overlay_state_active = "module_jetpack_on"
+ /// Do we stop the wearer from gliding in space.
+ var/stabilizers = FALSE
+
+/obj/item/mod/module/jetpack/proc/set_stabilizers(new_stabilizers)
+ if(stabilizers == new_stabilizers)
+ return
+ stabilizers = new_stabilizers
+
+/obj/item/mod/module/jetpack/get_configuration()
+ . = ..()
+ .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers)
+
+/obj/item/mod/module/jetpack/configure_edit(key, value)
+ switch(key)
+ if("stabilizers")
+ set_stabilizers(text2bool(value))
+
+/obj/item/mod/module/jetpack/proc/allow_thrust()
+ if(!active)
+ return
+ if(!drain_power(use_power_cost))
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/jetpack/proc/get_user()
+ return mod.wearer
+
+/obj/item/mod/module/jetpack/on_activation()
+ . = ..()
+ mod.jetpack_active = TRUE
+
+/obj/item/mod/module/jetpack/on_deactivation(display_message, deleting)
+ . = ..()
+ mod.jetpack_active = FALSE
+
+/obj/item/mod/module/jetpack/advanced
+ name = "MOD advanced ion jetpack module"
+ desc = "An improvement on the previous model of electric thrusters. This one achieves better efficency through \
+ mounting of more jets and a red paint applied on it."
+ icon_state = "jetpack_advanced"
+ overlay_state_inactive = "module_jetpackadv"
+ overlay_state_active = "module_jetpackadv_on"
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.25
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ origin_tech = "materials=4;magnets=4;engineering=5" //To replace the old hardsuit upgrade jetpack levels.
+
+///EMP Shield - Protects the suit from EMPs.
+/obj/item/mod/module/emp_shield
+ name = "MOD EMP shield module"
+ desc = "A field inhibitor installed into the suit, protecting it against feedback such as \
+ electromagnetic pulses that would otherwise damage the electronic systems of the suit or it's modules. \
+ However, it will take from the suit's power to do so."
+ icon_state = "empshield"
+ origin_tech = "materials=6;bluespace=5;syndicate=2"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/emp_shield, /obj/item/mod/module/dna_lock)
+
+/obj/item/mod/module/emp_shield/on_install()
+ mod.emp_proof = TRUE
+
+/obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE)
+ mod.emp_proof = FALSE
+
+///Flashlight - Gives the suit a customizable flashlight.
+/obj/item/mod/module/flashlight
+ name = "MOD flashlight module"
+ desc = "A simple pair of configurable flashlights installed on the left and right sides of the helmet, \
+ useful for providing light in a variety of ranges and colors. \
+ Some survivalists prefer the color green for their illumination, for reasons unknown."
+ icon_state = "flashlight"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/flashlight)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_light"
+ overlay_state_active = "module_light_on"
+ light_color = COLOR_WHITE
+ ///The light power for the mod
+ var/mod_light_range = 4
+ ///The light range for the mod
+ var/mod_light_power = 2
+ var/light_on = FALSE
+ /// Charge drain per range amount.
+ var/base_power = DEFAULT_CHARGE_DRAIN * 0.1
+ /// Minimum range we can set.
+ var/min_range = 2
+ /// Maximum range we can set.
+ var/max_range = 5
+
+/obj/item/mod/module/flashlight/on_activation()
+ . = ..()
+ if(!.)
+ return
+ active_power_cost = base_power * mod_light_range
+ mod.set_light(mod_light_range, mod_light_power, light_color)
+
+/obj/item/mod/module/flashlight/on_deactivation(display_message = TRUE, deleting = FALSE)
+ mod.set_light(0, mod_light_power, light_color)
+ . = ..()
+ if(!.)
+ return
+
+/obj/item/mod/module/flashlight/on_process()
+ active_power_cost = base_power * mod_light_range
+ return ..()
+
+/obj/item/mod/module/flashlight/get_configuration()
+ . = ..()
+ .["light_color"] = add_ui_configuration("Light Color", "color", light_color)
+ .["light_range"] = add_ui_configuration("Light Range", "number", mod_light_range)
+
+/obj/item/mod/module/flashlight/configure_edit(key, value)
+ switch(key)
+ if("light_color")
+ value = input(usr, "Pick new light color", "Flashlight Color") as color|null
+ if(!value)
+ return
+ if(is_color_dark(value, 50))
+ to_chat(mod.wearer, ("That is too dark"))
+ return
+ light_color = value
+ mod.wearer.regenerate_icons()
+ if("light_range")
+ mod_light_range = (clamp(text2num(value), min_range, max_range))
+ mod.set_light(0, mod_light_power, light_color)
+ mod_color_overide = light_color
+ on_deactivation()
+
+///Dispenser - Dispenses an item after a time passes.
+/obj/item/mod/module/dispenser
+ name = "MOD burger dispenser module"
+ desc = "A rare piece of technology reverse-engineered from a prototype found in a Donk Corporation vessel. \
+ This can draw incredible amounts of power from the suit's charge to create edible organic matter in the \
+ palm of the wearer's glove; however, research seemed to have entirely stopped at cheeseburgers. \
+ Notably, all attempts to get it to dispense Earl Grey tea have failed."
+ icon_state = "dispenser"
+ module_type = MODULE_USABLE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 2
+ incompatible_modules = list(/obj/item/mod/module/dispenser)
+ cooldown_time = 5 SECONDS
+ /// Path we dispense.
+ var/dispense_type = /obj/item/reagent_containers/food/snacks/cheeseburger
+ /// Time it takes for us to dispense.
+ var/dispense_time = 0 SECONDS
+
+/obj/item/mod/module/dispenser/on_use()
+ . = ..()
+ if(!.)
+ return
+ if(dispense_time && !do_after(mod.wearer, dispense_time, target = mod.wearer))
+ return FALSE
+ var/obj/item/dispensed = new dispense_type(mod.wearer.loc)
+ mod.wearer.put_in_hands(dispensed)
+ playsound(src, 'sound/machines/click.ogg', 100, TRUE)
+ drain_power(use_power_cost)
+ return dispensed
+
+///Thermal Regulator - Regulates the wearer's core temperature.
+/obj/item/mod/module/thermal_regulator
+ name = "MOD thermal regulator module"
+ desc = "Advanced climate control, using an inner body glove interwoven with thousands of tiny, \
+ flexible cooling lines. This circulates coolant at various user-controlled temperatures, \
+ ensuring they're comfortable; even if they're some that like it hot."
+ icon_state = "regulator"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/thermal_regulator)
+ cooldown_time = 0.5 SECONDS
+ /// The temperature we are regulating to.
+ var/temperature_setting = BODYTEMP_NORMAL
+ /// Minimum temperature we can set.
+ var/min_temp = 293.15
+ /// Maximum temperature we can set.
+ var/max_temp = 318.15
+
+/obj/item/mod/module/thermal_regulator/get_configuration()
+ . = ..()
+ .["temperature_setting"] = add_ui_configuration("Temperature", "number", temperature_setting - T0C)
+
+/obj/item/mod/module/thermal_regulator/configure_edit(key, value)
+ switch(key)
+ if("temperature_setting")
+ temperature_setting = clamp(text2num(value) + T0C, min_temp, max_temp)
+
+/obj/item/mod/module/thermal_regulator/on_active_process()
+ if(mod.wearer.bodytemperature > temperature_setting)
+ mod.wearer.bodytemperature = max(temperature_setting, mod.wearer.bodytemperature - (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
+ else if(mod.wearer.bodytemperature < temperature_setting)
+ mod.wearer.bodytemperature = min(temperature_setting, mod.wearer.bodytemperature + (40 * TEMPERATURE_DAMAGE_COEFFICIENT))
+
+/obj/item/mod/module/dna_lock
+ name = "MOD DNA lock module"
+ desc = "A module which engages with the various locks and seals tied to the suit's systems, \
+ enabling it to only be worn by someone corresponding with the user's exact DNA profile; \
+ however, this incredibly sensitive module is shorted out by EMPs. Luckily, stable mutagen has been outlawed."
+ icon_state = "dnalock"
+ origin_tech = "materials=6;bluespace=5;syndicate=1"
+ module_type = MODULE_USABLE
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 3
+ incompatible_modules = list(/obj/item/mod/module/dna_lock, /obj/item/mod/module/emp_shield)
+ cooldown_time = 0.5 SECONDS
+ /// The DNA we lock with.
+ var/dna = null
+
+/obj/item/mod/module/dna_lock/on_install()
+ RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_mod_activation))
+ RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, PROC_REF(on_mod_removal))
+ RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
+ RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag))
+
+/obj/item/mod/module/dna_lock/on_uninstall(deleting = FALSE)
+ UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
+ UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL)
+ UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT)
+ UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT)
+
+/obj/item/mod/module/dna_lock/on_use()
+ . = ..()
+ if(!.)
+ return
+ dna = mod.wearer.dna.unique_enzymes
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/dna_lock/emp_act(severity)
+ . = ..()
+ if(mod.emp_proof)
+ return
+ on_emp(src, severity)
+
+/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card)
+ . = ..()
+ on_emag(src, user, emag_card)
+
+/obj/item/mod/module/dna_lock/proc/dna_check(mob/user)
+ if(!iscarbon(user))
+ return FALSE
+ if(!dna)
+ return TRUE
+ if(dna == mod.wearer.dna.unique_enzymes)
+ return TRUE
+ return FALSE
+
+/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity)
+ SIGNAL_HANDLER
+
+ dna = null
+
+/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card)
+ SIGNAL_HANDLER
+
+ dna = null
+
+/obj/item/mod/module/dna_lock/proc/on_mod_activation(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(!dna_check(user))
+ atom_say("ERROR: User does not match owner DNA")
+ return MOD_CANCEL_ACTIVATE
+
+/obj/item/mod/module/dna_lock/proc/on_mod_removal(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(!dna_check(user))
+ atom_say("ERROR: User does not match owner DNA")
+ return MOD_CANCEL_REMOVAL
+
+/obj/item/mod/module/dna_lock/emp_shield
+ name = "MOD DN-MP shield lock"
+ desc = "This syndicate module is a combination EMP shield and DNA lock. Provides the best of both worlds, with the weakness of niether."
+ icon_state = "dnalock"
+ origin_tech = "materials=6;bluespace=5;syndicate=3"
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+
+/obj/item/mod/module/dna_lock/emp_shield/on_install()
+ . = ..()
+ mod.emp_proof = TRUE
+
+/obj/item/mod/module/dna_lock/emp_shield/on_uninstall(deleting = FALSE)
+ . = ..()
+ mod.emp_proof = FALSE
+
+///Plasma Stabilizer - Prevents plasmamen from igniting in the suit
+/obj/item/mod/module/plasma_stabilizer
+ name = "MOD plasma stabilizer module"
+ desc = "This system essentially forms an atmosphere of its own, within the suit, \
+ efficiently and quickly preventing oxygen from causing the user's head to burst into flame. \
+ This allows plasmamen to safely remove their helmet, allowing for easier \
+ equipping of any MODsuit-related equipment, or otherwise. \
+ The purple glass of the visor seems to be constructed for nostalgic purposes."
+ icon_state = "plasma_stabilizer"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer)
+ overlay_state_inactive = "module_plasma"
+
+/obj/item/mod/module/plasma_stabilizer/on_equip()
+ ADD_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MODSUIT_TRAIT)
+
+/obj/item/mod/module/plasma_stabilizer/on_unequip()
+ REMOVE_TRAIT(mod.wearer, TRAIT_NOSELFIGNITION_HEAD_ONLY, MODSUIT_TRAIT)
diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm
new file mode 100644
index 000000000000..fddb3d826543
--- /dev/null
+++ b/code/modules/mod/modules/modules_maint.dm
@@ -0,0 +1,129 @@
+//Maint modules for MODsuits
+
+///Springlock Mechanism - allows your modsuit to activate faster, but reagents are very dangerous.
+/obj/item/mod/module/springlock
+ name = "MOD springlock module"
+ desc = "A module that spans the entire size of the MOD unit, sitting under the outer shell. \
+ This mechanical exoskeleton pushes out of the way when the user enters and it helps in booting \
+ up, but was taken out of modern suits because of the springlock's tendency to \"snap\" back \
+ into place when exposed to humidity. You know what it's like to have an entire exoskeleton enter you?"
+ icon_state = "springlock"
+ complexity = 3 // it is inside every part of your suit, so
+ incompatible_modules = list(/obj/item/mod/module/springlock)
+ ///How much faster will your suit deploy?
+ var/activation_step_time_booster = 2
+ ///Is this the syndicate version, which can be toggled on multitool?
+ var/nineteen_eighty_seven_edition = FALSE
+ ///If this is true, the suit will prevent you from retracting for 10 seconds, so an antag can smoke bomb you.
+ var/dont_let_you_come_back = FALSE
+ ///If this is true, we are about to spring shut on someone, and should not remove the retraction blocking.
+ var/incoming_jumpscare = FALSE
+
+/obj/item/mod/module/springlock/on_install()
+ mod.activation_step_time *= (1 / activation_step_time_booster)
+
+/obj/item/mod/module/springlock/on_uninstall(deleting = FALSE)
+ mod.activation_step_time *= activation_step_time_booster
+
+/obj/item/mod/module/springlock/on_suit_activation()
+ RegisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_wearer_exposed))
+ if(dont_let_you_come_back)
+ RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_activate_spring_block))
+ addtimer(CALLBACK(src, PROC_REF(remove_retraction_block)), 10 SECONDS)
+
+/obj/item/mod/module/springlock/on_suit_deactivation(deleting = FALSE)
+ UnregisterSignal(mod.wearer, COMSIG_ATOM_EXPOSE_REAGENTS)
+
+/obj/item/mod/module/springlock/multitool_act(mob/living/user, obj/item/I)
+ if(!nineteen_eighty_seven_edition)
+ return
+ . = TRUE
+ if(dont_let_you_come_back)
+ to_chat(user, "You disable the retraction blocking systems.")
+ dont_let_you_come_back = FALSE
+ return
+ to_chat(user, "You enable the retraction blocking systems, which will block people from retracting the modsuit for 10 seconds.")
+ dont_let_you_come_back = TRUE
+
+
+///Signal fired when wearer is exposed to reagents
+/obj/item/mod/module/springlock/proc/on_wearer_exposed(atom/source, list/reagents, datum/reagents/source_reagents, methods, volume_modifier, show_message)
+ SIGNAL_HANDLER
+ remove_retraction_block() //No double signals
+ to_chat(mod.wearer, "[src] makes an ominous click sound...")
+ incoming_jumpscare = TRUE
+ playsound(src, 'sound/items/modsuit/springlock.ogg', 75, TRUE)
+ addtimer(CALLBACK(src, PROC_REF(snap_shut)), rand(3 SECONDS, 5 SECONDS))
+ RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_activate_spring_block))
+
+///Signal fired when wearer attempts to activate/deactivate suits
+/obj/item/mod/module/springlock/proc/on_activate_spring_block(datum/source, user)
+ SIGNAL_HANDLER
+
+ to_chat(mod.wearer, "The springlocks aren't responding...?")
+ return MOD_CANCEL_ACTIVATE
+
+///Removes the retraction blocker from the springlock so long as they are not about to be killed
+/obj/item/mod/module/springlock/proc/remove_retraction_block()
+ if(!incoming_jumpscare)
+ UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
+
+///Delayed death proc of the suit after the wearer is exposed to reagents
+/obj/item/mod/module/springlock/proc/snap_shut()
+ UnregisterSignal(mod, COMSIG_MOD_ACTIVATE)
+ if(!mod.wearer) //while there is a guaranteed user when on_wearer_exposed() fires, that isn't the same case for this proc
+ return
+ mod.wearer.visible_message("[src] inside [mod.wearer]'s [mod.name] snaps shut, mutilating the user inside!", "*SNAP*")
+ mod.wearer.emote("scream")
+ playsound(mod.wearer, 'sound/effects/snap.ogg', 75, TRUE, frequency = 0.5)
+ playsound(mod.wearer, 'sound/effects/splat.ogg', 50, TRUE, frequency = 0.5)
+ mod.wearer.adjustBruteLoss(1987) //boggers, bogchamp, etc //why not just poggers, also this caps at 595 damage but comedy
+ incoming_jumpscare = FALSE
+
+///Balloon Blower - Blows a balloon.
+/obj/item/mod/module/balloon
+ name = "MOD balloon blower module"
+ desc = "A strange module invented years ago by some ingenious mimes. It blows balloons."
+ icon_state = "bloon"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ incompatible_modules = list(/obj/item/mod/module/balloon)
+ cooldown_time = 15 SECONDS
+
+/obj/item/mod/module/balloon/on_use()
+ . = ..()
+ if(!.)
+ return
+ if(!do_after(mod.wearer, 10 SECONDS, target = mod.wearer))
+ return FALSE
+ mod.wearer.adjustOxyLoss(20)
+ playsound(src, 'sound/items/modsuit/inflate_bloon.ogg', 50, TRUE)
+ var/obj/item/toy/balloon/balloon = new(get_turf(src))
+ mod.wearer.put_in_hands(balloon)
+ drain_power(use_power_cost)
+
+
+///Stamper - Extends a stamp that can switch between accept/deny modes.
+/obj/item/mod/module/stamp
+ name = "MOD stamper module"
+ desc = "A module installed into the wrist of the suit, this functions as a high-power stamp, \
+ able to switch between accept and deny modes."
+ icon_state = "stamp"
+ module_type = MODULE_ACTIVE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ device = /obj/item/stamp/mod
+ incompatible_modules = list(/obj/item/mod/module/stamp)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/stamp/mod
+ name = "MOD electronic stamp"
+ desc = "A high-power stamp, able to switch between accept and deny mode when used."
+
+/obj/item/stamp/mod/attack_self(mob/user, modifiers)
+ . = ..()
+ if(icon_state == "stamp-ok")
+ icon_state = "stamp-deny"
+ else
+ icon_state = "stamp-ok"
diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm
new file mode 100644
index 000000000000..43a6aaa79341
--- /dev/null
+++ b/code/modules/mod/modules/modules_medical.dm
@@ -0,0 +1,115 @@
+//Medical modules for MODsuits. Not much here, sorry.
+
+///Injector - Gives the suit an extendable large-capacity piercing syringe.
+/obj/item/mod/module/injector
+ name = "MOD injector module"
+ desc = "A module installed into the wrist of the suit, this functions as a high-capacity syringe, \
+ with a tip fine enough to locate the emergency injection ports on any suit of armor, \
+ penetrating it with ease. Even yours."
+ icon_state = "injector"
+ module_type = MODULE_ACTIVE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ device = /obj/item/reagent_containers/syringe/mod
+ incompatible_modules = list(/obj/item/mod/module/injector)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/reagent_containers/syringe/mod
+ name = "MOD injector syringe"
+ desc = "A high-capacity syringe, with a tip fine enough to locate \
+ the emergency injection ports on any suit of armor, penetrating it with ease. Even yours."
+ amount_per_transfer_from_this = 30
+ possible_transfer_amounts = list(5, 10, 15, 20, 30)
+ volume = 30
+ penetrates_thick = TRUE
+
+///Defibrillator - Gives the suit an extendable pair of shock paddles.
+/obj/item/mod/module/defibrillator
+ name = "MOD defibrillator module"
+ desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \
+ The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \
+ and a modded targeting computer determines the best position for the user to push. \
+ Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \
+ and counter-shock the heart, and the wearer returns to Medical a hero. Don't you even think about using it as a weapon; \
+ regulations on manufacture and software locks expressly forbid it."
+ icon_state = "defibrillator"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 200 // 1000 charge. Shocking, I know.
+ device = /obj/item/mod_defib
+ overlay_state_inactive = "module_defibrillator"
+ overlay_state_active = "module_defibrillator_active"
+ incompatible_modules = list(/obj/item/mod/module/defibrillator)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/mod/module/defibrillator/Initialize(mapload)
+ . = ..()
+ RegisterSignal(device, COMSIG_DEFIB_SHOCK_APPLIED, PROC_REF(on_defib_success))
+
+/obj/item/mod/module/defibrillator/proc/on_defib_success()
+ SIGNAL_HANDLER // COMSIG_DEFIB_SHOCK_APPLIED
+ drain_power(use_power_cost)
+
+/obj/item/mod_defib
+ name = "defibrillator gauntlets"
+ desc = "A pair of paddles with flat metal surfaces that are used to deliver powerful electric shocks."
+ icon = 'icons/obj/defib.dmi'
+ icon_state = "defibgauntlets0" //Inhands handled by the module overlays
+ force = 0
+ w_class = WEIGHT_CLASS_BULKY
+ toolspeed = 1
+ var/defib_cooldown = 5 SECONDS
+ var/safety = TRUE
+ /// Whether or not the paddles are on cooldown. Used for tracking icon states.
+ var/on_cooldown = FALSE
+
+
+/obj/item/mod_defib/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/defib, cooldown = defib_cooldown, speed_multiplier = toolspeed, combat = !safety, heart_attack_chance = safety ? 0 : 100, robotic = TRUE, safe_by_default = safety, emp_proof = TRUE)
+
+ RegisterSignal(src, COMSIG_DEFIB_READY, PROC_REF(on_cooldown_expire))
+ RegisterSignal(src, COMSIG_DEFIB_SHOCK_APPLIED, PROC_REF(after_shock))
+
+/obj/item/mod_defib/proc/after_shock(obj/item/defib, mob/user)
+ SIGNAL_HANDLER // COMSIG_DEFIB_SHOCK_APPLIED
+ on_cooldown = TRUE
+ update_icon(UPDATE_ICON_STATE)
+
+/obj/item/mod_defib/proc/on_cooldown_expire(obj/item/defib)
+ SIGNAL_HANDLER // COMSIG_DEFIB_READY
+ on_cooldown = FALSE
+ visible_message("[src] beeps: Defibrillation unit ready.")
+ playsound(get_turf(src), 'sound/machines/defib_ready.ogg', 50, FALSE)
+ update_icon(UPDATE_ICON_STATE)
+
+/obj/item/mod_defib/update_icon_state()
+ icon_state = "[initial(icon_state)]"
+ if(on_cooldown)
+ icon_state = "[initial(icon_state)]_cooldown"
+
+/obj/item/mod/module/defibrillator/combat
+ name = "MOD combat defibrillator module"
+ desc = "A module built into the gauntlets of the suit; commonly known as the 'Healing Hands' by medical professionals. \
+ The user places their palms above the patient. Onboard computers in the suit calculate the necessary voltage, \
+ and a modded targeting computer determines the best position for the user to push. \
+ Twenty five pounds of force are applied to the patient's skin. Shocks travel from the suit's gloves \
+ and counter-shock the heart, and the wearer returns to Medical a hero. \
+ Interdyne Pharmaceutics marketed the domestic version of the Healing Hands as foolproof and unusable as a weapon. \
+ But when it came time to provide their operatives with usable medical equipment, they didn't hesitate to remove \
+ those in-built safeties. Operatives in the field can benefit from what they dub as 'Stun Gloves', able to apply shocks \
+ straight to a victims heart to disable them, or maybe even outright stop their heart with enough power."
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 400 // 2000 charge. Since you like causing heart attacks, don't you?
+ module_type = MODULE_ACTIVE
+ overlay_state_inactive = "module_defibrillator_combat"
+ overlay_state_active = "module_defibrillator_combat_active"
+ device = /obj/item/mod_defib/syndicate
+
+/obj/item/mod_defib/syndicate
+ name = "combat defibrillator gauntlets"
+ icon_state = "syndiegauntlets0"
+ safety = FALSE
+ toolspeed = 2
+ defib_cooldown = 2.5 SECONDS
+
diff --git a/code/modules/mod/modules/modules_science.dm b/code/modules/mod/modules/modules_science.dm
new file mode 100644
index 000000000000..eab091c5acb0
--- /dev/null
+++ b/code/modules/mod/modules/modules_science.dm
@@ -0,0 +1,96 @@
+//Science modules for MODsuits
+
+///Reagent Scanner - Lets the user scan reagents.
+/obj/item/mod/module/reagent_scanner
+ name = "MOD reagent scanner module"
+ desc = "A module based off research-oriented Nanotrasen HUDs, this is capable of scanning the contents of \
+ containers and projecting the information in an easy-to-read format on the wearer's display. \
+ It cannot detect flavors, so that's up to you."
+ icon_state = "scanner"
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/reagent_scanner)
+ cooldown_time = 0.5 SECONDS
+
+/obj/item/mod/module/reagent_scanner/on_activation()
+ . = ..()
+ if(!.)
+ return
+ mod.helmet.scan_reagents = TRUE
+
+/obj/item/mod/module/reagent_scanner/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ mod.helmet.scan_reagents = FALSE
+
+/obj/item/mod/module/reagent_scanner/advanced
+ name = "MOD advanced reagent scanner module"
+ complexity = 0
+ removable = FALSE
+ var/explosion_detection_dist = 21
+
+/obj/item/mod/module/reagent_scanner/advanced/on_activation()
+ . = ..()
+ if(!.)
+ return
+ GLOB.doppler_arrays += src
+
+/obj/item/mod/module/reagent_scanner/advanced/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ GLOB.doppler_arrays -= src
+
+/obj/item/mod/module/reagent_scanner/advanced/proc/sense_explosion(x0, y0, z0, devastation_range, heavy_impact_range,
+ light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
+ var/turf/T = get_turf(src)
+ var/dx = abs(x0 - T.x)
+ var/dy = abs(y0 - T.y)
+ var/distance
+ if(T.z != z0)
+ return
+ if(dx > dy)
+ distance = dx
+ else
+ distance = dy
+ if(distance > explosion_detection_dist)
+ return
+ to_chat(mod.wearer, "Explosion detected! Epicenter: [devastation_range], Outer: [heavy_impact_range], Shock: [light_impact_range]")
+
+///Teleporter - Lets the user teleport to a nearby location.
+/obj/item/mod/module/anomaly_locked/teleporter
+ name = "MOD teleporter module"
+ desc = "A module that uses a bluespace core to let the user transport their particles elsewhere."
+ icon_state = "teleporter"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 5
+ cooldown_time = 5 SECONDS
+ accepted_anomalies = list(/obj/item/assembly/signaler/anomaly/bluespace)
+ /// Time it takes to teleport
+ var/teleport_time = 1.25 SECONDS //This is a bluespace core this should be fast, like you can get a phazon with this man, we don't have anomaly refining either
+
+/obj/item/mod/module/anomaly_locked/teleporter/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/turf/target_turf = get_turf(target)
+ if(!istype(target_turf) || target_turf.density || !(target_turf in view(9, mod.wearer))) //No. No camera bug shenanigins.
+ return
+ var/matrix/pre_matrix = matrix()
+ pre_matrix.Scale(4, 0.25)
+ var/matrix/post_matrix = matrix()
+ post_matrix.Scale(0.25, 4)
+ animate(mod.wearer, teleport_time, color = COLOR_CYAN, transform = pre_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_OUT)
+ if(!do_after(mod.wearer, teleport_time, target = mod.wearer))
+ animate(mod.wearer, teleport_time * 0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN)
+ return
+ animate(mod.wearer, teleport_time * 0.1, color = null, transform = post_matrix.Multiply(mod.wearer.transform), easing = SINE_EASING|EASE_IN)
+ if(!do_teleport(mod.wearer, target_turf, sound_in = 'sound/effects/phasein.ogg'))
+ return
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/anomaly_locked/teleporter/prebuilt
+ prebuilt = TRUE
diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm
new file mode 100644
index 000000000000..84d421147f98
--- /dev/null
+++ b/code/modules/mod/modules/modules_security.dm
@@ -0,0 +1,168 @@
+//Security modules for MODsuits
+
+///Holster - Instantly holsters any not huge gun.
+/obj/item/mod/module/holster
+ name = "MOD holster module"
+ desc = "Based off typical storage compartments, this system allows the suit to holster a \
+ standard firearm across its surface and allow for extremely quick retrieval. \
+ While some users prefer the chest, others the forearm for quick deployment, \
+ some law enforcement prefer the holster to extend from the thigh."
+ icon_state = "holster"
+ module_type = MODULE_USABLE
+ complexity = 2
+ incompatible_modules = list(/obj/item/mod/module/holster)
+ cooldown_time = 0.5 SECONDS
+ allow_flags = MODULE_ALLOW_INACTIVE
+ /// Gun we have holstered.
+ var/obj/item/gun/holstered
+
+/obj/item/mod/module/holster/on_use()
+ . = ..()
+ if(!.)
+ return
+ var/msg = "[holstered]"
+ if(!holstered)
+ var/obj/item/gun/holding = mod.wearer.get_active_hand()
+ if(!holding)
+ to_chat(mod.wearer, "Nothing to holster!")
+ return
+ if(!istype(holding) || holding.w_class > WEIGHT_CLASS_NORMAL) //god no holstering a BSG / combat shotgun
+ to_chat(mod.wearer, "It's too big to fit!")
+ return
+ holstered = holding
+ mod.wearer.visible_message("[mod.wearer] holsters [holstered].", "You holster [holstered].")
+ mod.wearer.unEquip(mod.wearer.get_active_hand())
+ holstered.forceMove(src)
+ else if(mod.wearer.put_in_active_hand(holstered))
+ mod.wearer.visible_message("[mod.wearer] draws [msg], ready to shoot!", \
+ "You draw [msg], ready to shoot!")
+ else
+ to_chat(mod.wearer, "You need an empty hand to draw [holstered]!")
+
+/obj/item/mod/module/holster/on_uninstall(deleting = FALSE)
+ if(holstered)
+ holstered.forceMove(drop_location())
+
+/obj/item/mod/module/holster/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone == holstered)
+ holstered = null
+
+/obj/item/mod/module/holster/Destroy()
+ QDEL_NULL(holstered)
+ return ..()
+
+///Mirage grenade dispenser - Dispenses grenades that copy the user's appearance.
+/obj/item/mod/module/dispenser/mirage
+ name = "MOD mirage grenade dispenser module"
+ desc = "This module can create mirage grenades at the user's liking. These grenades create holographic copies of the user."
+ icon_state = "mirage_grenade"
+ cooldown_time = 20 SECONDS
+ overlay_state_inactive = "module_mirage_grenade"
+ dispense_type = /obj/item/grenade/mirage
+
+/obj/item/mod/module/dispenser/mirage/on_use()
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/grenade/mirage/grenade = .
+ grenade.attack_self(mod.wearer)
+
+/obj/item/grenade/mirage
+ name = "mirage grenade"
+ desc = "A special device that, when activated, produces a holographic copy of the user."
+ icon_state = "mirage"
+ det_time = 3 SECONDS
+ /// Mob that threw the grenade.
+ var/mob/living/thrower
+
+
+/obj/item/grenade/mirage/Destroy()
+ thrower = null
+ return ..()
+
+/obj/item/grenade/mirage/attack_self(mob/user)
+ . = ..()
+ thrower = user
+
+/obj/item/grenade/mirage/prime()
+ do_sparks(rand(3, 6), FALSE, src)
+ if(thrower)
+ var/mob/living/simple_animal/hostile/illusion/mirage/M = new(get_turf(src))
+ M.Copy_Parent(thrower, 15 SECONDS)
+ qdel(src)
+
+/mob/living/simple_animal/hostile/illusion/mirage //It's just standing there, menacingly
+ AIStatus = AI_OFF
+ density = FALSE
+
+/mob/living/simple_animal/hostile/illusion/mirage/death(gibbed)
+ do_sparks(rand(3, 6), FALSE, src)
+ return ..()
+
+
+///Active Sonar - Displays a hud circle on the turf of any living creatures in the given radius
+/obj/item/mod/module/active_sonar
+ name = "MOD active sonar"
+ desc = "Ancient tech from the 20th century, this module uses sonic waves to detect living creatures within the user's radius. \
+ Its loud ping is much harder to hide in an indoor station than in the outdoor operations it was designed for."
+ icon_state = "active_sonar"
+ module_type = MODULE_USABLE
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 4
+ complexity = 2
+ incompatible_modules = list(/obj/item/mod/module/active_sonar)
+ cooldown_time = 7.5 SECONDS //come on man this is discount thermals, it doesnt need a 15 second cooldown
+
+/obj/item/mod/module/active_sonar/on_use()
+ . = ..()
+ if(!.)
+ return
+ playsound(mod.wearer, 'sound/mecha/skyfall_power_up.ogg', vol = 20, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
+ if(!do_after(mod.wearer, 1.1 SECONDS, target = mod.wearer))
+ return
+ var/creatures_detected = 0
+ for(var/mob/living/creature in range(9, mod.wearer))
+ if(creature == mod.wearer || creature.stat == DEAD)
+ continue
+ new /obj/effect/temp_visual/sonar_ping(mod.wearer.loc, mod.wearer, creature)
+ creatures_detected++
+ playsound(mod.wearer, 'sound/effects/ping_hit.ogg', vol = 75, vary = TRUE, extrarange = 9) // Should be audible for the radius of the sonar
+ to_chat(mod.wearer, ("You slam your fist into the ground, sending out a sonic wave that detects [creatures_detected] living beings nearby!"))
+
+/obj/effect/temp_visual/sonar_ping
+ duration = 3 SECONDS
+ resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ anchored = TRUE
+ randomdir = FALSE
+ /// The image shown to modsuit users
+ var/image/modsuit_image
+ /// The person in the modsuit at the moment, really just used to remove this from their screen
+ var/source_UID
+ /// The icon state applied to the image created for this ping.
+ var/real_icon_state = "sonar_ping"
+
+/obj/effect/temp_visual/sonar_ping/Initialize(mapload, mob/living/looker, mob/living/creature)
+ . = ..()
+ if(!looker || !creature)
+ return INITIALIZE_HINT_QDEL
+ modsuit_image = image(icon = icon, loc = src, icon_state = real_icon_state, layer = ABOVE_ALL_MOB_LAYER, pixel_x = ((creature.x - looker.x) * 32), pixel_y = ((creature.y - looker.y) * 32))
+ modsuit_image.plane = ABOVE_LIGHTING_PLANE
+ modsuit_image.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ source_UID = looker.UID()
+ add_mind(looker)
+
+/obj/effect/temp_visual/sonar_ping/Destroy()
+ var/mob/living/previous_user = locateUID(source_UID)
+ if(previous_user)
+ remove_mind(previous_user)
+ // Null so we don't shit the bed when we delete
+ modsuit_image = null
+ return ..()
+
+/// Add the image to the modsuit wearer's screen
+/obj/effect/temp_visual/sonar_ping/proc/add_mind(mob/living/looker)
+ looker?.client?.images |= modsuit_image
+
+/// Remove the image from the modsuit wearer's screen
+/obj/effect/temp_visual/sonar_ping/proc/remove_mind(mob/living/looker)
+ looker?.client?.images -= modsuit_image
diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm
new file mode 100644
index 000000000000..3670a576da93
--- /dev/null
+++ b/code/modules/mod/modules/modules_service.dm
@@ -0,0 +1,42 @@
+//Service modules for MODsuits
+
+///Bike Horn - Plays a bike horn sound.
+/obj/item/mod/module/bikehorn
+ name = "MOD bike horn module"
+ desc = "A shoulder-mounted piece of heavy sonic artillery, this module uses the finest femto-manipulator technology to \
+ precisely deliver an almost lethal squeeze to... a bike horn, producing a significantly memorable sound."
+ icon_state = "bikehorn"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/bikehorn)
+ cooldown_time = 1 SECONDS
+
+/obj/item/mod/module/bikehorn/on_use()
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/items/bikehorn.ogg', 100, FALSE)
+ drain_power(use_power_cost)
+
+//Waddle - Makes you waddle and squeak.
+/obj/item/mod/module/waddle
+ name = "MOD waddle module"
+ desc = "Some of the most primitive technology in use by Honk Co. This module works off an automatic intention system, \
+ utilizing its' sensitivity to the pilot's often-limited brainwaves to directly read their next step, \
+ affecting the boots they're installed in. Employing a twin-linked gravitonic drive to create \
+ miniaturized etheric blasts of space-time beneath the user's feet, this enables them to... \
+ to waddle around, bouncing to and fro with a pep in their step."
+ icon_state = "waddle"
+ complexity = 1
+ idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/waddle)
+
+/obj/item/mod/module/waddle/on_suit_activation()
+ mod.boots.AddComponent(/datum/component/squeak, list('sound/effects/clownstep1.ogg' = 1, 'sound/effects/clownstep2.ogg' = 1), 50, falloff_exponent = 20) //die off quick please
+ mod.wearer.AddElement(/datum/element/waddling)
+
+/obj/item/mod/module/waddle/on_suit_deactivation(deleting = FALSE)
+ if(!deleting)
+ qdel(mod.boots.GetComponent(/datum/component/squeak))
+ mod.wearer.RemoveElement(/datum/element/waddling)
diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm
new file mode 100644
index 000000000000..85f2c2f3fe8c
--- /dev/null
+++ b/code/modules/mod/modules/modules_supply.dm
@@ -0,0 +1,541 @@
+//Supply modules for MODsuits
+
+///Internal GPS - Extends a GPS you can use.
+/obj/item/mod/module/gps
+ name = "MOD internal GPS module"
+ desc = "This module uses common Nanotrasen technology to calculate the user's position anywhere in space, \
+ down to the exact coordinates. This information is fed to a central database viewable from the device itself, \
+ though using it to help people is up to you."
+ icon_state = "gps"
+ module_type = MODULE_ACTIVE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/gps)
+ cooldown_time = 0.5 SECONDS
+ device = /obj/item/gps/mod
+
+///Hydraulic Clamp - Lets you pick up and drop crates.
+/obj/item/mod/module/clamp
+ name = "MOD hydraulic clamp module"
+ desc = "A series of actuators installed into both arms of the suit, boasting a lifting capacity of almost a ton. \
+ However, this design has been locked by Nanotrasen to be primarily utilized for lifting various crates. \
+ A lot of people would say that loading cargo is a dull job, but you could not disagree more."
+ icon_state = "clamp"
+ module_type = MODULE_ACTIVE
+ complexity = 3
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/clamp)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_inactive = "module_clamp"
+ overlay_state_active = "module_clamp_on"
+ /// Time it takes to load a crate.
+ var/load_time = 3 SECONDS
+ /// The max amount of crates you can carry.
+ var/max_crates = 3
+ /// The crates stored in the module.
+ var/list/stored_crates = list()
+
+/obj/item/mod/module/clamp/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!mod.wearer.Adjacent(target))
+ return
+ if(istype(target, /obj/structure/closet/crate))
+ var/obj/structure/closet/crate/picked_crate = target
+ if(!check_crate_pickup(picked_crate))
+ return
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ if(!do_after(mod.wearer, load_time, target = target))
+ return
+ if(!check_crate_pickup(picked_crate))
+ return
+ stored_crates += picked_crate
+ picked_crate.forceMove(src)
+ drain_power(use_power_cost)
+ else if(length(stored_crates))
+ var/turf/target_turf = get_turf(target)
+ if(target_turf.density)
+ return
+ playsound(src, 'sound/mecha/hydraulic.ogg', 25, TRUE)
+ if(!do_after(mod.wearer, load_time, target = target))
+ return
+ if(target_turf.density)
+ return
+ var/obj/structure/closet/crate/dropped_crate = pop(stored_crates)
+ dropped_crate.forceMove(target_turf)
+ drain_power(use_power_cost)
+ else
+ to_chat(mod.wearer, "Invalid target!")
+
+/obj/item/mod/module/clamp/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ for(var/obj/structure/closet/crate/crate as anything in stored_crates)
+ crate.forceMove(drop_location())
+ stored_crates -= crate
+
+/obj/item/mod/module/clamp/proc/check_crate_pickup(atom/movable/target)
+ if(length(stored_crates) >= max_crates)
+ to_chat(mod.wearer, "Too many crates!")
+ return FALSE
+ for(var/mob/living/mob in target.client_mobs_in_contents)
+ if(mob.mob_size < MOB_SIZE_HUMAN)
+ continue
+ to_chat(mod.wearer, "Too heavy!")
+ return FALSE
+ return TRUE
+
+/obj/item/mod/module/clamp/loader
+ name = "MOD loader hydraulic clamp module"
+ icon_state = "clamp_loader"
+ complexity = 0
+ removable = FALSE
+ overlay_state_inactive = null
+ overlay_state_active = "module_clamp_loader"
+ load_time = 1 SECONDS
+ max_crates = 5
+ use_mod_colors = TRUE
+
+///Drill - Lets you dig through rock and basalt.
+/obj/item/mod/module/drill
+ name = "MOD drill module"
+ desc = "An integrated drill, typically extending over the user's hand. While useful for drilling through rock, \
+ your drill is surely the one that both pierces and creates the heavens."
+ icon_state = "drill"
+ module_type = MODULE_ACTIVE
+ complexity = 2
+ use_power_cost = DEFAULT_CHARGE_DRAIN
+ incompatible_modules = list(/obj/item/mod/module/drill)
+ cooldown_time = 0.5 SECONDS
+ overlay_state_active = "module_drill"
+
+/obj/item/mod/module/drill/on_activation()
+ . = ..()
+ if(!.)
+ return
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP, PROC_REF(bump_mine))
+
+/obj/item/mod/module/drill/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_BUMP)
+
+/obj/item/mod/module/drill/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(!mod.wearer.Adjacent(target))
+ return
+ if(ismineralturf(target))
+ var/turf/simulated/mineral/mineral_turf = target
+ mineral_turf.gets_drilled(mod.wearer)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/drill/proc/bump_mine(mob/living/carbon/human/bumper, atom/bumped_into, proximity)
+ SIGNAL_HANDLER
+ if(!ismineralturf(bumped_into) || !drain_power(use_power_cost))
+ return
+ var/turf/simulated/mineral/mineral_turf = bumped_into
+ mineral_turf.gets_drilled(mod.wearer)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+///Ore Bag - Lets you pick up ores and drop them from the suit.
+/obj/item/mod/module/orebag
+ name = "MOD ore bag module"
+ desc = "An integrated ore storage system installed into the suit, \
+ this utilizes precise electromagnets and storage compartments to automatically collect and deposit ore. \
+ It's recommended by Cybersun Industries to actually deposit that ore at local refineries."
+ icon_state = "ore"
+ module_type = MODULE_USABLE
+ complexity = 1
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ incompatible_modules = list(/obj/item/mod/module/orebag)
+ cooldown_time = 0.5 SECONDS
+ allow_flags = MODULE_ALLOW_INACTIVE
+
+/obj/item/mod/module/orebag/on_equip()
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(ore_pickup))
+ ..()
+
+/obj/item/mod/module/orebag/on_unequip()
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
+ ..()
+
+/obj/item/mod/module/orebag/proc/ore_pickup(atom/movable/source, atom/old_loc, dir, forced)
+ SIGNAL_HANDLER
+
+ for(var/obj/item/stack/ore/ore in get_turf(mod.wearer))
+ INVOKE_ASYNC(src, PROC_REF(move_ore), ore)
+
+/obj/item/mod/module/orebag/proc/move_ore(obj/item/stack/ore)
+ ore.forceMove(src)
+
+/obj/item/mod/module/orebag/on_use()
+ . = ..()
+ if(!.)
+ return
+ for(var/obj/item/ore as anything in contents)
+ ore.forceMove(drop_location())
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/hydraulic
+ name = "MOD loader hydraulic arms module"
+ desc = "A pair of powerful hydraulic arms installed in a MODsuit."
+ icon_state = "launch_loader"
+ module_type = MODULE_ACTIVE
+ removable = FALSE
+ use_power_cost = DEFAULT_CHARGE_DRAIN*10
+ incompatible_modules = list(/obj/item/mod/module/hydraulic)
+ cooldown_time = 4 SECONDS
+ overlay_state_inactive = "module_hydraulic"
+ overlay_state_active = "module_hydraulic_active"
+ use_mod_colors = TRUE
+ /// Time it takes to launch
+ var/launch_time = 2 SECONDS
+ /// The overlay used to show that you are charging.
+ var/image/charge_up_overlay
+
+/obj/item/mod/module/hydraulic/Initialize(mapload)
+ . = ..()
+ charge_up_overlay = image(icon = 'icons/effects/effects.dmi', icon_state = "electricity3", layer = EFFECTS_LAYER)
+
+/obj/item/mod/module/hydraulic/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(mod.wearer.buckled)
+ return
+ var/current_time = world.time
+ var/atom/movable/plane_master_controller/pm_controller = mod.wearer.hud_used.plane_master_controllers[PLANE_MASTERS_GAME]
+ for(var/key in pm_controller.controlled_planes)
+ animate(pm_controller.controlled_planes[key], launch_time, transform = matrix(1.25, MATRIX_SCALE))
+ mod.wearer.visible_message("[mod.wearer] starts whirring!")
+ playsound(src, 'sound/items/modsuit/loader_charge.ogg', 75, TRUE)
+ mod.wearer.add_overlay(charge_up_overlay)
+ var/power = launch_time
+ if(!do_after(mod.wearer, launch_time, target = mod.wearer))
+ power = world.time - current_time
+ drain_power(use_power_cost)
+ for(var/key in pm_controller.controlled_planes)
+ animate(pm_controller.controlled_planes[key], 0.1 SECONDS, transform = matrix(1, MATRIX_SCALE))
+ playsound(src, 'sound/items/modsuit/loader_launch.ogg', 75, TRUE)
+ var/angle = get_angle(mod.wearer, target)
+ mod.wearer.transform = mod.wearer.transform.Turn(mod.wearer.transform, angle)
+ mod.wearer.throw_at(get_ranged_target_turf_direct(mod.wearer, target, power), \
+ range = power, speed = max(round(0.2*power), 1), thrower = mod.wearer, spin = FALSE, \
+ callback = CALLBACK(src, PROC_REF(on_throw_end), mod.wearer, -angle))
+
+/obj/item/mod/module/hydraulic/proc/on_throw_end(mob/user, angle)
+ if(!user)
+ return
+ user.transform = user.transform.Turn(user.transform, angle)
+ user.cut_overlay(charge_up_overlay)
+
+/obj/item/mod/module/magnet
+ name = "MOD loader hydraulic magnet module"
+ desc = "A powerful hydraulic electromagnet able to launch crates and lockers towards the user, and keep them attached."
+ icon_state = "magnet_loader"
+ module_type = MODULE_ACTIVE
+ removable = FALSE
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 3
+ incompatible_modules = list(/obj/item/mod/module/magnet)
+ cooldown_time = 1.5 SECONDS
+ overlay_state_active = "module_magnet"
+ use_mod_colors = TRUE
+
+/obj/item/mod/module/magnet/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ if(istype(mod.wearer.pulling, /obj/structure/closet))
+ var/obj/structure/closet/locker = mod.wearer.pulling
+ playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
+ locker.forceMove(mod.wearer.loc)
+ locker.throw_at(target, range = 7, speed = 4, thrower = mod.wearer)
+ return
+ if(!istype(target, /obj/structure/closet) || !(target in view(mod.wearer)))
+ to_chat(mod.wearer, "Invalid target!")
+ return
+ var/obj/structure/closet/locker = target
+ if(locker.anchored || locker.move_resist >= MOVE_FORCE_OVERPOWERING)
+ return
+ playsound(locker, 'sound/effects/gravhit.ogg', 75, TRUE)
+ locker.throw_at(get_step_towards(mod.wearer, target), range = 7, speed = 3, force = MOVE_FORCE_WEAK, \
+ callback = CALLBACK(src, PROC_REF(check_locker), locker))
+
+/obj/item/mod/module/magnet/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(istype(mod.wearer.pulling, /obj/structure/closet))
+ mod.wearer.stop_pulling()
+
+/obj/item/mod/module/magnet/proc/check_locker(obj/structure/closet/locker)
+ if(!mod?.wearer)
+ return
+ if(!locker.Adjacent(mod.wearer) || !isturf(locker.loc) || !isturf(mod.wearer.loc))
+ return
+ mod.wearer.start_pulling(locker)
+
+/obj/item/mod/module/ash_accretion
+ name = "MOD ash accretion module"
+ desc = "A module that collects ash from the terrain, covering the suit in a protective layer, this layer is \
+ lost when moving across standard terrain."
+ icon_state = "ash_accretion"
+ removable = FALSE
+ incompatible_modules = list(/obj/item/mod/module/ash_accretion)
+ overlay_state_inactive = "module_ash"
+ use_mod_colors = TRUE
+ /// How many tiles we can travel to max out the armor.
+ var/max_traveled_tiles = 10
+ /// How many tiles we traveled through.
+ var/traveled_tiles = 0
+ /// Armor values per tile.
+ var/armor_mod_1 = /obj/item/mod/armor/mod_ash_accretion
+ /// the actual armor object
+ var/obj/item/mod/armor/armor_mod_2 = null
+ /// Speed added when you're fully covered in ash.
+ var/speed_added = 0.5
+ /// Speed that we actually added.
+ var/actual_speed_added = 0
+ /// Turfs that let us accrete ash.
+ var/static/list/accretion_turfs
+ /// Turfs that let us keep ash.
+ var/static/list/keep_turfs
+
+/obj/item/mod/module/ash_accretion/Initialize(mapload)
+ . = ..()
+ armor_mod_2 = new armor_mod_1
+
+/obj/item/mod/module/ash_accretion/Destroy()
+ QDEL_NULL(armor_mod_2)
+ return ..()
+
+/obj/item/mod/armor/mod_ash_accretion
+ armor = list(MELEE = 4, BULLET = 1, LASER = 2, ENERGY = 1, BOMB = 4, RAD = 0, FIRE = 0, ACID = 0)
+
+/obj/item/mod/module/ash_accretion/Initialize(mapload)
+ . = ..()
+ if(!accretion_turfs)
+ accretion_turfs = typecacheof(list(
+ /turf/simulated/floor/plating/asteroid
+ ))
+ if(!keep_turfs)
+ keep_turfs = typecacheof(list(
+ /turf/simulated/floor/plating/lava,
+ /turf/simulated/floor/indestructible/hierophant
+ ))
+
+/obj/item/mod/module/ash_accretion/on_suit_activation()
+ RegisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+
+/obj/item/mod/module/ash_accretion/on_suit_deactivation(deleting = FALSE)
+ UnregisterSignal(mod.wearer, COMSIG_MOVABLE_MOVED)
+ if(!traveled_tiles)
+ return
+ var/list/parts = mod.mod_parts + mod
+ var/speed_up = FALSE
+ if(traveled_tiles == max_traveled_tiles)
+ speed_up = TRUE
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.detachArmor(part.armor)
+ var/obj/item/mod/armor/mod_theme_mining/A = new(src)
+ part.armor = part.armor.attachArmor(A.armor) //TODO: ANYTHING BUT FUCKING THIS
+ if(speed_up)
+ part.slowdown += speed_added / 5
+ qdel(A)
+ traveled_tiles = 0
+ mod.wearer.weather_immunities -= "ash"
+
+/obj/item/mod/module/ash_accretion/generate_worn_overlay(user, mutable_appearance/standing)
+ overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]"
+ return ..()
+
+/obj/item/mod/module/ash_accretion/proc/on_move(atom/source, atom/oldloc, dir, forced)
+ if(!isturf(mod.wearer.loc)) //dont lose ash from going in a locker
+ return
+ if(traveled_tiles) //leave ash every tile
+ new /obj/effect/temp_visual/light_ash(get_turf(src))
+ if(is_type_in_typecache(mod.wearer.loc, accretion_turfs))
+ if(traveled_tiles >= max_traveled_tiles)
+ return
+ traveled_tiles++
+ var/list/parts = mod.mod_parts + mod
+ var/speed_up = FALSE
+ if(traveled_tiles >= max_traveled_tiles)
+ to_chat(mod.wearer, "You are fully covered in ash!")
+ mod.wearer.color = list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,3) //make them super light
+ animate(mod.wearer, 1 SECONDS, color = null, flags = ANIMATION_PARALLEL)
+ playsound(src, 'sound/effects/sparks1.ogg', 100, TRUE)
+ actual_speed_added = max(0, min(mod.slowdown_active, speed_added / 5))
+ mod.wearer.weather_immunities |= "ash"
+ speed_up = TRUE
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.attachArmor(armor_mod_2.armor)
+ if(speed_up)
+ part.slowdown -= speed_added / 5
+ else if(is_type_in_typecache(mod.wearer.loc, keep_turfs))
+ return
+ else
+ if(traveled_tiles <= 0)
+ return
+ var/speed_up = FALSE
+ if(traveled_tiles == max_traveled_tiles)
+ speed_up = TRUE
+ traveled_tiles--
+ var/list/parts = mod.mod_parts + mod
+ for(var/obj/item/part as anything in parts)
+ part.armor = part.armor.detachArmor(armor_mod_2.armor)
+ if(speed_up)
+ part.slowdown += actual_speed_added
+ if(traveled_tiles <= 0)
+ to_chat(mod.wearer, "You have ran out of ash!")
+ mod.wearer.weather_immunities -= "ash"
+
+/obj/effect/temp_visual/light_ash
+ icon_state = "light_ash"
+ icon = 'icons/effects/weather_effects.dmi'
+ duration = 3.2 SECONDS
+
+
+/obj/item/mod/module/sphere_transform
+ name = "MOD sphere transform module"
+ desc = "A module able to move the suit's parts around, turning it and the user into a sphere. \
+ The sphere can move quickly, and launch mining bombs to decimate terrain."
+ icon_state = "sphere"
+ module_type = MODULE_ACTIVE
+ removable = FALSE
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.5
+ use_power_cost = DEFAULT_CHARGE_DRAIN * 3
+ incompatible_modules = list(/obj/item/mod/module/sphere_transform)
+ cooldown_time = 2 SECONDS
+ allow_flags = MODULE_ALLOW_INCAPACITATED //Required so hands blocked doesnt block bombs
+ /// Time it takes us to complete the animation.
+ var/animate_time = 0.25 SECONDS
+
+/obj/item/mod/module/sphere_transform/on_activation()
+ if(!has_gravity(get_turf(src)))
+ to_chat(mod.wearer, "ERROR, NO GRAVITY!")
+ return FALSE
+ . = ..()
+ if(!.)
+ return
+ playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE)
+ mod.wearer.add_filter("mod_ball", 1, alpha_mask_filter(icon = icon('icons/mob/clothing/modsuit/mod_modules.dmi', "ball_mask"), flags = MASK_INVERSE))
+ mod.wearer.add_filter("mod_blur", 2, angular_blur_filter(size = 15))
+ mod.wearer.add_filter("mod_outline", 3, outline_filter(color = "#000000AA"))
+ animate(mod.wearer, animate_time, pixel_y = mod.wearer.pixel_y - 4, flags = ANIMATION_PARALLEL)
+ mod.wearer.SpinAnimation(1.5)
+ ADD_TRAIT(mod.wearer, TRAIT_HANDS_BLOCKED, "metriod[UID()]")
+ ADD_TRAIT(mod.wearer, TRAIT_GOTTAGOFAST, "metroid[UID()]")
+ RegisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE, PROC_REF(on_statchange))
+
+/obj/item/mod/module/sphere_transform/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(!deleting)
+ playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE, frequency = -1)
+ animate(mod.wearer, animate_time, pixel_y = 0)
+ addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/atom, remove_filter), list("mod_ball", "mod_blur", "mod_outline")), animate_time)
+ REMOVE_TRAIT(mod.wearer, TRAIT_HANDS_BLOCKED, "metriod[UID()]")
+ REMOVE_TRAIT(mod.wearer, TRAIT_GOTTAGOFAST, "metroid[UID()]")
+ UnregisterSignal(mod.wearer, COMSIG_MOB_STATCHANGE)
+
+/obj/item/mod/module/sphere_transform/on_use()
+ if(!lavaland_equipment_pressure_check(get_turf(src)))
+ to_chat(mod.wearer, "ERROR, OVER PRESSURE!")
+ playsound(src, 'sound/weapons/gun_interactions/dry_fire.ogg', 25, TRUE)
+ return FALSE
+ return ..()
+
+/obj/item/mod/module/sphere_transform/on_select_use(atom/target)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/projectile/bomb = new /obj/item/projectile/bullet/reusable/mining_bomb(get_turf(mod.wearer))
+ bomb.original = target
+ bomb.firer = mod.wearer
+ bomb.preparePixelProjectile(target, get_turf(target), mod.wearer)
+ bomb.fire()
+ playsound(src, 'sound/weapons/grenadelaunch.ogg', 75, TRUE)
+ drain_power(use_power_cost)
+
+/obj/item/mod/module/sphere_transform/on_active_process()
+ animate(mod.wearer) //stop the animation
+ mod.wearer.SpinAnimation(1.5) //start it back again
+ if(!has_gravity(get_turf(src)))
+ on_deactivation() //deactivate in no grav
+
+/obj/item/mod/module/sphere_transform/proc/on_statchange(datum/source)
+ SIGNAL_HANDLER
+ if(!mod.wearer.stat)
+ return
+ on_deactivation()
+
+/obj/item/projectile/bullet/reusable/mining_bomb
+ name = "mining bomb"
+ desc = "A bomb. Why are you staring at this?"
+ icon_state = "mine_bomb"
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ damage = 0
+ range = 6
+ flag = "bomb"
+ light_range = 1
+ light_power = 1
+ light_color = LIGHT_COLOR_ORANGE
+ ammo_type = /obj/structure/mining_bomb
+
+/obj/structure/mining_bomb
+ name = "mining bomb"
+ desc = "A bomb. Why are you staring at this?"
+ icon_state = "mine_bomb"
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ anchored = TRUE
+ resistance_flags = FIRE_PROOF|LAVA_PROOF
+ light_range = 1
+ light_power = 1
+ light_color = LIGHT_COLOR_ORANGE
+ /// Time to prime the explosion
+ var/prime_time = 0.5 SECONDS
+ /// Time to explode from the priming
+ var/explosion_time = 1 SECONDS
+ /// Damage done on explosion.
+ var/damage = 12
+ /// Damage multiplier on hostile fauna.
+ var/fauna_boost = 4
+ /// Image overlaid on explosion.
+ var/static/image/explosion_image
+
+/obj/structure/mining_bomb/Initialize(mapload, atom/movable/firer)
+ . = ..()
+ generate_image()
+ addtimer(CALLBACK(src, PROC_REF(prime), firer), prime_time)
+
+/obj/structure/mining_bomb/proc/generate_image()
+ explosion_image = image('icons/effects/96x96.dmi', "judicial_explosion")
+ explosion_image.pixel_x = -32
+ explosion_image.pixel_y = -32
+
+/obj/structure/mining_bomb/proc/prime(atom/movable/firer)
+ add_overlay(explosion_image)
+ addtimer(CALLBACK(src, PROC_REF(boom), firer), explosion_time)
+
+/obj/structure/mining_bomb/proc/boom(atom/movable/firer)
+ visible_message("[src] explodes!")
+ playsound(src, 'sound/magic/magic_missile.ogg', 200, vary = TRUE)
+ for(var/turf/T in circlerangeturfs(src, 2))
+ if(ismineralturf(T))
+ var/turf/simulated/mineral/mineral_turf = T
+ mineral_turf.gets_drilled(firer)
+ for(var/mob/living/mob in range(1, src))
+ mob.apply_damage(damage * (ishostile(mob) ? fauna_boost : 1), BRUTE, spread_damage = TRUE)
+ if(!ishostile(mob) || !firer)
+ continue
+ var/mob/living/simple_animal/hostile/hostile_mob = mob
+ hostile_mob.GiveTarget(firer)
+ for(var/obj/object in range(1, src))
+ object.take_damage(damage, BRUTE, BOMB)
+ qdel(src)
diff --git a/code/modules/mod/modules/modules_visor.dm b/code/modules/mod/modules/modules_visor.dm
new file mode 100644
index 000000000000..c43191b20b72
--- /dev/null
+++ b/code/modules/mod/modules/modules_visor.dm
@@ -0,0 +1,93 @@
+//Visor modules for MODsuits
+
+///Base Visor - Adds a specific HUD and traits to you.
+/obj/item/mod/module/visor
+ name = "MOD visor module"
+ desc = "A heads-up display installed into the visor of the suit. They say these also let you see behind you."
+ module_type = MODULE_TOGGLE
+ complexity = 1
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
+ incompatible_modules = list(/obj/item/mod/module/visor)
+ cooldown_time = 0.5 SECONDS
+ /// The HUD type given by the visor.
+ var/hud_type
+ /// The trait given by the visor.
+ var/visor_trait = list()
+
+/obj/item/mod/module/visor/on_activation()
+ . = ..()
+ if(!.)
+ return
+ if(hud_type)
+ var/datum/atom_hud/hud = GLOB.huds[hud_type]
+ hud.add_hud_to(mod.wearer)
+ if(length(visor_trait))
+ ADD_TRAIT(mod.wearer, visor_trait, MODSUIT_TRAIT)
+ mod.wearer.update_sight()
+
+/obj/item/mod/module/visor/on_deactivation(display_message = TRUE, deleting = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(hud_type)
+ var/datum/atom_hud/hud = GLOB.huds[hud_type]
+ hud.remove_hud_from(mod.wearer)
+ if(length(visor_trait))
+ REMOVE_TRAIT(mod.wearer, visor_trait, MODSUIT_TRAIT)
+ mod.wearer.update_sight()
+
+//Medical Visor - Gives you a medical HUD.
+/obj/item/mod/module/visor/medhud
+ name = "MOD medical visor module"
+ desc = "A heads-up display installed into the visor of the suit. This cross-references suit sensor data with a modern \
+ biological scanning suite, allowing the user to visualize the current health of organic lifeforms, as well as \
+ access data such as patient files in a convenient readout. They say these also let you see behind you."
+ icon_state = "medhud_visor"
+ hud_type = DATA_HUD_MEDICAL_ADVANCED
+
+//Diagnostic Visor - Gives you a diagnostic HUD.
+/obj/item/mod/module/visor/diaghud
+ name = "MOD diagnostic visor module"
+ desc = "A heads-up display installed into the visor of the suit. This uses a series of advanced sensors to access data \
+ from advanced machinery, exosuits, and other devices, allowing the user to visualize current power levels \
+ and integrity of such. They say these also let you see behind you."
+ icon_state = "diaghud_visor"
+ hud_type = DATA_HUD_DIAGNOSTIC_ADVANCED
+
+//Security Visor - Gives you a security HUD.
+/obj/item/mod/module/visor/sechud
+ name = "MOD security visor module"
+ desc = "A heads-up display installed into the visor of the suit. This module is a heavily-retrofitted targeting system, \
+ plugged into various criminal databases to be able to view arrest records, command simple security-oriented robots, \
+ and generally know who to shoot. They say these also let you see behind you."
+ icon_state = "sechud_visor"
+ hud_type = DATA_HUD_SECURITY_ADVANCED
+
+//Meson Visor - Gives you meson vision.
+/obj/item/mod/module/visor/meson
+ name = "MOD meson visor module"
+ desc = "A heads-up display installed into the visor of the suit. This module is based off well-loved meson scanner \
+ technology, used by construction workers and miners across the galaxy to see basic structural and terrain layouts \
+ through walls, regardless of lighting conditions. They say these also let you see behind you."
+ icon_state = "meson_visor"
+ visor_trait = TRAIT_MESON_VISION
+
+//Thermal Visor - Gives you thermal vision.
+/obj/item/mod/module/visor/thermal
+ name = "MOD thermal visor module"
+ desc = "A heads-up display installed into the visor of the suit. This uses a small IR scanner to detect and identify \
+ the thermal radiation output of objects near the user. While it can detect the heat output of even something as \
+ small as a rodent, it still produces irritating red overlay. They say these also let you see behind you."
+ icon_state = "thermal_visor"
+ origin_tech = "combat=6;engineering=6;syndicate=2"
+ visor_trait = TRAIT_THERMAL_VISION
+
+//Night Visor - Gives you night vision.
+/obj/item/mod/module/visor/night
+ name = "MOD night visor module"
+ desc = "A heads-up display installed into the visor of the suit. Typical for both civilian and military applications, \
+ this allows the user to perceive their surroundings while in complete darkness, enhancing the view by tenfold; \
+ yet brightening everything into a spooky green glow. They say these also let you see behind you."
+ icon_state = "night_visor"
+ origin_tech = "combat=5;engineering=5;syndicate=1"
+ visor_trait = TRAIT_NIGHT_VISION
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 6250daafdea5..75c2f99f0dd4 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -89,7 +89,7 @@
/obj/item/pen/fancy
name = "fancy pen"
- desc = "A fancy metal pen. It uses blue ink. An inscription on one side reads,\"L.L. - L.R.\""
+ desc = "A fancy metal pen. An inscription on one side reads, \"L.L. - L.R.\""
icon_state = "fancypen"
/obj/item/pen/multi/gold
@@ -127,17 +127,39 @@
if(reagents.total_volume && M.reagents)
transfered = reagents.trans_to(M, 50)
-
to_chat(user, "You sneakily stab [M] with the pen.")
add_attack_logs(user, M, "Stabbed with (sleepy) [src]. [transfered]u of reagents transfered from pen containing [english_list(contained)].")
+ for(var/datum/reagent/R as anything in reagents.reagent_list)
+ if(initial(R.id) == "????") // Yes this is a specific case that we don't really want
+ return TRUE
+ reagents.reaction(M, REAGENT_INGEST, 0.1)
return TRUE
/obj/item/pen/sleepy/Initialize(mapload)
. = ..()
create_reagents(100)
+ fill_pen()
+
+/obj/item/pen/sleepy/proc/fill_pen()
reagents.add_reagent("ketamine", 100)
+/obj/item/pen/sleepy/love
+ name = "fancy pen"
+ desc = "A fancy metal pen. An inscription on one side reads, \"L.L. - L.R.\""
+ icon_state = "fancypen"
+ container_type = DRAINABLE //cannot be refilled, love can be extracted for use in other items with syringe
+ origin_tech = "engineering=4;syndicate=2"
+
+/obj/item/pen/sleepy/love/attack(mob/living/M, mob/user)
+ var/can_transfer = reagents.total_volume && M.reagents
+ . = ..()
+ if(can_transfer && .)
+ M.apply_status_effect(STATUS_EFFECT_PACIFIED) //pacifies for 40 seconds
+ return TRUE
+
+/obj/item/pen/sleepy/love/fill_pen()
+ reagents.add_reagent("love", 100)
/*
* (Alan) Edaggers
diff --git a/code/modules/pda/app.dm b/code/modules/pda/app.dm
index d593b590ba93..cb4c4e19213a 100644
--- a/code/modules/pda/app.dm
+++ b/code/modules/pda/app.dm
@@ -40,8 +40,8 @@
pda.play_ringtone()
if(blink && !(src in pda.notifying_programs))
- pda.update_icon(UPDATE_OVERLAYS)
pda.notifying_programs |= src
+ pda.update_icon(UPDATE_OVERLAYS)
/datum/data/pda/proc/unnotify()
if(src in pda.notifying_programs)
diff --git a/code/modules/pda/messenger.dm b/code/modules/pda/messenger.dm
index 84e47b21cd35..a82f38fa06d6 100644
--- a/code/modules/pda/messenger.dm
+++ b/code/modules/pda/messenger.dm
@@ -185,7 +185,7 @@
useMS.send_pda_message("[P.owner]","[pda.owner]","[t]")
tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[html_decode(t)]", "target" = "[P.UID()]")))
PM.tnote.Add(list(list("sent" = 0, "owner" = "[pda.owner]", "job" = "[pda.ownjob]", "message" = "[html_decode(t)]", "target" = "[pda.UID()]")))
- pda.investigate_log("PDA Message - [U.key] - [pda.owner] -> [P.owner]: [t]", "pda")
+ pda.investigate_log("PDA Message - [pda.owner] ([U.key] [ADMIN_PP(U, "PP")]) -> [P.owner] ([ADMIN_VV(P, "VV")]), Message: \"[t]\"", "pda")
// Show it to ghosts
for(var/mob/M in GLOB.dead_mob_list)
diff --git a/code/modules/power/apc/apc.dm b/code/modules/power/apc/apc.dm
index a75c6f7f0593..38d6472ec4cd 100644
--- a/code/modules/power/apc/apc.dm
+++ b/code/modules/power/apc/apc.dm
@@ -1012,6 +1012,19 @@
to_chat(user, "You emag the APC interface.")
update_icon()
+/obj/machinery/power/apc/proc/apc_short()
+ // if it has internal wires, cut the power wires
+ if(wires)
+ if(!wires.is_cut(WIRE_MAIN_POWER1))
+ wires.cut(WIRE_MAIN_POWER1)
+ if(!wires.is_cut(WIRE_MAIN_POWER2))
+ wires.cut(WIRE_MAIN_POWER2)
+ // if it was operating, toggle off the breaker
+ if(operating)
+ toggle_breaker()
+ // no matter what, ensure the area knows something happened to the power
+ apc_area.powernet.power_change()
+
/// *************
/// APC subtypes
/// *************
diff --git a/code/modules/power/cables/cable.dm b/code/modules/power/cables/cable.dm
index bf0bfcfb030f..ff91ec6b020e 100644
--- a/code/modules/power/cables/cable.dm
+++ b/code/modules/power/cables/cable.dm
@@ -96,8 +96,8 @@ By design, d1 is the smallest direction and d2 is the highest
return
coil.cable_join(src, user)
- else if(istype(W, /obj/item/twohanded/rcl))
- var/obj/item/twohanded/rcl/R = W
+ else if(istype(W, /obj/item/rcl))
+ var/obj/item/rcl/R = W
if(R.loaded)
R.loaded.cable_join(src, user)
R.is_empty(user)
diff --git a/code/modules/power/engines/singularity/narsie.dm b/code/modules/power/engines/singularity/narsie.dm
index 41bf001c865e..a2424bad6651 100644
--- a/code/modules/power/engines/singularity/narsie.dm
+++ b/code/modules/power/engines/singularity/narsie.dm
@@ -30,7 +30,7 @@
icon_state = SSticker.cultdat?.entity_icon_state
name = SSticker.cultdat?.entity_name
- var/sound/cry = sound(pick('sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg'))
+ var/sound/cry = sound('modular_ss220/aesthetics_sounds/sound/narsie/narsie_risen.ogg') //SS220 EDIT
for(var/mob/living/player in GLOB.player_list)
if(isnewplayer(player))
@@ -69,9 +69,7 @@
/obj/singularity/narsie/large/attack_ghost(mob/dead/observer/user as mob)
user.forceMove(get_turf(src)) //make_new_construct spawns harvesters at observers locations, could be used to get into admin rooms/CC
- make_new_construct(/mob/living/simple_animal/hostile/construct/harvester, user, cult_override = TRUE)
- new /obj/effect/particle_effect/smoke/sleeping(user.loc)
-
+ make_new_construct(/mob/living/simple_animal/hostile/construct/harvester, user, cult_override = TRUE, create_smoke = TRUE)
/obj/singularity/narsie/process()
eat()
diff --git a/code/modules/power/engines/singularity/singularity.dm b/code/modules/power/engines/singularity/singularity.dm
index 4ce083c72768..cd63e924f61b 100644
--- a/code/modules/power/engines/singularity/singularity.dm
+++ b/code/modules/power/engines/singularity/singularity.dm
@@ -454,8 +454,8 @@
if(isbrain(M)) //Ignore brains
continue
- if(HAS_TRAIT(M, TRAIT_MESON_VISION))
- to_chat(M, "You look directly into [src], but your meson vision protects you!")
+ if(HAS_TRAIT(M, TRAIT_MESON_VISION) || HAS_TRAIT(M, SM_HALLUCINATION_IMMUNE))
+ to_chat(M, "You look directly into [src], but remain unaffected!")
return
M.Stun(6 SECONDS)
diff --git a/code/modules/power/engines/supermatter/supermatter.dm b/code/modules/power/engines/supermatter/supermatter.dm
index a4de8f13f46e..5bbefadad587 100644
--- a/code/modules/power/engines/supermatter/supermatter.dm
+++ b/code/modules/power/engines/supermatter/supermatter.dm
@@ -30,8 +30,8 @@
//Along with damage_penalty_point, makes flux anomalies.
/// The cutoff for the minimum amount of power required to trigger the crystal invasion delamination event.
#define EVENT_POWER_PENALTY_THRESHOLD 4500
-#define POWER_PENALTY_THRESHOLD 5000 //The cutoff on power properly doing damage, pulling shit around, and delamming into a tesla. Low chance of pyro anomalies, +2 bolts of electricity
-#define SEVERE_POWER_PENALTY_THRESHOLD 7000 //+1 bolt of electricity, allows for gravitational anomalies, and higher chances of pyro anomalies
+#define POWER_PENALTY_THRESHOLD 5000 //The cutoff on power properly doing damage, pulling shit around, and delamming into a tesla. Low chance of cryo anomalies, +2 bolts of electricity
+#define SEVERE_POWER_PENALTY_THRESHOLD 7000 //+1 bolt of electricity, allows for gravitational anomalies, and higher chances of cryo anomalies
#define CRITICAL_POWER_PENALTY_THRESHOLD 9000 //+1 bolt of electricity.
#define HEAT_PENALTY_THRESHOLD 40 //Higher == Crystal safe operational temperature is higher.
#define DAMAGE_HARDCAP 0.002
@@ -58,7 +58,7 @@
#define GRAVITATIONAL_ANOMALY "gravitational_anomaly"
#define FLUX_ANOMALY "flux_anomaly"
-#define PYRO_ANOMALY "pyro_anomaly"
+#define CRYO_ANOMALY "cryo_anomaly"
//If integrity percent remaining is less than these values, the monitor sets off the relevant alarm.
#define SUPERMATTER_DELAM_PERCENT 5
@@ -155,6 +155,8 @@
var/dynamic_heat_resistance = 1
///Uses powerloss_dynamic_scaling and combined_gas to lessen the effects of our powerloss functions
var/powerloss_inhibitor = 1
+ ///value plus T0C = temp at which the SM starts to take damage. Variable for event usage
+ var/heat_penalty_threshold = HEAT_PENALTY_THRESHOLD
///Based on co2 percentage, slowly moves between 0 and 1. We use it to calc the powerloss_inhibitor
var/powerloss_dynamic_scaling= 0
///Affects the amount of radiation the sm makes. We multiply this with power to find the rads.
@@ -175,6 +177,8 @@
var/obj/effect/warp_effect/supermatter/warp
///A variable to have the warp effect for singulo SM work properly
var/pulse_stage = 0
+ ///This list will hold 4 supermatter darkness effects when the supermatter is delaminating to a singulo delam. This lets me darken the area to look better, as it turns out, walls make the effect look ugly as shit.
+ var/list/darkness_effects = list()
///Boolean used for logging if we've been powered
var/has_been_powered = FALSE
@@ -204,10 +208,26 @@
///Disables the sm's proccessing totally.
var/processes = TRUE
+ //vars used for supermatter events (Anomalous crystal activityw)
+ /// Do we have an active event?
+ var/datum/supermatter_event/event_active
+ ///flat multiplies the amount of gas released by the SM.
+ var/gas_multiplier = 1
+ ///flat multiplies the heat released by the SM
+ var/heat_multiplier = 1
+ ///amount of EER to ADD
+ var/power_additive = 0
+ /// A list of all previous events
+ var/list/last_events = list()
+ /// Time of next event
+ var/next_event_time
+ /// Run S-Class event? So we can only run one S-class event per round per crystal
+ var/has_run_sclass = FALSE
+
+
/obj/machinery/atmospherics/supermatter_crystal/Initialize(mapload)
. = ..()
supermatter_id = global_supermatter_id++
- SSair.atmos_machinery += src
countdown = new(src)
countdown.start()
GLOB.poi_list |= src
@@ -234,18 +254,21 @@
if(is_main_engine && GLOB.main_supermatter_engine == src)
GLOB.main_supermatter_engine = null
QDEL_NULL(soundloop)
+ QDEL_NULL(darkness_effects)
return ..()
/obj/machinery/atmospherics/supermatter_crystal/examine(mob/user)
. = ..()
var/mob/living/carbon/human/H = user
if(istype(H))
- if(!HAS_TRAIT(H, TRAIT_MESON_VISION) && (get_dist(user, src) < HALLUCINATION_RANGE(power)))
+ if(!HAS_TRAIT(H, TRAIT_MESON_VISION) && !HAS_TRAIT(H, SM_HALLUCINATION_IMMUNE) && (get_dist(user, src) < HALLUCINATION_RANGE(power)))
. += "You get headaches just from looking at it."
. += "When actived by an item hitting this awe-inspiring feat of engineering, it emits radiation and heat. This is the basis of the use of the pseudo-perpetual energy source, the supermatter crystal."
. +="Any object that touches [src] instantly turns to dust, be it complex as a human or as simple as a metal rod. These bursts of energy can cause hallucinations if meson scanners are not worn near the crystal."
if(isAntag(user))
. += "Although a T.E.G. is more costly, there's a damn good reason the syndicate doesn't use this. If the integrity of [src] dips to 0%, perhaps from overheating, the supermatter will violently explode destroying nearly everything even somewhat close to it and releasing massive amounts of radiation."
+ if(moveable)
+ . += "It can be [anchored ? "unfastened from" : "fastened to"] the floor with a wrench."
/obj/machinery/atmospherics/supermatter_crystal/proc/get_status()
var/turf/T = get_turf(src)
@@ -279,8 +302,10 @@
switch(get_status())
if(SUPERMATTER_DELAMINATING)
playsound(src, 'sound/misc/bloblarm.ogg', 100, FALSE, 40, 30, falloff_distance = 10)
+ GLOB.major_announcement.Announce("WARNING, REACTOR CORE IS IN CRITICAL CHARGE!", "SUPERMATTER: STATUS CRITICAL", 'modular_ss220/aesthetics_sounds/sound/supermatter/meltdown.ogg') //SS220 EDIT - ADDITION
if(SUPERMATTER_EMERGENCY)
playsound(src, 'sound/machines/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
+ GLOB.major_announcement.Announce("WARNING, CORE OVERHEATTING. NUCLEAR KNOCKDOWN IMMINENT!", "SUPERMATTER: STATUS CRITICAL", 'modular_ss220/aesthetics_sounds/sound/supermatter/core_overheating.ogg') //SS220 EDIT - ADDITION
if(SUPERMATTER_DANGER)
playsound(src, 'sound/machines/engine_alert2.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
if(SUPERMATTER_WARNING)
@@ -375,6 +400,7 @@
// crystals.runEvent()
// return //No boom for me sir
//Dear mappers, balance the sm max explosion radius to 17.5, 37, 39, 41
+ playsound(src, 'modular_ss220/aesthetics_sounds/sound/supermatter/explode.ogg', 100, FALSE, 40, 30, falloff_distance = 10) //SS220 EDIT - ADDITION
explosion(get_turf(T), explosion_power * max(gasmix_power_ratio, 0.205) * 0.5 , explosion_power * max(gasmix_power_ratio, 0.205) + 2, explosion_power * max(gasmix_power_ratio, 0.205) + 4 , explosion_power * max(gasmix_power_ratio, 0.205) + 6, 1, 1)
qdel(src)
@@ -395,6 +421,10 @@
visible_message("[src] melts through [T]!")
return
+ try_events()
+ if(power > 100)
+ if(!has_been_powered)
+ enable_for_the_first_time()
//We vary volume by power, and handle OH FUCK FUSION IN COOLING LOOP noises.
if(power)
soundloop.volume = clamp((50 + (power / 50)), 50, 100)
@@ -435,7 +465,7 @@
//((((some value between 0.5 and 1 * temp - ((273.15 + 40) * some values between 1 and 10)) * some number between 0.25 and knock your socks off / 150) * 0.25
//Heat and mols account for each other, a lot of hot mols are more damaging then a few
//Mols start to have a positive effect on damage after 350
- damage = max(damage + (max(clamp(removed.total_moles() / 200, 0.5, 1) * removed.temperature - ((T0C + HEAT_PENALTY_THRESHOLD)*dynamic_heat_resistance), 0) * mole_heat_penalty / 150 ) * DAMAGE_INCREASE_MULTIPLIER, 0)
+ damage = max(damage + (max(clamp(removed.total_moles() / 200, 0.5, 1) * removed.temperature - ((T0C + heat_penalty_threshold)*dynamic_heat_resistance), 0) * mole_heat_penalty / 150 ) * DAMAGE_INCREASE_MULTIPLIER, 0)
//Power only starts affecting damage when it is above 5000
damage = max(damage + (max(power - POWER_PENALTY_THRESHOLD, 0)/500) * DAMAGE_INCREASE_MULTIPLIER, 0)
//Molar count only starts affecting damage when it is above 1800
@@ -445,7 +475,7 @@
//healing damage
if(combined_gas < MOLE_PENALTY_THRESHOLD)
//Only has a net positive effect when the temp is below 313.15, heals up to 2 damage. Psycologists increase this temp min by up to 45
- damage = max(damage + (min(removed.temperature - (T0C + HEAT_PENALTY_THRESHOLD), 0) / 150 ), 0)
+ damage = max(damage + (min(removed.temperature - (T0C + heat_penalty_threshold), 0) / 150 ), 0)
//Check for holes in the SM inner chamber
for(var/t in RANGE_TURFS(1, loc))
@@ -534,15 +564,15 @@
//Also keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock
//is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall.
//Power * 0.55 * (some value between 1.5 and 23) / 5
- removed.temperature += ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_MODIFIER)
+ removed.temperature += (((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_MODIFIER) * heat_multiplier)
//We can only emit so much heat, that being 57500
removed.temperature = max(0, min(removed.temperature, 2500 * dynamic_heat_modifier))
//Calculate how much gas to release
//Varies based on power and gas content
- removed.toxins += max((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER, 0)
+ removed.toxins += max(((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER) * gas_multiplier, 0)
//Varies based on power, gas content, and heat
- removed.oxygen += max(((device_energy + removed.temperature * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0)
+ removed.oxygen += max((((device_energy + removed.temperature * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER) * gas_multiplier, 0)
if(produces_gas)
env.merge(removed)
@@ -561,7 +591,7 @@
//Transitions between one function and another, one we use for the fast inital startup, the other is used to prevent errors with fusion temperatures.
//Use of the second function improves the power gain imparted by using co2
if(power_changes)
- power = max(power - min(((power / 500) ** 3) * powerloss_inhibitor, power * 0.83 * powerloss_inhibitor), 0)
+ power = max((power - min(((power / 500) ** 3) * powerloss_inhibitor, power * 0.83 * powerloss_inhibitor) + power_additive), 0)
//After this point power is lowered
//This wraps around to the begining of the function
//Handle high power zaps/anomaly generation
@@ -607,7 +637,7 @@
if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(5) || prob(1))
supermatter_anomaly_gen(src, GRAVITATIONAL_ANOMALY, rand(5, 10))
if((power > SEVERE_POWER_PENALTY_THRESHOLD && prob(2)) || (prob(0.3) && power > POWER_PENALTY_THRESHOLD))
- supermatter_anomaly_gen(src, PYRO_ANOMALY, rand(5, 10))
+ supermatter_anomaly_gen(src, CRYO_ANOMALY, rand(5, 10))
if(prob(15))
supermatter_pull(loc, min(power / 850, 3)) //850, 1700, 2550
@@ -645,7 +675,6 @@
//Boom (Mind blown)
if(damage > explosion_point)
countdown()
-
return 1
/obj/machinery/atmospherics/supermatter_crystal/bullet_act(obj/item/projectile/Proj)
@@ -658,9 +687,7 @@
if(power_changes) //This needs to be here I swear
power += Proj.damage * bullet_energy
if(!has_been_powered)
- investigate_log("has been powered for the first time.", "supermatter")
- message_admins("[src] has been powered for the first time [ADMIN_JMP(src)].")
- has_been_powered = TRUE
+ enable_for_the_first_time()
else if(takes_damage)
damage += Proj.damage * bullet_energy
return FALSE
@@ -800,6 +827,10 @@
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE)
Consume(AM)
+/obj/machinery/atmospherics/supermatter_crystal/Bump(atom/A, yes)
+ ..()
+ Bumped(A)
+
/obj/machinery/atmospherics/supermatter_crystal/proc/Consume(atom/movable/AM)
if(isliving(AM))
var/mob/living/user = AM
@@ -907,13 +938,34 @@
l_power = 3,
l_color = SUPERMATTER_TESLA_COLOUR,
)
- if(combined_gas > MOLE_PENALTY_THRESHOLD)
+ if(combined_gas > MOLE_PENALTY_THRESHOLD && get_integrity() > SUPERMATTER_DANGER_PERCENT)
set_light(
- l_range = 4 + clamp(damage / 2, 10, 50),
+ l_range = 4 + clamp((450 - damage) / 10, 1, 50),
l_power = 3,
l_color = SUPERMATTER_SINGULARITY_LIGHT_COLOUR,
)
+ if(!combined_gas > MOLE_PENALTY_THRESHOLD || !get_integrity() < SUPERMATTER_DANGER_PERCENT)
+ for(var/obj/D in darkness_effects)
+ qdel(D)
+ return
+ var/darkness_strength = clamp((damage - 450) / 75, 1, 8) / 2
+ var/darkness_aoe = clamp((damage - 450) / 25, 1, 25)
+ set_light(
+ l_range = 4 + darkness_aoe,
+ l_power = -1 - darkness_strength,
+ l_color = "#ddd6cf")
+ if(!length(darkness_effects) && moveable) //Don't do this on movable sms oh god. Ideally don't do this at all, but hey, that's lightning for you
+ darkness_effects += new /obj/effect/abstract(locate(x-3,y+3,z))
+ darkness_effects += new /obj/effect/abstract(locate(x+3,y+3,z))
+ darkness_effects += new /obj/effect/abstract(locate(x-3,y-3,z))
+ darkness_effects += new /obj/effect/abstract(locate(x+3,y-3,z))
+ else
+ for(var/obj/O in darkness_effects)
+ O.set_light(
+ l_range = 0 + darkness_aoe,
+ l_power = -1 - darkness_strength / 1.25,
+ l_color = "#ddd6cf")
/obj/effect/warp_effect/supermatter
plane = GRAVITY_PULSE_PLANE
@@ -986,8 +1038,8 @@
A.explosive = FALSE
if(GRAVITATIONAL_ANOMALY)
new /obj/effect/anomaly/grav(L, 250, FALSE, FALSE)
- if(PYRO_ANOMALY)
- new /obj/effect/anomaly/pyro(L, 200, FALSE)
+ if(CRYO_ANOMALY)
+ new /obj/effect/anomaly/cryo(L, 200, FALSE)
/obj/machinery/atmospherics/supermatter_crystal/proc/supermatter_zap(atom/zapstart = src, range = 5, zap_str = 4000, zap_flags = ZAP_SUPERMATTER_FLAGS, list/targets_hit = list())
if(QDELETED(zapstart))
@@ -1123,10 +1175,56 @@
power += amount
message_admins("[src] has been activated and given an increase EER of [amount] at [ADMIN_JMP(src)]")
+/obj/machinery/atmospherics/supermatter_crystal/proc/make_next_event_time()
+ // Some completely random bullshit to make a "bell curve"
+ var/fake_time = rand(5 MINUTES, 25 MINUTES)
+ if(fake_time < 15 MINUTES && prob(30))
+ fake_time += rand(2 MINUTES, 10 MINUTES)
+ else if(fake_time > 15 MINUTES && prob(30))
+ fake_time -= rand(2 MINUTES, 10 MINUTES)
+ next_event_time = fake_time + world.time
+
+/obj/machinery/atmospherics/supermatter_crystal/proc/try_events()
+ if(has_been_powered == FALSE)
+ return
+ if(!next_event_time) // for when the SM starts
+ make_next_event_time()
+ return
+ if(world.time < next_event_time)
+ return
+ if(event_active)
+ return
+ var/static/list/events = list(/datum/supermatter_event/delta_tier = 40,
+ /datum/supermatter_event/charlie_tier = 40,
+ /datum/supermatter_event/bravo_tier = 15,
+ /datum/supermatter_event/alpha_tier = 5,
+ /datum/supermatter_event/sierra_tier = 1)
+
+ var/datum/supermatter_event/event = pick(subtypesof(pickweight(events)))
+ if(istype(event, /datum/supermatter_event/sierra_tier) && has_run_sclass)
+ make_next_event_time()
+ return // We're only gonna have one s-class per round, take a break engineers
+ run_event(event)
+ make_next_event_time()
+
+/obj/machinery/atmospherics/supermatter_crystal/proc/run_event(datum/supermatter_event/event) // mostly admin testing and stuff
+ if(ispath(event))
+ event = new event(src)
+ if(!istype(event))
+ log_debug("Attempted supermatter event aborted due to incorrect path. Incorrect path type: [event.type].")
+ return
+ event.start_event()
+
+/obj/machinery/atmospherics/supermatter_crystal/proc/enable_for_the_first_time()
+ investigate_log("has been powered for the first time.", "supermatter")
+ message_admins("[src] has been powered for the first time [ADMIN_JMP(src)].")
+ has_been_powered = TRUE
+ make_next_event_time()
+
#undef HALLUCINATION_RANGE
#undef GRAVITATIONAL_ANOMALY
#undef FLUX_ANOMALY
-#undef PYRO_ANOMALY
+#undef CRYO_ANOMALY
#undef COIL
#undef ROD
#undef LIVING
diff --git a/code/modules/power/engines/supermatter/supermatter_event.dm b/code/modules/power/engines/supermatter/supermatter_event.dm
new file mode 100644
index 000000000000..a0a52205cac7
--- /dev/null
+++ b/code/modules/power/engines/supermatter/supermatter_event.dm
@@ -0,0 +1,210 @@
+/datum/supermatter_event
+ var/name = "Unknown X-K (Report this to coders)"
+ var/obj/machinery/atmospherics/supermatter_crystal/supermatter
+ var/datum/gas_mixture/environment
+ /// Probability of the event not running, higher tiers being rarer
+ var/threat_level
+ var/duration
+
+/datum/supermatter_event/New(obj/machinery/atmospherics/supermatter_crystal/_supermatter)
+ . = ..()
+ supermatter = _supermatter
+ if(!supermatter)
+ stack_trace("a /datum/supermatter_event was called without an involved supermatter.")
+ return
+ if(!istype(supermatter))
+ stack_trace("a /datum/supermatter_event was called with (name: [supermatter], type: [supermatter.type]) instead of a supermatter!")
+ return
+ var/turf/T = get_turf(supermatter)
+ environment = T.return_air()
+
+/datum/supermatter_event/proc/start_event()
+ supermatter.event_active = src
+ on_start()
+ alert_engi()
+ if(duration)
+ addtimer(CALLBACK(src, PROC_REF(on_end)), duration)
+
+/datum/supermatter_event/proc/on_start()
+ return
+
+/datum/supermatter_event/proc/alert_engi()
+ return
+
+/datum/supermatter_event/proc/on_end()
+ sm_radio_say("Anomalous crystal activity has ended.")
+ supermatter.heat_penalty_threshold = HEAT_PENALTY_THRESHOLD
+ supermatter.gas_multiplier = 1
+ supermatter.power_additive = 0
+ supermatter.heat_multiplier = 1
+ supermatter.event_active = null
+ supermatter.last_events += src
+
+/datum/supermatter_event/proc/sm_radio_say(text)
+ if(!text)
+ return
+ supermatter.radio.autosay(text, supermatter, "Engineering", list(supermatter.z))
+
+/datum/supermatter_event/proc/general_radio_say(text)
+ if(!text)
+ return
+ supermatter.radio.autosay(text, supermatter, null, list(supermatter.z))
+
+// Below this are procs used for the SM events, in order of severity
+
+//D class events
+
+/datum/supermatter_event/delta_tier
+ threat_level = SM_EVENT_THREAT_D
+ duration = 10 SECONDS
+
+/datum/supermatter_event/delta_tier/alert_engi()
+ sm_radio_say("Abnormal crystal activity detected! Activity class: [name].")
+
+// sleeping gas
+/datum/supermatter_event/delta_tier/sleeping_gas
+ name = "D-1"
+
+/datum/supermatter_event/delta_tier/sleeping_gas/on_start()
+ environment.sleeping_agent += 200
+
+// nitrogen
+/datum/supermatter_event/delta_tier/nitrogen
+ name = "D-2"
+
+/datum/supermatter_event/delta_tier/nitrogen/on_start()
+ environment.nitrogen += 200
+
+// carbon dioxide
+/datum/supermatter_event/delta_tier/carbon_dioxide
+ name = "D-3"
+
+/datum/supermatter_event/delta_tier/carbon_dioxide/on_start()
+ environment.carbon_dioxide += 250
+
+
+// C class events
+
+/datum/supermatter_event/charlie_tier
+ threat_level = SM_EVENT_THREAT_C
+ duration = 15 SECONDS
+
+/datum/supermatter_event/charlie_tier/alert_engi()
+ sm_radio_say("Anomalous crystal activity detected. Activity class: [name]. Operator intervention may be required.")
+
+// oxygen
+/datum/supermatter_event/charlie_tier/oxygen
+ name = "C-1"
+
+/datum/supermatter_event/charlie_tier/oxygen/on_start()
+ environment.oxygen += 250
+
+// plasma
+/datum/supermatter_event/charlie_tier/plasma
+ name = "C-2"
+
+/datum/supermatter_event/charlie_tier/plasma/on_start()
+ environment.toxins += 200
+
+// lowers the temp required for the SM to take damage.
+/datum/supermatter_event/charlie_tier/heat_penalty_threshold
+ name = "C-3"
+ duration = 5 MINUTES
+
+/datum/supermatter_event/charlie_tier/heat_penalty_threshold/on_start()
+ supermatter.heat_penalty_threshold -= -73
+
+//Class B events
+/datum/supermatter_event/bravo_tier
+ threat_level = SM_EVENT_THREAT_B
+ duration = 1 MINUTES
+
+/datum/supermatter_event/bravo_tier/alert_engi()
+ sm_radio_say("Anomalous crystal activity detected! Activity class: [name]. Operator intervention is required!")
+
+
+// more gas
+/datum/supermatter_event/bravo_tier/gas_multiply
+ name = "B-1"
+
+/datum/supermatter_event/bravo_tier/gas_multiply/on_start()
+ supermatter.gas_multiplier = 1.5
+
+
+/datum/supermatter_event/bravo_tier/heat_multiplier
+ name = "B-2"
+
+/datum/supermatter_event/bravo_tier/heat_multiplier/on_start()
+ supermatter.heat_multiplier = 1.25
+
+/datum/supermatter_event/bravo_tier/power_additive
+ name = "B-3"
+
+/datum/supermatter_event/bravo_tier/power_additive/on_start()
+ supermatter.power += 3000
+ duration = 10 SECONDS
+
+//A class events
+/datum/supermatter_event/alpha_tier
+ threat_level = SM_EVENT_THREAT_A
+ duration = 10 SECONDS
+
+/datum/supermatter_event/alpha_tier/alert_engi()
+ sm_radio_say("ALERT: Critical anomalous crystal activity detected! Activity class: [name]. IMMEDIATE Operator intervention is REQUIRED!")
+
+/datum/supermatter_event/alpha_tier/apc_short
+ name = "A-1"
+
+/datum/supermatter_event/alpha_tier/apc_short/on_start()
+ var/area/current_area = get_area(supermatter)
+ var/obj/machinery/power/apc/A = current_area.get_apc()
+ A.apc_short()
+
+/datum/supermatter_event/alpha_tier/air_siphon
+ name = "A-2"
+
+/datum/supermatter_event/alpha_tier/air_siphon/on_start()
+ var/area/current_area = get_area(supermatter)
+ for(var/obj/machinery/alarm/A in current_area)
+ A.apply_mode(AALARM_MODE_OFF)
+
+/datum/supermatter_event/alpha_tier/gas_multiplier
+ name = "A-3"
+ duration = 2 MINUTES
+
+/datum/supermatter_event/alpha_tier/gas_multiplier/on_start()
+ supermatter.gas_multiplier = 4
+
+// S-tier events are special. They are very dangerous, but give a 5 minute warning to the engis.
+/datum/supermatter_event/sierra_tier
+ threat_level = SM_EVENT_THREAT_S
+ duration = 7 MINUTES // 2 MINUTES of s-tier anomaly
+
+/datum/supermatter_event/sierra_tier/alert_engi()
+ general_radio_say("ALERT: Anomalous supermatter state expected in: 5 minutes.")
+ sm_radio_say("EMERGENCY ALERT: 5 MINUTES UNTIL [supermatter] EXHIBITS [name] CLASS ANOMALOUS ACTIVITY!")
+
+/datum/supermatter_event/sierra_tier/on_start()
+ addtimer(CALLBACK(src, PROC_REF(start_sierra_event)), 5 MINUTES)
+ supermatter.has_run_sclass = TRUE
+
+/datum/supermatter_event/sierra_tier/proc/start_sierra_event()
+ general_radio_say("ALERT: ANOMALOUS SUPERMATTER STATE DETECTED!")
+ sm_radio_say("EMERGENCY ALERT: Class [name] anomalous behavior in progress!")
+
+//S class events
+//Arc-type
+/datum/supermatter_event/sierra_tier/arc
+ name = "S-ARC"
+
+/datum/supermatter_event/sierra_tier/arc/start_sierra_event()
+ ..()
+ supermatter.power_additive = 6000
+
+// Laminate type
+/datum/supermatter_event/sierra_tier/laminate
+ name = "S-LAMINATE REJECTION"
+
+/datum/supermatter_event/sierra_tier/laminate/start_sierra_event()
+ ..()
+ supermatter.heat_multiplier = 10
diff --git a/code/modules/power/generators/portable generators/port_gen.dm b/code/modules/power/generators/portable generators/port_gen.dm
index 6c34b16da862..45e11ce9ac21 100644
--- a/code/modules/power/generators/portable generators/port_gen.dm
+++ b/code/modules/power/generators/portable generators/port_gen.dm
@@ -59,11 +59,13 @@
stat &= BROKEN
if(prob(75))
explode()
+ return
if(2)
if(prob(25))
stat &= BROKEN
if(prob(10))
explode()
+ return
if(3)
if(prob(10))
stat &= BROKEN
diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm
index 5311a2178871..53b7575d7163 100644
--- a/code/modules/projectiles/ammunition/ammo_casings.dm
+++ b/code/modules/projectiles/ammunition/ammo_casings.dm
@@ -152,7 +152,7 @@
icon_state = "cshell"
projectile_type = /obj/item/projectile/bullet/pellet/rubber
pellets = 6
- variance = 25
+ variance = 35
materials = list(MAT_METAL=4000)
@@ -173,7 +173,7 @@
projectile_type = /obj/item/projectile/bullet/pellet/weak
materials = list(MAT_METAL=250)
pellets = 10
- variance = 25
+ variance = 35
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
@@ -257,7 +257,7 @@
icon_state = "lshell"
projectile_type = /obj/item/projectile/beam/scatter
pellets = 8
- variance = 25
+ variance = 35
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
muzzle_flash_color = LIGHT_COLOR_DARKRED
diff --git a/code/modules/projectiles/ammunition/energy_lens.dm b/code/modules/projectiles/ammunition/energy_lens.dm
index 7348611df602..93f700959ae1 100644
--- a/code/modules/projectiles/ammunition/energy_lens.dm
+++ b/code/modules/projectiles/ammunition/energy_lens.dm
@@ -196,8 +196,8 @@
harmful = FALSE
/obj/item/ammo_casing/energy/wormhole/New(obj/item/gun/energy/wormhole_projector/wh)
- . = ..()
gun = wh
+ return ..()
/obj/item/ammo_casing/energy/wormhole/orange
projectile_type = /obj/item/projectile/beam/wormhole/orange
@@ -297,6 +297,10 @@
e_cost = 350 // about 42 shots on an engineering borg from a borging machine, Reads a lot better than it actually is because people miss shots and often your better abilities require charge as well
delay = 1 SECONDS
+/obj/item/ammo_casing/energy/emitter/cyborg/proto // needed a slightly weaker ranged option to give to Safety Overriden borgs. The fire rate is about the same as an emitter if you put it on the ground.
+ e_cost = 500
+ delay = 2 SECONDS
+
/obj/item/ammo_casing/energy/bsg
projectile_type = /obj/item/projectile/energy/bsg
muzzle_flash_color = LIGHT_COLOR_DARKBLUE
@@ -355,5 +359,5 @@
projectile_type = /obj/item/projectile/beam/silencer
muzzle_flash_effect = null
select_name = "silencing dissidents"
- e_cost = 62.5 // 16 shots
+ e_cost = 50 // 16 shots
fire_sound = 'sound/weapons/silencer_laser.ogg'
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index f175aadd5f83..c41149186c69 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -118,9 +118,10 @@
if(recoil)
shake_camera(user, recoil + 1, recoil)
- var/muzzle_range = chambered.muzzle_flash_range
- var/muzzle_strength = chambered.muzzle_flash_strength
+ var/muzzle_range = chambered?.muzzle_flash_range
+ var/muzzle_strength = chambered?.muzzle_flash_strength
var/muzzle_flash_time = 0.2 SECONDS
+
if(suppressed)
playsound(user, fire_sound, 10, TRUE, ignore_walls = FALSE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
muzzle_range *= 0.5
@@ -474,7 +475,7 @@
gun = null
return ..()
-/datum/action/toggle_scope_zoom/Trigger()
+/datum/action/toggle_scope_zoom/Trigger(left_click)
gun.zoom(owner)
/datum/action/toggle_scope_zoom/IsAvailable()
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 89788897814c..6b51e585fd1e 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -273,6 +273,10 @@
/obj/item/gun/energy/emitter/cyborg/emp_act()
return
+/obj/item/gun/energy/emitter/cyborg/proto
+ ammo_type = list(/obj/item/ammo_casing/energy/emitter/cyborg/proto)
+
+
////////Laser Tag////////////////////
/obj/item/gun/energy/laser/tag
diff --git a/code/modules/projectiles/guns/energy/special_eguns.dm b/code/modules/projectiles/guns/energy/special_eguns.dm
index 1a3cafbdc4d2..8cb90adf9c3e 100644
--- a/code/modules/projectiles/guns/energy/special_eguns.dm
+++ b/code/modules/projectiles/guns/energy/special_eguns.dm
@@ -204,7 +204,7 @@
name = "bluespace wormhole projector"
desc = "A projector that emits high density quantum-coupled bluespace beams."
ammo_type = list(/obj/item/ammo_casing/energy/wormhole, /obj/item/ammo_casing/energy/wormhole/orange)
- item_state = null
+ item_state = "wormhole_projector1"
icon_state = "wormhole_projector1"
origin_tech = "combat=4;bluespace=6;plasmatech=4;engineering=4"
charge_delay = 5
@@ -856,5 +856,54 @@
C.stop_tracking()
tracking_target_UID = null
+
+/obj/item/gun/energy/spikethrower //It's like the cyborg LMG, uses energy to make spikes
+ name = "\improper Vox spike thrower"
+ desc = "A vicious alien projectile weapon. Parts of it quiver gelatinously, as though the thing is insectile and alive."
+ icon = 'icons/obj/guns/projectile.dmi'
+ icon_state = "spikethrower"
+ item_state = "toxgun"
+ w_class = WEIGHT_CLASS_NORMAL
+ fire_sound_text = "a strange noise"
+ can_suppress = 0
+ burst_size = 2 // burst has to be stored here
+ can_charge = FALSE
+ selfcharge = TRUE
+ charge_delay = 10
+ restricted_species = list(/datum/species/vox)
+ ammo_type = list(/obj/item/ammo_casing/energy/spike)
+
+/obj/item/gun/energy/spikethrower/emp_act()
+ return
+
+/obj/item/ammo_casing/energy/spike
+ name = "alloy spike"
+ desc = "A broadhead spike made out of a weird silvery metal."
+ projectile_type = /obj/item/projectile/bullet/spike
+ muzzle_flash_effect = null
+ e_cost = 100
+ delay = 3 //and delay has to be stored here on energy guns
+ select_name = "spike"
+ fire_sound = 'sound/weapons/bladeslice.ogg'
+
+/obj/item/projectile/bullet/spike
+ name = "alloy spike"
+ desc = "It's about a foot of weird silvery metal with a wicked point."
+ damage = 25
+ knockdown = 2
+ armour_penetration_flat = 30
+ icon_state = "magspear"
+
+/obj/item/projectile/bullet/spike/on_hit(atom/target, blocked = 0)
+ if((blocked < 100) && ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.bleed(50)
+ ..()
+
+/obj/item/gun/energy/spikethrower/examine(mob/user)
+ . = ..()
+ . += "This item's cell recharges on its own. Known to drive people mad by forcing them to wait for shots to recharge. Not compatible with rechargers."
+
+
#undef PLASMA_CHARGE_USE_PER_SECOND
#undef PLASMA_DISCHARGE_LIMIT
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index fdbd3f43469a..5dbb10fd51b3 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -54,6 +54,11 @@
flight_y_offset = 10
can_holster = TRUE
+/obj/item/gun/energy/disabler/Initialize(mapload)
+ . = ..()
+ cell.maxcharge = 800
+ cell.charge = 800
+
/obj/item/gun/energy/disabler/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread)
var/obj/item/gun/energy/disabler/offhand_disabler = user.get_inactive_hand()
if(istype(offhand_disabler) && offhand_disabler.semicd && (user.a_intent != INTENT_HARM))
diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm
index b4f9fa404e36..63dbcf271c84 100644
--- a/code/modules/projectiles/guns/magic.dm
+++ b/code/modules/projectiles/guns/magic.dm
@@ -65,12 +65,18 @@
/obj/item/gun/magic/process()
+ // Don't start recharging until we lose a charge
+ if(charges >= max_charges)
+ charge_tick = 0
+ return FALSE
+
charge_tick++
- if(charge_tick < recharge_rate || charges >= max_charges)
- return 0
- charge_tick = 0
- charges++
- return 1
+ if(charge_tick >= recharge_rate)
+ charge_tick = 0
+ charges++
+ return TRUE
+ else
+ return FALSE
/obj/item/gun/magic/update_icon_state()
return
diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm
index 827c56ace2e2..f02ab5ea1811 100644
--- a/code/modules/projectiles/guns/projectile/revolver.dm
+++ b/code/modules/projectiles/guns/projectile/revolver.dm
@@ -91,6 +91,34 @@
. = ..()
. += "[get_ammo(0,0)] of those are live rounds."
+/obj/item/gun/projectile/revolver/fake
+
+/obj/item/gun/projectile/revolver/fake/examine(mob/user)
+ . = ..()
+ if(HAS_TRAIT(user, TRAIT_CLUMSY))
+ . += "Its mechanism seems to shoot backwards."
+
+/obj/item/gun/projectile/revolver/fake/process_fire(atom/target, mob/living/carbon/human/user, message, params, zone_override, bonus_spread)
+ var/zone = "chest"
+ if(user.has_organ("head"))
+ zone = "head"
+ add_fingerprint(user)
+ if(!chambered)
+ shoot_with_empty_chamber(user)
+ return
+ if(!chambered.fire(target = user, user = user, params = params, distro = null, quiet = suppressed, zone_override = zone, spread = 0, firer_source_atom = src))
+ shoot_with_empty_chamber(user)
+ return
+ process_chamber()
+ update_icon()
+ playsound(src, 'sound/weapons/gunshots/gunshot_strong.ogg', 50, TRUE)
+ user.visible_message("[src] goes off!")
+ to_chat(user, "[src] did look pretty dodgey!")
+ SEND_SOUND(user, sound('sound/misc/sadtrombone.ogg')) //HONK
+ user.apply_damage(300, BRUTE, zone, sharp = TRUE, used_weapon = "Self-inflicted gunshot wound to the [zone].")
+ user.bleed(BLOOD_VOLUME_NORMAL)
+ user.death() // Just in case
+
/obj/item/gun/projectile/revolver/fingergun //Summoned by the Finger Gun spell, from advanced mimery traitor item
name = "\improper finger gun"
desc = "Bang bang bang!"
diff --git a/code/modules/projectiles/guns/syringe_gun.dm b/code/modules/projectiles/guns/syringe_gun.dm
index 1864c65d3da8..c391431425f3 100644
--- a/code/modules/projectiles/guns/syringe_gun.dm
+++ b/code/modules/projectiles/guns/syringe_gun.dm
@@ -315,7 +315,7 @@
if(!S)
return
- chambered.BB = new S.projectile_type(src)
+ chambered.BB = new /obj/item/projectile/bullet/dart/syringe/pierce_ignore(src)
update_loaded_syringe()
chambered.BB.name = S.name
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index f255f5709f81..338d4c3fc264 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -12,6 +12,14 @@
damage = 5
stamina = 40
+/obj/item/projectile/bullet/weakbullet/on_hit(atom/target, blocked = 0)
+ . = ..()
+ if(isliving(target))
+ var/mob/living/L = target
+ if(L.move_resist < INFINITY)
+ var/atom/throw_target = get_edge_target_turf(L, get_dir(src, get_step_away(L, starting)))
+ L.throw_at(throw_target, 1, 2)
+
/obj/item/projectile/bullet/weakbullet/booze
/obj/item/projectile/bullet/weakbullet/booze/on_hit(atom/target, blocked = 0)
@@ -256,8 +264,13 @@
if(blocked != INFINITY)
if(M.can_inject(null, FALSE, hit_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
..()
+ for(var/datum/reagent/R as anything in reagents.reagent_list)
+ if(initial(R.id) == "????") // Yes this is a specific case that we don't really want
+ reagents.trans_to(M, reagents.total_volume)
+ return TRUE
reagents.trans_to(M, reagents.total_volume)
- return 1
+ reagents.reaction(M, REAGENT_INGEST, 0.1)
+ return TRUE
else
blocked = INFINITY
target.visible_message("[src] was deflected!", \
@@ -265,7 +278,7 @@
..(target, blocked, hit_zone)
reagents.set_reacting(TRUE)
reagents.handle_reactions()
- return 1
+ return TRUE
/obj/item/projectile/bullet/dart/metalfoam
@@ -281,6 +294,9 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "syringeproj"
+/obj/item/projectile/bullet/dart/syringe/pierce_ignore
+ piercing = TRUE
+
/obj/item/projectile/bullet/dart/syringe/tranquilizer
/obj/item/projectile/bullet/dart/syringe/tranquilizer/New()
diff --git a/code/modules/projectiles/projectile/special_projectiles.dm b/code/modules/projectiles/projectile/special_projectiles.dm
index 5559f70e878e..a2b5d607a6f8 100644
--- a/code/modules/projectiles/projectile/special_projectiles.dm
+++ b/code/modules/projectiles/projectile/special_projectiles.dm
@@ -195,8 +195,8 @@
color = "#FF6600"
/obj/item/projectile/beam/wormhole/New(obj/item/ammo_casing/energy/wormhole/casing)
- . = ..()
- if(casing)
+ ..()
+ if(istype(casing))
gun = casing.gun
/obj/item/projectile/beam/wormhole/on_hit(atom/target)
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 6b4f91ed28b7..01532fe16d21 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -313,7 +313,7 @@
if(stat & BROKEN)
return
if(!anchored)
- to_chat("[src] must be anchored first!")
+ to_chat(user, "[src] must be anchored first!")
return
ui_interact(user)
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index c4d8d05e04ed..b74f2f782270 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -4,6 +4,9 @@
#define MAX_UNITS_PER_PATCH 30 // Max amount of units in a patch
#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever
+#define TRANSFER_TO_DISPOSAL 0
+#define TRANSFER_TO_BEAKER 1
+
/obj/machinery/chem_master
name = "\improper ChemMaster 3000"
density = TRUE
@@ -15,7 +18,7 @@
var/obj/item/reagent_containers/beaker = null
var/obj/item/storage/pill_bottle/loaded_pill_bottle = null
- var/mode = 0
+ var/mode = TRANSFER_TO_BEAKER
var/condi = FALSE
var/useramount = 30 // Last used amount
var/pillamount = 10
@@ -351,10 +354,13 @@
if("create_pill")
if(condi || !reagents.total_volume)
return
- var/num = round(text2num(arguments["num"] || 1))
+
+ var/num = arguments["num"] || 1 // Multi puts a string in `num`, single leaves it null
+ num = clamp(round(text2num(num)), 0, MAX_MULTI_AMOUNT)
if(!num)
return
arguments["num"] = num
+
var/amount_per_pill = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PILL)
var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_pill]u)"
var/pills_text = num == 1 ? "new pill" : "[num] new pills"
@@ -371,10 +377,13 @@
if("create_patch")
if(condi || !reagents.total_volume)
return
- var/num = round(text2num(arguments["num"] || 1))
+
+ var/num = arguments["num"] || 1 // Multi puts a string in `num`, single leaves it null
+ num = clamp(round(text2num(num)), 0, MAX_MULTI_AMOUNT)
if(!num)
return
arguments["num"] = num
+
var/amount_per_patch = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PATCH)
var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_patch]u)"
var/patches_text = num == 1 ? "new patch" : "[num] new patches"
@@ -437,7 +446,7 @@
if("create_pill")
if(condi || !reagents.total_volume)
return
- var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
+ var/count = text2num(arguments["num"])
if(!count)
return
@@ -456,7 +465,7 @@
P.icon_state = "pill[pillsprite]"
reagents.trans_to(P, amount_per_pill)
// Load the pills in the bottle if there's one loaded
- if(istype(loaded_pill_bottle) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots)
+ if(istype(loaded_pill_bottle) && loaded_pill_bottle.can_be_inserted(P, TRUE))
P.forceMove(loaded_pill_bottle)
if("create_pill_multiple")
if(condi || !reagents.total_volume)
@@ -470,7 +479,7 @@
if("create_patch")
if(condi || !reagents.total_volume)
return
- var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
+ var/count = text2num(arguments["num"])
if(!count)
return
@@ -492,7 +501,7 @@
P.instant_application = TRUE
P.icon_state = "bandaid_med"
// Load the patches in the bottle if there's one loaded
- if(istype(loaded_pill_bottle, /obj/item/storage/pill_bottle/patch_pack) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots)
+ if(istype(loaded_pill_bottle) && loaded_pill_bottle.can_be_inserted(P, TRUE))
P.forceMove(loaded_pill_bottle)
if("create_patch_multiple")
if(condi || !reagents.total_volume)
@@ -560,3 +569,6 @@
#undef MAX_UNITS_PER_PILL
#undef MAX_UNITS_PER_PATCH
#undef MAX_CUSTOM_NAME_LEN
+
+#undef TRANSFER_TO_DISPOSAL
+#undef TRANSFER_TO_BEAKER
diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
index 40bf0c12e2a8..38097573b8e5 100644
--- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
+++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
@@ -70,11 +70,10 @@
/obj/item/reagent_containers/food/snacks/grown/watermelon = list("watermelonjuice" = 0),
/obj/item/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/berries/poison = list("poisonberryjuice" = 0),
+ /obj/item/reagent_containers/food/snacks/grown/pumpkin/blumpkin = list("blumpkinjuice" = 0), //order is important here as blumpkin is a subtype of pumpkin, if switched blumpkins will produce pumpkin juice
/obj/item/reagent_containers/food/snacks/grown/pumpkin = list("pumpkinjuice" = 0),
- /obj/item/reagent_containers/food/snacks/grown/pumpkin/blumpkin = list("blumpkinjuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/apple = list("applejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/grapes = list("grapejuice" = 0),
- /obj/item/reagent_containers/food/snacks/grown/grapes/green = list("grapejuice" = 0),
/obj/item/reagent_containers/food/snacks/grown/pineapple = list("pineapplejuice" = 0)
)
diff --git a/code/modules/reagents/chemistry/reagents/drugs.dm b/code/modules/reagents/chemistry/reagents/drugs.dm
index f804a1ffe955..7ca29a6b89db 100644
--- a/code/modules/reagents/chemistry/reagents/drugs.dm
+++ b/code/modules/reagents/chemistry/reagents/drugs.dm
@@ -262,7 +262,7 @@
if(prob(20))
if(ishuman(M))
var/mob/living/carbon/human/H = M
- H.vomit(lost_nutrition = 0, blood = TRUE, stun = FALSE)
+ H.vomit(lost_nutrition = 0, blood = TRUE, should_confuse = FALSE)
M.KnockDown(1 SECONDS)
else
update_flags |= M.adjustStaminaLoss(10, FALSE)
@@ -586,10 +586,72 @@
var/mob/living/carbon/human/H = M
H.physiology.stun_mod /= tenacity
H.physiology.stamina_mod /= tenacity
- H.vomit(blood = TRUE, stun = FALSE) // just a visual, very gritty. don't do drugs kids
+ H.vomit(blood = TRUE, should_confuse = FALSE) // just a visual, very gritty. don't do drugs kids
H.LoseBreath(10 SECONDS) // procs 5 times, mostly a visual thing. damage could stack to cause a slowdown.
H.Confused(10 SECONDS)
+/datum/reagent/happiness
+ name = "Happiness"
+ id = "happiness"
+ description = "Fills you with ecstasic numbness and causes minor brain damage. If overdosed, causes sudden mood swings and spikes in heart rate."
+ reagent_state = LIQUID
+ color = "#f2ff00"
+ overdose_threshold = 20
+ taste_description = "paint thinner"
+ shock_reduction = 20
+ allowed_overdose_process = TRUE
+ addiction_chance = 2 // fairly rare, but funny
+ addiction_chance_additional = 20
+ addiction_threshold = 20
+ minor_addiction = TRUE
+
+/datum/reagent/happiness/on_mob_life(mob/living/M)
+ var/update_flags = STATUS_UPDATE_NONE
+ if(prob(15))
+ M.emote(pick("laugh", "giggle", "smile", "grin"))
+ else if(prob(10))
+ to_chat(M, "You feel [pick("great", "good", "amazing", "really nice", "magical")]!")
+ else if(prob(1))
+ M.say("hehehe") // you WILL hehehe
+
+ if(prob(50))
+ M.AdjustConfused(-10 SECONDS) // same as degreaser
+ M.AdjustJitter(-10 SECONDS)
+ update_flags |= M.adjustBrainLoss(0.2, FALSE)
+ return ..() | update_flags
+
+/datum/reagent/happiness/overdose_process(mob/living/M, severity)
+ var/list/overdose_info = ..()
+ var/effect = overdose_info[REAGENT_OVERDOSE_EFFECT]
+ var/update_flags = overdose_info[REAGENT_OVERDOSE_FLAGS]
+
+ if(prob(15))
+ M.emote(pick("cry", "frown", "sulk", "gurgle"))
+ else if(prob(10))
+ to_chat(M, "You feel [pick("like shit", "terrible", "weak", "like a rhumba beat", "hollow")]!")
+ update_flags |= M.adjustBrainLoss(0.2, FALSE)
+ if(!prob(10 * (severity ** 2))) // 1 - 10, 2 - 40
+ return list(effect, update_flags)
+
+ var/static/list/good_messages = list("YES! YES!! YES!!", "I AM UNSTOPPABLE", "THIS IS GREAT", "THERE WILL NEVER BE ANYONE BETTER",
+ "GLORY IS MINE", "WE'RE SO BACK", "I AM FUCKING INVINCIBLE", "I'M HANGING IN THERE")
+ var/static/list/bad_messages = list("NO! NO!! NO!!", "OH MY GOD", "THEY'RE LOOKING AT ME", "KILLLL MEEE, KILLLLL ME",
+ "I CAN'T FUCKING TAKE IT ANYMORE", "ARRGH, IT'S OVER")
+
+ var/message = pick(good_messages)
+ var/class = "greenannounce" // theres not many good green classes
+ if(prob(50))
+ message = pick(bad_messages)
+ class = "danger"
+
+ M.Dizzy(50 SECONDS) // shaking with glee or fear, whichever you prefer
+ to_chat(M, "[message][pick("!", "!!", "!!!")]")
+ return list(effect, update_flags)
+
+/datum/reagent/happiness/has_heart_rate_increase()
+ return overdosed
+
+
/datum/reagent/thc
name = "Tetrahydrocannabinol"
id = "thc"
@@ -859,4 +921,100 @@
update_flags |= M.adjustBruteLoss(rand(1,5)*REAGENTS_EFFECT_MULTIPLIER, FALSE)
return list(0, update_flags)
+
+//surge+, used in supercharge implants
+/datum/reagent/surge_plus
+ name = "Surge Plus"
+ id = "surge_plus"
+ description = "A superconducting gel that overloads processors, causing an effect reportedly similar to benzodiazepines in synthetic units."
+ reagent_state = LIQUID
+ color = "#28b581"
+
+ process_flags = SYNTHETIC
+ overdose_threshold = 30
+ addiction_chance = 1
+ addiction_chance_additional = 20
+ addiction_threshold = 5
+ addiction_decay_rate = 0.2
+ taste_description = "silicon"
+
+/datum/reagent/surge_plus/on_mob_life(mob/living/M)
+ var/update_flags = STATUS_UPDATE_NONE
+ M.AdjustParalysis(-8 SECONDS)
+ M.AdjustStunned(-8 SECONDS)
+ M.AdjustWeakened(-8 SECONDS)
+ M.AdjustKnockDown(-8 SECONDS)
+ update_flags |= M.adjustStaminaLoss(-25, FALSE)
+ if(prob(5))
+ var/high_message = pick("You feel calm.", "You feel collected.", "You feel like you need to relax.")
+ if(prob(10))
+ high_message = "0100011101001111010101000101010001000001010001110100111101000110010000010101001101010100!"
+ to_chat(M, "[high_message]")
+
+ return ..() | update_flags
+
+/datum/reagent/surge_plus/overdose_process(mob/living/M, severity)
+ var/update_flags = STATUS_UPDATE_NONE
+ var/recent_consumption = holder.addiction_threshold_accumulated[type]
+ M.Jitter(40 SECONDS)
+ M.Stuttering(10 SECONDS)
+ if(prob(5 * DRAWBACK_CHANCE_MODIFIER(recent_consumption)))
+ to_chat(M, "Your circuits overheat!") // synth fever
+ M.bodytemperature += 30 * recent_consumption
+ M.Confused(2 SECONDS * recent_consumption)
+ if(prob(10))
+ to_chat(M, "You experience a violent electrical discharge!")
+ playsound(get_turf(M), 'sound/effects/eleczap.ogg', 75, TRUE)
+ var/icon/I = new('icons/obj/zap.dmi', "lightningend")
+ I.Turn(-135)
+ var/obj/effect/overlay/beam/B = new(get_turf(M))
+ B.pixel_x = rand(-20, 0)
+ B.pixel_y = rand(-20, 0)
+ B.icon = I
+ update_flags |= M.adjustFireLoss(rand(1, 5) * REAGENTS_EFFECT_MULTIPLIER, FALSE)
+ update_flags |= M.adjustBruteLoss(rand(1, 5) * REAGENTS_EFFECT_MULTIPLIER, FALSE)
+ return list(0, update_flags)
+
+//Servo Lube, supercharge
+/datum/reagent/lube/combat
+ name = "Combat-Lube"
+ id = "combatlube"
+ description = "Combat-Lube is a refined and enhanced lubricant which induces effect stronger than Methamphetamine in synthetic users by drastically reducing internal friction and increasing cooling capabilities."
+ overdose_threshold = 30
+ addiction_chance = 1
+ addiction_chance_additional = 20
+
+/datum/reagent/lube/combat/on_mob_add(mob/living/L)
+ ADD_TRAIT(L, TRAIT_GOTTAGOFAST, id)
+
+/datum/reagent/lube/combat/on_mob_life(mob/living/M)
+ M.SetSleeping(0)
+ M.SetDrowsy(0)
+
+ var/high_message = pick("You feel your servos whir!", "You feel like you need to go faster.", "You feel like you were just overclocked!")
+ if(prob(10))
+ high_message = "0100011101001111010101000101010001000001010001110100111101000110010000010101001101010100!"
+ if(prob(5))
+ to_chat(M, "[high_message]")
+ return ..()
+
+/datum/reagent/lube/combat/on_mob_delete(mob/living/M)
+ REMOVE_TRAIT(M, TRAIT_GOTTAGOFAST, id)
+ ..()
+
+/datum/reagent/lube/combat/overdose_process(mob/living/M, severity)
+ var/list/overdose_info = ..()
+ var/effect = overdose_info[REAGENT_OVERDOSE_EFFECT]
+ var/update_flags = overdose_info[REAGENT_OVERDOSE_FLAGS]
+ if(prob(20))
+ M.emote("ping")
+ if(prob(33))
+ M.visible_message("[M]'s hands flip out and flail everywhere!")
+ var/obj/item/I = M.get_active_hand()
+ if(I)
+ M.drop_item()
+ update_flags |= M.adjustFireLoss(5, FALSE)
+ update_flags |= M.adjustBrainLoss(3, FALSE)
+ return list(effect, update_flags)
+
#undef DRAWBACK_CHANCE_MODIFIER
diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm
index 8ae055aa399a..a6e86a50b8e2 100644
--- a/code/modules/reagents/chemistry/reagents/medicine.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine.dm
@@ -122,24 +122,25 @@
heart_rate_decrease = 1
taste_description = "a safe refuge"
+/datum/reagent/medicine/cryoxadone/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume, show_message = TRUE)
+ if(iscarbon(M))
+ if(method == REAGENT_INGEST && M.bodytemperature < TCRYO)
+ data = "Ingested"
+ if(show_message)
+ to_chat(M, "[src] freezes solid as it enters your body!") //Burn damage already happens on ingesting
+ ..()
+
/datum/reagent/medicine/cryoxadone/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
-
- var/external_temp
- if(istype(M.loc, /obj/machinery/atmospherics/unary/cryo_cell))
- var/obj/machinery/atmospherics/unary/cryo_cell/C = M.loc
- external_temp = C.air_contents.temperature
- else
- var/turf/T = get_turf(M)
- external_temp = T.temperature
-
- if(external_temp < TCRYO)
+ if(M.bodytemperature < TCRYO && data != "Ingested")
update_flags |= M.adjustCloneLoss(-4, FALSE)
update_flags |= M.adjustOxyLoss(-10, FALSE)
update_flags |= M.adjustToxLoss(-3, FALSE)
update_flags |= M.adjustBruteLoss(-12, FALSE)
update_flags |= M.adjustFireLoss(-12, FALSE)
-
+ M.Stun(4 SECONDS) //You freeze up, but get good healing. Stops use as a combat drug, or for meming on blobs in space.
+ if(M.stat == CONSCIOUS && prob(25)) //So people know what is going on outside cryo tubes, in the event someone weaponises this.
+ to_chat(M, "Your veins and muscles are freezing!")
if(ishuman(M))
var/mob/living/carbon/human/H = M
var/obj/item/organ/external/head/head = H.get_organ("head")
@@ -536,9 +537,6 @@
color = "#C8A5DC"
metabolization_rate = 0.3
overdose_threshold = 35
- addiction_chance = 1
- addiction_chance = 10
- addiction_threshold = 10
harmless = FALSE
taste_description = "stimulation"
@@ -855,7 +853,7 @@
if(volume < 20)
if(prob(10))
to_chat(H, "[overdose_message]")
@@ -863,7 +861,7 @@
if(prob(10))
to_chat(H, "You choke on congealed blood!")
H.AdjustLoseBreath(2 SECONDS)
- H.vomit(blood = TRUE, stun = FALSE)
+ H.vomit(blood = TRUE, should_confuse = FALSE)
else if(prob(10))
var/overdose_message = pick("You're seeing red!", "Your heartbeat thunders in your ears!", "Your veins writhe under your skin!")
to_chat(H, "[overdose_message]")
@@ -984,7 +982,9 @@
update_flags |= M.adjustToxLoss(2, FALSE)
update_flags |= M.adjustBruteLoss(1, FALSE)
if(prob(10))
- M.Stun(6 SECONDS)
+ to_chat(M, "It feels like every single one of your muscles is cramping at once!")
+ M.emote("scream")
+ M.Weaken(6 SECONDS)
return ..() | update_flags
diff --git a/code/modules/reagents/chemistry/reagents/misc_reagents.dm b/code/modules/reagents/chemistry/reagents/misc_reagents.dm
index f304e88adafc..f4584a967e1a 100644
--- a/code/modules/reagents/chemistry/reagents/misc_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/misc_reagents.dm
@@ -484,7 +484,7 @@
if(M.mind.assigned_role == "Clown")
update_flags |= M.adjustBruteLoss(-1.5 * REAGENTS_EFFECT_MULTIPLIER) //Screw those pesky clown beatings!
else
- M.AdjustDizzy(20 SECONDS, 0, 1000 SECONDS)
+ M.AdjustDizzy(20 SECONDS, 0, 100 SECONDS)
M.Druggy(30 SECONDS)
if(prob(10))
M.EyeBlurry(10 SECONDS)
diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic.dm
index 9960656e37ee..a1e6795070bc 100644
--- a/code/modules/reagents/chemistry/reagents/pyrotechnic.dm
+++ b/code/modules/reagents/chemistry/reagents/pyrotechnic.dm
@@ -209,7 +209,6 @@
if(!S.reagents)
S.create_reagents(volume)
S.reagents.add_reagent("thermite", volume)
- S.thermite = TRUE
if(S.active_hotspot)
S.reagents.temperature_reagents(S.active_hotspot.temperature, 10, 300)
diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm
index 1721d81d766e..16f1df0c91f9 100644
--- a/code/modules/reagents/chemistry/reagents/toxins.dm
+++ b/code/modules/reagents/chemistry/reagents/toxins.dm
@@ -49,7 +49,14 @@
/datum/reagent/slimejelly/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
- if(M.get_blood_id() != id) // no effect on slime people
+ var/mob/living/carbon/C = M
+ if(iscarbon(C) && C.mind?.has_antag_datum(/datum/antagonist/vampire))
+ M.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, M.nutrition + 10))
+ if(M.get_blood_id() != id)
+ M.blood_volume = min(M.blood_volume + REAGENTS_METABOLISM, BLOOD_VOLUME_NORMAL)
+ return ..() | update_flags
+
+ if(M.get_blood_id() != id)
if(prob(10))
to_chat(M, "Your insides are burning!")
update_flags |= M.adjustToxLoss(rand(2, 6) * REAGENTS_EFFECT_MULTIPLIER, FALSE) // avg 0.4 toxin per cycle, not unreasonable
@@ -68,7 +75,6 @@
B.basecolor = color
B.update_icon()
-
/datum/reagent/slimetoxin
name = "Mutation Toxin"
id = "mutationtoxin"
@@ -353,9 +359,16 @@
melted_something = TRUE
if(H.head && !(H.head.resistance_flags & ACID_PROOF))
- to_chat(H, "Your [H.head.name] melts away!")
- qdel(H.head)
melted_something = TRUE
+ if(istype(H.head, /obj/item/clothing/head/mod) && ismodcontrol(H.back))
+ var/obj/item/mod/control/C = H.back
+ var/name = H.head.name
+ C.seal_part(H.head, FALSE)
+ C.retract(null, H.head)
+ to_chat(H, "Your [name] melts away as your [C.name] performs emergency cleaning on the helmet, deactivating the suit!")
+ else
+ to_chat(H, "Your [H.head.name] melts away!")
+ qdel(H.head)
if(melted_something)
return
diff --git a/code/modules/reagents/chemistry/reagents/water.dm b/code/modules/reagents/chemistry/reagents/water.dm
index 7c248dbd8b76..6d909c8c3201 100644
--- a/code/modules/reagents/chemistry/reagents/water.dm
+++ b/code/modules/reagents/chemistry/reagents/water.dm
@@ -103,7 +103,7 @@
taste_description = "blood"
taste_mult = 1.3
-/datum/reagent/blood/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)
+/datum/reagent/blood/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume)
if(data && data["viruses"])
for(var/thing in data["viruses"])
var/datum/disease/D = thing
@@ -120,6 +120,7 @@
var/mob/living/carbon/C = M
if(C.mind?.has_antag_datum(/datum/antagonist/vampire))
C.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, C.nutrition + 10))
+ C.blood_volume = min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL)
..()
/datum/reagent/blood/on_new(list/data)
diff --git a/code/modules/reagents/chemistry/reagents_datum.dm b/code/modules/reagents/chemistry/reagents_datum.dm
index cc61576ee8be..001232a37e1e 100644
--- a/code/modules/reagents/chemistry/reagents_datum.dm
+++ b/code/modules/reagents/chemistry/reagents_datum.dm
@@ -72,8 +72,9 @@
if(can_become_addicted)
if(is_type_in_list(src, M.reagents.addiction_list))
to_chat(M, "You feel slightly better, but for how long?") //sate_addiction handles this now, but kept this for the feed back.
-
var/mob/living/carbon/C = M
+ if(C.mind?.has_antag_datum(/datum/antagonist/vampire))
+ return
if(method == REAGENT_INGEST && istype(C) && C.get_blood_id() == id)
if(id == "blood" && !(data?["blood_type"] in get_safe_blood(C.dna?.blood_type)) || C.dna?.species.name != data?["species"] && (data?["species_only"] || C.dna?.species.own_species_blood))
C.reagents.add_reagent("toxin", volume * 0.5)
@@ -120,15 +121,18 @@
return STATUS_UPDATE_NONE
/datum/reagent/proc/handle_addiction(mob/living/M, consumption_rate)
- if(addiction_chance && !is_type_in_list(src, M.reagents.addiction_list))
- M.reagents.addiction_threshold_accumulated[type] += consumption_rate
- var/current_threshold_accumulated = M.reagents.addiction_threshold_accumulated[type]
+ if(!addiction_chance)
+ return
+ M.reagents.addiction_threshold_accumulated[type] += consumption_rate
+ if(is_type_in_list(src, M.reagents.addiction_list))
+ return
+ var/current_threshold_accumulated = M.reagents.addiction_threshold_accumulated[type]
- if(addiction_threshold < current_threshold_accumulated && prob(addiction_chance) && prob(addiction_chance_additional))
- to_chat(M, "You suddenly feel invigorated and guilty...")
- var/datum/reagent/new_reagent = new type()
- new_reagent.last_addiction_dose = world.timeofday
- M.reagents.addiction_list.Add(new_reagent)
+ if(addiction_threshold < current_threshold_accumulated && prob(addiction_chance) && prob(addiction_chance_additional))
+ to_chat(M, "You suddenly feel invigorated and guilty...")
+ var/datum/reagent/new_reagent = new type()
+ new_reagent.last_addiction_dose = world.timeofday
+ M.reagents.addiction_list.Add(new_reagent)
/datum/reagent/proc/sate_addiction(mob/living/M) //reagents sate their own withdrawals
if(is_type_in_list(src, M.reagents.addiction_list))
@@ -287,3 +291,6 @@
if(M.healthdoll)
M.healthdoll.cached_healthdoll_overlays.Cut()
M.updatehealth("fakedeath reagent end")
+
+/datum/reagent/proc/has_heart_rate_increase()
+ return heart_rate_increase
diff --git a/code/modules/reagents/chemistry/recipes/drugs_reactions.dm b/code/modules/reagents/chemistry/recipes/drugs_reactions.dm
index 11db37ca83ce..d9d928ff7ec9 100644
--- a/code/modules/reagents/chemistry/recipes/drugs_reactions.dm
+++ b/code/modules/reagents/chemistry/recipes/drugs_reactions.dm
@@ -91,6 +91,14 @@
required_reagents = list("epinephrine" = 1, "atropine" = 1, "insulin" = 1)
result_amount = 3
+/datum/chemical_reaction/happiness
+ name = "Happiness"
+ id = "happiness"
+ result = "happiness"
+ required_reagents = list("space_drugs" = 1, "nitrogen" = 1, "oxygen" = 1, "sacid" = 2)
+ required_catalysts = list("plasma" = 5)
+ result_amount = 5
+
/datum/chemical_reaction/fliptonium
name = "fliptonium"
id = "fliptonium"
diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
index 3fdf4b8508a9..6fe535ff9d0e 100644
--- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm
+++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
@@ -532,6 +532,20 @@
log_game("[who] triggered an oil slime explosion at [COORD(extract_turf)].")
explosion(extract_turf, 1, 3, 6)
+/datum/chemical_reaction/oil_slick
+ name = "Oil Potion"
+ id = "O_potion"
+ result = null
+ required_reagents = list("blood" = 1)
+ result_amount = 1
+ required_container = /obj/item/slime_extract/oil
+ required_other = TRUE
+
+/datum/chemical_reaction/oil_slick/on_reaction(datum/reagents/holder)
+ SSblackbox.record_feedback("tally", "slime_cores_used", 1, type)
+ var/obj/item/slimepotion/oil_slick/P = new /obj/item/slimepotion/oil_slick
+ P.forceMove(get_turf(holder.my_atom))
+
//Light Pink
/datum/chemical_reaction/slimepotion2
name = "Slime Potion 2"
diff --git a/code/modules/reagents/reagent_containers/applicator.dm b/code/modules/reagents/reagent_containers/applicator.dm
index d17b4fea4b85..cd9603d0e7e1 100644
--- a/code/modules/reagents/reagent_containers/applicator.dm
+++ b/code/modules/reagents/reagent_containers/applicator.dm
@@ -48,11 +48,11 @@
/obj/item/reagent_containers/applicator/update_overlays()
. = ..()
if(reagents.total_volume)
- var/mutable_appearance/filling = mutable_appearance('icons/goonstation/objects/objects.dmi', "mender-fluid")
+ var/mutable_appearance/filling = mutable_appearance('modular_ss220/aesthetics/applicator/icons/applicator.dmi', "mender-fluid") // SS220 EDIT - ORIGINAL: icons/goonstation/objects/objects.dmi
filling.color = mix_color_from_reagents(reagents.reagent_list)
. += filling
var/reag_pct = round((reagents.total_volume / volume) * 100)
- var/mutable_appearance/applicator_bar = mutable_appearance('icons/goonstation/objects/objects.dmi', "app_e")
+ var/mutable_appearance/applicator_bar = mutable_appearance('modular_ss220/aesthetics/applicator/icons/applicator.dmi', "app_e") // SS220 EDIT - ORIGINAL: icons/goonstation/objects/objects.dmi
switch(reag_pct)
if(51 to 100)
applicator_bar.icon_state = "app_hf"
@@ -62,7 +62,7 @@
applicator_bar.icon_state = "app_e"
. += applicator_bar
-/obj/item/reagent_containers/applicator/attack(mob/living/M, mob/user)
+/obj/item/reagent_containers/applicator/proc/apply(mob/living/M, mob/user)
if(!reagents.total_volume)
to_chat(user, "[src] is empty!")
return
@@ -93,6 +93,11 @@
update_icon()
user.changeNext_move(CLICK_CD_MELEE)
+/obj/item/reagent_containers/applicator/attack(mob/living/M, mob/user)
+ return apply(M, user)
+
+/obj/item/reagent_containers/applicator/attack_self(mob/user)
+ return apply(user, user)
/obj/item/reagent_containers/applicator/proc/apply_to(mob/living/carbon/M, mob/user, multiplier = 1, show_message = TRUE)
var/total_applied_amount = applied_amount * multiplier
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 911a4a02aaf7..17b119a69d3d 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -18,7 +18,7 @@
var/ignore_flags = FALSE
var/safety_hypo = FALSE
-/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user)
+/obj/item/reagent_containers/hypospray/proc/apply(mob/living/M, mob/user)
if(!reagents.total_volume)
to_chat(user, "[src] is empty!")
return
@@ -46,9 +46,18 @@
var/contained = english_list(injected)
add_attack_logs(user, M, "Injected with [src] containing ([contained])", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null)
-
+ for(var/datum/reagent/R as anything in reagents.reagent_list)
+ if(initial(R.id) == "????") // Yes this is a specific case that we don't really want
+ return TRUE
+ reagents.reaction(M, REAGENT_INGEST, 0.1)
return TRUE
+/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user)
+ return apply(M, user)
+
+/obj/item/reagent_containers/hypospray/attack_self(mob/user)
+ return apply(user, user)
+
/obj/item/reagent_containers/hypospray/on_reagent_change()
if(safety_hypo && !emagged)
var/found_forbidden_reagent = FALSE
@@ -126,6 +135,11 @@
update_icon(UPDATE_ICON_STATE)
return TRUE
+/obj/item/reagent_containers/hypospray/autoinjector/attack_self(mob/user)
+ ..()
+ update_icon(UPDATE_ICON_STATE)
+ return TRUE
+
/obj/item/reagent_containers/hypospray/autoinjector/update_icon_state()
if(reagents.total_volume > 0)
icon_state = initial(icon_state)
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index b5fc7ce019a5..0bbe6956b928 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -22,7 +22,7 @@
if(!icon_state)
icon_state = "pill[rand(1, 20)]"
-/obj/item/reagent_containers/food/pill/attack(mob/living/carbon/M, mob/user, def_zone)
+/obj/item/reagent_containers/food/pill/proc/apply(mob/living/carbon/M, mob/user, def_zone)
if(!istype(M))
return FALSE
bitesize = reagents.total_volume
@@ -31,6 +31,12 @@
return TRUE
return FALSE
+/obj/item/reagent_containers/food/pill/attack(mob/living/carbon/M, mob/user, def_zone)
+ return apply(M, user)
+
+/obj/item/reagent_containers/food/pill/attack_self(mob/user)
+ return apply(user, user)
+
/obj/item/reagent_containers/food/pill/afterattack(obj/target, mob/user, proximity)
if(!proximity || !target.is_refillable())
return
@@ -87,12 +93,23 @@
icon_state = "pill8"
list_reagents = list("haloperidol" = 15)
+/obj/item/reagent_containers/food/pill/happy_psych
+ name = "mood stabilizer pill"
+ desc = "Used to temporarily alleviate anxiety and depression. Take only as prescribed."
+ icon_state = "pill_happy"
+ list_reagents = list("happiness" = 15, "mannitol" = 5)
+
/obj/item/reagent_containers/food/pill/happy
name = "happy pill"
- desc = "Happy happy joy joy!"
- icon_state = "pill18"
+ desc = "They have little happy faces on them and smell like marker pens."
+ icon_state = "pill_happy"
list_reagents = list("space_drugs" = 15, "sugar" = 15)
+/obj/item/reagent_containers/food/pill/happy/happiness
+ name = "fun pill"
+ desc = "Makes you feel real good!"
+ list_reagents = list("happiness" = 15)
+
/obj/item/reagent_containers/food/pill/zoom
name = "zoom pill"
desc = "Zoooom!"
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 44fc47d246ae..06c11acc50dd 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -18,6 +18,8 @@
var/projectile_type = /obj/item/projectile/bullet/dart/syringe
materials = list(MAT_METAL=10, MAT_GLASS=20)
container_type = TRANSPARENT
+ ///If this variable is true, the syringe will work through hardsuits / modsuits / biosuits.
+ var/penetrates_thick = FALSE
/obj/item/reagent_containers/syringe/Initialize(mapload)
. = ..()
@@ -63,7 +65,7 @@
var/mob/living/L
if(isliving(target))
L = target
- if(!L.can_inject(user, TRUE))
+ if(!L.can_inject(user, TRUE, penetrate_thick = penetrates_thick))
return
switch(mode)
@@ -120,7 +122,7 @@
return
if(L) //living mob
- if(!L.can_inject(user, TRUE))
+ if(!L.can_inject(user, TRUE, penetrate_thick = penetrates_thick))
return
if(L != user)
L.visible_message("[user] is trying to inject [L]!", \
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index b6db1c6c3031..9b4dfeb9ced7 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -316,6 +316,7 @@ GLOBAL_LIST_INIT(conveyor_switches, list())
/obj/machinery/conveyor_switch/proc/toggle(mob/user)
add_fingerprint(user)
+ playsound(loc, 'sound/machines/switch.ogg', 10, TRUE)
if(!allowed(user) && !user.can_advanced_admin_interact()) //this is in Para but not TG. I don't think there's any which are set anyway.
to_chat(user, "Access denied.")
return
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index 843204359f74..9fced0d5879b 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -1427,12 +1427,14 @@
for(var/atom/movable/AM in contents)
AM.forceMove(loc)
AM.pipe_eject(dir)
- if(isdrone(AM) || istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z
+ if(QDELETED(AM))
return
- spawn(5)
- if(QDELETED(AM))
+ if(isliving(AM))
+ var/mob/living/mob_to_immobilize = AM
+ if(isdrone(mob_to_immobilize) || istype(mob_to_immobilize, /mob/living/silicon/robot/syndicate/saboteur)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z
return
- AM.throw_at(target, 3, 1)
+ mob_to_immobilize.Immobilize(1 SECONDS)
+ AM.throw_at(target, 3, 1)
/obj/structure/disposaloutlet/screwdriver_act(mob/living/user, obj/item/I)
add_fingerprint(user)
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index b6a66781525f..3bc3a19c1c57 100644
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -148,7 +148,7 @@
amount = 25
max_amount = 25
resistance_flags = FLAMMABLE
- var/static/list/no_wrap = list(/obj/item/smallDelivery, /obj/structure/bigDelivery, /obj/item/evidencebag, /obj/structure/closet/body_bag, /obj/item/twohanded/required)
+ var/static/list/no_wrap = list(/obj/item/smallDelivery, /obj/structure/bigDelivery, /obj/item/evidencebag, /obj/structure/closet/body_bag)
/obj/item/stack/packageWrap/pre_attack(atom/A, mob/living/user, params)
. = ..()
@@ -198,6 +198,9 @@
return FALSE
D.init_welded = C.welded
C.welded = TRUE
+ else if (target.GetComponent(/datum/component/two_handed))
+ to_chat(user, "[target] is too unwieldy to wrap effectively.")
+ return FALSE
else
to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery.")
return FALSE
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index cf6faf757da4..49ccab63937c 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -692,7 +692,7 @@
id = "rcl"
build_type = AUTOLATHE
materials = list(MAT_METAL = 5000)
- build_path = /obj/item/twohanded/rcl
+ build_path = /obj/item/rcl
category = list("initial", "Construction")
//hacked autolathe recipes
diff --git a/code/modules/research/designs/bluespace_designs.dm b/code/modules/research/designs/bluespace_designs.dm
index 504991597b9e..8c94a2492ea7 100644
--- a/code/modules/research/designs/bluespace_designs.dm
+++ b/code/modules/research/designs/bluespace_designs.dm
@@ -90,3 +90,13 @@
materials = list(MAT_METAL = 75000, MAT_GLASS = 37500, MAT_SILVER = 3000)
build_path = /obj/item/rpd/bluespace
category = list("Bluespace")
+
+/datum/design/bluespaceshotglass
+ name = "Bluespace Shot Glass"
+ desc = "For when you need to make the Bartender's life extra hell."
+ req_tech = list("bluespace" = 5, "materials" = 3, "plasmatech" = 4)
+ id = "bluespaceshotglass"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1000, MAT_BLUESPACE = 500)
+ build_path = /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/bluespace
+ category = list("Bluespace")
diff --git a/code/modules/research/designs/modsuit_designs.dm b/code/modules/research/designs/modsuit_designs.dm
new file mode 100644
index 000000000000..e45d66c6f997
--- /dev/null
+++ b/code/modules/research/designs/modsuit_designs.dm
@@ -0,0 +1,367 @@
+/////////////////////////////////////////
+///////////////////MOD///////////////////
+/////////////////////////////////////////
+
+/datum/design/mod_shell
+ name = "MOD Shell"
+ desc = "A 'Cybersun Industries' designed shell for a Modular Suit."
+ id = "mod_shell"
+ build_type = MECHFAB
+ materials = list(MAT_METAL = 10000, MAT_PLASMA = 5000)
+ construction_time = 25 SECONDS
+ build_path = /obj/item/mod/construction/shell
+ category = list("MODsuit Construction")
+
+/datum/design/mod_helmet
+ name = "MOD Helmet"
+ desc = "A 'Cybersun Industries' designed helmet for a Modular Suit."
+ id = "mod_helmet"
+ build_type = MECHFAB
+ materials = list(MAT_METAL = 5000)
+ construction_time = 10 SECONDS
+ build_path = /obj/item/mod/construction/helmet
+ category = list("MODsuit Construction")
+
+/datum/design/mod_chestplate
+ name = "MOD Chestplate"
+ desc = "A 'Cybersun Industries' designed chestplate for a Modular Suit."
+ id = "mod_chestplate"
+ build_type = MECHFAB
+ materials = list(MAT_METAL = 5000)
+ construction_time = 10 SECONDS
+ build_path = /obj/item/mod/construction/chestplate
+ category = list("MODsuit Construction")
+
+/datum/design/mod_gauntlets
+ name = "MOD Gauntlets"
+ desc = "'Cybersun Industries' designed gauntlets for a Modular Suit."
+ id = "mod_gauntlets"
+ build_type = MECHFAB
+ materials = list(MAT_METAL = 5000)
+ construction_time = 10 SECONDS
+ build_path = /obj/item/mod/construction/gauntlets
+ category = list("MODsuit Construction")
+
+/datum/design/mod_boots
+ name = "MOD Boots"
+ desc = "'Cybersun Industries' designed boots for a Modular Suit."
+ id = "mod_boots"
+ build_type = MECHFAB
+ materials = list(MAT_METAL = 5000)
+ construction_time = 10 SECONDS
+ build_path = /obj/item/mod/construction/boots
+ category = list("MODsuit Construction")
+
+/datum/design/mod_plating
+ name = "MOD External Plating"
+ desc = "External plating for a MODsuit."
+ id = "mod_plating_standard"
+ build_type = MECHFAB
+ materials = list(MAT_METAL = 6000, MAT_GLASS = 3000, MAT_PLASMA = 1000)
+ construction_time = 15 SECONDS
+ build_path = /obj/item/mod/construction/plating
+ category = list("MODsuit Construction")
+
+/datum/design/mod_plating/engineering
+ name = "MOD Engineering Plating"
+ id = "mod_plating_engineering"
+ build_path = /obj/item/mod/construction/plating/engineering
+ materials = list(MAT_METAL = 6000, MAT_GLASS = 1000, MAT_GOLD = 2000, MAT_PLASMA = 1000)
+ locked = TRUE
+ access_requirement = list(ACCESS_ENGINE)
+
+/datum/design/mod_plating/atmospheric
+ name = "MOD Atmospheric Plating"
+ id = "mod_plating_atmospheric"
+ build_path = /obj/item/mod/construction/plating/atmospheric
+ materials = list(MAT_METAL = 6000, MAT_GLASS = 1000, MAT_TITANIUM = 2000, MAT_PLASMA = 1000)
+ locked = TRUE
+ access_requirement = list(ACCESS_ATMOSPHERICS)
+
+/datum/design/mod_plating/medical
+ name = "MOD Medical Plating"
+ id = "mod_plating_medical"
+ build_path = /obj/item/mod/construction/plating/medical
+ materials = list(MAT_METAL = 6000, MAT_GLASS = 1000, MAT_SILVER = 2000, MAT_PLASMA = 1000)
+ locked = TRUE
+ access_requirement = list(ACCESS_MEDICAL)
+
+/datum/design/mod_plating/security
+ name = "MOD Security Plating"
+ id = "mod_plating_security"
+ build_path = /obj/item/mod/construction/plating/security
+ materials = list(MAT_METAL = 6000, MAT_GLASS = 1000, MAT_URANIUM = 2000, MAT_PLASMA = 1000)
+ locked = TRUE
+ access_requirement = list(ACCESS_SECURITY)
+
+/datum/design/mod_plating/cosmohonk
+ name = "MOD Cosmohonk Plating"
+ id = "mod_plating_cosmohonk"
+ build_path = /obj/item/mod/construction/plating/cosmohonk
+ materials = list(MAT_METAL = 6000, MAT_GLASS = 1000, MAT_BANANIUM = 2000, MAT_PLASMA = 1000)
+ locked = TRUE
+ access_requirement = list(ACCESS_CLOWN)
+
+/datum/design/mod_skin
+ name = "MOD Civilian Skin"
+ desc = "A skin applier for a modsuit."
+ id = "mod_skin_civilian"
+ build_type = MECHFAB
+ materials = list(MAT_METAL = 6000, MAT_GLASS = 3000, MAT_PLASMA = 1000)
+ construction_time = 5 SECONDS
+ build_path = /obj/item/mod/skin_applier
+ category = list("MODsuit Construction")
+
+/datum/design/mod_skin/corpsman
+ name = "MOD Corpsman Skin"
+ id = "mod_skin_corpsman"
+ build_path = /obj/item/mod/skin_applier/corpsman
+
+/datum/design/module
+ name = "Storage Module"
+ id = "mod_storage"
+ build_type = MECHFAB
+ construction_time = 5 SECONDS
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 10000)
+ build_path = /obj/item/mod/module/storage
+ category = list("MODsuit Modules")
+
+/datum/design/module/mod_storage_expanded
+ name = "Expanded Storage Module"
+ id = "mod_storage_expanded"
+ req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 6, "syndicate" = 2)
+ materials = list(MAT_METAL = 2500, MAT_URANIUM = 10000)
+ build_path = /obj/item/mod/module/storage/large_capacity
+
+/datum/design/module/mod_storage_syndicate
+ name = "Syndicate Storage Module"
+ id = "mod_storage_syndicate"
+ req_tech = list("materials" = 7, "powerstorage" = 7, "engineering" = 7, "syndicate" = 4) // 3 felt too low.
+ materials = list(MAT_METAL = 12000, MAT_GLASS = 2000, MAT_SILVER = 4000, MAT_PLASMA = 4000, MAT_TITANIUM = 4000, MAT_BLUESPACE = 6000) //Requires Evidence Raid to function.
+ build_path = /obj/item/mod/module/storage/syndicate
+
+/datum/design/module/mod_visor_medhud
+ name = "Medical Visor Module"
+ id = "mod_visor_medhud"
+ req_tech = list("materials" = 5, "programming" = 4, "biotech" = 4)
+ materials = list(MAT_SILVER = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/visor/medhud
+
+/datum/design/module/mod_visor_diaghud
+ name = "Diagnostic Visor Module"
+ id = "mod_visor_diaghud"
+ req_tech = list("materials" = 5, "engineering" = 4, "programming" = 4, "biotech" = 4)
+ materials = list(MAT_GOLD = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/visor/diaghud
+
+/datum/design/module/mod_visor_sechud
+ name = "Security Visor Module"
+ id = "mod_visor_sechud"
+ req_tech = list("materials" = 5, "programming" = 4, "biotech" = 4, "combat" = 3)
+ materials = list(MAT_TITANIUM = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/visor/sechud
+
+/datum/design/module/mod_visor_meson
+ name = "Meson Visor Module"
+ id = "mod_visor_meson"
+ req_tech = list("materials" = 4, "biotech" = 4, "engineering" = 4)
+ materials = list(MAT_URANIUM = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/visor/meson
+
+/datum/design/module/mod_visor_welding
+ name = "Welding Protection Module"
+ id = "mod_welding"
+ req_tech = list("materials" = 4, "biotech" = 4, "engineering" = 5, "plasmatech" = 4)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/welding
+
+/datum/design/module/mod_t_ray
+ name = "T-Ray Scanner Module"
+ id = "mod_t_ray"
+ req_tech = list("materials" = 2, "engineering" = 2)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/t_ray
+
+/datum/design/module/mod_stealth
+ name = "Cloak Module"
+ id = "mod_stealth"
+ req_tech = list("combat" = 7, "magnets" = 6, "syndicate" = 3)
+ materials = list(MAT_METAL = 12000, MAT_GLASS = 2000, MAT_SILVER = 4000, MAT_PLASMA = 4000, MAT_TITANIUM = 4000, MAT_BLUESPACE = 6000) //It's a cloaking device, while not foolproof I am making it expencive
+ build_path = /obj/item/mod/module/stealth
+
+/datum/design/module/mod_jetpack
+ name = "Ion Jetpack Module"
+ id = "mod_jetpack"
+ req_tech = list("materials" = 7, "magnets" = 7, "engineering" = 7)
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000) //Jetpacks are rare, so might as well make it... sorta expencive, I guess.
+ build_path = /obj/item/mod/module/jetpack
+
+/datum/design/module/mod_magboot
+ name = "Magnetic Stabilizator Module"
+ id = "mod_magboot"
+ req_tech = list("materials" = 4, "magnets" = 4, "engineering" = 5)
+ materials = list(MAT_METAL = 4500, MAT_SILVER = 1500, MAT_GOLD = 2500)
+ build_path = /obj/item/mod/module/magboot
+
+/datum/design/module/mod_rad_protection
+ name = "Radiation Protection Module"
+ id = "mod_rad_protection"
+ req_tech = list("materials" = 4, "magnets" = 4, "combat" = 5)
+ materials = list(MAT_URANIUM = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/rad_protection
+
+/datum/design/module/mod_emp_shield
+ name = "EMP Shield Module"
+ id = "mod_emp_shield"
+ req_tech = list("combat" = 7, "magnets" = 6, "syndicate" = 3)
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000) //While you are not EMP proof with this, your modules / cell are, and that is quite strong.
+ build_path = /obj/item/mod/module/emp_shield
+
+/datum/design/module/mod_flashlight
+ name = "Flashlight Module"
+ id = "mod_flashlight"
+ req_tech = list("magnets" = 2, "engineering" = 2, "plasmatech" = 2)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/flashlight
+
+
+/datum/design/module/mod_tether
+ name = "Emergency Tether Module"
+ id = "mod_tether"
+ req_tech = list("materials" = 4, "magnets" = 4, "engineering" = 5)
+ materials = list(MAT_METAL = 4500, MAT_SILVER = 1500, MAT_GOLD = 2500)
+ build_path = /obj/item/mod/module/tether
+
+
+/datum/design/module/mod_reagent_scanner
+ name = "Reagent Scanner Module"
+ id = "mod_reagent_scanner"
+ req_tech = list("magnets" = 2, "engineering" = 2, "plasmatech" = 2)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/reagent_scanner
+
+/datum/design/module/mod_gps
+ name = "Internal GPS Module"
+ id = "mod_gps"
+ req_tech = list("materials" = 2, "bluespace" = 2)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/gps
+
+/datum/design/module/mod_thermal_regulator
+ name = "Thermal Regulator Module"
+ id = "mod_thermal_regulator"
+ req_tech = list("materials" = 3, "plasmatech" = 3, "magnets" = 2)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/thermal_regulator
+
+/datum/design/module/mod_injector
+ name = "Injector Module"
+ id = "mod_injector"
+ req_tech = list("biotech" = 4, "materials" = 6, "magnets" = 5)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/injector
+
+/datum/design/module/defibrillator
+ name = "Defibrillator Module"
+ id = "mod_defib"
+ req_tech = list("materials" = 7, "biotech" = 7, "powerstorage" = 6)
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 4000, MAT_SILVER = 2000)
+ build_path = /obj/item/mod/module/defibrillator
+
+/datum/design/module/mod_bikehorn
+ name = "Bike Horn Module"
+ id = "mod_bikehorn"
+ req_tech = list("programming" = 3, "materials" = 3)
+ materials = list(MAT_METAL = 2500, MAT_BANANIUM = 2000)
+ build_path = /obj/item/mod/module/bikehorn
+
+/datum/design/module/mod_waddle
+ name = "Waddle Module"
+ id = "mod_waddle"
+ req_tech = list("programming" = 3, "materials" = 3)
+ materials = list(MAT_METAL = 2500, MAT_BANANIUM = 2000)
+ build_path = /obj/item/mod/module/waddle
+
+/datum/design/module/mod_clamp
+ name = "Crate Clamp Module"
+ id = "mod_clamp"
+ req_tech = list("programming" = 3, "materials" = 3)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/clamp
+
+/datum/design/module/mod_drill
+ name = "Drill Module"
+ id = "mod_drill"
+ req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 5)
+ materials = list(MAT_METAL = 12500, MAT_DIAMOND = 4000) //This drills **really** fast
+ build_path = /obj/item/mod/module/drill
+
+/datum/design/module/mod_orebag
+ name = "Ore Bag Module"
+ id = "mod_orebag"
+ req_tech = list("materials" = 2, "powerstorage" = 2, "engineering" = 3)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/orebag
+
+/datum/design/module/mod_dna_lock
+ name = "DNA Lock Module"
+ id = "mod_dna_lock"
+ req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 6, "syndicate" = 2)
+ materials = list(MAT_METAL = 12500, MAT_DIAMOND = 4000) //EMP beats it, but still, anti theft is a premium price in these here parts partner
+ build_path = /obj/item/mod/module/dna_lock
+
+/datum/design/module/mod_holster
+ name = "Holster Module"
+ id = "mod_holster"
+ req_tech = list("materials" = 2, "powerstorage" = 2, "engineering" = 3)
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 5000)
+ build_path = /obj/item/mod/module/holster
+
+/datum/design/module/mod_sonar
+ name = "Active Sonar Module"
+ id = "mod_sonar"
+ req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 5)
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000)
+ build_path = /obj/item/mod/module/active_sonar
+
+/datum/design/module/pathfinder
+ name = "Pathfinder Module"
+ id = "mod_pathfinder"
+ req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 5)
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000)
+ build_path = /obj/item/mod/module/pathfinder
+
+/datum/design/module/plasma_stabilizer
+ name = "Plasma Stabilizer Module"
+ id = "mod_plasmastable"
+ req_tech = list("materials" = 2, "powerstorage" = 2, "engineering" = 3)
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 4000, MAT_SILVER = 2000)
+ build_path = /obj/item/mod/module/plasma_stabilizer
+
+/datum/design/module/plate_compression
+ name = "Plate Compression Module"
+ id = "mod_compression"
+ req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 6, "syndicate" = 2)
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000)
+ build_path = /obj/item/mod/module/plate_compression
+
+/datum/design/module/status_readout
+ name = "Status Readout Module"
+ id = "mod_status_readout"
+ req_tech = list("materials" = 5, "powerstorage" = 5, "biotech" = 6, "syndicate" = 2)
+ materials = list(MAT_METAL = 10000, MAT_GLASS = 4000, MAT_SILVER = 2000)
+ build_path = /obj/item/mod/module/status_readout
+
+/datum/design/module/mod_teleporter
+ name = "Teleporter Module"
+ id = "mod_teleporter"
+ req_tech = list("combat" = 5, "engineering" = 5, "bluespace" = 7, "plasmatech" = 6)
+ materials = list(MAT_METAL = 12000, MAT_GLASS = 2000, MAT_SILVER = 4000, MAT_PLASMA = 4000, MAT_TITANIUM = 4000, MAT_BLUESPACE = 6000) //Requires bluespace anomaly core to function.
+ build_path = /obj/item/mod/module/anomaly_locked/teleporter
+
+/datum/design/module/mod_kinesis
+ name = "Kinesis Module"
+ id = "mod_kinesis"
+ req_tech = list("combat" = 5, "engineering" = 5, "bluespace" = 7, "plasmatech" = 6)
+ materials = list(MAT_METAL = 12000, MAT_GLASS = 2000, MAT_SILVER = 4000, MAT_PLASMA = 4000, MAT_TITANIUM = 4000, MAT_BLUESPACE = 6000) //Requires Gravitational anomaly core to function.
+ build_path = /obj/item/mod/module/anomaly_locked/kinesis
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index abd557236a06..e439addf30a7 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -447,6 +447,57 @@
if(loc == usr && loc.Adjacent(over_object))
afterattack(over_object, usr, TRUE)
+/obj/item/slimepotion/oil_slick
+ name = "slime oil potion"
+ desc = "A potent chemical mix that will remove the slowdown from any item by reducing friction. Doesn't mix well with water."
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "bottle4"
+ origin_tech = "biotech=5"
+
+/obj/item/slimepotion/oil_slick/afterattack(obj/O, mob/user, proximity_flag)
+ if(!proximity_flag)
+ return
+ ..()
+ if(!isitem(O))
+ if(!istype(O, /obj/structure/table))
+ to_chat(user, "The potion can only be used on items!")
+ return
+ var/obj/structure/table/T = O
+ if(T.slippery)
+ to_chat(user, "[T] can luckily not be made any slippier!")
+ return
+ to_chat(user, "You go to place the potion on [T], but before you know it, your hands are moving on your own!") //Speed table must remain.
+ T.slippery = TRUE
+ else
+ var/obj/item/I = O
+ if(I.slowdown <= 0)
+ to_chat(user, "[I] can't be made any faster!")
+ return
+ I.slowdown = 0
+ if(ismodcontrol(O))
+ var/obj/item/mod/control/C = O
+ if(C.active)
+ to_chat(user, "It is too dangerous to smear [src] on [C] while it is active!")
+ return
+ C.slowdown_inactive = 0
+ C.slowdown_active = 0
+ C.update_speed()
+
+ to_chat(user, "You slather the oily gunk over [O], making it slick and slippery.")
+ O.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
+ O.add_atom_colour("#6e6e86", FIXED_COLOUR_PRIORITY)
+ ADD_TRAIT(O, TRAIT_OIL_SLICKED, "potion")
+ if(ishuman(O.loc))
+ var/mob/living/carbon/human/H = O.loc
+ H.regenerate_icons()
+ qdel(src)
+
+/obj/item/slimepotion/oil_slick/MouseDrop(obj/over_object)
+ if(usr.incapacitated())
+ return
+ if(loc == usr && loc.Adjacent(over_object))
+ afterattack(over_object, usr, TRUE)
+
/obj/effect/timestop
anchored = TRUE
name = "chronofield"
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index 8c90fb02e9b3..bae9afc7f69d 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -40,22 +40,22 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
/mob/dead/observer/proc/JoinResponseTeam()
if(!GLOB.send_emergency_team)
to_chat(src, "No emergency response team is currently being sent.")
- return 0
+ return FALSE
if(jobban_isbanned(src, ROLE_ERT))
to_chat(src, "You are jobbanned from playing on an emergency response team!")
- return 0
+ return FALSE
var/player_age_check = check_client_age(client, GLOB.responseteam_age)
if(player_age_check && GLOB.configuration.gamemode.antag_account_age_restriction)
to_chat(src, "This role is not yet available to you. You need to wait another [player_age_check] days.")
- return 0
+ return FALSE
if(cannotPossess(src))
to_chat(src, "Upon using the antagHUD you forfeited the ability to join the round.")
- return 0
+ return FALSE
- return 1
+ return TRUE
/proc/trigger_armed_response_team(datum/response_team/response_team_type, commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots, cyborg_security)
GLOB.response_team_members = list()
@@ -65,7 +65,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
GLOB.send_emergency_team = TRUE
var/list/ert_candidates = shuffle(SSghost_spawns.poll_candidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 45 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_ERT]))
- if(!ert_candidates.len)
+ if(!length(ert_candidates))
GLOB.active_team.cannot_send_team()
GLOB.send_emergency_team = FALSE
return
@@ -76,12 +76,15 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
continue
if((M in GLOB.respawnable_list) && M.JoinResponseTeam())
GLOB.response_team_members |= M
+ M.RegisterSignal(M, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/mob/dead/observer, remove_from_ert_list), TRUE)
+
// If there's still open slots, non-respawnable players can fill them
for(var/mob/dead/observer/M in (ert_candidates - GLOB.respawnable_list))
if(M.JoinResponseTeam())
GLOB.response_team_members |= M
+ M.RegisterSignal(M, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/mob/dead/observer, remove_from_ert_list), TRUE)
- if(!GLOB.response_team_members.len)
+ if(!length(GLOB.response_team_members))
GLOB.active_team.cannot_send_team()
GLOB.send_emergency_team = FALSE
return
@@ -220,6 +223,10 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE)
return M
+/mob/dead/observer/proc/remove_from_ert_list(ghost)
+ SIGNAL_HANDLER
+ GLOB.response_team_members -= src
+ remove_from_respawnable_list()
/datum/response_team
var/list/slots = list(
diff --git a/code/modules/response_team/ert_outfits.dm b/code/modules/response_team/ert_outfits.dm
index 4a686877fe48..4f760df64cc4 100644
--- a/code/modules/response_team/ert_outfits.dm
+++ b/code/modules/response_team/ert_outfits.dm
@@ -36,7 +36,7 @@
rt_job = "Emergency Response Team Leader"
rt_mob_job = "ERT Commander"
- uniform = /obj/item/clothing/under/rank/centcom/sensor
+ uniform = /obj/item/clothing/under/rank/centcom/ert/commander
back = /obj/item/storage/backpack/ert/commander
l_ear = /obj/item/radio/headset/ert/alt/commander
@@ -93,10 +93,9 @@
/datum/outfit/job/centcom/response_team/commander/gamma
name = "RT Commander (Gamma)"
shoes = /obj/item/clothing/shoes/magboots/advance
- suit = /obj/item/clothing/suit/space/hardsuit/ert/commander/gamma
+ back = /obj/item/mod/control/pre_equipped/responsory/commander
glasses = /obj/item/clothing/glasses/night
mask = /obj/item/clothing/mask/gas/sechailer/swat
- suit_store = /obj/item/gun/energy/gun/blueshield/pdw9
belt = /obj/item/gun/projectile/automatic/pistol/enforcer/lethal
backpack_contents = list(
@@ -104,7 +103,8 @@
/obj/item/storage/box/mindshield = 1,
/obj/item/camera_bug/ert = 1,
/obj/item/door_remote/omni = 1,
- /obj/item/ammo_box/magazine/enforcer/lethal = 2
+ /obj/item/ammo_box/magazine/enforcer/lethal = 2,
+ /obj/item/gun/energy/gun/blueshield/pdw9 = 1
)
cybernetic_implants = list(
@@ -125,7 +125,7 @@
rt_assignment = "Emergency Response Team Officer"
rt_job = "Emergency Response Team Officer"
rt_mob_job = "ERT Security"
- uniform = /obj/item/clothing/under/rank/security/officer/sensor
+ uniform = /obj/item/clothing/under/rank/centcom/ert/security
back = /obj/item/storage/backpack/ert/security
belt = /obj/item/storage/belt/security/response_team
pda = /obj/item/pda/heads/ert/security
@@ -179,21 +179,20 @@
/datum/outfit/job/centcom/response_team/security/gamma
name = "RT Security (Gamma)"
shoes = /obj/item/clothing/shoes/magboots/advance
- suit = /obj/item/clothing/suit/space/hardsuit/ert/security/gamma
belt = /obj/item/storage/belt/security/response_team_gamma
- suit_store = /obj/item/gun/energy/gun/nuclear
+ back = /obj/item/mod/control/pre_equipped/responsory/security
glasses = /obj/item/clothing/glasses/night
mask = /obj/item/clothing/mask/gas/sechailer/swat
l_pocket = /obj/item/restraints/legcuffs/bola/energy
r_pocket = /obj/item/extinguisher/mini
l_hand = /obj/item/gun/energy/immolator/multi
-
backpack_contents = list(
/obj/item/storage/box/handcuffs = 1,
/obj/item/storage/box/flashbangs = 1,
/obj/item/whetstone = 1,
- /obj/item/storage/box/breaching = 1
+ /obj/item/storage/box/breaching = 1,
+ /obj/item/gun/energy/gun/nuclear = 1
)
cybernetic_implants = list(
@@ -216,7 +215,7 @@
rt_job = "Emergency Response Team Engineer"
rt_mob_job = "ERT Engineering"
back = /obj/item/storage/backpack/ert/engineer
- uniform = /obj/item/clothing/under/rank/engineering/engineer
+ uniform = /obj/item/clothing/under/rank/centcom/ert/engineer
belt = /obj/item/storage/belt/utility/full/multitool
pda = /obj/item/pda/heads/ert/engineering
id = /obj/item/card/id/ert/engineering
@@ -269,18 +268,19 @@
/datum/outfit/job/centcom/response_team/engineer/gamma
name = "RT Engineer (Gamma)"
shoes = /obj/item/clothing/shoes/magboots/advance
+ back = /obj/item/mod/control/pre_equipped/responsory/engineer
belt = /obj/item/storage/belt/utility/chief/full
- suit = /obj/item/clothing/suit/space/hardsuit/ert/engineer/gamma
- suit_store = /obj/item/gun/energy/gun/blueshield/pdw9
glasses = /obj/item/clothing/glasses/meson/night
mask = /obj/item/clothing/mask/gas/sechailer/swat
l_pocket = /obj/item/t_scanner
r_pocket = /obj/item/melee/classic_baton/telescopic
+
backpack_contents = list(
/obj/item/rcd/combat = 1,
- /obj/item/rcd_ammo/large = 3
+ /obj/item/rcd_ammo/large = 3,
+ /obj/item/gun/energy/gun/blueshield/pdw9 = 1
)
cybernetic_implants = list(
@@ -303,7 +303,7 @@
rt_assignment = "Emergency Response Team Medic"
rt_job = "Emergency Response Team Medic"
rt_mob_job = "ERT Medical"
- uniform = /obj/item/clothing/under/rank/medical/doctor
+ uniform = /obj/item/clothing/under/rank/centcom/ert/medical
back = /obj/item/storage/backpack/ert/medical
pda = /obj/item/pda/heads/ert/medical
id = /obj/item/card/id/ert/medic
@@ -371,16 +371,16 @@
/datum/outfit/job/centcom/response_team/medic/gamma
name = "RT Medic (Gamma)"
shoes = /obj/item/clothing/shoes/magboots/advance
- suit = /obj/item/clothing/suit/space/hardsuit/ert/medical/gamma
glasses = /obj/item/clothing/glasses/night
mask = /obj/item/clothing/mask/gas/sechailer/swat
- suit_store = /obj/item/gun/energy/gun/blueshield/pdw9
+ back = /obj/item/mod/control/pre_equipped/responsory/medic
- belt = /obj/item/defibrillator/compact/advanced/loaded
+ belt = /obj/item/storage/belt/medical //No need for belt defib, they got the built in ones. This gives them some extra storage.
l_pocket = /obj/item/reagent_containers/hypospray/combat/nanites
r_pocket = /obj/item/reagent_containers/hypospray/autoinjector
+
backpack_contents = list(
/obj/item/bodyanalyzer/advanced = 1,
/obj/item/storage/firstaid/ert = 1,
@@ -389,7 +389,8 @@
/obj/item/handheld_defibrillator = 1,
/obj/item/storage/box/autoinjectors = 1,
/obj/item/storage/pill_bottle/ert_gamma = 1,
- /obj/item/storage/pill_bottle/patch_pack/ert/gamma = 1
+ /obj/item/storage/pill_bottle/patch_pack/ert/gamma = 1,
+ /obj/item/gun/energy/gun/blueshield/pdw9 = 1
)
cybernetic_implants = list(
@@ -411,7 +412,7 @@
rt_assignment = "Emergency Response Team Inquisitor"
rt_job = "Emergency Response Team Inquisitor"
rt_mob_job = "ERT Paranormal"
- uniform = /obj/item/clothing/under/rank/civilian/chaplain
+ uniform = /obj/item/clothing/under/rank/centcom/ert/chaplain
back = /obj/item/storage/backpack/ert/security
shoes = /obj/item/clothing/shoes/combat
l_ear = /obj/item/radio/headset/ert/alt
@@ -455,13 +456,18 @@
/datum/outfit/job/centcom/response_team/paranormal/gamma
name = "RT Paranormal (Gamma)"
- suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
- suit_store = /obj/item/gun/energy/gun/nuclear
l_pocket = /obj/item/grenade/clusterbuster/holy
+ back = /obj/item/mod/control/pre_equipped/responsory/inquisitory/chaplain
shoes = /obj/item/clothing/shoes/magboots/advance
glasses = /obj/item/clothing/glasses/night
r_pocket = /obj/item/nullrod/ert
+ backpack_contents = list(
+ /obj/item/storage/box/zipties = 1,
+ /obj/item/flashlight/seclite = 1,
+ /obj/item/gun/energy/gun/nuclear = 1
+ )
+
cybernetic_implants = list(
/obj/item/organ/internal/cyberimp/chest/nutriment/plus/hardened,
/obj/item/organ/internal/cyberimp/eyes/hud/security,
@@ -479,7 +485,7 @@
rt_assignment = "Emergency Response Team Janitor"
rt_job = "Emergency Response Team Janitor"
rt_mob_job = "ERT Janitor"
- uniform = /obj/item/clothing/under/color/purple/sensor
+ uniform = /obj/item/clothing/under/rank/centcom/ert/janitor
back = /obj/item/storage/backpack/ert/janitor
belt = /obj/item/storage/belt/janitor/full
shoes = /obj/item/clothing/shoes/galoshes
@@ -491,7 +497,7 @@
/obj/item/grenade/chem_grenade/antiweed = 2,
/obj/item/lightreplacer = 1,
/obj/item/storage/bag/trash = 1,
- /obj/item/twohanded/push_broom,
+ /obj/item/push_broom,
/obj/item/storage/box/lights/mixed = 1,
/obj/item/melee/flyswatter = 1)
@@ -525,9 +531,8 @@
/datum/outfit/job/centcom/response_team/janitorial/gamma
name = "RT Janitor (Gamma)"
- suit = /obj/item/clothing/suit/space/hardsuit/ert/janitor/gamma
glasses = /obj/item/clothing/glasses/night
- suit_store = /obj/item/gun/energy/gun
+ back = /obj/item/mod/control/pre_equipped/responsory/janitor
r_pocket = /obj/item/scythe/tele
shoes = /obj/item/clothing/shoes/magboots/advance
@@ -537,7 +542,8 @@
/obj/item/storage/box/lights/mixed = 1,
/obj/item/storage/bag/trash/bluespace = 1,
/obj/item/lightreplacer/bluespace = 1,
- /obj/item/melee/flyswatter = 1
+ /obj/item/melee/flyswatter = 1,
+ /obj/item/gun/energy/gun = 1
)
cybernetic_implants = list(
diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm
index 2e2e842979f4..fa27e2450106 100644
--- a/code/modules/ruins/lavalandruin_code/puzzle.dm
+++ b/code/modules/ruins/lavalandruin_code/puzzle.dm
@@ -254,7 +254,7 @@
//Ruin version
/obj/effect/sliding_puzzle/lavaland
- reward_type = /obj/structure/closet/crate/necropolis/puzzle
+ reward_type = /obj/structure/closet/crate/necropolis
/obj/effect/sliding_puzzle/lavaland/dispense_reward()
if(prob(25))
diff --git a/code/modules/ruins/spaceruin_code/voyager.dm b/code/modules/ruins/spaceruin_code/voyager.dm
index 31fa9012cff8..8dde5e094e18 100644
--- a/code/modules/ruins/spaceruin_code/voyager.dm
+++ b/code/modules/ruins/spaceruin_code/voyager.dm
@@ -1,4 +1,4 @@
-/obj/item/twohanded/required/golden_record
+/obj/item/golden_record
name = "Golden Record"
desc = "A relic of the past, you don't know what lies inside, but you remember someone talking about it arriving in 250356 years"
icon = 'icons/obj/space/voyager.dmi'
@@ -12,6 +12,10 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
origin_tech = "programming=6;biotech=6"
+/obj/item/golden_record/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
+
/turf/simulated/satellite
name = "satellite components storage"
desc = "There is plate covering inside storage, its wide and it have engraved 'Voyager' on it."
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index 721f6c61f75e..2fac705488d6 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -169,9 +169,10 @@
addtimer(CALLBACK(src, PROC_REF(reset)), confirm_delay)
/obj/machinery/keycard_auth/proc/trigger_event()
+ SHOULD_NOT_SLEEP(TRUE) // trigger_armed_response_team sleeps, which can cause issues for procs that call trigger_event(). We want to avoid that
switch(event)
if("Red Alert")
- set_security_level(SEC_LEVEL_RED)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(set_security_level), SEC_LEVEL_RED)
if("Grant Emergency Maintenance Access")
make_maint_all_access()
if("Revoke Emergency Maintenance Access")
@@ -187,17 +188,19 @@
atom_say("ERT request transmitted!")
GLOB.command_announcer.autosay("ERT request transmitted. Reason: [ert_reason]", name, follow_target_override = src)
print_centcom_report(ert_reason, station_time_timestamp() + " ERT Request")
+ SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("ert", "called"))
var/fullmin_count = 0
for(var/client/C in GLOB.admins)
if(check_rights(R_EVENT, 0, C.mob))
fullmin_count++
if(!fullmin_count)
- trigger_armed_response_team(new /datum/response_team/amber) // No admins? No problem. Automatically send a code amber ERT.
+ // No admins? No problem. Automatically send a code amber ERT.
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(trigger_armed_response_team), new /datum/response_team/amber)
+ ert_reason = null
return
ERT_Announce(ert_reason, triggered_by, repeat_warning = FALSE)
- SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("ert", "called"))
addtimer(CALLBACK(src, PROC_REF(remind_admins), ert_reason, triggered_by), 5 MINUTES)
ert_reason = null
diff --git a/code/modules/security_levels/security_levels.dm b/code/modules/security_levels/security_levels.dm
index cfd347bbcf19..5218aa0fb366 100644
--- a/code/modules/security_levels/security_levels.dm
+++ b/code/modules/security_levels/security_levels.dm
@@ -24,9 +24,6 @@ GLOBAL_DATUM_INIT(security_announcement, /datum/announcer, new(config_type = /da
if("delta")
level = SEC_LEVEL_DELTA
- if(level != SEC_LEVEL_DELTA)
- stop_delta_alarm()
-
//Will not be announced if you try to set to the same level as it already is
if(level >= SEC_LEVEL_GREEN && level <= SEC_LEVEL_DELTA && level != GLOB.security_level)
if(level >= SEC_LEVEL_RED && GLOB.security_level < SEC_LEVEL_RED)
@@ -98,14 +95,13 @@ GLOBAL_DATUM_INIT(security_announcement, /datum/announcer, new(config_type = /da
var/temp_sound = GLOB.security_announcement.config.sound
GLOB.security_announcement.config.sound = null
GLOB.security_announcement.Announce("The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.","Attention! Delta security level reached!",
- new_sound = null,
- new_sound2 = null)
+ new_sound = 'sound/effects/delta_klaxon.ogg',
+ new_sound2 = 'sound/AI/delta.ogg')
GLOB.security_announcement.config.sound = temp_sound
GLOB.security_level = SEC_LEVEL_DELTA
post_status(STATUS_DISPLAY_ALERT, "deltaalert")
update_firealarms()
set_stationwide_emergency_lighting()
- delta_alarm()
SSblackbox.record_feedback("tally", "security_level_changes", 1, level)
return
@@ -215,7 +211,7 @@ GLOBAL_DATUM_INIT(security_announcement, /datum/announcer, new(config_type = /da
INVOKE_ASYNC(L, TYPE_PROC_REF(/obj/machinery/light, update), FALSE)
/proc/epsilon_process()
- GLOB.security_announcement.Announce("Central Command has ordered the Epsilon security level on the station. Consider all contracts terminated.", "Attention! Epsilon security level activated!", 'sound/effects/purge_siren.ogg')
+ GLOB.security_announcement.Announce("Central Command has ordered the Epsilon security level on the station. Consider all contracts terminated.", "Attention! Epsilon security level activated!", 'modular_ss220/aesthetics_sounds/sound/epsilon/epsilon.ogg') //SS220 EDIT
GLOB.security_level = SEC_LEVEL_EPSILON
post_status(STATUS_DISPLAY_ALERT, "epsilonalert")
for(var/area/A as anything in GLOB.all_areas)
@@ -227,23 +223,3 @@ GLOBAL_DATUM_INIT(security_announcement, /datum/announcer, new(config_type = /da
L.fire_mode = TRUE
L.update()
update_firealarms()
-
-/proc/delta_alarm()
- var/station_z = level_name_to_num(MAIN_STATION)
- var/list/mobs_for_alarm = list()
-
- for(var/mob/M in get_mob_with_client_list())
- if(M.z != station_z)
- continue
-
- mobs_for_alarm += M
-
- var/datum/looping_sound/decreasing/delta_alarm/alarm = new(_output_atoms = mobs_for_alarm, _direct = TRUE)
- alarm.channel = CHANNEL_DELTA_ALARM
- alarm.start()
-
-/proc/stop_delta_alarm()
- for(var/datum/looping_sound/decreasing/delta_alarm/alarm in GLOB.looping_sounds)
- for(var/mob/hearer in alarm.output_atoms) // Immediately stop the alarm for anyone who can hear it.
- hearer.stop_sound_channel(CHANNEL_DELTA_ALARM)
- qdel(alarm)
diff --git a/code/modules/station_goals/bluespace_tap.dm b/code/modules/station_goals/bluespace_tap.dm
index 98bea054d3b4..6a2fa5613497 100644
--- a/code/modules/station_goals/bluespace_tap.dm
+++ b/code/modules/station_goals/bluespace_tap.dm
@@ -102,7 +102,7 @@
/obj/item/gun/projectile/shotgun/toy/crossbow = 1,
/obj/item/gun/projectile/shotgun/toy/tommygun = 1,
/obj/item/gun/projectile/automatic/sniper_rifle/toy = 1,
- /obj/item/twohanded/dualsaber/toy = 5,
+ /obj/item/dualsaber/toy = 5,
/obj/machinery/snow_machine = 10,
/obj/item/clothing/head/kitty = 5,
/obj/item/coin/antagtoken = 5,
diff --git a/code/modules/supply/supply_packs/pack_miscellaneous.dm b/code/modules/supply/supply_packs/pack_miscellaneous.dm
index 4d0f5667ec2c..a424d500f7cc 100644
--- a/code/modules/supply/supply_packs/pack_miscellaneous.dm
+++ b/code/modules/supply/supply_packs/pack_miscellaneous.dm
@@ -10,6 +10,14 @@
containername = "\improper MULEbot crate"
department_restrictions = list(DEPARTMENT_SUPPLY)
+/datum/supply_packs/misc/loader
+ name = "Loader MODsuit Crate"
+ contains = list(/obj/item/mod/control/pre_equipped/loader)
+ cost = 750 //Unique, expencive. Better sell that plasma
+ containertype = /obj/structure/largecrate
+ containername = "\improper Loader MODsuit crate"
+ department_restrictions = list(DEPARTMENT_SUPPLY)
+
/datum/supply_packs/misc/hightank
name = "High-Capacity Water Tank Crate"
contains = list(/obj/structure/reagent_dispensers/watertank/high)
@@ -143,7 +151,7 @@
/obj/item/reagent_containers/glass/bucket,
/obj/item/reagent_containers/glass/bucket,
/obj/item/mop,
- /obj/item/twohanded/push_broom,
+ /obj/item/push_broom,
/obj/item/caution,
/obj/item/caution,
/obj/item/caution,
@@ -217,7 +225,7 @@
/datum/supply_packs/misc/costume
name = "Costume Crate"
- contains = list(/obj/item/twohanded/staff,
+ contains = list(/obj/item/staff,
/obj/item/clothing/suit/wizrobe/fake,
/obj/item/clothing/shoes/sandal,
/obj/item/clothing/head/wizard/fake,
@@ -261,6 +269,15 @@
/obj/item/clothing/glasses/sunglasses)
cost = 450
containername = "sunglasses crate"
+
+/datum/supply_packs/misc/welding_goggles
+ name = "Welding Goggles Crate"
+ contains = list(/obj/item/clothing/glasses/welding,
+ /obj/item/clothing/glasses/welding,
+ /obj/item/clothing/glasses/welding)
+ cost = 300
+ containername = "welding goggles crate"
+
/datum/supply_packs/misc/randomised
var/num_contained = 3 //number of items picked to be contained in a randomised crate
contains = list(/obj/item/clothing/head/collectable/chef,
@@ -292,6 +309,11 @@
manifest += "Contains any [num_contained] of:"
..()
+/datum/supply_packs/misc/randomised/plushie
+ name = "Collectable Plushies Crate"
+ cost = 1000
+ containername = "collectable plushies crate! Brought to you by Bass.inc!"
+ contains = list(/obj/random/plushie, /obj/random/plushie, /obj/random/plushie/explosive)
/datum/supply_packs/misc/foamforce
name = "Foam Force Crate"
@@ -413,12 +435,12 @@
/datum/supply_packs/misc/polo //For space polo! Or horsehead Quiditch
name = "Polo Supply Crate"
// 6 brooms, 6 horse masks for the brooms, and 1 beach ball
- contains = list(/obj/item/twohanded/staff/broom,
- /obj/item/twohanded/staff/broom,
- /obj/item/twohanded/staff/broom,
- /obj/item/twohanded/staff/broom,
- /obj/item/twohanded/staff/broom,
- /obj/item/twohanded/staff/broom,
+ contains = list(/obj/item/staff/broom,
+ /obj/item/staff/broom,
+ /obj/item/staff/broom,
+ /obj/item/staff/broom,
+ /obj/item/staff/broom,
+ /obj/item/staff/broom,
/obj/item/clothing/mask/horsehead,
/obj/item/clothing/mask/horsehead,
/obj/item/clothing/mask/horsehead,
diff --git a/code/modules/supply/supply_packs/pack_science.dm b/code/modules/supply/supply_packs/pack_science.dm
index ae746564c7d2..d70a663c7bf0 100644
--- a/code/modules/supply/supply_packs/pack_science.dm
+++ b/code/modules/supply/supply_packs/pack_science.dm
@@ -20,6 +20,18 @@
access = ACCESS_ROBOTICS
announce_beacons = list("Research Division" = list("Robotics", "Research Director's Desk"))
+/datum/supply_packs/science/mod_core
+ name = "MOD core Crate"
+ contains = list(/obj/item/mod/core/standard,
+ /obj/item/mod/core/standard,
+ /obj/item/mod/core/standard)
+ cost = 450
+ containertype = /obj/structure/closet/crate/secure/scisec
+ containername = "\improper MOD core crate"
+ access = ACCESS_ROBOTICS
+ announce_beacons = list("Research Division" = list("Robotics"))
+ department_restrictions = list() //The crew can order modcores without RD requirement. As a treat.
+
/datum/supply_packs/science/mechcore
name = "Mech Power Core Crate"
contains = list(/obj/item/mecha_parts/core)
@@ -27,6 +39,7 @@
containertype = /obj/structure/closet/crate/secure/scisec
containername = "mech power core crate"
access = ACCESS_RD
+
/datum/supply_packs/science/robotics/mecha_ripley
name = "Circuit Crate (Ripley APLU)"
contains = list(/obj/item/book/manual/ripley_build_and_repair,
diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm
index f8f9ec322b8f..47b09e448b6c 100644
--- a/code/modules/surgery/dental_implant.dm
+++ b/code/modules/surgery/dental_implant.dm
@@ -48,7 +48,7 @@
/datum/action/item_action/hands_free/activate_pill
name = "Activate Pill"
-/datum/action/item_action/hands_free/activate_pill/Trigger()
+/datum/action/item_action/hands_free/activate_pill/Trigger(left_click)
if(!..())
return
to_chat(owner, "You grit your teeth and burst the implanted [target]!")
diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm
index d2af34214f83..4070b6fc2d0d 100644
--- a/code/modules/surgery/generic.dm
+++ b/code/modules/surgery/generic.dm
@@ -14,7 +14,7 @@
/obj/item/kitchen/knife = 90,
/obj/item/shard = 60,
/obj/item/scissors = 12,
- /obj/item/twohanded/chainsaw = 1,
+ /obj/item/butcher_chainsaw = 1,
/obj/item/claymore = 6,
/obj/item/melee/energy = 6,
/obj/item/pen/edagger = 6,
diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm
index e58f9456c1a1..7f70d8cb8078 100644
--- a/code/modules/surgery/organs/augments_arms.dm
+++ b/code/modules/surgery/organs/augments_arms.dm
@@ -117,7 +117,7 @@
var/obj/item/arm_item = owner.get_item_by_slot(arm_slot)
if(arm_item)
- if(istype(arm_item, /obj/item/twohanded/offhand))
+ if(istype(arm_item, /obj/item/offhand))
var/obj/item/offhand_arm_item = owner.get_active_hand()
to_chat(owner, "Your hands are too encumbered wielding [offhand_arm_item] to deploy [src]!")
return
@@ -184,11 +184,12 @@
Retract()
owner.visible_message("A loud bang comes from [owner]\'s [parent_organ == "r_arm" ? "right" : "left"] arm!")
playsound(get_turf(owner), 'sound/weapons/flashbang.ogg', 100, 1)
- to_chat(owner, "You feel an explosion erupt inside your [parent_organ == "r_arm" ? "right" : "left"] arm as your implant breaks!")
+ to_chat(owner, "You feel an explosion erupt inside your [parent_organ == "r_arm" ? "right" : "left"] arm as your implant misfires!")
owner.adjust_fire_stacks(20)
owner.IgniteMob()
owner.adjustFireLoss(25)
crit_fail = TRUE
+ addtimer(VARSET_CALLBACK(src, crit_fail, FALSE), 60 SECONDS) //I would rather not have the weapon be permamently disabled, especially as there is no way to fix it.
else // The gun will still discharge anyway.
..()
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index f637f3b0bb93..dc77bae859c1 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -187,7 +187,7 @@
name = "Pump your heart"
//You are now brea- pumping blood manually
-/datum/action/item_action/organ_action/cursed_heart/Trigger()
+/datum/action/item_action/organ_action/cursed_heart/Trigger(left_click)
. = ..()
if(. && istype(target, /obj/item/organ/internal/heart/cursed))
var/obj/item/organ/internal/heart/cursed/cursed_heart = target
diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm
index 2d56706c8461..c62115cc8105 100644
--- a/code/modules/surgery/organs/organ_external.dm
+++ b/code/modules/surgery/organs/organ_external.dm
@@ -169,6 +169,9 @@
parent.children = list()
parent.children.Add(src)
+ if(owner.has_embedded_objects())
+ owner.throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
+
/obj/item/organ/external/attempt_become_organ(obj/item/organ/external/parent,mob/living/carbon/human/H)
if(parent_organ != parent.limb_name)
return FALSE
@@ -191,6 +194,12 @@
brute *= brute_mod
burn *= burn_mod
+ //I would move the delimb check below fractures and burn wounds, but it would cause issues. As such we save the incoming damage here.
+ var/original_brute = brute
+ var/original_burn = burn
+ var/limb_brute = brute_dam
+ var/limb_burn = burn_dam
+
// See if bones need to break
check_fracture(brute)
// See if we need to inflict severe burns
@@ -268,9 +277,9 @@
//If limb took enough damage, try to cut or tear it off
if(owner)
if(sharp && !(limb_flags & CANNOT_DISMEMBER))
- if(brute_dam >= max_damage && prob(brute))
+ if(limb_brute >= max_damage && prob(original_brute / 2))
droplimb(0, DROPLIMB_SHARP)
- if(burn_dam >= max_damage && prob(burn))
+ if(limb_burn >= max_damage && prob(original_burn / 2))
droplimb(0, DROPLIMB_BURN)
if(owner_old)
@@ -483,12 +492,14 @@ Note that amputating the affected organ does in fact remove the infection from t
owner.splinted_limbs -= src
return
if(owner.step_count >= splinted_count + SPLINT_LIFE)
- status &= ~ORGAN_SPLINTED //oh no, we actually need surgery now!
- owner.visible_message("[owner] screams in pain as [owner.p_their()] splint pops off their [name]!","You scream in pain as your splint pops off your [name]!")
- owner.emote("scream")
- owner.Stun(4 SECONDS)
+ status &= ~ORGAN_SPLINTED // Oh no, we actually need surgery now!
owner.handle_splints()
-
+ if(!(status & ORGAN_BROKEN))
+ to_chat(owner, "Your splint harmlessly pops off your [name].") // If we fixed our bones, a splint popping off shouldn't be painful and stun us.
+ return
+ owner.visible_message("[owner] screams in pain as [owner.p_their()] splint pops off [owner.p_their()] [name]!","You scream in pain as your splint pops off your [name]!")
+ owner.emote("scream")
+ owner.Weaken(4 SECONDS) // Better feedback compared to stun() - We won't be just standing there menancingly
/****************************************************
DISMEMBERMENT
@@ -781,11 +792,11 @@ Note that amputating the affected organ does in fact remove the infection from t
I.forceMove(src)
RegisterSignal(I, COMSIG_MOVABLE_MOVED, PROC_REF(remove_embedded_object))
- if(!owner.has_embedded_objects())
- owner.clear_alert("embeddedobject")
-
. = ..()
+ if(!victim.has_embedded_objects())
+ victim.clear_alert("embeddedobject")
+
// Attached organs also fly off.
if(!ignore_children)
for(var/obj/item/organ/external/O in children)
diff --git a/code/modules/surgery/organs/subtypes/standard_organs.dm b/code/modules/surgery/organs/subtypes/standard_organs.dm
index ad496d9ca2ba..873f46084e6e 100644
--- a/code/modules/surgery/organs/subtypes/standard_organs.dm
+++ b/code/modules/surgery/organs/subtypes/standard_organs.dm
@@ -177,6 +177,7 @@
/obj/item/organ/external/hand/remove()
if(owner)
+ update_hand_missing()
if(owner.gloves)
owner.unEquip(owner.gloves)
if(owner.l_hand)
@@ -186,6 +187,28 @@
. = ..()
+/obj/item/organ/external/hand/necrotize(update_sprite)
+ . = ..()
+ update_hand_missing()
+
+/obj/item/organ/external/hand/mutate()
+ . = ..()
+ update_hand_missing()
+
+/obj/item/organ/external/hand/receive_damage(brute, burn, sharp, used_weapon, list/forbidden_limbs, ignore_resists, updating_health)
+ . = ..()
+ update_hand_missing()
+
+/obj/item/organ/external/hand/droplimb(clean, disintegrate, ignore_children, nodamage)
+ . = ..()
+ update_hand_missing()
+
+/obj/item/organ/external/hand/proc/update_hand_missing()
+ // we need to come back to this once the hand is actually removed/dead
+ if(!owner) // Rather not have this trigger on already remove limbs
+ return
+ addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob/living/carbon/human, update_hands_hud), 0))
+
/obj/item/organ/external/hand/right
limb_name = "r_hand"
name = "right hand"
diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm
index dc62641c1a11..21ecf45b88b5 100644
--- a/code/modules/surgery/organs/vocal_cords.dm
+++ b/code/modules/surgery/organs/vocal_cords.dm
@@ -64,7 +64,7 @@ GLOBAL_DATUM_INIT(multispin_words, /regex, regex("like a record baby"))
actions_types = list(/datum/action/item_action/organ_action/use/adamantine_vocal_cords)
icon_state = "adamantine_cords"
-/datum/action/item_action/organ_action/use/adamantine_vocal_cords/Trigger()
+/datum/action/item_action/organ_action/use/adamantine_vocal_cords/Trigger(left_click)
if(!IsAvailable())
return
var/message = input(owner, "Resonate a message to all nearby golems.", "Resonate")
@@ -117,7 +117,7 @@ GLOBAL_DATUM_INIT(multispin_words, /regex, regex("like a record baby"))
return FALSE
return TRUE
-/datum/action/item_action/organ_action/colossus/Trigger()
+/datum/action/item_action/organ_action/colossus/Trigger(left_click)
. = ..()
if(!IsAvailable())
if(world.time < cords.next_command)
diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm
index 600a350f81f2..2f1c0da66b8a 100644
--- a/code/modules/surgery/organs_internal.dm
+++ b/code/modules/surgery/organs_internal.dm
@@ -714,7 +714,7 @@
/obj/item/kitchen/knife = 90,
/obj/item/shard = 60,
/obj/item/scissors = 12,
- /obj/item/twohanded/chainsaw = 1,
+ /obj/item/butcher_chainsaw = 1,
/obj/item/claymore = 6,
/obj/item/melee/energy = 6,
/obj/item/pen/edagger = 6
diff --git a/code/modules/surgery/remove_embedded_object.dm b/code/modules/surgery/remove_embedded_object.dm
index 07240c281a61..513fcd1f022c 100644
--- a/code/modules/surgery/remove_embedded_object.dm
+++ b/code/modules/surgery/remove_embedded_object.dm
@@ -24,12 +24,10 @@
. = ..()
if(!.)
return FALSE
- if(!istype(target))
- return FALSE
var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
if(!affected)
return FALSE
- if(affected.is_robotic())
+ if(!length(affected.embedded_objects))
return FALSE
return TRUE
@@ -39,11 +37,6 @@
return FALSE
if(!istype(target))
return FALSE
- var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(!affected.is_robotic())
- return FALSE
return TRUE
@@ -76,7 +69,7 @@
H.clear_alert("embeddedobject")
if(objects > 0)
- user.visible_message("[user] sucessfully removes [objects] object\s from [H]'s [parse_zone(user.zone_selected)]!", "You successfully remove [objects] object\s from [H]'s [L.name].")
+ user.visible_message("[user] successfully removes [objects] object\s from [H]'s [parse_zone(user.zone_selected)]!", "You successfully remove [objects] object\s from [H]'s [L.name].")
else
to_chat(user, "You find no objects embedded in [H]'s [parse_zone(user.zone_selected)]!")
diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm
index d1500469cf32..3877ef90b96c 100644
--- a/code/modules/surgery/robotics.dm
+++ b/code/modules/surgery/robotics.dm
@@ -504,6 +504,8 @@
add_attack_logs(user, target, "Surgically removed [I.name]. INTENT: [uppertext(user.a_intent)]")
spread_germs_to_organ(I, user)
var/obj/item/thing = I.remove(target)
+ if(QDELETED(thing))
+ return ..()
if(!istype(thing))
thing.forceMove(get_turf(target))
else
diff --git a/code/modules/surgery/surgery_helpers.dm b/code/modules/surgery/surgery_helpers.dm
index dfd0c849afee..43e2f710ca79 100644
--- a/code/modules/surgery/surgery_helpers.dm
+++ b/code/modules/surgery/surgery_helpers.dm
@@ -22,6 +22,8 @@
return 0.85
if(drunk >= 15)//a little drunk
return 0.85
+ if(target.reagents.has_reagent("happiness")) // fuck yeah
+ return 0.81
return 0.8 //20% failure chance
/proc/get_location_modifier(mob/target)
diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm
index 9616d6bb38c3..8952c63f9677 100644
--- a/code/modules/telesci/gps.dm
+++ b/code/modules/telesci/gps.dm
@@ -165,6 +165,11 @@ GLOBAL_LIST_EMPTY(GPS_list)
gpstag = "MINE0"
desc = "A positioning system helpful for rescuing trapped or injured miners, keeping one on you at all times while mining might just save your life."
+/obj/item/gps/mod
+ icon_state = "gps-m"
+ gpstag = "MOD0"
+ desc = "A positioning system helpful for rescuing trapped or injured miners, after you have become lost from rolling around at the speed of sound."
+
/obj/item/gps/cyborg
icon_state = "gps-b"
gpstag = "BORG0"
diff --git a/code/modules/vehicle/ambulance.dm b/code/modules/vehicle/ambulance.dm
index b1a9ee182ee1..eff9b69eec5e 100644
--- a/code/modules/vehicle/ambulance.dm
+++ b/code/modules/vehicle/ambulance.dm
@@ -26,7 +26,7 @@
var/cooldown = 0
-/datum/action/ambulance_alarm/Trigger()
+/datum/action/ambulance_alarm/Trigger(left_click)
if(!..())
return FALSE
diff --git a/code/modules/vehicle/janivehicle.dm b/code/modules/vehicle/janivehicle.dm
index 679dbe388eb5..492da4410228 100644
--- a/code/modules/vehicle/janivehicle.dm
+++ b/code/modules/vehicle/janivehicle.dm
@@ -51,7 +51,7 @@
icon_icon = 'icons/obj/vehicles.dmi'
button_icon_state = "upgrade"
-/datum/action/floor_buffer/Trigger()
+/datum/action/floor_buffer/Trigger(left_click)
. = ..()
var/obj/vehicle/janicart/J = target
if(!J.floorbuffer)
diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm
index 6f8f3f7c357a..09ef99dfcace 100644
--- a/code/modules/vehicle/vehicle.dm
+++ b/code/modules/vehicle/vehicle.dm
@@ -238,3 +238,4 @@
/obj/vehicle/zap_act(power, zap_flags)
zap_buckle_check(power)
return ..()
+
diff --git a/config/example/config.toml b/config/example/config.toml
index fa1b37dd16ee..107d603f9832 100644
--- a/config/example/config.toml
+++ b/config/example/config.toml
@@ -32,6 +32,9 @@
# - url_configuration
# - voting_configuration
+# SS220 CONFIGS
+# - gateway_configuration
+# - tts_configuration
################################################################
@@ -144,7 +147,7 @@ ipc_screens = [
# Enable/disable the database on a whole
sql_enabled = false
# SQL version. If this is a mismatch, round start will be delayed
-sql_version = 49
+sql_version = 502204
# SQL server address. Can be an IP or DNS name
sql_address = "127.0.0.1"
# SQL server port
@@ -621,6 +624,13 @@ active_space_ruins = [
"_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm",
"_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm",
"_maps/map_files/RandomRuins/SpaceRuins/voyager.dmm",
+ "_maps/map_files/RandomRuins/SpaceRuins/wreckedcargoship.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/mechtransport_new.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/destroyed_infiltrator.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/transit_bar.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/infected_ship.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/convoy_ambush.dmm",
+
### The following ruins are based from past pre-spawned Zlevel content ###
"_maps/map_files/RandomRuins/SpaceRuins/abandonedtele.dmm",
@@ -638,7 +648,7 @@ active_space_ruins = [
# The following is the white ship ruin. Its force-spawned and is required to stop SSshuttle runtiming on startup
# Its also important incase a white-ship console is ever built midround
# DO NOT DISABLE THIS UNLESS YOU HAVE A GOOD REASON
- "_maps/map_files/RandomRuins/SpaceRuins/whiteship.dmm",
+ "_maps/map_files220/RandomRuins/SpaceRuins/whiteship.dmm",
# The following is a force-spawned ruin consisting mostly of empty space with a shuttle docking port for the free golem shuttle
# Disabling it will lead to the free golem shuttle sometimes being stuck on lavaland.
@@ -818,3 +828,41 @@ map_vote_day_types = [
################################################################
# Congratulations. You reached the end. heres a cookie.
+
+# And some SS220 configs ratge.
+
+################################################################
+
+
+[gateway_configuration]
+# This section contains everything relating to the ingame gateway (away mission) system.
+
+# Do we want to enable this at all? Disable for faster load times in testing
+enable_away_mission = false
+# Delay (in deciseconds) before the gateway is usable
+away_mission_delay = 0
+# List of all enabled away missions. Comment out an entry to disable it.
+enabled_away_missions = [
+ "_maps/map_files220/RandomZLevels/beach.dmm",
+ "_maps/map_files220/RandomZLevels/wildwest.dmm",
+ "_maps/map_files220/RandomZLevels/terrorspiders.dmm",
+ "_maps/map_files220/RandomZLevels/blackmarketpackers.dmm",
+]
+
+
+################################################################
+
+[tts_configuration]
+# This section contains all settings relating to the TTS system
+
+# Is TTS enabled
+tts_enabled = false
+# If enabled, what token does it use for API
+tts_token_silero = ""
+# Are the audio files saved after usage
+tts_cache_enabled = false
+# What CPU threads are used for ffmpeg. Example valid values: "0-3" or "1,4-7"
+ffmpeg_cpuaffinity = ""
+
+
+################################################################
diff --git a/icons/_nanomaps/CereStation_nanomap_z1.png b/icons/_nanomaps/CereStation_nanomap_z1.png
index b5facb3e3821..736593d74232 100644
Binary files a/icons/_nanomaps/CereStation_nanomap_z1.png and b/icons/_nanomaps/CereStation_nanomap_z1.png differ
diff --git a/icons/_nanomaps/Cyberiad_nanomap_z1.png b/icons/_nanomaps/Cyberiad_nanomap_z1.png
index a5ce4c6b2fef..4664acad34b1 100644
Binary files a/icons/_nanomaps/Cyberiad_nanomap_z1.png and b/icons/_nanomaps/Cyberiad_nanomap_z1.png differ
diff --git a/icons/_nanomaps/Delta_nanomap_z1.png b/icons/_nanomaps/Delta_nanomap_z1.png
index 5467048f9db1..157c7974be8e 100644
Binary files a/icons/_nanomaps/Delta_nanomap_z1.png and b/icons/_nanomaps/Delta_nanomap_z1.png differ
diff --git a/icons/_nanomaps/MetaStation_nanomap_z1.png b/icons/_nanomaps/MetaStation_nanomap_z1.png
index c6882f7d2ff5..a26556e10837 100644
Binary files a/icons/_nanomaps/MetaStation_nanomap_z1.png and b/icons/_nanomaps/MetaStation_nanomap_z1.png differ
diff --git a/icons/effects/32x96.dmi b/icons/effects/32x96.dmi
new file mode 100644
index 000000000000..b5eed61222ba
Binary files /dev/null and b/icons/effects/32x96.dmi differ
diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi
index eb20689d0392..812bda5b5ce5 100644
Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index 4fa468971dc9..a361ed51d90a 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 9db0364de2ea..6c4fee22b8d1 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/hud/radial.dmi b/icons/hud/radial.dmi
new file mode 100644
index 000000000000..897cb3a872e2
Binary files /dev/null and b/icons/hud/radial.dmi differ
diff --git a/icons/mob/actions/actions_mod.dmi b/icons/mob/actions/actions_mod.dmi
new file mode 100644
index 000000000000..1b0617d847a8
Binary files /dev/null and b/icons/mob/actions/actions_mod.dmi differ
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index 29db0c666eff..4477b2d6c74a 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi
index 6d2180025c0e..2f11f258d0b9 100644
Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ
diff --git a/icons/mob/clothing/head.dmi b/icons/mob/clothing/head.dmi
index 3d4a734e945f..3c48e781567c 100644
Binary files a/icons/mob/clothing/head.dmi and b/icons/mob/clothing/head.dmi differ
diff --git a/icons/mob/clothing/masking_helpers.dmi b/icons/mob/clothing/masking_helpers.dmi
index ca7f39889458..3bfc8224add1 100644
Binary files a/icons/mob/clothing/masking_helpers.dmi and b/icons/mob/clothing/masking_helpers.dmi differ
diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi
new file mode 100644
index 000000000000..180a65f5cc93
Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/mob/clothing/modsuit/mod_modules.dmi b/icons/mob/clothing/modsuit/mod_modules.dmi
new file mode 100644
index 000000000000..1001ae77d2d6
Binary files /dev/null and b/icons/mob/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/grey_helmets.dmi b/icons/mob/clothing/modsuit/species/grey_helmets.dmi
new file mode 100644
index 000000000000..d9e715539eb1
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/grey_helmets.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/grey_mod_modules.dmi b/icons/mob/clothing/modsuit/species/grey_mod_modules.dmi
new file mode 100644
index 000000000000..5fa7916f4380
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/grey_mod_modules.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi b/icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi
new file mode 100644
index 000000000000..990fa6eabc09
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/modsuits_younahthee.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/modules_taj.dmi b/icons/mob/clothing/modsuit/species/modules_taj.dmi
new file mode 100644
index 000000000000..4ede6a754f46
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/modules_taj.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/modules_unathi.dmi b/icons/mob/clothing/modsuit/species/modules_unathi.dmi
new file mode 100644
index 000000000000..aedc1619f3b2
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/modules_unathi.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/modules_vulp.dmi b/icons/mob/clothing/modsuit/species/modules_vulp.dmi
new file mode 100644
index 000000000000..37357754c955
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/modules_vulp.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/taj_modsuits.dmi b/icons/mob/clothing/modsuit/species/taj_modsuits.dmi
new file mode 100644
index 000000000000..0614e3328bd2
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/taj_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/vulp_modsuits.dmi b/icons/mob/clothing/modsuit/species/vulp_modsuits.dmi
new file mode 100644
index 000000000000..3ba9e8c04ca3
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/vulp_modsuits.dmi differ
diff --git a/icons/mob/clothing/species/drask/head.dmi b/icons/mob/clothing/species/drask/head.dmi
index 0de2e55a7175..b54a7d74f5cb 100644
Binary files a/icons/mob/clothing/species/drask/head.dmi and b/icons/mob/clothing/species/drask/head.dmi differ
diff --git a/icons/mob/clothing/species/drask/under/centcom.dmi b/icons/mob/clothing/species/drask/under/centcom.dmi
index 9cebf9d668d3..21e88ec9ff63 100644
Binary files a/icons/mob/clothing/species/drask/under/centcom.dmi and b/icons/mob/clothing/species/drask/under/centcom.dmi differ
diff --git a/icons/mob/clothing/species/grey/head.dmi b/icons/mob/clothing/species/grey/head.dmi
index 8b00c30ae01b..6d4f7985ea87 100644
Binary files a/icons/mob/clothing/species/grey/head.dmi and b/icons/mob/clothing/species/grey/head.dmi differ
diff --git a/icons/mob/clothing/species/grey/under/centcom.dmi b/icons/mob/clothing/species/grey/under/centcom.dmi
index 22d5902fc448..f0b1e4778f14 100644
Binary files a/icons/mob/clothing/species/grey/under/centcom.dmi and b/icons/mob/clothing/species/grey/under/centcom.dmi differ
diff --git a/icons/mob/clothing/species/kidan/head.dmi b/icons/mob/clothing/species/kidan/head.dmi
index 83818995f69f..e2e3a7f7d9ae 100644
Binary files a/icons/mob/clothing/species/kidan/head.dmi and b/icons/mob/clothing/species/kidan/head.dmi differ
diff --git a/icons/mob/clothing/species/kidan/under/centcom.dmi b/icons/mob/clothing/species/kidan/under/centcom.dmi
index 8d4e6b73a5d8..54cc3b85c6ea 100644
Binary files a/icons/mob/clothing/species/kidan/under/centcom.dmi and b/icons/mob/clothing/species/kidan/under/centcom.dmi differ
diff --git a/icons/mob/clothing/species/kidan/under/color.dmi b/icons/mob/clothing/species/kidan/under/color.dmi
new file mode 100644
index 000000000000..1ce002f45a56
Binary files /dev/null and b/icons/mob/clothing/species/kidan/under/color.dmi differ
diff --git a/icons/mob/clothing/species/kidan/under/pants.dmi b/icons/mob/clothing/species/kidan/under/pants.dmi
new file mode 100644
index 000000000000..072739a4469d
Binary files /dev/null and b/icons/mob/clothing/species/kidan/under/pants.dmi differ
diff --git a/icons/mob/clothing/species/kidan/under/suit.dmi b/icons/mob/clothing/species/kidan/under/suit.dmi
new file mode 100644
index 000000000000..c5f9002995e3
Binary files /dev/null and b/icons/mob/clothing/species/kidan/under/suit.dmi differ
diff --git a/icons/mob/clothing/species/kidan/underwear.dmi b/icons/mob/clothing/species/kidan/underwear.dmi
new file mode 100644
index 000000000000..fef359356846
Binary files /dev/null and b/icons/mob/clothing/species/kidan/underwear.dmi differ
diff --git a/icons/mob/clothing/species/vox/head.dmi b/icons/mob/clothing/species/vox/head.dmi
index c5ce01a0f071..f2767bf36c11 100644
Binary files a/icons/mob/clothing/species/vox/head.dmi and b/icons/mob/clothing/species/vox/head.dmi differ
diff --git a/icons/mob/clothing/species/vox/under/centcom.dmi b/icons/mob/clothing/species/vox/under/centcom.dmi
index 8897f814edd7..7078f770479d 100644
Binary files a/icons/mob/clothing/species/vox/under/centcom.dmi and b/icons/mob/clothing/species/vox/under/centcom.dmi differ
diff --git a/icons/mob/clothing/under/centcom.dmi b/icons/mob/clothing/under/centcom.dmi
index 300dc5b94462..b9cc635973a0 100644
Binary files a/icons/mob/clothing/under/centcom.dmi and b/icons/mob/clothing/under/centcom.dmi differ
diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi
index aa4f84098de6..fa2d49111f9b 100644
Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ
diff --git a/icons/mob/human_races/r_kidan.dmi b/icons/mob/human_races/r_kidan.dmi
index 44a0f439899d..2663350fc4b7 100644
Binary files a/icons/mob/human_races/r_kidan.dmi and b/icons/mob/human_races/r_kidan.dmi differ
diff --git a/icons/mob/inhands/guns_righthand.dmi b/icons/mob/inhands/guns_righthand.dmi
index 8e18427f19c1..b4e8d80aa2d8 100644
Binary files a/icons/mob/inhands/guns_righthand.dmi and b/icons/mob/inhands/guns_righthand.dmi differ
diff --git a/icons/mob/inhands/items/devices_lefthand.dmi b/icons/mob/inhands/items/devices_lefthand.dmi
new file mode 100644
index 000000000000..bf9c3154c62b
Binary files /dev/null and b/icons/mob/inhands/items/devices_lefthand.dmi differ
diff --git a/icons/mob/inhands/items/devices_righthand.dmi b/icons/mob/inhands/items/devices_righthand.dmi
new file mode 100644
index 000000000000..93a5d9296104
Binary files /dev/null and b/icons/mob/inhands/items/devices_righthand.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index 87ef3e598c9b..f5511c5ec826 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index b66341600da9..182b4f39131a 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/screen_full.dmi b/icons/mob/screen_full.dmi
index d1aa736b3521..cfcf182c840f 100644
Binary files a/icons/mob/screen_full.dmi and b/icons/mob/screen_full.dmi differ
diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi
index 6de701932708..27c2bde8ddfb 100644
Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ
diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi
index 0da54f95f801..9030742c394f 100644
Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index d0edc5a9155c..8a4ff7a3f46f 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi
new file mode 100644
index 000000000000..ed85ade52499
Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_construction.dmi b/icons/obj/clothing/modsuit/mod_construction.dmi
new file mode 100644
index 000000000000..2d73c42d3edd
Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_construction.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
new file mode 100644
index 000000000000..37e377f952cc
Binary files /dev/null and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/obj/clothing/under/centcom.dmi b/icons/obj/clothing/under/centcom.dmi
index 52643d17eeac..52a80bcb918e 100644
Binary files a/icons/obj/clothing/under/centcom.dmi and b/icons/obj/clothing/under/centcom.dmi differ
diff --git a/icons/obj/defib.dmi b/icons/obj/defib.dmi
index 23172d4fdd83..e59deaac377c 100644
Binary files a/icons/obj/defib.dmi and b/icons/obj/defib.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index abf401265984..f2d01d772b17 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/doors/windoor.dmi b/icons/obj/doors/windoor.dmi
index 9bf105faaf68..9569f51490b4 100644
Binary files a/icons/obj/doors/windoor.dmi and b/icons/obj/doors/windoor.dmi differ
diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi
index 768024f9193b..83954bf057c0 100644
Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ
diff --git a/icons/obj/fence.dmi b/icons/obj/fence.dmi
index 132482b4874e..0f9a6fe173e2 100644
Binary files a/icons/obj/fence.dmi and b/icons/obj/fence.dmi differ
diff --git a/icons/obj/grenade.dmi b/icons/obj/grenade.dmi
index f7f598296619..5d619868eadc 100644
Binary files a/icons/obj/grenade.dmi and b/icons/obj/grenade.dmi differ
diff --git a/icons/obj/guns/magic.dmi b/icons/obj/guns/magic.dmi
index e4153046565a..88c124d82578 100644
Binary files a/icons/obj/guns/magic.dmi and b/icons/obj/guns/magic.dmi differ
diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi
index fa261d59edd4..41e3d2e6bf08 100644
Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ
diff --git a/icons/obj/lavaland/ash_flora.dmi b/icons/obj/lavaland/ash_flora.dmi
index c8c128a0ab58..3c262cd882ce 100644
Binary files a/icons/obj/lavaland/ash_flora.dmi and b/icons/obj/lavaland/ash_flora.dmi differ
diff --git a/icons/obj/playing_cards.dmi b/icons/obj/playing_cards.dmi
index 468e6af9d007..1ff9f3409e50 100644
Binary files a/icons/obj/playing_cards.dmi and b/icons/obj/playing_cards.dmi differ
diff --git a/icons/obj/radio.dmi b/icons/obj/radio.dmi
index cc8095d0affb..e744151be5a5 100644
Binary files a/icons/obj/radio.dmi and b/icons/obj/radio.dmi differ
diff --git a/icons/obj/smooth_structures/windows/plasma_window.dmi b/icons/obj/smooth_structures/windows/plasma_window.dmi
index 1b21a58e950a..11c08011a67c 100644
Binary files a/icons/obj/smooth_structures/windows/plasma_window.dmi and b/icons/obj/smooth_structures/windows/plasma_window.dmi differ
diff --git a/icons/obj/smooth_structures/windows/reinforced_window.dmi b/icons/obj/smooth_structures/windows/reinforced_window.dmi
index 3259096ee863..8433522914ae 100644
Binary files a/icons/obj/smooth_structures/windows/reinforced_window.dmi and b/icons/obj/smooth_structures/windows/reinforced_window.dmi differ
diff --git a/icons/obj/smooth_structures/windows/reinforced_window_edges.dmi b/icons/obj/smooth_structures/windows/reinforced_window_edges.dmi
index e6519858d262..3e32388dd536 100644
Binary files a/icons/obj/smooth_structures/windows/reinforced_window_edges.dmi and b/icons/obj/smooth_structures/windows/reinforced_window_edges.dmi differ
diff --git a/icons/obj/smooth_structures/windows/rplasma_window.dmi b/icons/obj/smooth_structures/windows/rplasma_window.dmi
index 5497819faa39..665a04402505 100644
Binary files a/icons/obj/smooth_structures/windows/rplasma_window.dmi and b/icons/obj/smooth_structures/windows/rplasma_window.dmi differ
diff --git a/icons/obj/smooth_structures/windows/tinted_window.dmi b/icons/obj/smooth_structures/windows/tinted_window.dmi
index 0a0bcb9ff050..a17c39244010 100644
Binary files a/icons/obj/smooth_structures/windows/tinted_window.dmi and b/icons/obj/smooth_structures/windows/tinted_window.dmi differ
diff --git a/icons/obj/smooth_structures/windows/window.dmi b/icons/obj/smooth_structures/windows/window.dmi
index ee33f8b78db9..ff049fad0d51 100644
Binary files a/icons/obj/smooth_structures/windows/window.dmi and b/icons/obj/smooth_structures/windows/window.dmi differ
diff --git a/icons/obj/smooth_structures/windows/window_edges.dmi b/icons/obj/smooth_structures/windows/window_edges.dmi
index 866717e386ac..2eda848769ee 100644
Binary files a/icons/obj/smooth_structures/windows/window_edges.dmi and b/icons/obj/smooth_structures/windows/window_edges.dmi differ
diff --git a/icons/obj/stacks/miscellaneous.dmi b/icons/obj/stacks/miscellaneous.dmi
index ed99d899b199..37b748446dcc 100644
Binary files a/icons/obj/stacks/miscellaneous.dmi and b/icons/obj/stacks/miscellaneous.dmi differ
diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi
index 42f2d5118182..b659213b38ad 100644
Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ
diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi
index fe2d652694ed..276f34b86292 100644
Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ
diff --git a/icons/ss220.png b/icons/ss220.png
new file mode 100644
index 000000000000..68abcb9b8e2b
Binary files /dev/null and b/icons/ss220.png differ
diff --git a/icons/turf/floors/lava.dmi b/icons/turf/floors/lava.dmi
index 3b889c9a5f68..f3c51368fece 100644
Binary files a/icons/turf/floors/lava.dmi and b/icons/turf/floors/lava.dmi differ
diff --git a/icons/turf/floors/liquidplasma.dmi b/icons/turf/floors/liquidplasma.dmi
new file mode 100644
index 000000000000..f4917c238784
Binary files /dev/null and b/icons/turf/floors/liquidplasma.dmi differ
diff --git a/icons/turf/walls/reinforced_wall.dmi b/icons/turf/walls/reinforced_wall.dmi
index 67cbaf57088c..d08446795e71 100644
Binary files a/icons/turf/walls/reinforced_wall.dmi and b/icons/turf/walls/reinforced_wall.dmi differ
diff --git a/icons/turf/walls/wall.dmi b/icons/turf/walls/wall.dmi
index c2f75e8c10de..51442c8fff24 100644
Binary files a/icons/turf/walls/wall.dmi and b/icons/turf/walls/wall.dmi differ
diff --git a/interface/skin.dmf b/interface/skin.dmf
index 031070886e69..3e0db491a2fb 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -3,126 +3,124 @@ macro "default"
menu "menu"
elem
- name = "&File"
+ name = "&Файл"
elem
- name = "&Quick screenshot"
+ name = "&Быстрый скриншот"
command = ".screenshot auto"
- category = "&File"
+ category = "&Файл"
elem
- name = "&Save screenshot as..."
+ name = "&Сохранить скриншот как..."
command = ".screenshot"
- category = "&File"
+ category = "&Файл"
elem "reconnectbutton"
- name = "&Reconnect"
+ name = "&Переподключиться"
command = ".reconnect"
- category = "&File"
+ category = "&Файл"
elem
- name = ""
- category = "&File"
- elem
- name = "&Quit"
+ name = "&Выход"
command = ".quit"
- category = "&File"
+ category = "&Файл"
elem
- name = "&Icons"
+ name = "&Вид"
elem
- name = "&Size"
- category = "&Icons"
+ name = "&Размер"
+ category = "&Вид"
elem "stretch"
- name = "&Stretch to fit"
+ name = "&Растянуть"
command = ".winset \"mapwindow.map.icon-size=0\""
- category = "&Size"
+ category = "&Размер"
is-checked = true
can-check = true
group = "size"
elem "icon128"
name = "&128x128 (4x)"
command = ".winset \"mapwindow.map.icon-size=128\""
- category = "&Size"
+ category = "&Размер"
can-check = true
group = "size"
elem "icon112"
name = "&112x112 (3.5x)"
command = ".winset \"mapwindow.map.icon-size=112\""
- category = "&Size"
+ category = "&Размер"
can-check = true
group = "size"
elem "icon96"
name = "&96x96 (3x)"
command = ".winset \"mapwindow.map.icon-size=96\""
- category = "&Size"
+ category = "&Размер"
can-check = true
group = "size"
elem "icon80"
name = "&80x80 (2.5x)"
command = ".winset \"mapwindow.map.icon-size=80\""
- category = "&Size"
+ category = "&Размер"
can-check = true
group = "size"
elem "icon64"
name = "&64x64 (2x)"
command = ".winset \"mapwindow.map.icon-size=64\""
- category = "&Size"
+ category = "&Размер"
can-check = true
group = "size"
elem "icon48"
name = "&48x48 (1.5x)"
command = ".winset \"mapwindow.map.icon-size=48\""
- category = "&Size"
+ category = "&Размер"
can-check = true
group = "size"
elem "icon32"
name = "&32x32"
command = ".winset \"mapwindow.map.icon-size=32\""
- category = "&Size"
+ category = "&Размер"
can-check = true
group = "size"
elem
- name = "&Scaling"
- category = "&Icons"
+ name = "&Масштабирование"
+ category = "&Вид"
+ saved-params = "is-checked;command"
elem "NN"
- name = "&Nearest Neighbor"
+ name = "&По соседним пикселям"
command = ".winset \"mapwindow.map.zoom-mode=distort\""
- category = "&Scaling"
+ category = "&Масштабирование"
can-check = true
- is-checked = true
group = "scale"
elem "PS"
- name = "&Point Sampling"
+ name = "&Точечный отбор"
command = ".winset \"mapwindow.map.zoom-mode=normal\""
- category = "&Scaling"
+ category = "&Масштабирование"
can-check = true
+ is-checked = true
group = "scale"
elem "BL"
- name = "&Bilinear"
+ name = "&Билинейное"
command = ".winset \"mapwindow.map.zoom-mode=blur\""
- category = "&Scaling"
+ category = "&Масштабирование"
can-check = true
group = "scale"
elem "textmode"
- name = "&Text"
+ name = "&Текстовый режим"
command = ".winset \"menu.textmode.is-checked=true?mapwindow.map.text-mode=true:mapwindow.map.text-mode=false\""
- category = "&Icons"
+ category = "&Вид"
can-check = true
elem
- name = "&Options"
+ name = "&Опции"
elem
- name = "&Open Volume Mixer"
+ name = "&Открыть микшер"
command = "Open-Volume-Mixer"
- category = "&Options"
+ category = "&Опции"
elem "statusbar"
- name = "&Show status bar"
- category = "&Options"
+ name = "&Показывать статус бар"
+ category = "&Опции"
can-check = true
is-checked = true
saved-params = "is-checked"
command = ".winset \"menu.statusbar.is-checked=true?mapwindow.status_bar.is-visible=true:mapwindow.status_bar.is-visible=false\""
elem
- name = "&Help"
+ name = "&Помощь"
elem
name = "&Admin help"
command = "adminhelp"
- category = "&Help"
+ category = "&Помощь"
window "mainwindow"
@@ -131,10 +129,10 @@ window "mainwindow"
size = 640x440
is-default = true
saved-params = "pos;size;is-minimized;is-maximized"
- title = "Paradise Station 13"
+ title = "SS220 | WyccStation (Paradise)"
statusbar = false
is-maximized = true
- icon = 'icons\\paradise.png'
+ icon = 'icons\\ss220.png'
macro = "default"
menu = "menu"
elem "asset_cache_browser"
@@ -171,9 +169,21 @@ window "mapwindow"
text-color = none
is-default = true
saved-params = "icon-size"
- zoom-mode = "distort"
+ zoom-mode = "normal"
style = ".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .small { font-size: 6px; } .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .clown { color: #FF69Bf;} .tajaran {color: #803B56;} .skrell {color: #00CED1;} .solcom {color: #22228B;} .com_srus {color: #7c4848;} .zombie\t{color: #ff0000;} .soghun {color: #228B22;} .vox {color: #AA00AA;} .diona {color: #804000; font-weight: bold;} .trinary {color: #727272;} .kidan {color: #664205;} .slime {color: #0077AA;} .drask {color: #a3d4eb;} .vulpkanin {color: #B97A57;} .abductor {color: #800080; font-style: italic;} .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; }"
on-show = ".winset \"menu.statusbar.is-checked=true?mapwindow.status_bar.is-visible=true:mapwindow.status_bar.is-visible=false\""
+ elem "title_browser"
+ type = BROWSER
+ pos = 0,0
+ size = 640x480
+ anchor1 = 0,0
+ anchor2 = 100,100
+ background-color = none
+ is-visible = false
+ is-disabled = true
+ saved-params = ""
+ auto-format = false
+ style = ".center { text-align: center; } .maptext { font-family: 'Grand9K Pixel'; font-size: 6pt; -dm-text-outline: 1px black; color: white; line-height: 1.0; } .command_headset { font-weight: bold; } .small { font-family: 'TinyUnicode'; font-size: 12pt; line-height: 0.75; } .big { font-size: 8pt; } .reallybig { font-size: 8pt; } .extremelybig { font-size: 8pt; } .greentext { color: #00FF00; font-size: 6pt; } .redtext { color: #FF0000; font-size: 6pt; } .clown { color: #FF69BF; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-family: 'TinyUnicode'; font-size: 12pt; line-height: 0.75; }"
elem "status_bar"
type = LABEL
pos = 0,464
@@ -210,7 +220,7 @@ window "outputwindow"
size = 80x20
anchor1 = 100,100
anchor2 = -1,-1
- text = "Say"
+ text = "Чат"
command = ".winset \"saybutton.is-checked=true?input.command=\"!say \\\"\" macrobutton.is-checked=false:input.command=\"\"saybutton.is-checked=true?mebutton.is-checked=false\""
button-type = pushbox
elem "mebutton"
@@ -219,7 +229,7 @@ window "outputwindow"
size = 80x20
anchor1 = 100,100
anchor2 = -1,-1
- text = "Me"
+ text = "Действие"
command = ".winset \"mebutton.is-checked=true ? input.command=\"!me \\\"\" : input.command=\"\"mebutton.is-checked=true ? saybutton.is-checked=false\"\"mebutton.is-checked=true ? oocbutton.is-checked=false\""
button-type = pushbox
elem "browseroutput"
@@ -248,64 +258,43 @@ window "rpane"
left = "infowindow"
right = "outputwindow"
is-vert = false
- elem "textb"
- type = BUTTON
- pos = 0,7
- size = 60x16
- saved-params = "is-checked"
- text = "Text"
- command = ".winset \"rpanewindow.top=;\""
- group = "rpanemode"
- button-type = pushbox
elem "infob"
type = BUTTON
- pos = 62,7
- size = 60x16
+ pos = 3,7
+ size = 40x16
is-checked = true
saved-params = "is-checked"
- text = "Info"
+ text = "Инфо"
command = ".winset \"rpanewindow.top=infowindow\""
group = "rpanemode"
button-type = pushbox
elem "wikib"
type = BUTTON
- pos = 134,7
+ pos = 48,7
size = 50x16
- text = "Wiki"
+ text = "Вики"
command = "wiki"
- elem "forumb"
- type = BUTTON
- pos = 186,7
- size = 50x16
- text = "Forum"
- command = "forum"
elem "rulesb"
type = BUTTON
- pos = 238,7
- size = 50x16
- text = "Rules"
+ pos = 99,7
+ size = 60x16
+ text = "Правила"
command = "rules"
elem "githubb"
type = BUTTON
- pos = 290,7
+ pos = 160,7
size = 50x16
text = "GitHub"
command = "github"
elem "webmap"
type = BUTTON
- pos = 342,7
+ pos = 211,7
size = 50x16
- text = "Map"
+ text = "Карта"
command = "webmap"
- elem "changelog"
- type = BUTTON
- pos = 404,7
- size = 67x16
- text = "Changelog"
- command = "Changelog"
elem "discordb"
type = BUTTON
- pos = 473,7
+ pos = 266,7
size = 60x16
font-style = "bold"
text-color = #ffffff
@@ -314,13 +303,26 @@ window "rpane"
command = "discord"
elem "donate"
type = BUTTON
- pos = 535,7
+ pos = 327,7
size = 60x16
font-style = "bold"
text-color = #ffffff
- background-color = #008000
- text = "Donate"
+ background-color = #ef642b
+ text = "Бусти"
command = "Donate"
+ elem "changelog"
+ type = BUTTON
+ pos = 393,7
+ size = 70x16
+ text = "Чейнджлог"
+ command = "Changelog"
+ elem "fullscreenb"
+ type = BUTTON
+ pos = 464,7
+ size = 70x16
+ saved-params = "is-checked"
+ text = "Fullscreen"
+ command = "fullscreen"
window "infowindow"
elem "infowindow"
diff --git a/modular_ss220/README.md b/modular_ss220/README.md
new file mode 100644
index 000000000000..57edda14603c
--- /dev/null
+++ b/modular_ss220/README.md
@@ -0,0 +1,78 @@
+# Модпаки
+
+Модпаки загружаются компилятором сразу после всего остального кода и после карты, что позволяет делать оверрайды, безболезненно добавлять предметы, пользуясь дефайнами из *core code* и много много всего.
+
+Инициализируются же модпаки на этапе `INIT_ORDER_MODPACKS` сразу после `INIT_ORDER_INPUT` с вызовом соответствующего прока у датума.
+Подробнее можно почитать в [`mods/_modpack.dm`]
+
+## Зачем?
+
+1. Чтобы не изменять *core code*, а оверрайдить здесь
+2. Удобный ввод новых предметов
+3. Сохранять ивентовые паки для последующего возможного использования в других ивентах
+
+## Создание модпака
+
+Любой модпак состоит из:
+
+- Папки пака
+
+- `.dme` файла с подключением всех остальных
+- `.dm` файла с датумом, содержащем информацию о паке
+- Остальных файлов пака
+
+### Структура модпака
+
+Если мы условимся, что наш пак будет называется `hello_world`, то это будет выглядеть так:
+
+```text
+mods/hello_world
+|- _hello_world.dm
+|- _hello_world.dme
+|- any_file.dm
+|- ...
+\- some_file.dm
+```
+
+А теперь каждый файл по отдельности:
+
+### `mods/hello_world/_hello_world.dme`
+
+```dm
+#ifndef MODPACK_HELLO_WORLD
+#define MODPACK_HELLO_WORLD
+
+#include "_hello_world.dm"
+
+#include "any_file.dm"
+// ...
+#include "some_file.dm"
+
+#endif
+```
+
+Здесь подключаются все необходимые файлы, включая файл с синглтоном. Ничего особенного.
+
+Стоит лишь учитывать, что пути локальные, а не глобальные.
+
+### `mods/hello_world/_hello_world.dm`
+
+```dm
+/datum/modpack/hello_world
+ name = "Hello world"
+ desc = "Описание модпака"
+ author = "SuhEugene"
+```
+
+- `name` - Имя пака. Необязательно такое же, как в коде, но желательно.
+- `desc` - Очевидно, описание пака. Хотелось бы, чтобы оно было достаточно подробным, чтобы можно было понять что содержит пак, но не сильно замудрённым, чтобы любой игрок осилил буквы.
+- `author` - Никнейм на гитхабе, тег дискорда или даже несколько таких. Просто чтобы обозначить автора(ов) в простейшей форме.
+
+### `mods/hello_world/any_file.dm`
+
+Это просто любой файл. Как в названии и написано. И название необязательно такое. Просто какой-либо угодно файл с dm кодом.
+
+## Ассеты
+
+Этим обобщающим словом обычно называют картинки и звуки. Их я попрошу оставлять в папке `mods/hello_world/icons/` и в `mods/hello_world/sound/` соответственно.
+Лучше всего, если модпак будет использовать собственные ассеты и не обращаться к *пакам* или же другим модпакам.
diff --git a/modular_ss220/_defines220/_defines220.dm b/modular_ss220/_defines220/_defines220.dm
new file mode 100644
index 000000000000..89c20b6a61ef
--- /dev/null
+++ b/modular_ss220/_defines220/_defines220.dm
@@ -0,0 +1,4 @@
+/datum/modpack/defines220
+ name = "Дефайны220"
+ desc = "Добавляет дефайны, которые нам нужны"
+ author = "larentoun"
diff --git a/modular_ss220/_defines220/_defines220.dme b/modular_ss220/_defines220/_defines220.dme
new file mode 100644
index 000000000000..c52c56e18e17
--- /dev/null
+++ b/modular_ss220/_defines220/_defines220.dme
@@ -0,0 +1,9 @@
+#include "_defines220.dm"
+
+#include "code/signals_mob/signals_mob_ai.dm"
+#include "code/signals_mob/signals_mob_carbon.dm"
+#include "code/signals_mob/signals_mob_living.dm"
+#include "code/signals_mob/signals_mob_main.dm"
+#include "code/signals_mob/signals_mob_silicon.dm"
+#include "code/signals_mob/signals_mob_simple.dm"
+#include "code/signals_keybindings.dm"
diff --git a/modular_ss220/_defines220/code/signals_keybindings.dm b/modular_ss220/_defines220/code/signals_keybindings.dm
new file mode 100644
index 000000000000..9ce840354df7
--- /dev/null
+++ b/modular_ss220/_defines220/code/signals_keybindings.dm
@@ -0,0 +1 @@
+#define COMSIG_KB_ACTIVATED (1<<0)
diff --git a/modular_ss220/_defines220/code/signals_mob/signals_mob_ai.dm b/modular_ss220/_defines220/code/signals_mob/signals_mob_ai.dm
new file mode 100644
index 000000000000..fa7df8ae680b
--- /dev/null
+++ b/modular_ss220/_defines220/code/signals_mob/signals_mob_ai.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon/ai
diff --git a/modular_ss220/_defines220/code/signals_mob/signals_mob_carbon.dm b/modular_ss220/_defines220/code/signals_mob/signals_mob_carbon.dm
new file mode 100644
index 000000000000..ef6039c440fc
--- /dev/null
+++ b/modular_ss220/_defines220/code/signals_mob/signals_mob_carbon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/carbon
diff --git a/modular_ss220/_defines220/code/signals_mob/signals_mob_living.dm b/modular_ss220/_defines220/code/signals_mob/signals_mob_living.dm
new file mode 100644
index 000000000000..b6d8049bc00e
--- /dev/null
+++ b/modular_ss220/_defines220/code/signals_mob/signals_mob_living.dm
@@ -0,0 +1,12 @@
+// Signals for /mob/living
+
+/// from mob/living/Life(): (deltatime, times_fired)
+#define COMSIG_LIVING_LIFE "living_life"
+
+/// from mob/living/handle_message_mode(): (message_mode, list/message_pieces, verb, used_radios)
+#define COMSIG_LIVING_HANDLE_MESSAGE_MODE "living_handle_message_mode"
+ #define COMPONENT_FORCE_WHISPER (1<<0)
+
+/// from mob/living/CanPass(): (atom/movable/mover, turf/target, height)
+#define COMSIG_LIVING_CAN_PASS "living_can_pass"
+ #define COMPONENT_LIVING_PASSABLE (1<<0)
diff --git a/modular_ss220/_defines220/code/signals_mob/signals_mob_main.dm b/modular_ss220/_defines220/code/signals_mob/signals_mob_main.dm
new file mode 100644
index 000000000000..1908d846992d
--- /dev/null
+++ b/modular_ss220/_defines220/code/signals_mob/signals_mob_main.dm
@@ -0,0 +1,5 @@
+// Signals for /mob
+
+/// from mob/living/Process_Spacemove(): (movement_dir)
+#define COMSIG_LIVING_PROCESS_SPACEMOVE "mob_client_pre_living_move"
+ #define COMPONENT_BLOCK_SPACEMOVE (1<<0)
diff --git a/modular_ss220/_defines220/code/signals_mob/signals_mob_silicon.dm b/modular_ss220/_defines220/code/signals_mob/signals_mob_silicon.dm
new file mode 100644
index 000000000000..1e776fa5270e
--- /dev/null
+++ b/modular_ss220/_defines220/code/signals_mob/signals_mob_silicon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon
diff --git a/modular_ss220/_defines220/code/signals_mob/signals_mob_simple.dm b/modular_ss220/_defines220/code/signals_mob/signals_mob_simple.dm
new file mode 100644
index 000000000000..937b109659eb
--- /dev/null
+++ b/modular_ss220/_defines220/code/signals_mob/signals_mob_simple.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/simple_animal
diff --git a/modular_ss220/_misc/_misc.dm b/modular_ss220/_misc/_misc.dm
new file mode 100644
index 000000000000..ec9069a4b4bf
--- /dev/null
+++ b/modular_ss220/_misc/_misc.dm
@@ -0,0 +1,4 @@
+/datum/modpack/misc
+ name = "Различное"
+ desc = "Различные полезные функции, которые обязательно(нет) кому то понадобятся"
+ author = "furior"
diff --git a/modular_ss220/_misc/_misc.dme b/modular_ss220/_misc/_misc.dme
new file mode 100644
index 000000000000..0f5846ee9db8
--- /dev/null
+++ b/modular_ss220/_misc/_misc.dme
@@ -0,0 +1,3 @@
+#include "_misc.dm"
+
+#include "code/icons.dm"
diff --git a/modular_ss220/_misc/code/icons.dm b/modular_ss220/_misc/code/icons.dm
new file mode 100644
index 000000000000..1a2c3cac8ed3
--- /dev/null
+++ b/modular_ss220/_misc/code/icons.dm
@@ -0,0 +1,2 @@
+/proc/flatbicon(obj, use_class = TRUE)
+ return bicon(getFlatIcon(obj))
diff --git a/modular_ss220/_modpack.dm b/modular_ss220/_modpack.dm
new file mode 100644
index 000000000000..5569426fa640
--- /dev/null
+++ b/modular_ss220/_modpack.dm
@@ -0,0 +1,17 @@
+/datum/modpack
+ /// A string name for the modpack. Used for looking up other modpacks in init.
+ var/name
+ /// A string desc for the modpack. Can be used for modpack verb list as description.
+ var/desc
+ /// A string with authors of this modpack.
+ var/author
+
+/datum/modpack/proc/pre_initialize()
+ if(!name)
+ return "Modpack name is unset."
+
+/datum/modpack/proc/initialize()
+ return
+
+/datum/modpack/proc/post_initialize()
+ return
diff --git a/modular_ss220/_modpacks.dm b/modular_ss220/_modpacks.dm
new file mode 100644
index 000000000000..dd115aa7f56e
--- /dev/null
+++ b/modular_ss220/_modpacks.dm
@@ -0,0 +1,60 @@
+#define INIT_ORDER_MODPACKS 16.5
+
+SUBSYSTEM_DEF(modpacks)
+ name = "Modpacks"
+ init_order = INIT_ORDER_SOUNDS
+ flags = SS_NO_FIRE
+ var/list/loaded_modpacks = list()
+
+/datum/controller/subsystem/modpacks/Initialize()
+ var/list/all_modpacks = list()
+ for(var/modpack in subtypesof(/datum/modpack/))
+ all_modpacks.Add(new modpack)
+ // Pre-init and register all compiled modpacks.
+ for(var/datum/modpack/package as anything in all_modpacks)
+ var/fail_msg = package.pre_initialize()
+ if(QDELETED(package))
+ CRASH("Modpack of type [package.type] is null or queued for deletion.")
+ if(fail_msg)
+ CRASH("Modpack [package.name] failed to pre-initialize: [fail_msg].")
+ if(loaded_modpacks[package.name])
+ CRASH("Attempted to register duplicate modpack name [package.name].")
+ loaded_modpacks.Add(package)
+
+ // Handle init and post-init (two stages in case a modpack needs to implement behavior based on the presence of other packs).
+ for(var/datum/modpack/package as anything in all_modpacks)
+ var/fail_msg = package.initialize()
+ if(fail_msg)
+ CRASH("Modpack [(istype(package) && package.name) || "Unknown"] failed to initialize: [fail_msg]")
+ for(var/datum/modpack/package as anything in all_modpacks)
+ var/fail_msg = package.post_initialize()
+ if(fail_msg)
+ CRASH("Modpack [(istype(package) && package.name) || "Unknown"] failed to post-initialize: [fail_msg]")
+
+/client/verb/modpacks_list()
+ set name = "Modpacks List"
+ set category = "OOC"
+
+ if(!mob || !SSmodpacks.initialized)
+ return
+
+ if(length(SSmodpacks.loaded_modpacks))
+ . = "
Список модификаций
"
+ for(var/datum/modpack/M as anything in SSmodpacks.loaded_modpacks)
+ if(M.name)
+ . += ""
+ . += " [M.name]"
+
+ if(M.desc || M.author)
+ . += " "
+ if(M.desc)
+ . += " Описание: [M.desc]"
+ if(M.author)
+ . += " Автор: [M.author]"
+ . += " "
+
+ var/datum/browser/popup = new(mob, "modpacks_list", "Список Модификаций", 480, 580)
+ popup.set_content(.)
+ popup.open()
+ else
+ to_chat(src, "Этот сервер не использует какие-либо модификации.")
diff --git a/modular_ss220/_signals220/_signals220.dm b/modular_ss220/_signals220/_signals220.dm
new file mode 100644
index 000000000000..e925e6f886c7
--- /dev/null
+++ b/modular_ss220/_signals220/_signals220.dm
@@ -0,0 +1,5 @@
+/datum/modpack/signals220
+
+ name = "Сигналы220"
+ desc = "Добавляет сигналы"
+ author = "larentoun"
diff --git a/modular_ss220/_signals220/_signals220.dme b/modular_ss220/_signals220/_signals220.dme
new file mode 100644
index 000000000000..26671df7a7f9
--- /dev/null
+++ b/modular_ss220/_signals220/_signals220.dme
@@ -0,0 +1,8 @@
+#include "_signals220.dm"
+
+#include "code/signals_mob/signals_mob_ai.dm"
+#include "code/signals_mob/signals_mob_carbon.dm"
+#include "code/signals_mob/signals_mob_living.dm"
+#include "code/signals_mob/signals_mob_main.dm"
+#include "code/signals_mob/signals_mob_silicon.dm"
+#include "code/signals_mob/signals_mob_simple.dm"
diff --git a/modular_ss220/_signals220/code/signals_mob/signals_mob_ai.dm b/modular_ss220/_signals220/code/signals_mob/signals_mob_ai.dm
new file mode 100644
index 000000000000..fa7df8ae680b
--- /dev/null
+++ b/modular_ss220/_signals220/code/signals_mob/signals_mob_ai.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon/ai
diff --git a/modular_ss220/_signals220/code/signals_mob/signals_mob_carbon.dm b/modular_ss220/_signals220/code/signals_mob/signals_mob_carbon.dm
new file mode 100644
index 000000000000..ef6039c440fc
--- /dev/null
+++ b/modular_ss220/_signals220/code/signals_mob/signals_mob_carbon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/carbon
diff --git a/modular_ss220/_signals220/code/signals_mob/signals_mob_living.dm b/modular_ss220/_signals220/code/signals_mob/signals_mob_living.dm
new file mode 100644
index 000000000000..e531cf715cb2
--- /dev/null
+++ b/modular_ss220/_signals220/code/signals_mob/signals_mob_living.dm
@@ -0,0 +1,28 @@
+// Signals for /mob/living
+
+/mob/living/CanPass(atom/movable/mover, turf/target, height)
+ if(SEND_SIGNAL(src, COMSIG_LIVING_CAN_PASS, mover, target, height) & COMPONENT_LIVING_PASSABLE)
+ return TRUE
+ return ..()
+
+/mob/living/Life(seconds, times_fired)
+ SEND_SIGNAL(src, COMSIG_LIVING_LIFE, seconds, times_fired)
+ . = ..()
+
+/mob/living/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
+ if(SEND_SIGNAL(src, COMSIG_LIVING_HANDLE_MESSAGE_MODE, message_mode, message_pieces, verb, used_radios) & COMPONENT_FORCE_WHISPER)
+ whisper_say(message_pieces)
+ return TRUE
+ . = ..()
+
+/mob/living/carbon/human/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
+ if(SEND_SIGNAL(src, COMSIG_LIVING_HANDLE_MESSAGE_MODE, message_mode, message_pieces, verb, used_radios) & COMPONENT_FORCE_WHISPER)
+ whisper_say(message_pieces)
+ return TRUE
+ . = ..()
+
+// Да, костыльно, но модульно по другому не вижу как - PIXEL_SHIFT
+/mob/living/Process_Spacemove(movement_dir)
+ if(SEND_SIGNAL(src, COMSIG_LIVING_PROCESS_SPACEMOVE, movement_dir) & COMPONENT_BLOCK_SPACEMOVE)
+ return FALSE
+ . = ..()
diff --git a/modular_ss220/_signals220/code/signals_mob/signals_mob_main.dm b/modular_ss220/_signals220/code/signals_mob/signals_mob_main.dm
new file mode 100644
index 000000000000..7aa41cead07d
--- /dev/null
+++ b/modular_ss220/_signals220/code/signals_mob/signals_mob_main.dm
@@ -0,0 +1 @@
+// Signals for /mob
diff --git a/modular_ss220/_signals220/code/signals_mob/signals_mob_silicon.dm b/modular_ss220/_signals220/code/signals_mob/signals_mob_silicon.dm
new file mode 100644
index 000000000000..1e776fa5270e
--- /dev/null
+++ b/modular_ss220/_signals220/code/signals_mob/signals_mob_silicon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon
diff --git a/modular_ss220/_signals220/code/signals_mob/signals_mob_simple.dm b/modular_ss220/_signals220/code/signals_mob/signals_mob_simple.dm
new file mode 100644
index 000000000000..937b109659eb
--- /dev/null
+++ b/modular_ss220/_signals220/code/signals_mob/signals_mob_simple.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/simple_animal
diff --git a/modular_ss220/_span/_span.dm b/modular_ss220/_span/_span.dm
new file mode 100644
index 000000000000..ceacea89c2e0
--- /dev/null
+++ b/modular_ss220/_span/_span.dm
@@ -0,0 +1,4 @@
+/datum/modpack/span_defines
+ name = "Span Defines"
+ desc = "Порт дефайнов для `span` с TG. Просто для стиля в коде."
+ author = "larentoun"
diff --git a/modular_ss220/_span/_span.dme b/modular_ss220/_span/_span.dme
new file mode 100644
index 000000000000..2fa2551fbe93
--- /dev/null
+++ b/modular_ss220/_span/_span.dme
@@ -0,0 +1,3 @@
+#include "_span.dm"
+
+#include "code/span.dm"
diff --git a/modular_ss220/_span/code/span.dm b/modular_ss220/_span/code/span.dm
new file mode 100644
index 000000000000..70c015f8a427
--- /dev/null
+++ b/modular_ss220/_span/code/span.dm
@@ -0,0 +1,167 @@
+// From 'goon\browserassets\css\browserOutput.css'
+#define span_darkmblue(str) ("" + str + "")
+#define span_prefix(str) ("" + str + "")
+#define span_ooc(str) ("" + str + "")
+#define span_looc(str) ("" + str + "")
+#define span_adminobserverooc(str) ("" + str + "")
+#define span_adminooc(str) ("" + str + "")
+#define span_adminobserver(str) ("" + str + "")
+#define span_admin(str) ("" + str + "")
+#define span_adminsay(str) ("" + str + "")
+#define span_mentorhelp(str) ("" + str + "")
+#define span_adminhelp(str) ("" + str + "")
+#define span_playerreply(str) ("" + str + "")
+#define span_pmsend(str) ("" + str + "")
+#define span_debug(str) ("" + str + "")
+#define span_name(str) ("" + str + "")
+#define span_say(str) ("" + str + "")
+#define span_yell(str) ("" + str + "")
+#define span_siliconsay(str) ("" + str + "")
+#define span_deadsay(str) ("" + str + "")
+#define span_radio(str) ("" + str + "")
+#define span_deptradio(str) ("" + str + "")
+#define span_comradio(str) ("" + str + "")
+#define span_syndradio(str) ("" + str + "")
+#define span_dsquadradio(str) ("" + str + "")
+#define span_resteamradio(str) ("" + str + "")
+#define span_airadio(str) ("" + str + "")
+#define span_centradio(str) ("" + str + "")
+#define span_secradio(str) ("" + str + "")
+#define span_engradio(str) ("" + str + "")
+#define span_medradio(str) ("" + str + "")
+#define span_sciradio(str) ("" + str + "")
+#define span_supradio(str) ("" + str + "")
+#define span_srvradio(str) ("" + str + "")
+#define span_proradio(str) ("" + str + "")
+#define span_admin_channel(str) ("" + str + "")
+#define span_all_admin_ping(str) ("" + str + "")
+#define span_mentor_channel(str) ("" + str + "")
+#define span_mentor_channel_admin(str) ("" + str + "")
+#define span_djradio(str) ("" + str + "")
+#define span_binaryradio(str) ("" + str + "")
+#define span_mommiradio(str) ("" + str + "")
+#define span_alert(str) ("" + str + "")
+#define span_ghostalert(str) ("" + str + "")
+#define span_emote(str) ("" + str + "")
+#define span_selecteddna(str) ("" + str + "")
+
+#define span_attack(str) ("" + str + "")
+#define span_moderate(str) ("" + str + "")
+#define span_disarm(str) ("" + str + "")
+#define span_passive(str) ("" + str + "")
+
+#define span_warning(str) ("" + str + "")
+#define span_boldwarning(str) ("" + str + "")
+#define span_danger(str) ("" + str + "")
+#define span_userdanger(str) ("" + str + "")
+#define span_biggerdanger(str) ("" + str + "")
+
+#define span_info(str) ("" + str + "")
+#define span_notice(str) ("" + str + "")
+#define span_boldnotice(str) ("" + str + "")
+#define span_suicide(str) ("" + str + "")
+#define span_green(str) ("" + str + "")
+#define span_pr_announce(str) ("" + str + "")
+#define span_boldannounce(str) ("" + str + "")
+#define span_greenannounce(str) ("" + str + "")
+
+#define span_alien(str) ("" + str + "")
+#define span_noticealien(str) ("" + str + "")
+#define span_alertalien(str) ("" + str + "")
+#define span_terrorspider(str) ("" + str + "")
+#define span_dantalion(str) ("" + str + "")
+
+#define span_sinister(str) ("" + str + "")
+#define span_blob(str) ("" + str + "")
+#define span_confirm(str) ("" + str + "")
+#define span_rose(str) ("" + str + "")
+#define span_sans(str) ("" + str + "")
+#define span_wingdings(str) ("" + str + "")
+#define span_robot(str) ("" + str + "")
+#define span_ancient(str) ("" + str + "")
+#define span_newscaster(str) ("" + str + "")
+#define span_mod(str) ("" + str + "")
+#define span_modooc(str) ("" + str + "")
+#define span_adminmod(str) ("" + str + "")
+#define span_tajaran(str) ("" + str + "")
+#define span_skrell(str) ("" + str + "")
+#define span_solcom(str) ("" + str + "")
+#define span_com_srus(str) ("" + str + "")
+#define span_zombie(str) ("" + str + "")
+#define span_soghun(str) ("" + str + "")
+#define span_changeling(str) ("" + str + "")
+#define span_vox(str) ("" + str + "")
+#define span_diona(str) ("" + str + "")
+#define span_trinary(str) ("" + str + "")
+#define span_kidan(str) ("" + str + "")
+#define span_slime(str) ("" + str + "")
+#define span_drask(str) ("" + str + "")
+#define span_moth(str) ("" + str + "")
+#define span_clown(str) ("" + str + "")
+#define span_vulpkanin(str) ("" + str + "")
+#define span_abductor(str) ("" + str + "")
+#define span_mind_control(str) ("" + str + "")
+#define span_rough(str) ("" + str + "")
+#define span_say_quote(str) ("" + str + "")
+#define span_cult(str) ("" + str + "")
+#define span_cultspeech(str) ("" + str + "")
+#define span_cultitalic(str) ("" + str + "")
+#define span_cultlarge(str) ("" + str + "")
+#define span_narsie(str) ("" + str + "")
+#define span_narsiesmall(str) ("" + str + "")
+#define span_interface(str) ("" + str + "")
+#define span_big(str) ("" + str + "")
+#define span_reallybig(str) ("" + str + "")
+#define span_greentext(str) ("" + str + "")
+#define span_redtext(str) ("" + str + "")
+#define span_bold(str) ("" + str + "")
+#define span_his_grace(str) ("" + str + "")
+#define span_center(str) ("" + str + "")
+#define span_red(str) ("" + str + "")
+#define span_purple(str) ("" + str + "")
+#define span_skeleton(str) ("" + str + "")
+#define span_gutter(str) ("" + str + "")
+#define span_orange(str) ("" + str + "")
+#define span_orangei(str) ("" + str + "")
+#define span_orangeb(str) ("" + str + "")
+#define span_resonate(str) ("" + str + "")
+
+#define span_revennotice(str) ("" + str + "")
+#define span_revenboldnotice(str) ("" + str + "")
+#define span_revenbignotice(str) ("" + str + "")
+#define span_revenminor(str) ("" + str + "")
+#define span_revenwarning(str) ("" + str + "")
+#define span_revendanger(str) ("" + str + "")
+
+#define span_specialnoticebold(str) ("" + str + "")
+
+#define span_specialnotice(str) ("" + str + "")
+
+#define span_good(str) ("" + str + "")
+#define span_average(str) ("" + str + "")
+#define span_bad(str) ("" + str + "")
+#define span_italics(str) ("" + str + "")
+#define span_talkinto(str) ("" + str + "")
+#define span_whisper(str) ("" + str + "")
+#define span_recruit(str) ("" + str + "")
+
+#define span_memo(str) ("" + str + "")
+#define span_memoedit(str) ("" + str + "")
+#define span_connectionClosed(str) ("" + str + "")
+#define span_fatalError(str) ("" + str + "")
+
+#define span_rebooting(str) ("" + str + "")
+
+#define span_colossus(str) ("" + str + "")
+#define span_hierophant(str) ("" + str + "")
+#define span_hierophant_warning(str) ("" + str + "")
+
+#define span_emoji(str) ("" + str + "")
+
+#define span_adminticket(str) ("" + str + "")
+
+#define span_adminticketalt(str) ("" + str + "")
+
+#define span_announcement(str) ("" + str + "")
+
+#define span_bolditalics(str) ("" + str + "")
diff --git a/modular_ss220/aesthetics/_aesthetics.dm b/modular_ss220/aesthetics/_aesthetics.dm
new file mode 100644
index 000000000000..bcccd3158922
--- /dev/null
+++ b/modular_ss220/aesthetics/_aesthetics.dm
@@ -0,0 +1,4 @@
+/datum/modpack/aesthetics
+ name = "Эстетика"
+ desc = "Обновление визуального ряда"
+ author = "larentoun, Aylong220"
diff --git a/modular_ss220/aesthetics/_aesthetics.dme b/modular_ss220/aesthetics/_aesthetics.dme
new file mode 100644
index 000000000000..2fd0a930c535
--- /dev/null
+++ b/modular_ss220/aesthetics/_aesthetics.dme
@@ -0,0 +1,48 @@
+#include "_aesthetics.dm"
+
+#include "airalarm\code\airalarm.dm"
+#include "apc\code\apc.dm"
+#include "atm\code\atm.dm"
+#include "atmospherics\code\atmospherics.dm"
+#include "better_ids\code\better_ids.dm"
+#include "blastdoor\code\blastdoor.dm"
+#include "boxes\code\boxes.dm"
+#include "cameras\code\cameras.dm"
+#include "chairs\code\chairs.dm"
+#include "defib\code\defib.dm"
+#include "dirwindows\code\dirwindows.dm"
+#include "door_control\code\door_control.dm"
+#include "extinguisher\code\extinguisher.dm"
+#include "firealarm\code\firealarm.dm"
+#include "floors\code\floors.dm"
+#include "hydroponics\code\hydroponics.dm"
+#include "keycard\code\keycard.dm"
+#include "labeler\code\labeler.dm"
+#include "library\code\library.dm"
+#include "light_switch\code\light_switch.dm"
+#include "newscaster\code\newscaster.dm"
+#include "piano\code\piano.dm"
+#include "racks\code\racks.dm"
+#include "requests_console\code\requests_console.dm"
+#include "rollerbed\code\rollerbed.dm"
+#include "safe\code\safe.dm"
+#include "shutters\code\shutters.dm"
+#include "soap\code\soap.dm"
+#include "surgery_table\code\surgery_table.dm"
+#include "toolboxes\code\toolboxes.dm"
+#include "wallcloset\code\wallcloset.dm"
+#include "windoor\code\windoor.dm"
+#include "windowtint\code\windowtint.dm"
+#include "zippo\code\zippo.dm"
+#include "intercom\code\intercom.dm"
+#include "walls\code\walls.dm"
+#include "windows\code\windows.dm"
+#include "floors\code\tile_types.dm"
+#include "decals\code\decals.dm"
+#include "airlocks\code\airlock.dm"
+#include "airlocks\code\airlock_types.dm"
+#include "airlocks\code\airlock_assembly_types.dm"
+#include "lights\code\lights.dm"
+#include "skin\code\darkmode.dm"
+#include "sheets\code\sheets.dm"
+#include "applicator\code\applicator.dm"
diff --git a/modular_ss220/aesthetics/airalarm/code/airalarm.dm b/modular_ss220/aesthetics/airalarm/code/airalarm.dm
new file mode 100644
index 000000000000..8e7204612158
--- /dev/null
+++ b/modular_ss220/aesthetics/airalarm/code/airalarm.dm
@@ -0,0 +1,6 @@
+/obj/machinery/alarm
+ icon = 'modular_ss220/aesthetics/airalarm/icons/airalarm.dmi'
+ layer = ABOVE_WINDOW_LAYER
+
+/obj/item/mounted/frame/alarm_frame
+ icon = 'modular_ss220/aesthetics/airalarm/icons/airalarm.dmi'
diff --git a/modular_ss220/aesthetics/airalarm/icons/airalarm.dmi b/modular_ss220/aesthetics/airalarm/icons/airalarm.dmi
new file mode 100644
index 000000000000..7f31570b4dae
Binary files /dev/null and b/modular_ss220/aesthetics/airalarm/icons/airalarm.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/code/airlock.dm b/modular_ss220/aesthetics/airlocks/code/airlock.dm
new file mode 100644
index 000000000000..a7b766f6e37d
--- /dev/null
+++ b/modular_ss220/aesthetics/airlocks/code/airlock.dm
@@ -0,0 +1,9 @@
+/obj/machinery/door/airlock
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/public.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/station/overlays.dmi'
+ note_overlay_file = 'modular_ss220/aesthetics/airlocks/icons/station/overlays.dmi'
+
+ doorOpen = 'modular_ss220/aesthetics/airlocks/sound/open.ogg'
+ doorClose = 'modular_ss220/aesthetics/airlocks/sound/close.ogg'
+ boltUp = 'modular_ss220/aesthetics/airlocks/sound/bolts_up.ogg'
+ boltDown = 'modular_ss220/aesthetics/airlocks/sound/bolts_down.ogg'
diff --git a/modular_ss220/aesthetics/airlocks/code/airlock_assembly_types.dm b/modular_ss220/aesthetics/airlocks/code/airlock_assembly_types.dm
new file mode 100644
index 000000000000..935fb680421d
--- /dev/null
+++ b/modular_ss220/aesthetics/airlocks/code/airlock_assembly_types.dm
@@ -0,0 +1,133 @@
+/obj/structure/door_assembly
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/public.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/station/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_public
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station2/glass.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/station2/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_com
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/command.dmi'
+
+/obj/structure/door_assembly/door_assembly_cap
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/cap.dmi'
+
+/obj/structure/door_assembly/door_assembly_hop
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/hop.dmi'
+
+/obj/structure/door_assembly/door_assembly_cmo
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/cmo.dmi'
+
+/obj/structure/door_assembly/door_assembly_rd
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/rd.dmi'
+
+/obj/structure/door_assembly/door_assembly_hos
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/hos.dmi'
+
+/obj/structure/door_assembly/door_assembly_qm
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/qm.dmi'
+
+/obj/structure/door_assembly/door_assembly_ce
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/ce.dmi'
+
+/obj/structure/door_assembly/door_assembly_sec
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/security.dmi'
+
+/obj/structure/door_assembly/door_assembly_eng
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/engineering.dmi'
+
+/obj/structure/door_assembly/door_assembly_min
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/mining.dmi'
+
+/obj/structure/door_assembly/door_assembly_atmo
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/atmos.dmi'
+
+/obj/structure/door_assembly/door_assembly_research
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/research.dmi'
+
+/obj/structure/door_assembly/door_assembly_science
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/science.dmi'
+
+/obj/structure/door_assembly/door_assembly_med
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/medical.dmi'
+
+/obj/structure/door_assembly/door_assembly_viro
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/virology.dmi'
+
+/obj/structure/door_assembly/door_assembly_hydro
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/botany.dmi'
+
+/obj/structure/door_assembly/door_assembly_eva
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/eva.dmi'
+
+/obj/structure/door_assembly/door_assembly_service
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/service.dmi'
+
+/obj/structure/door_assembly/door_assembly_psych
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/psych.dmi'
+
+/obj/structure/door_assembly/door_assembly_bathroom
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/bathroom.dmi'
+
+/obj/structure/door_assembly/door_assembly_lawyer
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/corporate.dmi'
+
+/obj/structure/door_assembly/door_assembly_mai
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/maintenance.dmi'
+
+/obj/structure/door_assembly/door_assembly_extmai
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/maintenanceexternal.dmi'
+
+/obj/structure/door_assembly/door_assembly_ext
+ icon = 'modular_ss220/aesthetics/airlocks/icons/external/external.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/external/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_fre
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/freezer.dmi'
+
+/obj/structure/door_assembly/door_assembly_hatch
+ icon = 'modular_ss220/aesthetics/airlocks/icons/hatch/centcom.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_mhatch
+ icon = 'modular_ss220/aesthetics/airlocks/icons/hatch/maintenance.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_highsecurity
+ icon = 'modular_ss220/aesthetics/airlocks/icons/highsec/highsec.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/highsec/overlays.dmi'
+
+/obj/structure/door_assembly/multi_tile
+ icon = 'modular_ss220/aesthetics/airlocks/icons/glass_large/glass_large.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/glass_large/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_centcom
+ icon = 'modular_ss220/aesthetics/airlocks/icons/centcom/centcom.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/centcom/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_gold
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/gold.dmi'
+
+/obj/structure/door_assembly/door_assembly_silver
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/silver.dmi'
+
+/obj/structure/door_assembly/door_assembly_diamond
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/diamond.dmi'
+
+/obj/structure/door_assembly/door_assembly_uranium
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/uranium.dmi'
+
+/obj/structure/door_assembly/door_assembly_plasma
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/plasma.dmi'
+
+/obj/structure/door_assembly/door_assembly_bananium
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/bananium.dmi'
+
+/obj/structure/door_assembly/door_assembly_tranquillite
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/tranquilite.dmi'
+
+/obj/structure/door_assembly/door_assembly_sandstone
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/sandstone.dmi'
+
+/obj/structure/door_assembly/door_assembly_wood
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/wood.dmi'
diff --git a/modular_ss220/aesthetics/airlocks/code/airlock_types.dm b/modular_ss220/aesthetics/airlocks/code/airlock_types.dm
new file mode 100644
index 000000000000..5d8ccd830452
--- /dev/null
+++ b/modular_ss220/aesthetics/airlocks/code/airlock_types.dm
@@ -0,0 +1,228 @@
+/*
+ Station Airlocks Regular
+*/
+/obj/machinery/door/airlock/command
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/command.dmi'
+
+/obj/machinery/door/airlock/command/cap
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/cap.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_cap
+
+/obj/machinery/door/airlock/command/hop
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/hop.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_hop
+
+/obj/machinery/door/airlock/command/cmo
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/cmo.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_cmo
+
+/obj/machinery/door/airlock/command/rd
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/rd.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_rd
+
+/obj/machinery/door/airlock/command/hos
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/hos.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_hos
+
+/obj/machinery/door/airlock/command/qm
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/qm.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_qm
+
+/obj/machinery/door/airlock/command/ce
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/heads/ce.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_ce
+
+/obj/machinery/door/airlock/security
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/security.dmi'
+
+/obj/machinery/door/airlock/engineering
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/engineering.dmi'
+
+/obj/machinery/door/airlock/medical
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/medical.dmi'
+
+/obj/machinery/door/airlock/maintenance
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/maintenance.dmi'
+
+/obj/machinery/door/airlock/maintenance/external
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/maintenanceexternal.dmi'
+
+/obj/machinery/door/airlock/mining
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/mining.dmi'
+
+/obj/machinery/door/airlock/atmos
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/atmos.dmi'
+
+/obj/machinery/door/airlock/research
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/research.dmi'
+
+/obj/machinery/door/airlock/freezer
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/freezer.dmi'
+
+/obj/machinery/door/airlock/science
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/science.dmi'
+
+/obj/machinery/door/airlock/virology
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/virology.dmi'
+
+/obj/machinery/door/airlock/hydroponics
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/botany.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_hydro
+
+/obj/machinery/door/airlock/eva
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/eva.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_eva
+
+/obj/machinery/door/airlock/service
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/service.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_service
+
+/obj/machinery/door/airlock/psych
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/psych.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_psych
+
+/obj/machinery/door/airlock/bathroom
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/bathroom.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_bathroom
+
+/obj/machinery/door/airlock/lawyer
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/corporate.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_lawyer
+
+/*
+ Station Airlocks Glass
+*/
+/obj/machinery/door/airlock/command/cap/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/hop/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/cmo/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/rd/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/hos/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/qm/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/ce/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/hydroponics/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/eva/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/service/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/psych/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/lawyer/glass
+ opacity = 0
+ glass = TRUE
+
+/*
+ Station Airlocks Mineral
+*/
+/obj/machinery/door/airlock/gold
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/gold.dmi'
+
+/obj/machinery/door/airlock/silver
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/silver.dmi'
+
+/obj/machinery/door/airlock/diamond
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/diamond.dmi'
+
+/obj/machinery/door/airlock/uranium
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/uranium.dmi'
+
+/obj/machinery/door/airlock/plasma
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/plasma.dmi'
+
+/obj/machinery/door/airlock/bananium
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/bananium.dmi'
+
+/obj/machinery/door/airlock/tranquillite
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/tranquilite.dmi'
+
+/obj/machinery/door/airlock/sandstone
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/sandstone.dmi'
+
+/obj/machinery/door/airlock/wood
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station/wood.dmi'
+
+/*
+ Station2 Airlocks
+*/
+/obj/machinery/door/airlock/public
+ icon = 'modular_ss220/aesthetics/airlocks/icons/station2/glass.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/station2/overlays.dmi'
+
+/*
+ External Airlocks
+*/
+/obj/machinery/door/airlock/external
+ icon = 'modular_ss220/aesthetics/airlocks/icons/external/external.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/external/overlays.dmi'
+ note_overlay_file = 'modular_ss220/aesthetics/airlocks/icons/external/overlays.dmi'
+
+/*
+ CentCom Airlocks
+*/
+/obj/machinery/door/airlock/centcom
+ icon = 'modular_ss220/aesthetics/airlocks/icons/centcom/centcom.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/centcom/overlays.dmi'
+
+/*
+ Hatch Airlocks
+*/
+/obj/machinery/door/airlock/hatch
+ icon = 'modular_ss220/aesthetics/airlocks/icons/hatch/centcom.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi'
+ note_overlay_file = 'modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/obj/machinery/door/airlock/maintenance_hatch
+ icon = 'modular_ss220/aesthetics/airlocks/icons/hatch/maintenance.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi'
+ note_overlay_file = 'modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/*
+ High Security Airlocks
+*/
+/obj/machinery/door/airlock/highsecurity
+ icon = 'modular_ss220/aesthetics/airlocks/icons/highsec/highsec.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/highsec/overlays.dmi'
+
+/*
+ Misc Airlocks
+*/
+/obj/machinery/door/airlock/multi_tile
+ icon = 'modular_ss220/aesthetics/airlocks/icons/glass_large/glass_large.dmi'
+ overlays_file = 'modular_ss220/aesthetics/airlocks/icons/glass_large/overlays.dmi'
+ note_overlay_file = 'modular_ss220/aesthetics/airlocks/icons/glass_large/overlays.dmi'
diff --git a/modular_ss220/aesthetics/airlocks/icons/centcom/centcom.dmi b/modular_ss220/aesthetics/airlocks/icons/centcom/centcom.dmi
new file mode 100644
index 000000000000..9d45122dd12c
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/centcom/centcom.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/centcom/overlays.dmi b/modular_ss220/aesthetics/airlocks/icons/centcom/overlays.dmi
new file mode 100644
index 000000000000..eb4f3d850021
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/centcom/overlays.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/external/external.dmi b/modular_ss220/aesthetics/airlocks/icons/external/external.dmi
new file mode 100644
index 000000000000..8a00a16ba3c0
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/external/external.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/external/overlays.dmi b/modular_ss220/aesthetics/airlocks/icons/external/overlays.dmi
new file mode 100644
index 000000000000..20e02d7a6c1c
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/external/overlays.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/glass_large/glass_large.dmi b/modular_ss220/aesthetics/airlocks/icons/glass_large/glass_large.dmi
new file mode 100644
index 000000000000..ddd1871e6fef
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/glass_large/glass_large.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/glass_large/overlays.dmi b/modular_ss220/aesthetics/airlocks/icons/glass_large/overlays.dmi
new file mode 100644
index 000000000000..bf5be7bccd4f
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/glass_large/overlays.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/hatch/centcom.dmi b/modular_ss220/aesthetics/airlocks/icons/hatch/centcom.dmi
new file mode 100644
index 000000000000..64d15207b599
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/hatch/centcom.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/hatch/maintenance.dmi b/modular_ss220/aesthetics/airlocks/icons/hatch/maintenance.dmi
new file mode 100644
index 000000000000..3303591517f6
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/hatch/maintenance.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi b/modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi
new file mode 100644
index 000000000000..b504cce39662
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/hatch/overlays.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/highsec/highsec.dmi b/modular_ss220/aesthetics/airlocks/icons/highsec/highsec.dmi
new file mode 100644
index 000000000000..eb5e312d93c9
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/highsec/highsec.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/highsec/overlays.dmi b/modular_ss220/aesthetics/airlocks/icons/highsec/overlays.dmi
new file mode 100644
index 000000000000..97f2c7fc5c5c
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/highsec/overlays.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/atmos.dmi b/modular_ss220/aesthetics/airlocks/icons/station/atmos.dmi
new file mode 100644
index 000000000000..8bf12b5fa751
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/atmos.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/bananium.dmi b/modular_ss220/aesthetics/airlocks/icons/station/bananium.dmi
new file mode 100644
index 000000000000..cad8ba902d80
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/bananium.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/bathroom.dmi b/modular_ss220/aesthetics/airlocks/icons/station/bathroom.dmi
new file mode 100644
index 000000000000..68fd49dc9ac1
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/bathroom.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/botany.dmi b/modular_ss220/aesthetics/airlocks/icons/station/botany.dmi
new file mode 100644
index 000000000000..2a8ef28d0e1d
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/botany.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/corporate.dmi b/modular_ss220/aesthetics/airlocks/icons/station/corporate.dmi
new file mode 100644
index 000000000000..3209e7f59ba4
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/corporate.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/diamond.dmi b/modular_ss220/aesthetics/airlocks/icons/station/diamond.dmi
new file mode 100644
index 000000000000..e7ec1cffb65d
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/diamond.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/engineering.dmi b/modular_ss220/aesthetics/airlocks/icons/station/engineering.dmi
new file mode 100644
index 000000000000..f9e1f5915f79
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/engineering.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/eva.dmi b/modular_ss220/aesthetics/airlocks/icons/station/eva.dmi
new file mode 100644
index 000000000000..6c495b54322f
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/eva.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/freezer.dmi b/modular_ss220/aesthetics/airlocks/icons/station/freezer.dmi
new file mode 100644
index 000000000000..ad3a1c72a84c
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/freezer.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/gold.dmi b/modular_ss220/aesthetics/airlocks/icons/station/gold.dmi
new file mode 100644
index 000000000000..a2977d95cf9d
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/gold.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/cap.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/cap.dmi
new file mode 100644
index 000000000000..7417588b5390
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/cap.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/ce.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/ce.dmi
new file mode 100644
index 000000000000..2223e252cb00
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/ce.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/cmo.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/cmo.dmi
new file mode 100644
index 000000000000..a44e60ac2c62
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/cmo.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/command.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/command.dmi
new file mode 100644
index 000000000000..affe4c1fd2f9
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/command.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/hop.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/hop.dmi
new file mode 100644
index 000000000000..c17a3c2cebe7
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/hop.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/hos.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/hos.dmi
new file mode 100644
index 000000000000..9ddc7cb63ba1
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/hos.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/qm.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/qm.dmi
new file mode 100644
index 000000000000..0ebfb5fd5e47
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/qm.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/heads/rd.dmi b/modular_ss220/aesthetics/airlocks/icons/station/heads/rd.dmi
new file mode 100644
index 000000000000..cac461fb63c5
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/heads/rd.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/maintenance.dmi b/modular_ss220/aesthetics/airlocks/icons/station/maintenance.dmi
new file mode 100644
index 000000000000..cc96c755a8e2
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/maintenance.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/maintenanceexternal.dmi b/modular_ss220/aesthetics/airlocks/icons/station/maintenanceexternal.dmi
new file mode 100644
index 000000000000..c7d8e6154e8a
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/maintenanceexternal.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/medical.dmi b/modular_ss220/aesthetics/airlocks/icons/station/medical.dmi
new file mode 100644
index 000000000000..e33bff783ebc
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/medical.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/mining.dmi b/modular_ss220/aesthetics/airlocks/icons/station/mining.dmi
new file mode 100644
index 000000000000..86ec771471e3
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/mining.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/overlays.dmi b/modular_ss220/aesthetics/airlocks/icons/station/overlays.dmi
new file mode 100644
index 000000000000..8f8801051180
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/overlays.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/plasma.dmi b/modular_ss220/aesthetics/airlocks/icons/station/plasma.dmi
new file mode 100644
index 000000000000..a90fa8f0bce5
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/plasma.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/psych.dmi b/modular_ss220/aesthetics/airlocks/icons/station/psych.dmi
new file mode 100644
index 000000000000..d8bbc788f9eb
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/psych.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/public.dmi b/modular_ss220/aesthetics/airlocks/icons/station/public.dmi
new file mode 100644
index 000000000000..1e3258bd1708
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/public.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/research.dmi b/modular_ss220/aesthetics/airlocks/icons/station/research.dmi
new file mode 100644
index 000000000000..6c3b23107858
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/research.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/sandstone.dmi b/modular_ss220/aesthetics/airlocks/icons/station/sandstone.dmi
new file mode 100644
index 000000000000..ac4dc7179059
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/sandstone.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/science.dmi b/modular_ss220/aesthetics/airlocks/icons/station/science.dmi
new file mode 100644
index 000000000000..95396c1d403e
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/science.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/security.dmi b/modular_ss220/aesthetics/airlocks/icons/station/security.dmi
new file mode 100644
index 000000000000..f38b9e05405d
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/security.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/service.dmi b/modular_ss220/aesthetics/airlocks/icons/station/service.dmi
new file mode 100644
index 000000000000..e155823d8d3d
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/service.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/silver.dmi b/modular_ss220/aesthetics/airlocks/icons/station/silver.dmi
new file mode 100644
index 000000000000..47105c88bf14
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/silver.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/tranquilite.dmi b/modular_ss220/aesthetics/airlocks/icons/station/tranquilite.dmi
new file mode 100644
index 000000000000..851568e1dab2
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/tranquilite.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/uranium.dmi b/modular_ss220/aesthetics/airlocks/icons/station/uranium.dmi
new file mode 100644
index 000000000000..d23811bacd40
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/uranium.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/virology.dmi b/modular_ss220/aesthetics/airlocks/icons/station/virology.dmi
new file mode 100644
index 000000000000..600dd9ee225b
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/virology.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station/wood.dmi b/modular_ss220/aesthetics/airlocks/icons/station/wood.dmi
new file mode 100644
index 000000000000..b5973a7219f2
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station/wood.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station2/glass.dmi b/modular_ss220/aesthetics/airlocks/icons/station2/glass.dmi
new file mode 100644
index 000000000000..6c8259ab0de3
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station2/glass.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/icons/station2/overlays.dmi b/modular_ss220/aesthetics/airlocks/icons/station2/overlays.dmi
new file mode 100644
index 000000000000..639306e58cdf
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/icons/station2/overlays.dmi differ
diff --git a/modular_ss220/aesthetics/airlocks/sound/bolts_down.ogg b/modular_ss220/aesthetics/airlocks/sound/bolts_down.ogg
new file mode 100644
index 000000000000..19d62b8acb2a
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/sound/bolts_down.ogg differ
diff --git a/modular_ss220/aesthetics/airlocks/sound/bolts_up.ogg b/modular_ss220/aesthetics/airlocks/sound/bolts_up.ogg
new file mode 100644
index 000000000000..0aac1a44fcc5
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/sound/bolts_up.ogg differ
diff --git a/modular_ss220/aesthetics/airlocks/sound/close.ogg b/modular_ss220/aesthetics/airlocks/sound/close.ogg
new file mode 100644
index 000000000000..db94b73fb4b6
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/sound/close.ogg differ
diff --git a/modular_ss220/aesthetics/airlocks/sound/close_force.ogg b/modular_ss220/aesthetics/airlocks/sound/close_force.ogg
new file mode 100644
index 000000000000..28b190d8e095
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/sound/close_force.ogg differ
diff --git a/modular_ss220/aesthetics/airlocks/sound/open.ogg b/modular_ss220/aesthetics/airlocks/sound/open.ogg
new file mode 100644
index 000000000000..0b8a0d5f94f1
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/sound/open.ogg differ
diff --git a/modular_ss220/aesthetics/airlocks/sound/open_force.ogg b/modular_ss220/aesthetics/airlocks/sound/open_force.ogg
new file mode 100644
index 000000000000..4caefc0b9e4d
Binary files /dev/null and b/modular_ss220/aesthetics/airlocks/sound/open_force.ogg differ
diff --git a/modular_ss220/aesthetics/apc/code/apc.dm b/modular_ss220/aesthetics/apc/code/apc.dm
new file mode 100644
index 000000000000..251ea933dccb
--- /dev/null
+++ b/modular_ss220/aesthetics/apc/code/apc.dm
@@ -0,0 +1,3 @@
+/obj/machinery/power/apc
+ icon = 'modular_ss220/aesthetics/apc/icons/apc.dmi'
+ layer = ABOVE_WINDOW_LAYER
diff --git a/modular_ss220/aesthetics/apc/icons/apc.dmi b/modular_ss220/aesthetics/apc/icons/apc.dmi
new file mode 100644
index 000000000000..ac8fc2b845dd
Binary files /dev/null and b/modular_ss220/aesthetics/apc/icons/apc.dmi differ
diff --git a/modular_ss220/aesthetics/applicator/code/applicator.dm b/modular_ss220/aesthetics/applicator/code/applicator.dm
new file mode 100644
index 000000000000..16c33c328a71
--- /dev/null
+++ b/modular_ss220/aesthetics/applicator/code/applicator.dm
@@ -0,0 +1,2 @@
+/obj/item/reagent_containers/applicator
+ icon = 'modular_ss220/aesthetics/applicator/icons/applicator.dmi'
diff --git a/modular_ss220/aesthetics/applicator/icons/applicator.dmi b/modular_ss220/aesthetics/applicator/icons/applicator.dmi
new file mode 100644
index 000000000000..a39e0b9ea9fb
Binary files /dev/null and b/modular_ss220/aesthetics/applicator/icons/applicator.dmi differ
diff --git a/modular_ss220/aesthetics/atm/code/atm.dm b/modular_ss220/aesthetics/atm/code/atm.dm
new file mode 100644
index 000000000000..f79e8bf177dc
--- /dev/null
+++ b/modular_ss220/aesthetics/atm/code/atm.dm
@@ -0,0 +1,2 @@
+/obj/machinery/economy/atm
+ icon = 'modular_ss220/aesthetics/atm/icons/atm.dmi'
diff --git a/modular_ss220/aesthetics/atm/icons/atm.dmi b/modular_ss220/aesthetics/atm/icons/atm.dmi
new file mode 100644
index 000000000000..23e9a29ce8a6
Binary files /dev/null and b/modular_ss220/aesthetics/atm/icons/atm.dmi differ
diff --git a/modular_ss220/aesthetics/atmospherics/code/atmospherics.dm b/modular_ss220/aesthetics/atmospherics/code/atmospherics.dm
new file mode 100644
index 000000000000..02fa78963b9a
--- /dev/null
+++ b/modular_ss220/aesthetics/atmospherics/code/atmospherics.dm
@@ -0,0 +1,5 @@
+/obj/machinery/atmospherics/unary/vent_pump
+ icon = 'modular_ss220/aesthetics/atmospherics/icons/vent_pump.dmi'
+
+/obj/machinery/atmospherics/unary/vent_scrubber
+ icon = 'modular_ss220/aesthetics/atmospherics/icons/vent_scrubber.dmi'
diff --git a/modular_ss220/aesthetics/atmospherics/icons/vent_pump.dmi b/modular_ss220/aesthetics/atmospherics/icons/vent_pump.dmi
new file mode 100644
index 000000000000..82bc44ecd9a0
Binary files /dev/null and b/modular_ss220/aesthetics/atmospherics/icons/vent_pump.dmi differ
diff --git a/modular_ss220/aesthetics/atmospherics/icons/vent_scrubber.dmi b/modular_ss220/aesthetics/atmospherics/icons/vent_scrubber.dmi
new file mode 100644
index 000000000000..4639f470fa51
Binary files /dev/null and b/modular_ss220/aesthetics/atmospherics/icons/vent_scrubber.dmi differ
diff --git a/modular_ss220/aesthetics/better_ids/code/better_ids.dm b/modular_ss220/aesthetics/better_ids/code/better_ids.dm
new file mode 100644
index 000000000000..71b3e578e7a8
--- /dev/null
+++ b/modular_ss220/aesthetics/better_ids/code/better_ids.dm
@@ -0,0 +1,5 @@
+/obj/item/card
+ icon = 'modular_ss220/aesthetics/better_ids/icons/better_ids.dmi'
+
+/obj/item/nanomob_card
+ icon = 'modular_ss220/aesthetics/better_ids/icons/better_ids.dmi'
diff --git a/modular_ss220/aesthetics/better_ids/icons/better_ids.dmi b/modular_ss220/aesthetics/better_ids/icons/better_ids.dmi
new file mode 100644
index 000000000000..f66a128decab
Binary files /dev/null and b/modular_ss220/aesthetics/better_ids/icons/better_ids.dmi differ
diff --git a/modular_ss220/aesthetics/blastdoor/code/blastdoor.dm b/modular_ss220/aesthetics/blastdoor/code/blastdoor.dm
new file mode 100644
index 000000000000..9ddd064aae49
--- /dev/null
+++ b/modular_ss220/aesthetics/blastdoor/code/blastdoor.dm
@@ -0,0 +1,11 @@
+/obj/machinery/door/poddoor
+ icon = 'modular_ss220/aesthetics/blastdoor/icons/blastdoor.dmi'
+
+/obj/machinery/door/poddoor/do_animate(animation)
+ switch(animation)
+ if("opening")
+ flick("opening", src)
+ playsound(src, 'modular_ss220/aesthetics/blastdoor/sound/blastdoor.ogg', 30, 1)
+ if("closing")
+ flick("closing", src)
+ playsound(src, 'modular_ss220/aesthetics/blastdoor/sound/blastdoor.ogg', 30, 1)
diff --git a/modular_ss220/aesthetics/blastdoor/icons/blastdoor.dmi b/modular_ss220/aesthetics/blastdoor/icons/blastdoor.dmi
new file mode 100644
index 000000000000..672c161c10ed
Binary files /dev/null and b/modular_ss220/aesthetics/blastdoor/icons/blastdoor.dmi differ
diff --git a/modular_ss220/aesthetics/blastdoor/sound/blastdoor.ogg b/modular_ss220/aesthetics/blastdoor/sound/blastdoor.ogg
new file mode 100644
index 000000000000..93e53513985a
Binary files /dev/null and b/modular_ss220/aesthetics/blastdoor/sound/blastdoor.ogg differ
diff --git a/modular_ss220/aesthetics/boxes/code/boxes.dm b/modular_ss220/aesthetics/boxes/code/boxes.dm
new file mode 100644
index 000000000000..2168ebe255ba
--- /dev/null
+++ b/modular_ss220/aesthetics/boxes/code/boxes.dm
@@ -0,0 +1,287 @@
+/obj/item/storage/box/survival
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "civ_box"
+
+/obj/item/storage/box/survival_vox
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "vox_box"
+
+/obj/item/storage/box/survival_plasmaman
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "plasma_box"
+
+/obj/item/storage/box/engineer
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "eng_box"
+
+/obj/item/storage/box/survival_mining
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "min_box"
+
+/obj/item/storage/box/survival_syndi
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "syndie_box"
+
+/obj/item/storage/box/gloves
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "latex_box"
+
+/obj/item/storage/box/masks
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "sterile_box"
+
+/obj/item/storage/box/syringes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "syringe_box"
+
+/obj/item/storage/box/beakers
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "beaker_box"
+
+/obj/item/storage/box/beakers/bluespace
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "beaker_bluespace_box"
+
+/obj/item/storage/box/iv_bags
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "iv_box"
+
+/obj/item/storage/box/injectors
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "syringe_box"
+
+/obj/item/storage/box/flashbangs
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "flashbang_box"
+
+/obj/item/storage/box/flashes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "flash_box"
+
+/obj/item/storage/box/teargas
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "teargas_box"
+
+/obj/item/storage/box/emps
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "emp_box"
+
+/obj/item/storage/box/trackimp
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/minertracker
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/chemimp
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/exileimp
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/deathimp
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/tapes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "tape_box"
+
+/obj/item/storage/box/rxglasses
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "glasses_box"
+
+/obj/item/storage/box/drinkingglasses
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/condimentbottles
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/cups
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "cup_box"
+
+/obj/item/storage/box/donkpockets
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "donk_box"
+
+/obj/item/storage/box/syndidonkpockets
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_syndie_box" // TODO: Need new icon
+
+/obj/item/storage/box/monkeycubes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "monkey_box"
+
+/obj/item/storage/box/monkeycubes/neaeracubes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "neaera_box"
+
+/obj/item/storage/box/monkeycubes/stokcubes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "stok_box"
+
+/obj/item/storage/box/monkeycubes/farwacubes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "farwa_box"
+
+/obj/item/storage/box/monkeycubes/wolpincubes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "wolpin_box"
+
+/obj/item/storage/box/permits
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_box"
+
+/obj/item/storage/box/ids
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_box"
+
+/obj/item/storage/box/prisoner
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_box"
+
+/obj/item/storage/box/seccarts
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "pda_box"
+
+/obj/item/storage/box/holobadge
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "badge_box"
+
+/obj/item/storage/box/evidence
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "evidence_box"
+
+/obj/item/storage/box/handcuffs
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "handcuff_box"
+
+/obj/item/storage/box/zipties
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "zipties_box"
+
+/obj/item/storage/box/alienhandcuffs
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "alien_handcuff_box"
+
+/obj/item/storage/box/fakesyndiesuit
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "doom_box"
+
+/obj/item/storage/box/enforcer_rubber
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/enforcer_lethal
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/bartender_rare_ingredients_kit
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/chef_rare_ingredients_kit
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/mousetraps
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "mousetraps_box"
+
+/obj/item/storage/box/pillbottles
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "pill_box"
+
+/obj/item/storage/box/patch_packs
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "patch_box"
+
+/obj/item/storage/box/bodybags
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "bodybag_box"
+
+/obj/item/storage/box/autoinjectors
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "injector_box"
+
+/obj/item/storage/box/lights
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "light_box"
+
+/obj/item/storage/box/lights/tubes
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "lighttube_box"
+
+/obj/item/storage/box/lights/mixed
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "lightmixed_box"
+
+/obj/item/storage/box/barber
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/lip_stick
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/centcomofficer
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/responseteam
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/deathsquad
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "doom_box"
+
+/obj/item/storage/box/soviet
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "soviet_box"
+
+/obj/item/storage/box/clown
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "clown_box"
+
+/obj/item/storage/box/emptysandbags
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/rndboards
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/stockparts
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/hug
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "hug_box"
+
+/obj/item/storage/box/wizard
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "wizard_box"
+
+/obj/item/storage/box/breaching
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "flashbang_box"
+
+/obj/item/storage/box/mindshield
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/dish_drive
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/disks_plantgene
+ icon = 'modular_ss220/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "disk_box"
diff --git a/modular_ss220/aesthetics/boxes/icons/boxes.dmi b/modular_ss220/aesthetics/boxes/icons/boxes.dmi
new file mode 100644
index 000000000000..97467e6f3b1d
Binary files /dev/null and b/modular_ss220/aesthetics/boxes/icons/boxes.dmi differ
diff --git a/modular_ss220/aesthetics/cameras/code/cameras.dm b/modular_ss220/aesthetics/cameras/code/cameras.dm
new file mode 100644
index 000000000000..061af8042290
--- /dev/null
+++ b/modular_ss220/aesthetics/cameras/code/cameras.dm
@@ -0,0 +1,2 @@
+/obj/machinery/camera
+ icon = 'modular_ss220/aesthetics/cameras/icons/cameras.dmi'
diff --git a/modular_ss220/aesthetics/cameras/icons/cameras.dmi b/modular_ss220/aesthetics/cameras/icons/cameras.dmi
new file mode 100644
index 000000000000..82ab045a7735
Binary files /dev/null and b/modular_ss220/aesthetics/cameras/icons/cameras.dmi differ
diff --git a/modular_ss220/aesthetics/chairs/code/chairs.dm b/modular_ss220/aesthetics/chairs/code/chairs.dm
new file mode 100644
index 000000000000..92090157dd3a
--- /dev/null
+++ b/modular_ss220/aesthetics/chairs/code/chairs.dm
@@ -0,0 +1,35 @@
+/obj/structure/chair/comfy
+ icon = 'modular_ss220/aesthetics/chairs/icons/chairs.dmi'
+
+/obj/structure/chair/comfy/GetArmrest()
+ return mutable_appearance('modular_ss220/aesthetics/chairs/icons/chairs.dmi', "[icon_state]_armrest")
+
+/obj/structure/chair/comfy/corp
+ icon = 'icons/obj/chairs.dmi'
+
+/obj/structure/chair/comfy/shuttle
+ icon = 'icons/obj/chairs.dmi'
+
+/obj/structure/chair/office/dark
+ icon = 'modular_ss220/aesthetics/chairs/icons/chairs.dmi'
+
+/obj/structure/chair/office/light
+ icon = 'modular_ss220/aesthetics/chairs/icons/chairs.dmi'
+
+/obj/structure/chair/e_chair
+ icon = 'modular_ss220/aesthetics/chairs/icons/chairs.dmi'
+
+//TODO: Support or chairs
+
+/obj/item/chair/stool/bar/dark
+ icon = 'modular_ss220/aesthetics/chairs/icons/chairs.dmi'
+ icon_state = "bar_toppled_dark"
+ item_state = "stool_bar_dark"
+ origin_type = /obj/structure/chair/stool/bar/dark
+ lefthand_file = 'modular_ss220/aesthetics/chairs/icons/chairs_lefthand.dmi'
+ righthand_file = 'modular_ss220/aesthetics/chairs/icons/chairs_righthand.dmi'
+
+/obj/structure/chair/stool/bar/dark
+ icon = 'modular_ss220/aesthetics/chairs/icons/chairs.dmi'
+ icon_state = "bar_dark"
+ item_chair = /obj/item/chair/stool/bar/dark
diff --git a/modular_ss220/aesthetics/chairs/icons/chairs.dmi b/modular_ss220/aesthetics/chairs/icons/chairs.dmi
new file mode 100644
index 000000000000..6aba9bf8697a
Binary files /dev/null and b/modular_ss220/aesthetics/chairs/icons/chairs.dmi differ
diff --git a/modular_ss220/aesthetics/chairs/icons/chairs_lefthand.dmi b/modular_ss220/aesthetics/chairs/icons/chairs_lefthand.dmi
new file mode 100644
index 000000000000..f3238b0616ba
Binary files /dev/null and b/modular_ss220/aesthetics/chairs/icons/chairs_lefthand.dmi differ
diff --git a/modular_ss220/aesthetics/chairs/icons/chairs_righthand.dmi b/modular_ss220/aesthetics/chairs/icons/chairs_righthand.dmi
new file mode 100644
index 000000000000..ac882e8ebf82
Binary files /dev/null and b/modular_ss220/aesthetics/chairs/icons/chairs_righthand.dmi differ
diff --git a/modular_ss220/aesthetics/decals/code/decals.dm b/modular_ss220/aesthetics/decals/code/decals.dm
new file mode 100644
index 000000000000..fc765f841f82
--- /dev/null
+++ b/modular_ss220/aesthetics/decals/code/decals.dm
@@ -0,0 +1,112 @@
+/* SIDING */
+/obj/effect/turf_decal/siding/wood
+ icon = 'modular_ss220/aesthetics/decals/icons/siding.dmi'
+ icon_state = "siding_wood_line"
+ color = "#55391A"
+
+/obj/effect/turf_decal/siding/wood/corner
+ icon_state = "siding_wood_corner"
+
+/obj/effect/turf_decal/siding/wood/end
+ icon_state = "siding_wood_end"
+
+/obj/effect/turf_decal/siding
+ icon = 'modular_ss220/aesthetics/decals/icons/siding.dmi'
+ icon_state = "siding_line"
+
+/obj/effect/turf_decal/siding/white
+ color = "#BCBCBC"
+
+/obj/effect/turf_decal/siding/white/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/white/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/red
+ color = "#DE3A3A"
+
+/obj/effect/turf_decal/siding/red/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/red/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/green
+ color = "#9FED58"
+
+/obj/effect/turf_decal/siding/green/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/green/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/blue
+ color = "#52B4E9"
+
+/obj/effect/turf_decal/siding/blue/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/blue/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/yellow
+ color = "#EFB341"
+
+/obj/effect/turf_decal/siding/yellow/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/yellow/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/purple
+ color = "#D381C9"
+
+/obj/effect/turf_decal/siding/purple/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/purple/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/brown
+ color = "#A46106"
+
+/obj/effect/turf_decal/siding/brown/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/brown/end
+ icon_state = "siding_end"
+
+/* NT LOGO */
+/obj/effect/decal/nanotrasen_logo
+ name = "NanoTrasen Logo"
+ icon = 'modular_ss220/aesthetics/decals/icons/NT_logo.dmi'
+
+/obj/effect/decal/nanotrasen_logo/n1
+ icon_state = "logo1"
+
+/obj/effect/decal/nanotrasen_logo/n2
+ icon_state = "logo2"
+
+/obj/effect/decal/nanotrasen_logo/n3
+ icon_state = "logo3"
+
+/obj/effect/decal/nanotrasen_logo/n4
+ icon_state = "logo4"
+
+/obj/effect/decal/nanotrasen_logo/n5
+ icon_state = "logo5"
+
+/obj/effect/decal/nanotrasen_logo/n6
+ icon_state = "logo6"
+
+/obj/effect/decal/cleanable/dust
+ name = "dust"
+ desc = "It's a little dusty. Someone should clean that up."
+ gender = PLURAL
+ density = FALSE
+ layer = TURF_LAYER
+ icon = 'modular_ss220/aesthetics/decals/icons/dirt.dmi'
+ icon_state = "dust"
+ base_icon_state = "dust"
+ mouse_opacity = FALSE
diff --git a/modular_ss220/aesthetics/decals/icons/NT_logo.dmi b/modular_ss220/aesthetics/decals/icons/NT_logo.dmi
new file mode 100644
index 000000000000..db400f9f79bb
Binary files /dev/null and b/modular_ss220/aesthetics/decals/icons/NT_logo.dmi differ
diff --git a/modular_ss220/aesthetics/decals/icons/dirt.dmi b/modular_ss220/aesthetics/decals/icons/dirt.dmi
new file mode 100644
index 000000000000..5686e3282b3f
Binary files /dev/null and b/modular_ss220/aesthetics/decals/icons/dirt.dmi differ
diff --git a/modular_ss220/aesthetics/decals/icons/siding.dmi b/modular_ss220/aesthetics/decals/icons/siding.dmi
new file mode 100644
index 000000000000..f3ac9b02751a
Binary files /dev/null and b/modular_ss220/aesthetics/decals/icons/siding.dmi differ
diff --git a/modular_ss220/aesthetics/defib/code/defib.dm b/modular_ss220/aesthetics/defib/code/defib.dm
new file mode 100644
index 000000000000..4339edb41d8d
--- /dev/null
+++ b/modular_ss220/aesthetics/defib/code/defib.dm
@@ -0,0 +1,6 @@
+/obj/item/defibrillator
+ icon = 'modular_ss220/aesthetics/defib/icons/defib.dmi'
+
+/obj/item/defibrillator/compact
+ icon = 'icons/obj/defib.dmi'
+ //TODO: Compact defibs
diff --git a/modular_ss220/aesthetics/defib/icons/defib.dmi b/modular_ss220/aesthetics/defib/icons/defib.dmi
new file mode 100644
index 000000000000..f4fbe3712d4e
Binary files /dev/null and b/modular_ss220/aesthetics/defib/icons/defib.dmi differ
diff --git a/modular_ss220/aesthetics/dirwindows/code/dirwindows.dm b/modular_ss220/aesthetics/dirwindows/code/dirwindows.dm
new file mode 100644
index 000000000000..b6fbfd812cb2
--- /dev/null
+++ b/modular_ss220/aesthetics/dirwindows/code/dirwindows.dm
@@ -0,0 +1,17 @@
+/obj/structure/holowindow
+ icon = 'modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/basic
+ icon = 'modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/reinforced
+ icon = 'modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/reinforced/tinted
+ icon = 'modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/plasmabasic
+ icon = 'modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/plasmareinforced
+ icon = 'modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi'
diff --git a/modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi b/modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi
new file mode 100644
index 000000000000..c9aa5b12d911
Binary files /dev/null and b/modular_ss220/aesthetics/dirwindows/icons/dirwindows.dmi differ
diff --git a/modular_ss220/aesthetics/door_control/code/door_control.dm b/modular_ss220/aesthetics/door_control/code/door_control.dm
new file mode 100644
index 000000000000..4e7c60ff77e5
--- /dev/null
+++ b/modular_ss220/aesthetics/door_control/code/door_control.dm
@@ -0,0 +1,2 @@
+/obj/machinery/door_control
+ layer = ABOVE_WINDOW_LAYER
diff --git a/modular_ss220/aesthetics/extinguisher/code/extinguisher.dm b/modular_ss220/aesthetics/extinguisher/code/extinguisher.dm
new file mode 100644
index 000000000000..ce5a190294d3
--- /dev/null
+++ b/modular_ss220/aesthetics/extinguisher/code/extinguisher.dm
@@ -0,0 +1,18 @@
+/obj/structure/extinguisher_cabinet
+ icon = 'modular_ss220/aesthetics/extinguisher/icons/extinguisher.dmi'
+
+/obj/structure/extinguisher_cabinet/update_icon_state()
+ if(!opened)
+ if(has_extinguisher)
+ icon_state = "extinguisher_closed"
+ else
+ icon_state = "extinguisher_empty_closed"
+ return
+ if(has_extinguisher)
+ if(istype(has_extinguisher, /obj/item/extinguisher/mini))
+ icon_state = "extinguisher_mini"
+ else
+ icon_state = "extinguisher_full"
+ else
+ icon_state = "extinguisher_empty"
+ //TODO: Frame
diff --git a/modular_ss220/aesthetics/extinguisher/icons/extinguisher.dmi b/modular_ss220/aesthetics/extinguisher/icons/extinguisher.dmi
new file mode 100644
index 000000000000..9bae622331ae
Binary files /dev/null and b/modular_ss220/aesthetics/extinguisher/icons/extinguisher.dmi differ
diff --git a/modular_ss220/aesthetics/firealarm/code/firealarm.dm b/modular_ss220/aesthetics/firealarm/code/firealarm.dm
new file mode 100644
index 000000000000..9f8dfe3632d2
--- /dev/null
+++ b/modular_ss220/aesthetics/firealarm/code/firealarm.dm
@@ -0,0 +1,7 @@
+/obj/machinery/firealarm
+ icon = 'modular_ss220/aesthetics/firealarm/icons/firealarm.dmi'
+ layer = ABOVE_WINDOW_LAYER
+ //TODO: Detect
+
+/obj/item/mounted/frame/firealarm
+ icon = 'modular_ss220/aesthetics/firealarm/icons/firealarm.dmi'
diff --git a/modular_ss220/aesthetics/firealarm/icons/firealarm.dmi b/modular_ss220/aesthetics/firealarm/icons/firealarm.dmi
new file mode 100644
index 000000000000..17f9897e3fbe
Binary files /dev/null and b/modular_ss220/aesthetics/firealarm/icons/firealarm.dmi differ
diff --git a/modular_ss220/aesthetics/floors/code/floors.dm b/modular_ss220/aesthetics/floors/code/floors.dm
new file mode 100644
index 000000000000..03346f476a7e
--- /dev/null
+++ b/modular_ss220/aesthetics/floors/code/floors.dm
@@ -0,0 +1,58 @@
+/turf/simulated/floor
+ icon = 'modular_ss220/aesthetics/floors/icons/floors.dmi'
+
+// WOODEN FLOORS
+/turf/simulated/floor/wood/oak
+ icon_state = "wood-oak"
+ floor_tile = /obj/item/stack/tile/wood/oak
+ broken_states = list("wood-oak-broken", "wood-oak-broken2", "wood-oak-broken3", "wood-oak-broken4", "wood-oak-broken5", "wood-oak-broken6", "wood-oak-broken7")
+
+/turf/simulated/floor/wood/birch
+ icon_state = "wood-birch"
+ floor_tile = /obj/item/stack/tile/wood/birch
+ broken_states = list("wood-birch-broken", "wood-birch-broken2", "wood-birch-broken3", "wood-birch-broken4", "wood-birch-broken5", "wood-birch-broken6", "wood-birch-broken7")
+
+/turf/simulated/floor/wood/cherry
+ icon_state = "wood-cherry"
+ floor_tile = /obj/item/stack/tile/wood/cherry
+ broken_states = list("wood-cherry-broken", "wood-cherry-broken2", "wood-cherry-broken3", "wood-cherry-broken4", "wood-cherry-broken5", "wood-cherry-broken6", "wood-cherry-broken7")
+
+/turf/simulated/floor/wood/fancy/oak
+ icon_state = "fancy-wood-oak"
+ floor_tile = /obj/item/stack/tile/wood/fancy/oak
+ broken_states = list("fancy-wood-oak-broken", "fancy-wood-oak-broken2", "fancy-wood-oak-broken3", "fancy-wood-oak-broken4", "fancy-wood-oak-broken5", "fancy-wood-oak-broken6", "fancy-wood-oak-broken7")
+
+/turf/simulated/floor/wood/fancy/birch
+ icon_state = "fancy-wood-birch"
+ floor_tile = /obj/item/stack/tile/wood/fancy/birch
+ broken_states = list("fancy-wood-birch-broken", "fancy-wood-birch-broken2", "fancy-wood-birch-broken3", "fancy-wood-birch-broken4", "fancy-wood-birch-broken5", "fancy-wood-birch-broken6", "fancy-wood-birch-broken7")
+
+/turf/simulated/floor/wood/fancy/cherry
+ icon_state = "fancy-wood-cherry"
+ floor_tile = /obj/item/stack/tile/wood/fancy/cherry
+ broken_states = list("fancy-wood-cherry-broken", "fancy-wood-cherry-broken2", "fancy-wood-cherry-broken3", "fancy-wood-cherry-broken4", "fancy-wood-cherry-broken5", "fancy-wood-cherry-broken6", "fancy-wood-cherry-broken7")
+
+/turf/simulated/floor/wood/fancy/light
+ icon_state = "light-fancy-wood"
+ floor_tile = /obj/item/stack/tile/wood/fancy/light
+ broken_states = list("light-fancy-wood-broken", "light-fancy-wood-broken2", "light-fancy-wood-broken3", "light-fancy-wood-broken4", "light-fancy-wood-broken5", "light-fancy-wood-broken6", "light-fancy-wood-broken7")
+
+// LIGHT FLOORS
+/turf/simulated/floor/light
+ icon = 'icons/turf/floors.dmi'
+
+/turf/simulated/floor/light/red
+ color = "#f23030"
+ light_color = "#f23030"
+
+/turf/simulated/floor/light/green
+ color = "#30f230"
+ light_color = "#30f230"
+
+/turf/simulated/floor/light/blue
+ color = "#3030f2"
+ light_color = "#3030f2"
+
+/turf/simulated/floor/light/purple
+ color = "#d493ff"
+ light_color = "#d493ff"
diff --git a/modular_ss220/aesthetics/floors/code/tile_types.dm b/modular_ss220/aesthetics/floors/code/tile_types.dm
new file mode 100644
index 000000000000..d4bf3fa165ae
--- /dev/null
+++ b/modular_ss220/aesthetics/floors/code/tile_types.dm
@@ -0,0 +1,42 @@
+// WOOD
+/obj/item/stack/tile/wood/oak
+ name = "oak wood floor tiles"
+ singular_name = "oak wood floor tile"
+ icon_state = "tile-wood-oak"
+ turf_type = /turf/simulated/floor/wood/oak
+
+/obj/item/stack/tile/wood/birch
+ name = "birch wood floor tiles"
+ singular_name = "birch wood floor tile"
+ icon_state = "tile-wood-birch"
+ turf_type = /turf/simulated/floor/wood/birch
+
+/obj/item/stack/tile/wood/cherry
+ name = "cherry wood floor tiles"
+ singular_name = "cherry wood floor tile"
+ icon_state = "tile-wood-cherry"
+ turf_type = /turf/simulated/floor/wood/cherry
+
+/obj/item/stack/tile/wood/fancy/oak
+ name = "fancy oak wood floor tiles"
+ singular_name = "fancy oak wood floor tile"
+ icon_state = "tile-wood-fancy-oak"
+ turf_type = /turf/simulated/floor/wood/fancy/oak
+
+/obj/item/stack/tile/wood/fancy/birch
+ name = "fancy birch wood floor tiles"
+ singular_name = "fancy birch wood floor tile"
+ icon_state = "tile-wood-fancy-birch"
+ turf_type = /turf/simulated/floor/wood/fancy/birch
+
+/obj/item/stack/tile/wood/fancy/cherry
+ name = "fancy cherry wood floor tiles"
+ singular_name = "fancy cherry wood floor tile"
+ icon_state = "tile-wood-fancy-cherry"
+ turf_type = /turf/simulated/floor/wood/fancy/cherry
+
+/obj/item/stack/tile/wood/fancy/light
+ name = "fancy light oak wood floor tiles"
+ singular_name = "fancy light oak wood floor tile"
+ icon_state = "tile-wood-fancy-light"
+ turf_type = /turf/simulated/floor/wood/fancy/light
diff --git a/modular_ss220/aesthetics/floors/icons/floors.dmi b/modular_ss220/aesthetics/floors/icons/floors.dmi
new file mode 100644
index 000000000000..064dddd8b008
Binary files /dev/null and b/modular_ss220/aesthetics/floors/icons/floors.dmi differ
diff --git a/modular_ss220/aesthetics/hydroponics/code/hydroponics.dm b/modular_ss220/aesthetics/hydroponics/code/hydroponics.dm
new file mode 100644
index 000000000000..332ee2b38e0f
--- /dev/null
+++ b/modular_ss220/aesthetics/hydroponics/code/hydroponics.dm
@@ -0,0 +1,21 @@
+/obj/machinery/plantgenes
+ icon = 'modular_ss220/aesthetics/hydroponics/icons/hydroponics.dmi'
+
+/obj/machinery/plantgenes/update_overlays()
+ . = ..()
+ if(disk)
+ . += "dnamod-disk"
+
+/obj/machinery/plantgenes/add_disk(obj/item/disk/plantgene/new_disk, mob/user)
+ . = ..()
+ update_icon(UPDATE_OVERLAYS)
+
+/obj/machinery/plantgenes/update_genes()
+ . = ..()
+ update_icon(UPDATE_OVERLAYS)
+
+/obj/item/storage/bag/plants
+ icon = 'modular_ss220/aesthetics/hydroponics/icons/hydroponics.dmi'
+
+/obj/structure/loom
+ icon = 'modular_ss220/aesthetics/hydroponics/icons/hydroponics.dmi'
diff --git a/modular_ss220/aesthetics/hydroponics/icons/hydroponics.dmi b/modular_ss220/aesthetics/hydroponics/icons/hydroponics.dmi
new file mode 100644
index 000000000000..82d7d663b9f0
Binary files /dev/null and b/modular_ss220/aesthetics/hydroponics/icons/hydroponics.dmi differ
diff --git a/modular_ss220/aesthetics/intercom/code/intercom.dm b/modular_ss220/aesthetics/intercom/code/intercom.dm
new file mode 100644
index 000000000000..9fa1f174e314
--- /dev/null
+++ b/modular_ss220/aesthetics/intercom/code/intercom.dm
@@ -0,0 +1,2 @@
+/obj/item/radio/intercom
+ icon = 'modular_ss220/aesthetics/intercom/icons/intercom.dmi'
diff --git a/modular_ss220/aesthetics/intercom/icons/intercom.dmi b/modular_ss220/aesthetics/intercom/icons/intercom.dmi
new file mode 100644
index 000000000000..3e9fbfd93583
Binary files /dev/null and b/modular_ss220/aesthetics/intercom/icons/intercom.dmi differ
diff --git a/modular_ss220/aesthetics/keycard/code/keycard.dm b/modular_ss220/aesthetics/keycard/code/keycard.dm
new file mode 100644
index 000000000000..fdb81b779055
--- /dev/null
+++ b/modular_ss220/aesthetics/keycard/code/keycard.dm
@@ -0,0 +1,5 @@
+/obj/machinery/keycard_auth
+ icon = 'modular_ss220/aesthetics/keycard/icons/keycard.dmi'
+
+/obj/machinery/readybutton
+ icon = 'modular_ss220/aesthetics/keycard/icons/keycard.dmi'
diff --git a/modular_ss220/aesthetics/keycard/icons/keycard.dmi b/modular_ss220/aesthetics/keycard/icons/keycard.dmi
new file mode 100644
index 000000000000..39bdcf13f962
Binary files /dev/null and b/modular_ss220/aesthetics/keycard/icons/keycard.dmi differ
diff --git a/modular_ss220/aesthetics/labeler/code/labeler.dm b/modular_ss220/aesthetics/labeler/code/labeler.dm
new file mode 100644
index 000000000000..b6306dd4f44a
--- /dev/null
+++ b/modular_ss220/aesthetics/labeler/code/labeler.dm
@@ -0,0 +1,2 @@
+/obj/item/hand_labeler
+ icon = 'modular_ss220/aesthetics/labeler/icons/labeler.dmi'
diff --git a/modular_ss220/aesthetics/labeler/icons/labeler.dmi b/modular_ss220/aesthetics/labeler/icons/labeler.dmi
new file mode 100644
index 000000000000..74946042a96a
Binary files /dev/null and b/modular_ss220/aesthetics/labeler/icons/labeler.dmi differ
diff --git a/modular_ss220/aesthetics/library/code/library.dm b/modular_ss220/aesthetics/library/code/library.dm
new file mode 100644
index 000000000000..46a6045bc6a4
--- /dev/null
+++ b/modular_ss220/aesthetics/library/code/library.dm
@@ -0,0 +1,5 @@
+/obj/structure/bookcase
+ icon = 'modular_ss220/aesthetics/library/icons/library.dmi'
+
+/obj/machinery/bookbinder
+ icon = 'modular_ss220/aesthetics/library/icons/library.dmi'
diff --git a/modular_ss220/aesthetics/library/icons/library.dmi b/modular_ss220/aesthetics/library/icons/library.dmi
new file mode 100644
index 000000000000..ef1556c35bc7
Binary files /dev/null and b/modular_ss220/aesthetics/library/icons/library.dmi differ
diff --git a/modular_ss220/aesthetics/light_switch/code/light_switch.dm b/modular_ss220/aesthetics/light_switch/code/light_switch.dm
new file mode 100644
index 000000000000..d33b7182510f
--- /dev/null
+++ b/modular_ss220/aesthetics/light_switch/code/light_switch.dm
@@ -0,0 +1,2 @@
+/obj/machinery/light_switch
+ layer = ABOVE_WINDOW_LAYER
diff --git a/modular_ss220/aesthetics/lights/code/lights.dm b/modular_ss220/aesthetics/lights/code/lights.dm
new file mode 100644
index 000000000000..d12e861994a2
--- /dev/null
+++ b/modular_ss220/aesthetics/lights/code/lights.dm
@@ -0,0 +1,7 @@
+/obj/machinery/light
+ icon = 'modular_ss220/aesthetics/lights/icons/lights.dmi'
+ layer = ABOVE_MOB_LAYER
+
+/obj/machinery/light/small
+ icon = 'icons/obj/lighting.dmi'
+ layer = ABOVE_MOB_LAYER
diff --git a/modular_ss220/aesthetics/lights/icons/lights.dmi b/modular_ss220/aesthetics/lights/icons/lights.dmi
new file mode 100644
index 000000000000..1027c73aefb6
Binary files /dev/null and b/modular_ss220/aesthetics/lights/icons/lights.dmi differ
diff --git a/modular_ss220/aesthetics/newscaster/code/newscaster.dm b/modular_ss220/aesthetics/newscaster/code/newscaster.dm
new file mode 100644
index 000000000000..9b7fc4f71053
--- /dev/null
+++ b/modular_ss220/aesthetics/newscaster/code/newscaster.dm
@@ -0,0 +1,2 @@
+/obj/machinery/newscaster
+ layer = ABOVE_WINDOW_LAYER
diff --git a/modular_ss220/aesthetics/piano/code/piano.dm b/modular_ss220/aesthetics/piano/code/piano.dm
new file mode 100644
index 000000000000..07755566de6b
--- /dev/null
+++ b/modular_ss220/aesthetics/piano/code/piano.dm
@@ -0,0 +1,2 @@
+/obj/structure/musician/piano
+ icon = 'modular_ss220/aesthetics/piano/icons/piano.dmi'
diff --git a/modular_ss220/aesthetics/piano/icons/piano.dmi b/modular_ss220/aesthetics/piano/icons/piano.dmi
new file mode 100644
index 000000000000..ff378fd56f2b
Binary files /dev/null and b/modular_ss220/aesthetics/piano/icons/piano.dmi differ
diff --git a/modular_ss220/aesthetics/racks/code/racks.dm b/modular_ss220/aesthetics/racks/code/racks.dm
new file mode 100644
index 000000000000..98dbba107a08
--- /dev/null
+++ b/modular_ss220/aesthetics/racks/code/racks.dm
@@ -0,0 +1,84 @@
+/obj/structure/rack
+ icon = 'modular_ss220/aesthetics/racks/icons/racks.dmi'
+
+/obj/structure/rack/skeletal_bar
+ icon = 'icons/obj/stationobjs.dmi'
+ //TODO: need icon
+
+/obj/structure/rack/gunrack
+ name = "gun rack"
+ desc = "A gun rack for storing guns."
+ icon_state = "gunrack"
+
+/obj/item/gun
+ var/on_rack = FALSE
+
+/obj/item/gun/proc/place_on_rack()
+ on_rack = TRUE
+ var/matrix/M = matrix()
+ M.Turn(-90)
+ transform = M
+
+/obj/item/gun/proc/remove_from_rack()
+ if(on_rack)
+ var/matrix/M = matrix()
+ transform = M
+ on_rack = FALSE
+
+/obj/item/gun/pickup(mob/user)
+ . = ..()
+ remove_from_rack()
+
+/obj/structure/rack/gunrack/MouseDrop_T(obj/O, mob/user)
+ if(!(istype(O, /obj/item/gun)))
+ to_chat(user, span_warning("This item doesn't fit!"))
+ return
+ . = ..()
+ if(.)
+ add_fingerprint(user)
+ var/obj/item/gun/our_gun = O
+ our_gun.place_on_rack()
+
+/obj/structure/rack/gunrack/attackby(obj/item/W, mob/user, params) //TODO: fix logic
+ if(!(istype(W, /obj/item/gun)))
+ to_chat(user, span_warning("This item doesn't fit!"))
+ return
+ . = ..()
+ if(W.loc == src)
+ add_fingerprint(user)
+ var/obj/item/gun/our_gun = W
+ our_gun.place_on_rack()
+ var/list/click_params = params2list(params)
+ //Center the icon where the user clicked.
+ if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
+ return
+ //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
+ W.pixel_x = clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
+ W.pixel_y = 0
+
+/obj/structure/rack/gunrack/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ return
+ for(var/obj/item/gun/gun_inside in loc.contents)
+ gun_inside.place_on_rack()
+
+/obj/structure/rack/gunrack/deconstruct(disassembled = TRUE)
+ if(!(flags & NODECONSTRUCT))
+ density = FALSE
+ var/obj/item/gunrack_parts/newparts = new(loc)
+ transfer_fingerprints_to(newparts)
+ for(var/obj/item/I in loc.contents)
+ if(istype(I, /obj/item/gun))
+ var/obj/item/gun/to_remove = I
+ to_remove.remove_from_rack()
+ qdel(src)
+
+/obj/item/gunrack_parts
+ name = "gun rack parts"
+ desc = "Parts of a gun rack."
+ icon = 'icons/obj/items.dmi'
+ icon_state = "gunrack_parts"
+ flags = CONDUCT
+ materials = list(MAT_METAL=2000)
+ var/building = FALSE
diff --git a/modular_ss220/aesthetics/racks/icons/racks.dmi b/modular_ss220/aesthetics/racks/icons/racks.dmi
new file mode 100644
index 000000000000..e19295aa1cae
Binary files /dev/null and b/modular_ss220/aesthetics/racks/icons/racks.dmi differ
diff --git a/modular_ss220/aesthetics/requests_console/code/requests_console.dm b/modular_ss220/aesthetics/requests_console/code/requests_console.dm
new file mode 100644
index 000000000000..0e51a40fae9e
--- /dev/null
+++ b/modular_ss220/aesthetics/requests_console/code/requests_console.dm
@@ -0,0 +1,4 @@
+/obj/machinery/requests_console
+ icon = 'modular_ss220/aesthetics/requests_console/icons/reqcomp.dmi'
+ layer = ABOVE_WINDOW_LAYER
+ //TODO: rewired icon
diff --git a/modular_ss220/aesthetics/requests_console/icons/reqcomp.dmi b/modular_ss220/aesthetics/requests_console/icons/reqcomp.dmi
new file mode 100644
index 000000000000..2ebb80eba2fb
Binary files /dev/null and b/modular_ss220/aesthetics/requests_console/icons/reqcomp.dmi differ
diff --git a/modular_ss220/aesthetics/rollerbed/code/rollerbed.dm b/modular_ss220/aesthetics/rollerbed/code/rollerbed.dm
new file mode 100644
index 000000000000..28efa7db76d6
--- /dev/null
+++ b/modular_ss220/aesthetics/rollerbed/code/rollerbed.dm
@@ -0,0 +1,8 @@
+/obj/structure/bed/roller
+ icon = 'modular_ss220/aesthetics/rollerbed/icons/rollerbed.dmi'
+
+/obj/item/roller
+ icon = 'modular_ss220/aesthetics/rollerbed/icons/rollerbed.dmi'
+ item_state = "rbed"
+ lefthand_file = 'modular_ss220/aesthetics/rollerbed/icons/lefthand.dmi'
+ righthand_file = 'modular_ss220/aesthetics/rollerbed/icons/righthand.dmi'
diff --git a/modular_ss220/aesthetics/rollerbed/icons/lefthand.dmi b/modular_ss220/aesthetics/rollerbed/icons/lefthand.dmi
new file mode 100644
index 000000000000..41a3429d0cb6
Binary files /dev/null and b/modular_ss220/aesthetics/rollerbed/icons/lefthand.dmi differ
diff --git a/modular_ss220/aesthetics/rollerbed/icons/righthand.dmi b/modular_ss220/aesthetics/rollerbed/icons/righthand.dmi
new file mode 100644
index 000000000000..ae4699daca0b
Binary files /dev/null and b/modular_ss220/aesthetics/rollerbed/icons/righthand.dmi differ
diff --git a/modular_ss220/aesthetics/rollerbed/icons/rollerbed.dmi b/modular_ss220/aesthetics/rollerbed/icons/rollerbed.dmi
new file mode 100644
index 000000000000..2e3cfe13271f
Binary files /dev/null and b/modular_ss220/aesthetics/rollerbed/icons/rollerbed.dmi differ
diff --git a/modular_ss220/aesthetics/safe/code/safe.dm b/modular_ss220/aesthetics/safe/code/safe.dm
new file mode 100644
index 000000000000..27534fcedace
--- /dev/null
+++ b/modular_ss220/aesthetics/safe/code/safe.dm
@@ -0,0 +1,2 @@
+/obj/item/storage/secure/safe
+ icon = 'modular_ss220/aesthetics/safe/icons/safe.dmi'
diff --git a/modular_ss220/aesthetics/safe/icons/safe.dmi b/modular_ss220/aesthetics/safe/icons/safe.dmi
new file mode 100644
index 000000000000..7f185fcf4800
Binary files /dev/null and b/modular_ss220/aesthetics/safe/icons/safe.dmi differ
diff --git a/modular_ss220/aesthetics/sheets/code/sheets.dm b/modular_ss220/aesthetics/sheets/code/sheets.dm
new file mode 100644
index 000000000000..6cf1735078a7
--- /dev/null
+++ b/modular_ss220/aesthetics/sheets/code/sheets.dm
@@ -0,0 +1,2 @@
+/obj/item/bedsheet
+ icon = 'modular_ss220/aesthetics/sheets/icons/sheets.dmi'
diff --git a/modular_ss220/aesthetics/sheets/icons/sheets.dmi b/modular_ss220/aesthetics/sheets/icons/sheets.dmi
new file mode 100644
index 000000000000..f63c8c26a0ab
Binary files /dev/null and b/modular_ss220/aesthetics/sheets/icons/sheets.dmi differ
diff --git a/modular_ss220/aesthetics/shutters/code/shutters.dm b/modular_ss220/aesthetics/shutters/code/shutters.dm
new file mode 100644
index 000000000000..382757908be3
--- /dev/null
+++ b/modular_ss220/aesthetics/shutters/code/shutters.dm
@@ -0,0 +1,13 @@
+/obj/machinery/door/poddoor/shutters
+ icon = 'modular_ss220/aesthetics/shutters/icons/shutters.dmi'
+ var/door_open_sound = 'modular_ss220/aesthetics/shutters/sound/shutters_open.ogg'
+ var/door_close_sound = 'modular_ss220/aesthetics/shutters/sound/shutters_close.ogg'
+
+/obj/machinery/door/poddoor/shutters/do_animate(animation)
+ switch(animation)
+ if("opening")
+ flick("opening", src)
+ playsound(src, door_open_sound, 30, TRUE)
+ if("closing")
+ flick("closing", src)
+ playsound(src, door_close_sound, 30, TRUE)
diff --git a/modular_ss220/aesthetics/shutters/icons/shutters.dmi b/modular_ss220/aesthetics/shutters/icons/shutters.dmi
new file mode 100644
index 000000000000..1cc727cdbf7a
Binary files /dev/null and b/modular_ss220/aesthetics/shutters/icons/shutters.dmi differ
diff --git a/modular_ss220/aesthetics/shutters/sound/shutters_close.ogg b/modular_ss220/aesthetics/shutters/sound/shutters_close.ogg
new file mode 100644
index 000000000000..548cea96c593
Binary files /dev/null and b/modular_ss220/aesthetics/shutters/sound/shutters_close.ogg differ
diff --git a/modular_ss220/aesthetics/shutters/sound/shutters_open.ogg b/modular_ss220/aesthetics/shutters/sound/shutters_open.ogg
new file mode 100644
index 000000000000..b8e0869c5389
Binary files /dev/null and b/modular_ss220/aesthetics/shutters/sound/shutters_open.ogg differ
diff --git a/modular_ss220/aesthetics/skin/code/darkmode.dm b/modular_ss220/aesthetics/skin/code/darkmode.dm
new file mode 100644
index 000000000000..16d6993cdae6
--- /dev/null
+++ b/modular_ss220/aesthetics/skin/code/darkmode.dm
@@ -0,0 +1,11 @@
+/client/activate_darkmode()
+ . = ..()
+ winset(src, "rpane.fullscreenb", "background-color=#494949;text-color=#a4bad6")
+
+/client/deactivate_darkmode()
+ . = ..()
+ winset(src, "rpane.fullscreenb", "background-color=none;text-color=#000000")
+
+/datum/preferences/New(client/C, datum/db_query/Q)
+ toggles |= PREFTOGGLE_UI_DARKMODE
+ . = ..()
diff --git a/modular_ss220/aesthetics/skin/icons/screen_clockwork.dmi b/modular_ss220/aesthetics/skin/icons/screen_clockwork.dmi
new file mode 100644
index 000000000000..33a1d999ec4a
Binary files /dev/null and b/modular_ss220/aesthetics/skin/icons/screen_clockwork.dmi differ
diff --git a/modular_ss220/aesthetics/skin/icons/screen_detective.dmi b/modular_ss220/aesthetics/skin/icons/screen_detective.dmi
new file mode 100644
index 000000000000..304b91e93363
Binary files /dev/null and b/modular_ss220/aesthetics/skin/icons/screen_detective.dmi differ
diff --git a/modular_ss220/aesthetics/skin/icons/screen_trasenknox.dmi b/modular_ss220/aesthetics/skin/icons/screen_trasenknox.dmi
new file mode 100644
index 000000000000..9f8c6c60be46
Binary files /dev/null and b/modular_ss220/aesthetics/skin/icons/screen_trasenknox.dmi differ
diff --git a/modular_ss220/aesthetics/skin/icons/screen_vaporwave.dmi b/modular_ss220/aesthetics/skin/icons/screen_vaporwave.dmi
new file mode 100644
index 000000000000..2001693943a8
Binary files /dev/null and b/modular_ss220/aesthetics/skin/icons/screen_vaporwave.dmi differ
diff --git a/modular_ss220/aesthetics/soap/code/soap.dm b/modular_ss220/aesthetics/soap/code/soap.dm
new file mode 100644
index 000000000000..0077236f3603
--- /dev/null
+++ b/modular_ss220/aesthetics/soap/code/soap.dm
@@ -0,0 +1,2 @@
+/obj/item/soap
+ //TODO: what is this soap?
diff --git a/modular_ss220/aesthetics/soap/icons/soap.dmi b/modular_ss220/aesthetics/soap/icons/soap.dmi
new file mode 100644
index 000000000000..a090b49a9b8c
Binary files /dev/null and b/modular_ss220/aesthetics/soap/icons/soap.dmi differ
diff --git a/modular_ss220/aesthetics/surgery_table/code/surgery_table.dm b/modular_ss220/aesthetics/surgery_table/code/surgery_table.dm
new file mode 100644
index 000000000000..159bbc898b0a
--- /dev/null
+++ b/modular_ss220/aesthetics/surgery_table/code/surgery_table.dm
@@ -0,0 +1,3 @@
+/obj/machinery/optable
+ icon = 'modular_ss220/aesthetics/surgery_table/icons/surgery_table.dmi'
+ //TODO: I didn't find slime table in the code. Legacy?
diff --git a/modular_ss220/aesthetics/surgery_table/icons/surgery_table.dmi b/modular_ss220/aesthetics/surgery_table/icons/surgery_table.dmi
new file mode 100644
index 000000000000..3370a4432b04
Binary files /dev/null and b/modular_ss220/aesthetics/surgery_table/icons/surgery_table.dmi differ
diff --git a/modular_ss220/aesthetics/toolboxes/code/toolboxes.dm b/modular_ss220/aesthetics/toolboxes/code/toolboxes.dm
new file mode 100644
index 000000000000..c3f924357241
--- /dev/null
+++ b/modular_ss220/aesthetics/toolboxes/code/toolboxes.dm
@@ -0,0 +1,2 @@
+/obj/item/storage/toolbox
+ icon = 'modular_ss220/aesthetics/toolboxes/icons/toolboxes.dmi'
diff --git a/modular_ss220/aesthetics/toolboxes/icons/toolboxes.dmi b/modular_ss220/aesthetics/toolboxes/icons/toolboxes.dmi
new file mode 100644
index 000000000000..c52f97b55bc9
Binary files /dev/null and b/modular_ss220/aesthetics/toolboxes/icons/toolboxes.dmi differ
diff --git a/modular_ss220/aesthetics/wallcloset/code/wallcloset.dm b/modular_ss220/aesthetics/wallcloset/code/wallcloset.dm
new file mode 100644
index 000000000000..6fc01b8817c5
--- /dev/null
+++ b/modular_ss220/aesthetics/wallcloset/code/wallcloset.dm
@@ -0,0 +1,55 @@
+/obj/structure/closet/walllocker
+ icon = 'modular_ss220/aesthetics/wallcloset/icons/wallclosets.dmi'
+
+/obj/structure/closet/walllocker/firelocker
+ icon_state = "firecloset"
+ icon_closed = "firecloset"
+ icon_opened = "firecloset_open"
+
+/obj/structure/closet/walllocker/firelocker/north
+ pixel_y = 32
+ dir = SOUTH
+
+/obj/structure/closet/walllocker/firelocker/south
+ pixel_y = -32
+ dir = NORTH
+
+/obj/structure/closet/walllocker/firelocker/west
+ pixel_x = -32
+ dir = WEST
+
+/obj/structure/closet/walllocker/firelocker/east
+ pixel_x = 32
+ dir = EAST
+
+/obj/structure/closet/walllocker/firelocker/populate_contents()
+ new /obj/item/extinguisher(src)
+ new /obj/item/clothing/suit/fire/firefighter(src)
+ new /obj/item/clothing/mask/gas(src)
+ new /obj/item/tank/internals/oxygen/red(src)
+ new /obj/item/clothing/head/hardhat/red(src)
+
+/obj/structure/closet/walllocker/medlocker
+ icon_state = "medcloset"
+ icon_closed = "medcloset"
+ icon_opened = "medcloset_open"
+
+/obj/structure/closet/walllocker/medlocker/north
+ pixel_y = 32
+ dir = SOUTH
+
+/obj/structure/closet/walllocker/medlocker/south
+ pixel_y = -32
+ dir = NORTH
+
+/obj/structure/closet/walllocker/medlocker/west
+ pixel_x = -32
+ dir = WEST
+
+/obj/structure/closet/walllocker/medlocker/east
+ pixel_x = 32
+ dir = EAST
+
+/obj/structure/closet/walllocker/medlocker/populate_contents()
+ new /obj/item/stack/medical/bruise_pack(src)
+ new /obj/item/stack/medical/ointment(src)
diff --git a/modular_ss220/aesthetics/wallcloset/icons/wallclosets.dmi b/modular_ss220/aesthetics/wallcloset/icons/wallclosets.dmi
new file mode 100644
index 000000000000..e9dcf2b5e73d
Binary files /dev/null and b/modular_ss220/aesthetics/wallcloset/icons/wallclosets.dmi differ
diff --git a/modular_ss220/aesthetics/walls/code/walls.dm b/modular_ss220/aesthetics/walls/code/walls.dm
new file mode 100644
index 000000000000..91c8105b1c74
--- /dev/null
+++ b/modular_ss220/aesthetics/walls/code/walls.dm
@@ -0,0 +1,14 @@
+/turf/simulated/wall
+ icon = 'modular_ss220/aesthetics/walls/icons/wall.dmi'
+
+/turf/simulated/wall/r_wall
+ icon = 'modular_ss220/aesthetics/walls/icons/reinforced_wall.dmi'
+
+/obj/structure/falsewall
+ icon = 'modular_ss220/aesthetics/walls/icons/wall.dmi'
+
+/obj/structure/falsewall/reinforced
+ icon = 'modular_ss220/aesthetics/walls/icons/reinforced_wall.dmi'
+
+/turf/simulated/wall/indestructible/riveted
+ icon = 'modular_ss220/aesthetics/walls/icons/reinforced_wall.dmi'
diff --git a/modular_ss220/aesthetics/walls/icons/reinforced_wall.dmi b/modular_ss220/aesthetics/walls/icons/reinforced_wall.dmi
new file mode 100644
index 000000000000..d08446795e71
Binary files /dev/null and b/modular_ss220/aesthetics/walls/icons/reinforced_wall.dmi differ
diff --git a/modular_ss220/aesthetics/walls/icons/wall.dmi b/modular_ss220/aesthetics/walls/icons/wall.dmi
new file mode 100644
index 000000000000..51442c8fff24
Binary files /dev/null and b/modular_ss220/aesthetics/walls/icons/wall.dmi differ
diff --git a/modular_ss220/aesthetics/windoor/code/windoor.dm b/modular_ss220/aesthetics/windoor/code/windoor.dm
new file mode 100644
index 000000000000..5d893cd0e388
--- /dev/null
+++ b/modular_ss220/aesthetics/windoor/code/windoor.dm
@@ -0,0 +1,8 @@
+/obj/structure/windoor_assembly
+ icon = 'modular_ss220/aesthetics/windoor/icons/windoor.dmi'
+
+/obj/machinery/door/window
+ icon = 'modular_ss220/aesthetics/windoor/icons/windoor.dmi'
+
+/obj/machinery/door/window/clockwork
+ icon = 'icons/obj/doors/windoor.dmi'
diff --git a/modular_ss220/aesthetics/windoor/icons/windoor.dmi b/modular_ss220/aesthetics/windoor/icons/windoor.dmi
new file mode 100644
index 000000000000..c7a70c7f77cb
Binary files /dev/null and b/modular_ss220/aesthetics/windoor/icons/windoor.dmi differ
diff --git a/modular_ss220/aesthetics/windows/code/windows.dm b/modular_ss220/aesthetics/windows/code/windows.dm
new file mode 100644
index 000000000000..9472e6d4ed6c
--- /dev/null
+++ b/modular_ss220/aesthetics/windows/code/windows.dm
@@ -0,0 +1,47 @@
+/obj/structure/window/full/basic
+ icon = 'modular_ss220/aesthetics/windows/icons/window.dmi'
+ edge_overlay_file = 'modular_ss220/aesthetics/windows/icons/window_edges.dmi'
+
+/obj/structure/window/full/reinforced
+ icon = 'modular_ss220/aesthetics/windows/icons/reinforced_window.dmi'
+ edge_overlay_file = 'modular_ss220/aesthetics/windows/icons/reinforced_window_edges.dmi'
+
+/obj/structure/window/full/reinforced/tinted
+ icon = 'modular_ss220/aesthetics/windows/icons/tinted_window.dmi'
+
+/obj/structure/window/full/plasmabasic
+ icon = 'modular_ss220/aesthetics/windows/icons/plasma_window.dmi'
+ edge_overlay_file = 'modular_ss220/aesthetics/windows/icons/window_edges.dmi'
+
+/obj/structure/window/full/plasmareinforced
+ icon = 'modular_ss220/aesthetics/windows/icons/rplasma_window.dmi'
+ edge_overlay_file = 'modular_ss220/aesthetics/windows/icons/reinforced_window_edges.dmi'
+
+/turf/simulated/wall/indestructible/fakeglass
+ icon = 'modular_ss220/aesthetics/windows/icons/reinforced_window.dmi'
+ edge_overlay_file = 'modular_ss220/aesthetics/windows/icons/reinforced_window_edges.dmi'
+
+//WINDOW SPAWNERS
+/obj/effect/spawner/window
+ icon = 'modular_ss220/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/window/reinforced
+ icon = 'modular_ss220/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/window/reinforced/tinted
+ icon = 'modular_ss220/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/window/reinforced/polarized
+ icon = 'modular_ss220/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/window/plasma
+ icon = 'modular_ss220/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/window/reinforced/plasma
+ icon = 'modular_ss220/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/window/plastitanium
+ icon = 'icons/obj/structures.dmi'
+
+/obj/effect/spawner/window/shuttle
+ icon = 'icons/obj/structures.dmi'
diff --git a/modular_ss220/aesthetics/windows/icons/plasma_window.dmi b/modular_ss220/aesthetics/windows/icons/plasma_window.dmi
new file mode 100644
index 000000000000..11c08011a67c
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/plasma_window.dmi differ
diff --git a/modular_ss220/aesthetics/windows/icons/reinforced_window.dmi b/modular_ss220/aesthetics/windows/icons/reinforced_window.dmi
new file mode 100644
index 000000000000..8433522914ae
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/reinforced_window.dmi differ
diff --git a/modular_ss220/aesthetics/windows/icons/reinforced_window_edges.dmi b/modular_ss220/aesthetics/windows/icons/reinforced_window_edges.dmi
new file mode 100644
index 000000000000..3e32388dd536
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/reinforced_window_edges.dmi differ
diff --git a/modular_ss220/aesthetics/windows/icons/rplasma_window.dmi b/modular_ss220/aesthetics/windows/icons/rplasma_window.dmi
new file mode 100644
index 000000000000..665a04402505
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/rplasma_window.dmi differ
diff --git a/modular_ss220/aesthetics/windows/icons/spawners.dmi b/modular_ss220/aesthetics/windows/icons/spawners.dmi
new file mode 100644
index 000000000000..3a0106b5a264
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/spawners.dmi differ
diff --git a/modular_ss220/aesthetics/windows/icons/tinted_window.dmi b/modular_ss220/aesthetics/windows/icons/tinted_window.dmi
new file mode 100644
index 000000000000..a17c39244010
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/tinted_window.dmi differ
diff --git a/modular_ss220/aesthetics/windows/icons/window.dmi b/modular_ss220/aesthetics/windows/icons/window.dmi
new file mode 100644
index 000000000000..ff049fad0d51
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/window.dmi differ
diff --git a/modular_ss220/aesthetics/windows/icons/window_edges.dmi b/modular_ss220/aesthetics/windows/icons/window_edges.dmi
new file mode 100644
index 000000000000..2eda848769ee
Binary files /dev/null and b/modular_ss220/aesthetics/windows/icons/window_edges.dmi differ
diff --git a/modular_ss220/aesthetics/windowtint/code/windowtint.dm b/modular_ss220/aesthetics/windowtint/code/windowtint.dm
new file mode 100644
index 000000000000..bf1a409571ab
--- /dev/null
+++ b/modular_ss220/aesthetics/windowtint/code/windowtint.dm
@@ -0,0 +1,22 @@
+/obj/machinery/button/windowtint
+ icon = 'modular_ss220/aesthetics/windowtint/icons/polarizer.dmi'
+ icon_state = "polarizer-0"
+ layer = ABOVE_WINDOW_LAYER
+
+/obj/machinery/button/windowtint/attack_hand(mob/user)
+ if(!allowed(user) && !user.can_advanced_admin_interact())
+ to_chat(user, span_warning("Access Denied."))
+ flick("polarizer-denied",src)
+ playsound(src, pick('modular_ss220/aesthetics/windowtint/sound/button.ogg', 'modular_ss220/aesthetics/windowtint/sound/button_alternate.ogg', 'modular_ss220/aesthetics/windowtint/sound/button_meloboom.ogg'), 20)
+ return 1
+
+ toggle_tint()
+ icon_state= "polarizer-turning_on"
+ addtimer(CALLBACK(src, PROC_REF(update_windowtint_icon)), 0.5 SECONDS)
+
+ if(!active)
+ icon_state= "polarizer-turning_off"
+ addtimer(CALLBACK(src, PROC_REF(update_windowtint_icon)), 0.5 SECONDS)
+
+/obj/machinery/button/windowtint/proc/update_windowtint_icon()
+ icon_state = "polarizer-[active]"
diff --git a/modular_ss220/aesthetics/windowtint/icons/polarizer.dmi b/modular_ss220/aesthetics/windowtint/icons/polarizer.dmi
new file mode 100644
index 000000000000..1af5616fd9e9
Binary files /dev/null and b/modular_ss220/aesthetics/windowtint/icons/polarizer.dmi differ
diff --git a/modular_ss220/aesthetics/windowtint/sound/button.ogg b/modular_ss220/aesthetics/windowtint/sound/button.ogg
new file mode 100644
index 000000000000..23b4f15265aa
Binary files /dev/null and b/modular_ss220/aesthetics/windowtint/sound/button.ogg differ
diff --git a/modular_ss220/aesthetics/windowtint/sound/button_alternate.ogg b/modular_ss220/aesthetics/windowtint/sound/button_alternate.ogg
new file mode 100644
index 000000000000..e35c6d69498f
Binary files /dev/null and b/modular_ss220/aesthetics/windowtint/sound/button_alternate.ogg differ
diff --git a/modular_ss220/aesthetics/windowtint/sound/button_meloboom.ogg b/modular_ss220/aesthetics/windowtint/sound/button_meloboom.ogg
new file mode 100644
index 000000000000..80d5cfa894a2
Binary files /dev/null and b/modular_ss220/aesthetics/windowtint/sound/button_meloboom.ogg differ
diff --git a/modular_ss220/aesthetics/zippo/code/zippo.dm b/modular_ss220/aesthetics/zippo/code/zippo.dm
new file mode 100644
index 000000000000..8fcafcc30649
--- /dev/null
+++ b/modular_ss220/aesthetics/zippo/code/zippo.dm
@@ -0,0 +1,3 @@
+/obj/item/lighter/zippo
+ icon = 'modular_ss220/aesthetics/zippo/icons/zippo.dmi'
+ //TODO: give heads their zippos
diff --git a/modular_ss220/aesthetics/zippo/icons/zippo.dmi b/modular_ss220/aesthetics/zippo/icons/zippo.dmi
new file mode 100644
index 000000000000..5113914135c9
Binary files /dev/null and b/modular_ss220/aesthetics/zippo/icons/zippo.dmi differ
diff --git a/modular_ss220/aesthetics_sounds/_aesthetics_sounds.dm b/modular_ss220/aesthetics_sounds/_aesthetics_sounds.dm
new file mode 100644
index 000000000000..d2f1e5bcee7b
--- /dev/null
+++ b/modular_ss220/aesthetics_sounds/_aesthetics_sounds.dm
@@ -0,0 +1,4 @@
+/datum/modpack/aesthetics_sounds
+ name = "Эстетика звуков"
+ desc = "Обновление звуков на новые, более красочные"
+ author = "dj-34"
diff --git a/modular_ss220/aesthetics_sounds/_aesthetics_sounds.dme b/modular_ss220/aesthetics_sounds/_aesthetics_sounds.dme
new file mode 100644
index 000000000000..0e26c19aafa6
--- /dev/null
+++ b/modular_ss220/aesthetics_sounds/_aesthetics_sounds.dme
@@ -0,0 +1 @@
+#include "_aesthetics_sounds.dm"
diff --git a/modular_ss220/aesthetics_sounds/sound/creepy/low_laugh.ogg b/modular_ss220/aesthetics_sounds/sound/creepy/low_laugh.ogg
new file mode 100644
index 000000000000..fe38d03162ca
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/creepy/low_laugh.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/creepy/many_whisper1.ogg b/modular_ss220/aesthetics_sounds/sound/creepy/many_whisper1.ogg
new file mode 100644
index 000000000000..6e2e9a6a7e30
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/creepy/many_whisper1.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/creepy/many_whisper2.ogg b/modular_ss220/aesthetics_sounds/sound/creepy/many_whisper2.ogg
new file mode 100644
index 000000000000..5feabf9106e6
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/creepy/many_whisper2.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/creepy/scary_whisper1.ogg b/modular_ss220/aesthetics_sounds/sound/creepy/scary_whisper1.ogg
new file mode 100644
index 000000000000..ef9e60b64103
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/creepy/scary_whisper1.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/creepy/scary_whisper2.ogg b/modular_ss220/aesthetics_sounds/sound/creepy/scary_whisper2.ogg
new file mode 100644
index 000000000000..7a62b7752083
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/creepy/scary_whisper2.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/epsilon/epsilon.ogg b/modular_ss220/aesthetics_sounds/sound/epsilon/epsilon.ogg
new file mode 100644
index 000000000000..a938c318cc55
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/epsilon/epsilon.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/music/Traitor.ogg b/modular_ss220/aesthetics_sounds/sound/music/Traitor.ogg
new file mode 100644
index 000000000000..34660095068b
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/music/Traitor.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/narsie/narsie_risen.ogg b/modular_ss220/aesthetics_sounds/sound/narsie/narsie_risen.ogg
new file mode 100644
index 000000000000..cec2c4ff063c
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/narsie/narsie_risen.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/narsie/narsie_summon.ogg b/modular_ss220/aesthetics_sounds/sound/narsie/narsie_summon.ogg
new file mode 100644
index 000000000000..a4c0e0ab571e
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/narsie/narsie_summon.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/supermatter/core_overheating.ogg b/modular_ss220/aesthetics_sounds/sound/supermatter/core_overheating.ogg
new file mode 100644
index 000000000000..2cc4c81398b3
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/supermatter/core_overheating.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/supermatter/explode.ogg b/modular_ss220/aesthetics_sounds/sound/supermatter/explode.ogg
new file mode 100644
index 000000000000..a16a0634b93d
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/supermatter/explode.ogg differ
diff --git a/modular_ss220/aesthetics_sounds/sound/supermatter/meltdown.ogg b/modular_ss220/aesthetics_sounds/sound/supermatter/meltdown.ogg
new file mode 100644
index 000000000000..c890581bcb6f
Binary files /dev/null and b/modular_ss220/aesthetics_sounds/sound/supermatter/meltdown.ogg differ
diff --git a/modular_ss220/awaymission_gun/_awaymission_gun.dm b/modular_ss220/awaymission_gun/_awaymission_gun.dm
new file mode 100644
index 000000000000..b9350a83a703
--- /dev/null
+++ b/modular_ss220/awaymission_gun/_awaymission_gun.dm
@@ -0,0 +1,4 @@
+/datum/modpack/awaymission_gun
+ name = "Гейтган"
+ desc = "Добавляет гейтган."
+ author = "PhantomRU"
diff --git a/modular_ss220/awaymission_gun/_awaymission_gun.dme b/modular_ss220/awaymission_gun/_awaymission_gun.dme
new file mode 100644
index 000000000000..25949a2657d2
--- /dev/null
+++ b/modular_ss220/awaymission_gun/_awaymission_gun.dme
@@ -0,0 +1,4 @@
+#include "_awaymission_gun.dm"
+
+#include "code/items/awaymission_gun.dm"
+#include "code/research_designs/weapon_designs.dm"
diff --git a/modular_ss220/awaymission_gun/code/items/awaymission_gun.dm b/modular_ss220/awaymission_gun/code/items/awaymission_gun.dm
new file mode 100644
index 000000000000..34483e230b07
--- /dev/null
+++ b/modular_ss220/awaymission_gun/code/items/awaymission_gun.dm
@@ -0,0 +1,50 @@
+/obj/item/gun/energy/laser/awaymission_aeg
+ name = "Wireless Energy Gun"
+ desc = "An energy gun that recharges wirelessly during away missions. Does not work outside the gate."
+ icon = 'modular_ss220/awaymission_gun/icons/items/energy.dmi'
+ lefthand_file = 'modular_ss220/awaymission_gun/icons/inhands/lefthand.dmi'
+ righthand_file = 'modular_ss220/awaymission_gun/icons/inhands/righthand.dmi'
+ icon_state = "laser_gate"
+ item_state = "nucgun"
+ force = 10
+ origin_tech = "combat=5;magnets=3;powerstorage=4"
+ selfcharge = TRUE // Selfcharge is enabled and disabled, and used as the away mission tracker
+ can_charge = 0
+ emagged = FALSE
+
+/obj/item/gun/energy/laser/awaymission_aeg/rnd
+ name = "Exploreverse Mk I"
+ desc = "Первый прототип оружия с миниатюрным реактором для исследований в крайне отдаленных секторах. Данную модель невозможно подключить к зарядной станции, во избежание истощения подключенных источников питания, в связи с протоколами безопасности, опустошающие заряд при нахождении вне предназначенных мест использования устройств."
+
+/obj/item/gun/energy/laser/awaymission_aeg/Initialize(mapload)
+ . = ..()
+ // Force update it incase it spawns outside an away mission and shouldnt be charged
+ onTransitZ(new_z = loc.z)
+
+/obj/item/gun/energy/laser/awaymission_aeg/onTransitZ(old_z, new_z)
+ if(emagged)
+ return
+
+ if(is_away_level(new_z))
+ if(ismob(loc))
+ to_chat(loc, "Ваш [src.name] активируется, начиная потреблять энергию от ближайшего беспроводного источника питания.")
+ selfcharge = TRUE
+ else
+ if(selfcharge)
+ if(ismob(loc))
+ to_chat(loc, "Ваш [src.name] деактивируется, так как он находится вне зоны действия источника питания.")
+ cell.charge = 0
+ selfcharge = FALSE
+ update_icon()
+
+/obj/item/gun/energy/laser/awaymission_aeg/emag_act(mob/user)
+ . = ..()
+ if (emagged)
+ return
+
+ user.visible_message("От [src.name] летят искры!", "Вы взломали [src.name], что привело к перезаписи протоколов безопасности. Устройство может быть использовано вне ограничений.")
+ playsound(src.loc, 'sound/effects/sparks4.ogg', 30, 1)
+ do_sparks(5, 1, src)
+
+ emagged = TRUE
+ selfcharge = TRUE
diff --git a/modular_ss220/awaymission_gun/code/research_designs/weapon_designs.dm b/modular_ss220/awaymission_gun/code/research_designs/weapon_designs.dm
new file mode 100644
index 000000000000..4b334f1d5321
--- /dev/null
+++ b/modular_ss220/awaymission_gun/code/research_designs/weapon_designs.dm
@@ -0,0 +1,10 @@
+/datum/design/gate_gun
+ name = "Gate Energy Gun"
+ desc = "An energy gun with an experimental miniaturized reactor. Only works in the gate" //не отображаемое описание, т.к. печатается без кейса
+ id = "gate_energy_gun"
+ req_tech = list("combat" = 5, "magnets" = 3, "powerstorage" = 4)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 8000, MAT_GLASS = 2000, MAT_URANIUM = 1500, MAT_TITANIUM = 500)
+ build_path = /obj/item/gun/energy/laser/awaymission_aeg/rnd
+ locked = 0
+ category = list("Weapons")
diff --git a/modular_ss220/awaymission_gun/icons/inhands/lefthand.dmi b/modular_ss220/awaymission_gun/icons/inhands/lefthand.dmi
new file mode 100644
index 000000000000..101610bd9fbd
Binary files /dev/null and b/modular_ss220/awaymission_gun/icons/inhands/lefthand.dmi differ
diff --git a/modular_ss220/awaymission_gun/icons/inhands/righthand.dmi b/modular_ss220/awaymission_gun/icons/inhands/righthand.dmi
new file mode 100644
index 000000000000..5174b1709ba8
Binary files /dev/null and b/modular_ss220/awaymission_gun/icons/inhands/righthand.dmi differ
diff --git a/modular_ss220/awaymission_gun/icons/items/energy.dmi b/modular_ss220/awaymission_gun/icons/items/energy.dmi
new file mode 100644
index 000000000000..e36a4fdb3ef2
Binary files /dev/null and b/modular_ss220/awaymission_gun/icons/items/energy.dmi differ
diff --git a/modular_ss220/balance/_balance.dm b/modular_ss220/balance/_balance.dm
new file mode 100644
index 000000000000..cee22be786f6
--- /dev/null
+++ b/modular_ss220/balance/_balance.dm
@@ -0,0 +1,4 @@
+/datum/modpack/balance
+ name = "Баланс"
+ desc = "Твики баланса."
+ author = "dj-34"
diff --git a/modular_ss220/balance/_balance.dme b/modular_ss220/balance/_balance.dme
new file mode 100644
index 000000000000..1931779ccb54
--- /dev/null
+++ b/modular_ss220/balance/_balance.dme
@@ -0,0 +1,3 @@
+#include "_balance.dm"
+
+#include "code/items/weapons.dm"
diff --git a/modular_ss220/balance/code/items/weapons.dm b/modular_ss220/balance/code/items/weapons.dm
new file mode 100644
index 000000000000..2a71b5f76849
--- /dev/null
+++ b/modular_ss220/balance/code/items/weapons.dm
@@ -0,0 +1,2 @@
+/obj/item/melee/baton/cattleprod
+ w_class = WEIGHT_CLASS_NORMAL
diff --git a/modular_ss220/barsigns/_barsigns.dm b/modular_ss220/barsigns/_barsigns.dm
new file mode 100644
index 000000000000..106f29bca442
--- /dev/null
+++ b/modular_ss220/barsigns/_barsigns.dm
@@ -0,0 +1,4 @@
+/datum/modpack/barsigns
+ name = "Барные вывески"
+ desc = "Добавляет новые барные вывески"
+ author = "Aylong220, larentoun"
diff --git a/modular_ss220/barsigns/_barsigns.dme b/modular_ss220/barsigns/_barsigns.dme
new file mode 100644
index 000000000000..5bdc02915902
--- /dev/null
+++ b/modular_ss220/barsigns/_barsigns.dme
@@ -0,0 +1,3 @@
+#include "_barsigns.dm"
+
+#include "code/barsigns.dm"
diff --git a/modular_ss220/barsigns/code/barsigns.dm b/modular_ss220/barsigns/code/barsigns.dm
new file mode 100644
index 000000000000..bc4368217814
--- /dev/null
+++ b/modular_ss220/barsigns/code/barsigns.dm
@@ -0,0 +1,53 @@
+/obj/structure/sign/barsign/set_sign(datum/barsign/sign)
+ if(!istype(sign))
+ return
+ if(initial(sign.ss220_icon))
+ icon = initial(sign.ss220_icon)
+ else
+ icon = initial(icon)
+ . = ..()
+
+/datum/barsign
+ var/ss220_icon
+
+/datum/barsign/evahumanspace
+ name = "SS220 EVA Human in Space"
+ icon = "evahumanspace"
+ desc = "Безопасность - это привелегия."
+ ss220_icon = 'modular_ss220/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/warpsurf
+ name = "SS220 Warp Surf"
+ icon = "warpsurf"
+ desc = "Welcome to the club, buddy!"
+ ss220_icon = 'modular_ss220/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/papacafe
+ name = "SS220 Space Daddy's Cafe"
+ icon = "papacafe"
+ desc = "Уважай своего Космического Папу!"
+ ss220_icon = 'modular_ss220/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/wycctide
+ name = "SS220 Wycctide"
+ icon = "wycctide"
+ desc = "О нет, он близится!"
+ ss220_icon = 'modular_ss220/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/shitcur
+ name = "SS220 Shitcur"
+ icon = "shitcur"
+ desc = "Невиновность ничего не доказывает."
+ ss220_icon = 'modular_ss220/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/pourndot
+ name = "SS220 Pour and that's it"
+ icon = "pourndot"
+ desc = "Нальют и Точка. Тяжёлые времена приближаются."
+ ss220_icon = 'modular_ss220/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/moonipub
+ name = "SS220 Mooniverse pub"
+ icon = "mooni"
+ desc = "Совершенно новый паб."
+ ss220_icon = 'modular_ss220/barsigns/icons/barsigns.dmi'
diff --git a/modular_ss220/barsigns/icons/barsigns.dmi b/modular_ss220/barsigns/icons/barsigns.dmi
new file mode 100644
index 000000000000..58bcb474b620
Binary files /dev/null and b/modular_ss220/barsigns/icons/barsigns.dmi differ
diff --git a/modular_ss220/bureaucracy/_bureaucracy.dm b/modular_ss220/bureaucracy/_bureaucracy.dm
new file mode 100644
index 000000000000..cc9b26b56dab
--- /dev/null
+++ b/modular_ss220/bureaucracy/_bureaucracy.dm
@@ -0,0 +1,9 @@
+/datum/modpack/bureaucracy
+ name = "Бюрократия"
+ desc = "Добавляет бланки в ксерокс."
+ author = "Aylong220, Furior, RV666"
+
+/datum/modpack/bureaucracy/initialize()
+ . = ..()
+ for(var/datum/bureaucratic_form/form as anything in subtypesof(/datum/bureaucratic_form))
+ GLOB.bureaucratic_forms["[form]"] = new form
diff --git a/modular_ss220/bureaucracy/_bureaucracy.dme b/modular_ss220/bureaucracy/_bureaucracy.dme
new file mode 100644
index 000000000000..ed7bf92d9036
--- /dev/null
+++ b/modular_ss220/bureaucracy/_bureaucracy.dme
@@ -0,0 +1,5 @@
+#include "_bureaucracy.dm"
+
+#include "code/paper.dm"
+#include "code/forms.dm"
+#include "code/photocopier.dm"
diff --git a/modular_ss220/bureaucracy/code/forms.dm b/modular_ss220/bureaucracy/code/forms.dm
new file mode 100644
index 000000000000..9d74ba71898b
--- /dev/null
+++ b/modular_ss220/bureaucracy/code/forms.dm
@@ -0,0 +1,539 @@
+GLOBAL_LIST_INIT(bureaucratic_forms, list())
+
+/datum/bureaucratic_form
+ /// Form name. Will be applied to a paper
+ var/name
+ /// Form id
+ var/id
+ /// Alternative form name. Appears in printer this way with id
+ var/altername
+ /// In what category the form is
+ var/category
+ /// What access is required to print this form
+ var/req_access
+
+ /// Text that will be applied to a paper
+ var/text
+ var/is_header_needed = TRUE
+ /// Header that will be apllied to a paper
+ var/header
+ /// Footer that will be apllied to a paper
+ var/footer = footer_signstampfax
+
+ /// Used in header to decide to add or not CONFEDENTIAL text
+ var/confidential = FALSE
+ /// Used in some forms as a reminder of some stuff
+ var/notice = "Перед заполнением прочтите от начала до конца | Во всех PDA имеется ручка"
+ /// Is generated based on station name. Used in some forms
+ var/from
+ var/const/footer_signstampfax = "
Подписи глав являются доказательством их согласия. Данный документ является недействительным при отсутствии релевантной печати. Пожалуйста, отправьте обратно подписанную/проштампованную копию факсом."
+ var/const/footer_signstamp = "
Подписи глав являются доказательством их согласия. Данный документ является недействительным при отсутствии релевантной печати."
+ var/const/footer_confidential = "
Данный документ является недействительным при отсутствии печати. Отказ от ответственности: Данный факс является конфиденциальным и не может быть прочтен сотрудниками не имеющего доступа. Если вы получили данный факс по ошибке, просим вас сообщить отправителю и удалить его из вашего почтового ящика или любого другого носителя. И Nanotrasen, и любой её агент не несёт ответственность за любые сделанные заявления, они являются исключительно заявлениями отправителя, за исключением если отправителем является Nanotrasen или один из её агентов. Отмечаем, что ни Nanotrasen, ни один из агентов корпорации не несёт ответственности за наличие вирусов, который могут содержаться в данном факсе или его приложения, и это только ваша прерогатива просканировать факс и приложения на них. Никакие контракты не могут быть заключены посредством факсимильной связи."
+
+/datum/bureaucratic_form/New()
+ . = ..()
+ from = "Научная станция Nanotrasen\
+ "[SSmapping.map_datum.fluff_name]""
+ if(is_header_needed)
+ header = " | [name][confidential ? " \[КОНФИДЕНЦИАЛЬНО\]" : ""] | | [altername] | |
| [notice]
"
+
+/datum/bureaucratic_form/proc/apply_to_paper(obj/item/paper/paper, mob/user = null)
+ paper.name = name
+ paper.info = admin_pencode_to_html(text, user)
+ paper.header = header
+ paper.footer = footer
+ paper.force_big = TRUE
+ paper.populatefields()
+
+// Главы станции
+/datum/bureaucratic_form/NT_COM_ST
+ name = "Форма NT-COM-ST"
+ id = "NT-COM-ST"
+ altername = "Отчет о ситуации на станции"
+ category = "Главы станции"
+ text = "Приветствую Центральное командование Сообщает вам , в должности .
В данный момент на станции код: Активные угрозы для станции: Потери среди экипажа: Повреждения на станции: Общее состояние станции: Дополнительная информация:
Подписи и штампы Подпись: *В данном документе описывается полное состояние станции, необходимо перечислить всю доступную информацию. *Информацию, которую вы считаете нужной, необходимо сообщить в разделе – дополнительная информация. *Данный документ считается официальным только после подписи уполномоченного лица и наличии на документе его печати. "
+
+/datum/bureaucratic_form/NT_COM_ACAP
+ name = "Форма NT-COM-ACAP"
+ id = "NT-COM-ACAP"
+ altername = "Заявление о повышении главы отдела до и.о. капитана"
+ category = "Главы станции"
+ text = "Я, , в должности главы отделения , прошу согласовать нынешнее командование станции Керберос, в повышении меня до и.о. капитана.
⠀⠀⠀При назначении меня на данную должность, я обязуюсь выполнять все рекомендации и правила, согласно стандартным рабочим процедурам капитана. До появления капитана, я обеспечиваю порядок и управление станцией, сохранность и безопасность диска с кодами авторизации ядерной боеголовки, а также самой боеголовки, коды от сейфов и личные вещи капитана.
⠀⠀⠀При появлении капитана мне необходибо будет сообщить: состояние и статус станции, о своем продвижении до и.о. капитана, и обнулить капитанский доступ при первому требованию капитана.
Подписи и штампы
Подпись заявителя: Подпись инициатора повышения: Время вступления в должность и.о. капитана: Подпись главы отделения : Подпись главы отделения : Подпись главы отделения :
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченного лица, производившего инициацию повышения, и выдаче заявителю. *Если один (или более) глав отсутствуют, необходимо собрать подписи, действующих глав. *Так же в данном документе, главам, которые согласились с кандидатом, необходимо поставить свою печать и подпись."
+
+/datum/bureaucratic_form/NT_COM_ACOM
+ name = "Форма NT-COM-ACOM"
+ id = "NT-COM-ACOM"
+ altername = "Заявление о повышении сотрудника до и.о. главы отделения"
+ category = "Главы станции"
+ text = " ᅠᅠЯ, , в должности сотрудника отделения , прошу согласовать нынешнее командование станции Керберос, в повышении меня до звания и.о. главы .
⠀⠀⠀При назначении меня на данную должность, я обязуюсь выполнять все рекомендации, и правила, которые присутствуют на главе отделения . До появления основного главы отделения, я обеспечиваю порядок и управление своим отделом, сохранность и безопасность личных вещей главы отделения.
⠀⠀⠀При появлении главы отделения, мне неообходимо сообщить: состояние и статус своего отдела, о своем продвижении до и.о. главы отделения, и сдать доступ и.о. главы и взятые вещи при первом требовании прибывшего главы.
Подписи и штампы Подпись заявителя: Подпись инициатора повышения: Время вступления в и.о. : Подпись главы отделения : Подпись главы отделения : Подпись главы отделения :
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченного лица, производившего инициацию повышения, и выдаче заявителю. *При указании главы, рекомендуется использовать сокращения: *СМО (главврач), СЕ (глав. инженер), РД (дир. исследований), КМ (завхоз), ГСБ (глава СБ), ГП (глава персонала). *Если один (или более) глав отсутствуют, необходимо собрать подписи, действующих глав. *Так же в данном документе, главам, которые согласились с кандидатом, необходимо поставить свою печать и подпись."
+
+/datum/bureaucratic_form/NT_COM_LCOM
+ name = "Форма NT-COM-LCOM"
+ id = "NT-COM-LCOM"
+ altername = "Заявление об увольнении главы отделения"
+ category = "Главы станции"
+ text = " ᅠᅠЯ, , в должности – , заявляю об официальном увольнении действующего главы , отделения . Причина увольнения: ⠀⠀⠀При наличии иных причин, от других глав, они так же могут написать их в данном документе.
Подписи и штампы Подпись инициатора увольнения: Подпись увольняемого, о ознакомлении: Дата и время увольнения:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченного лица, производившего инициацию увольнения, и выдаче увольняемому. *Для полной эффективности данного документа, необходимо собрать как можно больше причин для увольнения, и перечислить их. Инициировать увольнение может только капитан или глава персонала. "
+
+/datum/bureaucratic_form/NT_COM_REQ
+ name = "Форма NT-COM-REQ"
+ id = "NT-COM-REQ"
+ altername = "Запрос на поставку с Центрального командования"
+ category = "Главы станции"
+ text = " Приветствую Центральное командование
Сообщает вам , в должности .
Текст запроса:
Причина запроса:
Подписи и штампы
Подпись:
*В данном документе описывается запросы на поставку оборудования/ресурсов, необходимо перечислить по пунктно необходимое для поставки. *Данный документ считается, официальным, только после подписи уполномоченного лица, и наличии на документе его печати. "
+
+/datum/bureaucratic_form/NT_COM_OS
+ name = "Форма NT-COM-OS"
+ id = "NT-COM-OS"
+ altername = "Отчёт о выполнении цели"
+ category = "Главы станции"
+ text = " Цель станции: Статус цели: Общее состояние станции: Активные угрозы: Оценка работы экипажа: Дополнительные замечания:
Подписи и штампыДолжность уполномоченного лица: Подпись уполномоченного лица: *Данное сообщение должно сообщить вам о состоянии цели, установленной Центральным командованием Nanotrasen для ИСН "Керберос". Убедительная просьба внимательно прочитать данное сообщение для вынесения наиболее эффективных указаний для последующей деятельности станции. *Данный документ считается официальным только при наличии подписи уполномоченного лица и соответствующего его должности штампа. В случае отсутствия любого из указанных элементов данный документ не является официальным и рекомендуется его удалить с любого информационного носителя. ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Корпорация Nanotrasen не несёт ответственности, если данный документ не попал в руки первоначального предполагаемого получателя. Однако, корпорация Nanotrasen запрещает использование любой имеющейся в данном документе информации третьими лицами и сообщает, что это преследуется по закону, даже если информация в данном документе не является достоверной. "
+
+//Медицинский Отдел
+
+/datum/bureaucratic_form/NT_MD_01
+ name = "Форма NT-MD-01"
+ id = "NT-MD-01"
+ altername = "Постановление на поставку медикаментов"
+ category = "Медицинский отдел"
+ text = "⠀⠀⠀ Я, , в должности , запрашиваю следующие медикаменты на поставку в медбей:
Подписи и штампыПодпись заказчика: Подпись грузчика:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче грузчику или производившему поставку."
+ footer = footer_signstamp
+
+/datum/bureaucratic_form/NT_MD_02
+ name = "Форма NT-MD-02"
+ id = "NT-MD-02"
+ altername = "Отчёт о вскрытии"
+ category = "Медицинский отдел"
+ text = "Основная информация
Скончавшийся: |
| Раса: |
| Пол: |
| Возраст: |
| Группа крови: |
| Должность: |
|
Отчёт о вскрытии
Тип смерти: |
| Описание тела: |
| Метки и раны: |
| Вероятная причина смерти: |
|
Детали:
Подписи и штампы "
+ footer = footer_signstamp
+
+/datum/bureaucratic_form/NT_MD_03
+ name = "Форма NT-MD-03"
+ id = "NT-MD-03"
+ altername = "Постановление на изготовление химических препаратов"
+ category = "Медицинский отдел"
+ text = "⠀⠀⠀ Я, , в должности , запрашиваю следующие химические медикаменты, для служебного использования в медбее:
Подписи и штампыПодпись заказчика: Подпись исполняющего: Время заказа:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче лицу исполнившему заказ"
+ footer = footer_signstamp
+
+/datum/bureaucratic_form/NT_MD_04
+ name = "Форма NT-MD-04"
+ id = "NT-MD-04"
+ altername = "Сводка о вирусе"
+ category = "Медицинский отдел"
+ text = "Вирус: Полное название вируса: Свойства вируса: Передача вируса: Побочные эффекты:
Дополнительная информация:
Лечение вируса:
Подписи и штампы Подпись вирусолога: *В дополнительной информации, указывается вся остальная информация, по поводу данного вируса. "
+ footer = footer_signstamp
+
+/datum/bureaucratic_form/NT_MD_05
+ name = "Форма NT-MD-05"
+ id = "NT-MD-05"
+ altername = "Отчет об психологическом состоянии"
+ category = "Медицинский отдел"
+ text = " Пациент: Раздражители: Симптомы и побочные действия: Дополнительная информация:
Подписи и штампы Подпись психолога: Время обследования:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче пациенту"
+ footer = footer_signstamp
+
+//Мед-без нумерации
+/datum/bureaucratic_form/NT_MD_VRR
+ name = "Форма NT-MD-VRR"
+ id = "NT-MD-VRR"
+ altername = "Запрос на распространение вируса"
+ category = "Медицинский отдел"
+ text = "Основная информация Я, , в должности – , запрашиваю право на распространение вируса среди экипажа станции.
Название вируса: |
| Задачи вируса: |
| Лечение: |
| Вакцина была произведена и в данный момент находится: |
|
Подписи и штампы
Подпись вирусолога: |
| Подпись глав. Врача: |
| Подпись капитана: |
|
*Производитель вируса несет полную ответственность за его распространение, изолирование и лечение *При возникновении опасных или смертельных побочных эффектов у членов экипажа, производитель должен незамедлительно предоставить вакцину, от данного вируса."
+ footer = footer_signstamp
+
+//Исследовательский отдел
+/datum/bureaucratic_form/NT_RND_01
+ name = "Форма NT-RND-01"
+ id = "NT-RND-01"
+ altername = "Отчет о странном предмете"
+ category = "Исследовательский отдел"
+ text = " Название предмета: Тип предмета: Строение: Особенности и функционал: Дополнительная информация:
Подписи и штампы Подпись производившего осмотр:
*В дополнительной информации, рекомендуется указать остальную информацию о предмете, любое взаимодействие с ним, модификации, итоговый вариант после модификации."
+
+/datum/bureaucratic_form/NT_RND_02
+ name = "Форма NT-RND-02"
+ id = "NT-RND-02"
+ altername = "Заявление на киберизацию"
+ category = "Исследовательский отдел"
+ text = "⠀⠀⠀ Я, , в должности , самовольно подтверждаю согласие на проведение киберизации. ⠀⠀⠀ Я полностью доверяю работнику в должности – . Я хорошо осведомлен о рисках, связанных как с операцией, так и с киберизацией, и понимаю, что Nanotrasen не несет ответственности, если эти процедуры вызовут боль, заражение или иные случаи летального характера.
Подписи и штампы Подпись заявителя: Подпись уполномоченного:
*Если член экипажа мертв, данный документ нету необходимости создавать. *Если член экипажа жив, данный документ сохраняется только у уполномоченного лица. *Данный документ может использоваться как для создания киборгов, так и для ИИ"
+
+/datum/bureaucratic_form/NT_RND_03
+ name = "Форма NT-RND-03"
+ id = "NT-RND-03"
+ altername = "Заявление на получение и установку импланта"
+ category = "Исследовательский отдел"
+ text = "Заявление
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Требуемый имплантат: Может требовать дополнительного согласования |
| Причина: Объясните свои намерения
|
Подписи и штампы
Дата и время: |
| Подпись заявителя: |
| Подпись Руководителя Исследований: |
| Подпись выполняющего установку имплантата: |
| "
+
+// Общие формы
+/datum/bureaucratic_form/NT_BLANK
+ name = "Форма NT"
+ id = "NT-BLANK"
+ altername = "Пустой бланк для любых целей"
+ category = "Общие формы"
+ text = "Основная информация
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
|
Заявление
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись главы персонала: |
| Подпись (дополнительная): | "
+ footer = null
+
+/datum/bureaucratic_form/NT_E_112
+ name = "Форма NT-E-112"
+ id = "NT-E-112"
+ altername = "Экстренное письмо"
+ category = "Общие формы"
+ notice = "Форма предназначена только для экстренного использования."
+ text = "Основная информация
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
|
Отчёт о ситуации
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_signstamp
+
+// Отдел кадров
+/datum/bureaucratic_form/NT_HR_00
+ name = "Форма NT-HR-00"
+ id = "NT-HR-00"
+ altername = "Бланк заявления"
+ category = "Отдел кадров"
+ text = "Основная информация
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
|
Заявление
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись главы персонала: |
| Подпись (дополнительная): |
| "
+ footer = footer_signstamp
+
+/datum/bureaucratic_form/NT_HR_01
+ name = "Форма NT-HR-01"
+ id = "NT-HR-01"
+ altername = "Заявление о приеме на работу"
+ category = "Отдел кадров"
+ text = "Заявление
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Запрашиваемая должность: Требует наличия квалификации |
| Список компетенций:
|
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись главы персонала: |
| Подпись будущего главы: |
| "
+
+/datum/bureaucratic_form/NT_HR_02
+ name = "Форма NT-HR-02"
+ id = "NT-HR-02"
+ altername = "Заявление на смену должности"
+ category = "Отдел кадров"
+ text = "Заявление
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Запрашиваемая должность: Требует наличия квалификации |
| Причина: Объясните свои намерения
|
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись главы персонала: |
| Подпись текущего главы: |
| Подпись будущего главы: |
| "
+
+/datum/bureaucratic_form/NT_HR_12
+ name = "Форма NT-HR-12"
+ id = "NT-HR-12"
+ altername = "Приказ на смену должности"
+ category = "Отдел кадров"
+ text = "Приказ
Имя сотрудника: Полностью и без ошибок |
| Номер аккаунта сотрудника: Эта информация есть у главы персонала |
| Текущая должность: Указано на ID карте |
| Запрашиваемая должность: Требует наличия квалификации |
| Причина: Объясните свои намерения
|
Подписи и штампы
Время: |
| Подпись инициатора: |
| Подпись главы персонала: |
| "
+
+/datum/bureaucratic_form/NT_HR_03
+ name = "Форма NT-HR-03"
+ id = "NT-HR-03"
+ altername = "Заявление об увольнении"
+ category = "Отдел кадров"
+ text = "Заявление
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Причина: Объясните свои намерения
|
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись главы персонала: |
| Подпись текущего главы: |
| "
+
+/datum/bureaucratic_form/NT_HR_13
+ name = "Форма NT-HR-13"
+ id = "NT-HR-13"
+ altername = "Приказ об увольнении"
+ category = "Отдел кадров"
+ text = "Приказ
Имя увольняемого: Полностью и без ошибок |
| Номер аккаунта увольняемого: Эта информация есть у главы персонала |
| Текущая должность: Указано на ID карте |
| Причина: Объясните свои намерения
|
Подписи и штампы
Время: |
| Подпись инициатора: |
| Подпись главы персонала: |
| "
+
+/datum/bureaucratic_form/NT_HR_04
+ name = "Форма NT-HR-04"
+ id = "NT-HR-04"
+ altername = "Заявление на выдачу новой ID карты"
+ category = "Отдел кадров"
+ text = "Заявление
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Причина: Объясните свои намерения
|
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись главы персонала: |
| "
+
+/datum/bureaucratic_form/NT_HR_05
+ name = "Форма NT-HR-05"
+ id = "NT-HR-05"
+ altername = "Заявление на дополнительный доступ"
+ category = "Отдел кадров"
+ text = "Заявление
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Требуемый доступ: Может требовать дополнительного согласования |
| Причина: Объясните свои намерения
|
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись главы персонала: |
| Подпись текущего главы: |
| "
+
+/datum/bureaucratic_form/NT_HR_06
+ name = "Форма NT-HR-06"
+ id = "NT-HR-06"
+ altername = "Лицензия на создание организации/отдела"
+ category = "Отдел кадров"
+ text = "Заявление
Я , прошу Вашего разрешения на создание для работы с экипажем.
Наше Агенство/Отдел займет .
Наша Организация обязуется соблюдать Космический Закон. Также я , как глава отдела, буду нести ответственность за своих сотрудников и обязуюсь наказывать их за несоблюдение Космического Закона. Или же передавать сотрудникам Службы Безопасности.
Подписи и штампы
Время:
Подпись заявителя:
Подпись главы персонала:
*Обязательно провести копирование документа для главы персонала, оригинал документа должен быть выдан обладателю лицензии.
*Данная форма документа, обязательно должна подтверждаться печатью ответственного лица. В случае наличия опечаток и отсутствия подписей или печатей, лицензия будет являться недействительной."
+
+/datum/bureaucratic_form/NT_HR_07
+ name = "Форма NT-HR-07"
+ id = "NT-HR-07"
+ altername = "Разрешение на перестройку/перестановку"
+ category = "Отдел кадров"
+ text = "Разрешение Я , прошу Вашего разрешения на перестройку/перестановку помещения под свои нужды или нужды организации.
Должность заявителя:
Подписи и штампы
Время:
Подпись заявителя:
Подпись главы персонала:
*Обязательно провести копирование документа для главы персонала, оригинал документа должен быть выдан заявителю."
+
+/datum/bureaucratic_form/NT_HR_08
+ name = "Форма NT-HR-08"
+ id = "NT-HR-08"
+ altername = "Запрос о постройке меха"
+ category = "Отдел кадров"
+ text = "⠀⠀⠀Я, , прошу произвести постройку меха – , с данными модификациями – , для выполнения задач: . ⠀⠀⠀Так же я, , обязуюсь соблюдать все правила, законы и предупреждения, а также соглашаюсь выполнять все устные или письменные инструкции, или приказы со стороны командования, представителей или агентов Nanotrasen, и Центрального командования. ⠀⠀⠀При получении меха, я становлюсь ответственным за его повреждение, уничтожение, похищение, или попадание в руки людей, относящимся к врагам Nanotrasen.
Подписи и штампы Подпись заявителя: Время постройки меха: Время передачи меха заявителю: Подпись изготовителя меха:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче заявителю."
+
+/datum/bureaucratic_form/NT_HR_09
+ name = "Форма NT-HR-09"
+ id = "NT-HR-09"
+ altername = "Квитанция о продаже пода"
+ category = "Отдел кадров"
+ text = "⠀⠀⠀Я, , в должности – произвожу передачу транспортного средства на платной основе члену экипажа , в должности – . Продаваемый под имеет модификации: . Стоимость пода: . ⠀⠀⠀Я, , как покупатель, становлюсь ответственным за его повреждение, уничтожение, похищение, или попадание в руки людей, относящимся к врагам Nanotrasen. ⠀⠀⠀Так же я, обязуюсь соблюдать все правила, законы и предупреждения, а также соглашаюсь выполнять все устные или письменные инструкции, или приказы со стороны командования, представителей или агентов Nanotrasen, и Центрального командования.
Подписи и штампы Подпись продавца: Подпись покупателя: Время сделки:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче покупателю."
+
+// Отдел сервиса
+/datum/bureaucratic_form/NT_MR
+ name = "Форма NT-MR"
+ id = "NT-MR"
+ altername = "Свидетельство о заключении брака"
+ category = "Отдел сервиса"
+ text = "⠀⠀⠀Объявляется, что , и , официально прошли процедуру заключения гражданского брака.
Подписи и штампы Подпись уполномоченного: Подпись свидетеля: Подпись свидетеля:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче одному из представителей брака. *При заявлении о расторжении брака, необходимо наличие двух супругов, и данного документа."
+
+/datum/bureaucratic_form/NT_MRL
+ name = "Форма NT-MRL"
+ id = "NT-MRL"
+ altername = "Заявление о расторжении брака"
+ category = "Отдел сервиса"
+ text = "⠀⠀⠀Просим произвести регистрацию расторжения брака, подтверждаем взаимное согласие на расторжение брака.
Подписи и штампы Подпись супруга: Подпись супруги:
Подпись уполномоченного:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче каждому, из супругов."
+
+// Отдел снабжения
+/datum/bureaucratic_form/NT_REQ_01
+ name = "Форма NT-REQ-01"
+ id = "NT-REQ-01"
+ altername = "Запрос на поставку"
+ category = "Отдел снабжения"
+ text = "Сторона запроса
Имя запросившего: Полностью и без ошибок |
| Номер аккаунта: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Способ получения: Предпочитаемый способ |
| Причина запроса:
| Список запроса:
|
Сторона поставки
Имя поставщика: Полностью и без ошибок |
| Номер аккаунта: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
| Способ доставки: Утверждённый способ |
| Комментарии:
| Список поставки и цены:
| Итоговая стоимость: Пропустите, если бесплатно |
|
Подписи и штампы
Время: |
| Подпись стороны запроса: |
| Подпись стороны поставки: |
| Подпись главы (если требуется): |
| "
+ footer = footer_signstamp
+
+/datum/bureaucratic_form/NT_SUP_01
+ name = "Форма NT-SUP-01"
+ id = "NT-SUP-01"
+ altername = "Регистрационная форма для подтверждения заказа"
+ category = "Отдел снабжения"
+ text = "Отдел снабженияРегистрационная форма для подтверждения заказа Имя заявителя: Должность заявителя: Подробное объяснение о необходимости заказа:
Время: Подпись заявителя: Подпись руководителя: Подпись сотрудника снабжения:
Данная форма является приложением для оригинального автоматического документа, полученного с рук заявителя. Для подтверждения заказа заявителя необходимы указанные подписи и соответствующие печати отдела по заказу. "
+ footer = null
+
+// Служба безопасности
+/datum/bureaucratic_form/NT_SEC_01
+ name = "Форма NT-SEC-01"
+ id = "NT-SEC-01"
+ altername = "Свидетельские показания"
+ category = "Служба безопасности"
+ text = "Информация о свидетеле
Имя свидетеля: Полностью и без ошибок |
| Номер аккаунта свидетеля: Эта информация есть у главы персонала |
| Должность свидетеля: Указано на ID карте |
|
Свидетельство
Я, (подпись свидетеля) , подтверждаю, что приведенная выше информация является правдивой и точной, насколько мне известно, и передана в меру моих возможностей. Подписываясь ниже, я тем самым подтверждаю, что Верховный Суд может признать меня неуважительным или виновным в лжесвидетельстве согласно Закону SolGov 552 (a) (c) и Постановлению корпорации Nanotrasen 7716 (c).
Подписи и штампы
Время: |
| Подпись сотрудника, получающего показания: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_11
+ name = "Форма NT-SEC-11"
+ id = "NT-SEC-11"
+ altername = "Ордер на обыск"
+ category = "Служба безопасности"
+ text = "Информация о свидетеле
Имя свидетеля: Полностью и без ошибок |
| Номер аккаунта свидетеля: Эта информация есть у главы персонала |
| Должность свидетеля: Указано на ID карте |
|
Ордер
В целях обыска: (помещения, имущества, лица) | |
Ознакомившись с письменными показаниями свидетеля(-ей), у меня появились основания полагать, что на лицах или помещениях, указанных выше, имеются соответствующие доказательства в этой связи или в пределах, в частности:
и другое имущество, являющееся доказательством уголовного преступления, контрабанды, плодов преступления или предметов, иным образом принадлежащих преступнику, или имущество, спроектированное или предназначенное для использования, или которое используется или использовалось в качестве средства совершения уголовного преступления, в частности заговор с целью совершения преступления, или совершения злонамеренного предъявления ложных и фиктивных претензий к или против корпорации Нанотрейзен или его дочерних компаний.
Я удовлетворен тем, что показания под присягой и любые записанные показания устанавливают вероятную причину полагать, что описанное имущество в данный момент скрыто в описанных выше помещениях, лицах или имуществе, и устанавливают законные основания для выдачи этого ордера.
ВЫ НАСТОЯЩИМ КОМАНДИРОВАНЫ для обыска вышеуказанного помещения, имущества или лица в течение минут с даты выдачи настоящего ордера на указанное скрытое имущество, и если будет установлено, что имущество изъято, оставить копию этого ордера в качестве доказательства на реквизированную собственность, в соответствии с требованиями указа корпорации Nanotrasen.
Слава Корпорации Nanotrasen!
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_21
+ name = "Форма NT-SEC-21"
+ id = "NT-SEC-21"
+ altername = "Ордер на арест"
+ category = "Служба безопасности"
+ text = "Ордер
В целях ареста: Имя полностью и без ошибок |
| Должность: |
|
Сотрудники Службы Безопасности настоящим уполномочены и направлены на задержание и арест указанного лица. Они будут игнорировать любые заявления о неприкосновенности или привилегии со стороны подозреваемого или агентов, действующих от его имени. Сотрудники немедленно доставят указанное лицо в Бриг для отбывать наказание за следующие преступления:
Предполагается, что подозреваемый будет отбывать наказание в за вышеуказанные преступления.
Слава Корпорации Nanotrasen!
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_02
+ name = "Форма NT-SEC-02"
+ id = "NT-SEC-02"
+ altername = "Отчёт по результатам расследования"
+ category = "Служба безопасности"
+ text = "Дело
Тип проишествия/преступления: |
| Время проишествия/преступления: |
| Местоположение: |
| Краткое описание: |
|
Участвующие лица
Арестованные: |
| Подозреваемые: |
| Свидетели: |
| Раненные: |
| Пропавшие: |
| Скончавшиеся: |
|
Ход расследования
Прикреплённые доказательства: |
| Дополнительные замечания: |
|
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_03
+ name = "Форма NT-SEC-03"
+ id = "NT-SEC-03"
+ altername = "Заявление о краже"
+ category = "Служба безопасности"
+ text = "⠀⠀⠀Я, , в должности , заявляю:
Подписи и штампы Подпись потерпевшего: Подпись принимавшего заявление: Время принятия заявления:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче потерпевшему. *При обнаружении предмета кражи (предмет, жидкость или существо), данный предмет необходимо передать детективу, для дальнейшего осмотра и обследования. *После заключения детектива, предмет можно выдать владельцу. "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_04
+ name = "Форма NT-SEC-04"
+ id = "NT-SEC-04"
+ altername = "Заявление о причинении вреда здоровью или имуществу"
+ category = "Служба безопасности"
+ text = "⠀⠀⠀Я, , в должности , заявляю:
Подписи и штампы Подпись пострадавшего: Время происшествия: Подпись уполномоченного: Время принятия заявления:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче пострадавшему."
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_05
+ name = "Форма NT-SEC-05"
+ id = "NT-SEC-05"
+ altername = "Разрешение на оружие"
+ category = "Служба безопасности"
+ text = "⠀⠀⠀Члену экипажа, , в должности , было выдано разрешение на оружие. Я соглашаюсь с условиями его использования, хранения и применения. Данное оружие я обязуюсь применять только в целях самообороны, защиты своих личных вещей, и рабочего места, а так же для защиты своих коллег. ⠀⠀⠀При попытке применения оружия, против остальных членов экипажа не предоставляющих угрозу, или при запугивании данным оружием, я лишаюсь лицензии на оружие, а так же понесу наказания, при нарушении закона.
Название и тип оружия:
Подписи и штампы Подпись уполномоченного: Подпись получателя:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче получателю. *Документ не является действительным без печати Вардена/ГСБ и его подписи."
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_06
+ name = "Форма NT-SEC-06"
+ id = "NT-SEC-06"
+ altername = "Разрешение на присваивание канала связи"
+ category = "Служба безопасности"
+ text = "Разрешение Я , прошу Вашего разрешения на присваивание канала связи , для грамотной работы организации.
Должность заявителя:
Подписи и штампы
Время:
Подпись заявителя:
Подпись главы персонала:
Подпись главы службы безопасности:
*Обязательно провести копирование документа для главы персонала, оригинал документа должен быть выдан заявителю.
*Обязательно провести копирование документа для службы безопасности."
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_07
+ name = "Форма NT-SEC-07"
+ id = "NT-SEC-07"
+ altername = "Лицензия на использование канала связи и владение дополнительным оборудованием"
+ category = "Служба безопасности"
+ text = "Лицензия Имя обладателя лицензии:
Должность обладателя лицензии:
Зарегистрированный канал связи:
Перечень зарегистрированной экипировки:
Подписи и штампы
Время:
Подпись заявителя:
Подпись главы персонала:
Подпись главы службы безопасности:
*Обязательно провести копирование документа для главы персонала, оригинал документа должен быть выдан обладателю лицензии.
*Обязательно провести копирование документа для службы безопасности.
*Данная форма документа, обязательно должна подтверждаться печатью ответственного лица. В случае наличия опечаток и отсутствия подписей или печатей, лицензия будет являться недействительной."
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_SEC_08
+ name = "Форма NT-SEC-08"
+ id = "NT-SEC-08"
+ altername = "Лицензирование вооружения и экипировки для исполнения деятельности"
+ category = "Служба безопасности"
+ text = "Лицензия
Имя обладателя лицензии: Должность обладателя лицензии: Перечень зарегистрированного вооружения: Перечень зарегистрированной экипировки:
Подписи и штампы
Время: Подпись обладателя лицензии: Подпись главы службы безопасности:
*Данная форма документа, обязательно должна подтверждаться печатью ответственного лица. В случае наличия опечаток и отсутствия подписей или печатей, лицензия будет является недействительной. Обязательно провести копирование документа для службы безопасности, оригинал документа должен быть выдан обладателю лицензии. В случае несоответствия должности обладателя лицензии, можно приступить к процедуре аннулирования лицензии и изъятию вооружения, экипировки. "
+ footer = footer_confidential
+
+// Юридический отдел
+/datum/bureaucratic_form/NT_LD_00
+ name = "Форма NT-LD-00"
+ id = "NT-LD-00"
+ altername = "Бланк заявления"
+ category = "Юридический отдел"
+ text = "Основная информация
Имя заявителя: Полностью и без ошибок |
| Номер аккаунта заявителя: Эта информация есть в ваших заметках |
| Текущая должность: Указано на ID карте |
|
Заявление
Подписи и штампы
Время: |
| Подпись заявителя: |
| Подпись уполномоченного сотрудника: |
| "
+ footer = footer_signstamp
+
+/datum/bureaucratic_form/NT_LD_01
+ name = "Форма NT-LD-01"
+ id = "NT-LD-01"
+ altername = "Судебный приговор"
+ category = "Юридический отдел"
+ notice = "Данный документ является законным решением суда. Пожалуйста внимательно прочитайте его и следуйте предписаниям, указанные в нем."
+ text = "Дело
Имя обвинителя: Полностью и без ошибок |
| Имя обвиняемого: Полностью и без ошибок |
|
Приговор
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_LD_02
+ name = "Форма NT-LD-02"
+ id = "NT-LD-02"
+ altername = "Смертный приговор"
+ category = "Юридический отдел"
+ notice = "Любой смертный приговор, выданный человеком, званием младше, чем капитан, является не действительным, и все казни, действующие от этого приговора являются незаконными. Любой, кто незаконно привел в исполнение смертный приговор действую согласно ложному ордену виновен в убийстве первой степени, и должен быть приговорен минимум к пожизненному заключению и максимум к кибернизации. Этот документ или его факс-копия являются Приговором, который может оспорить только Магистрат или Дивизией защиты активов Nanotrasen (далее именуемой «Компанией»)"
+ text = "Дело Принимая во внимание, что (далее именуемый \"подсудимый\"), сознательно совершил преступления статей Космического закона (далее указаны как \"преступления\"), а именно: , суд приговаривает подсудимого к смертной казни через .
Приговор должен быть приведен в исполнение в течение 15 минут после получения данного приказа. Вещи подсудимого, включая ID-карту, ПДА, униформу и рюкзак, должны быть сохранены и переданы соответствующем органам (ID-карту передать главе персонала или капитану для уничтожения), возвращены в соответсвующий отдел или сложены в хранилище улик. Любая контрабанда должна немедленно помещена в хранилище улик. Любую контрабанду запрещено использовать защитой активов или другими персонами, представляющих компанию или её активы и цели, кроме сотрудников отдела исследований и развития.
Тело подсудимого должно быть помещено в морг и забальзамировано, только если данное действие не будет нести опасность станции, активам компании или её имуществу. Останки подсудимого должны быть собраны и подготовлены к доставке к близлежащему административному центру компании, всё имущество и активы должны быть переданы семье подсудимого после окончания смены.
Слава Nanotrasen!
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_LD_03
+ name = "Форма NT-LD-03"
+ id = "NT-LD-03"
+ altername = "Заявление о нарушении СРП членом экипажа"
+ category = "Юридический отдел"
+ text = " ⠀⠀⠀Я, , в должности – , заявляю, что член экипажа – , в должности , нарушил один (или несколько) пунктов из Стандартных Рабочих Процедур, а именно:
Примерное время нарушения:
Подписи и штампы Подпись заявителя: Подпись принимающего: Время принятия заявления:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче заявителю. *После вынесения решения в отношении правонарушителя, желательно сообщить о решении заявителю. "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_LD_04
+ name = "Форма NT-LD-04"
+ id = "NT-LD-04"
+ altername = "Заявление о нарушении СРП одним из отделов"
+ category = "Юридический отдел"
+ text = " ⠀⠀⠀Я, , в должности – , заявляю, что сотрудники в отделении , нарушили один (или несколько) пунктов из Стандартных Рабочих Процедур, а именно:
Примерное время нарушения: Подпись заявителя:
Подписи и штампы Подпись принимающего: Время принятия заявления:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче заявителю. *После вынесения решения в отношении правонарушителей, желательно сообщить о решении заявителю. "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_LD_05
+ name = "Форма NT-LD-05"
+ id = "NT-LD-05"
+ altername = "Отчет агента внутренних дел"
+ category = "Юридический отдел"
+ text = "ᅠᅠЯ, , Как агент внутренних дел, сообщаю:
Подписи и штампы Подпись АВД: Подпись уполномоченного: Время принятия отчета:
*Данный документ подлежит ксерокопированию, для сохранения в архиве уполномоченных лиц, и выдаче агенту. *Данный документ может содержать нарушения, неправильность выполнения работы, невыполнение правил/сводов/законов/СРП "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_LD_06
+ name = "Форма NT-LD-06"
+ id = "NT-LD-06"
+ altername = "Бланк жалоб АВД"
+ category = "Юридический отдел"
+ text = " Заявление
Заявитель: Укажите своё полное имя, должность и номер акаунта. Предмет жалобы: Укажите на что/кого вы жалуетесь. Обстоятельства: Укажите подробные обстоятельства произошедшего.
Подписи и штампы Подпись: Ваша подпись. Жалоба рассмотрена: Имя и фамилия рассмотревшего.
*Обязательно провести копирование документа для агента внутренних дел, оригинал документа должен быть приложен к отчету о расследовании. Копия документа должна быть сохранена в картотеке офиса агента внутренних дел.
*Обязательно донести жалобу до главы отдела, который отвечает за данного сотрудника, если таковой имеется. Если главы отдела нет на смене или он отсуствует по какой то причине, жалобу следует донести до вышестоящего сотрудника станции.
*Если жалоба была написана на главу отдела, следует донести жалобу до вышестоящего сотрудника станции.
*Глава отдела, которому была донесена жалоба, обязан провести беседу с указаным в жалобе сотрудником станции. В зависимости от тяжести проступка, глава отдела имеет право подать приказ об увольнении."
+ footer = footer_confidential
+
+// Центральное командование
+
+/datum/bureaucratic_form/NT_COM_00
+ name = "Форма NT-COM-00"
+ id = "NT-COM-00"
+ altername = "Общая форма ЦК"
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = "\[small\]Станция — \[b\]Центральное командование\[/b\]\[br\]Год: 2567\[br\]Время: \[time\]\[/small\]\[br\]\[i\]\[large\]\[b\]\[field\] \[b\]\[/large\]\[/i\]\[/grid\]\[hr\]\[center\]Приветствую экипаж и руководство \[station\]!\[/center\]\[br\]\[br\]\[field\]\[br\]\[small\]\[i\]\[br\]Подпись: \[sign\]\[/i\], в должности: \[i\]\[field\].\[/i\]\[/small\]"
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_COM_01
+ name = "Форма NT-COM-01"
+ id = "NT-COM-01"
+ altername = "Запрос отчёта общего состояния станции"
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = "Запрос Уполномоченный офицер, , в должности , запрашивает сведения об общем состоянии станции.
Ответ
Общее состояние станции: |
| Криминальный статус: |
|
Повышений: |
| Понижений: |
| Увольнений: |
|
Раненные: |
| Пропавшие: |
| Скончавшиеся: |
|
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_COM_02
+ name = "Форма NT-COM-02"
+ id = "NT-COM-02"
+ altername = "Запрос отчёта состояния трудовых активов станции"
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = "Запрос Уполномоченный офицер, , в должности , запрашивает сведения о состоянии трудовых активов станции.
Ответ
Количество сотрудников: |
| Количество гражданских: |
| Количество киборгов: |
| Количество ИИ: |
|
Заявлений о приёме на работу: |
| Заявлений на смену должности: |
| Приказов на смену должности: |
| Заявлений об увольнении: |
| Приказов об увольнении: |
| Заявлений на выдачу новой ID карты: |
| Заявлений на дополнительный доступ: |
|
Медианный уровень кваллификации смены: |
| Уровень взаимодействия отделов: |
| Самый продуктивный отдел смены: |
|
Приложите все имеющиеся документы: | NT-HR-00
| | NT-HR-01
| | NT-HR-02
| | NT-HR-12
| | NT-HR-03
| | NT-HR-13
| | NT-HR-04
| | NT-HR-05
|
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_COM_03
+ name = "Форма NT-COM-03"
+ id = "NT-COM-03"
+ altername = "Запрос отчёта криминального статуса станции"
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = "Запрос\
+ Уполномоченный офицер, , в должности , запрашивает сведения о криминальном статусе станции.\
+
Ответ
\
+ Текущий статус угрозы: |
| Количество офицеров в отделе: |
| Количество раненных офицеров: |
| Количество скончавшихся офицеров: |
| Количество серъёзных инцидентов: |
| Количество незначительных инцидентов: |
| Количество раскрытых дел: |
| Количество арестованных: |
| Количество сбежавших: |
|
Приложите все имеющиеся документы: | NT-SEC-01
| | NT-SEC-11
| | NT-SEC-21
| | NT-SEC-02
| | Лог камер заключения
|
Подписи и штампы
Время: |
| Подпись уполномоченного лица: |
| Должность уполномоченного лица: |
| "
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_COM_04
+ name = "Форма NT-COM-04"
+ id = "NT-COM-04"
+ altername = "Запрос отчёта здравоохранения станции"
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = ""
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_COM_05
+ name = "Форма NT-COM-05"
+ id = "NT-COM-05"
+ altername = "Запрос отчёта научно-технического прогресса станции"
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = ""
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_COM_06
+ name = "Форма NT-COM-06"
+ id = "NT-COM-06"
+ altername = "Запрос отчёта инженерного обеспечения станции"
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = ""
+ footer = footer_confidential
+
+/datum/bureaucratic_form/NT_COM_07
+ name = "Форма NT-COM-07"
+ id = "NT-COM-07"
+ altername = "Запрос отчёта статуса снабжения станции "
+ category = "Центральное командование"
+ from = "Административная станция Nanotrasen "Trurl""
+ notice = "Перед заполнением прочтите от начала до конца | Высокий приоритет"
+ confidential = TRUE
+ req_access = ACCESS_CENT_GENERAL
+ text = ""
+ footer = footer_confidential
diff --git a/modular_ss220/bureaucracy/code/paper.dm b/modular_ss220/bureaucracy/code/paper.dm
new file mode 100644
index 000000000000..093557d90d32
--- /dev/null
+++ b/modular_ss220/bureaucracy/code/paper.dm
@@ -0,0 +1,23 @@
+/obj/item/paper
+ var/paper_width_big = 600
+ var/paper_height_big = 700
+ var/small_paper_cap = 1024
+ var/force_big = FALSE
+
+/obj/item/paper/updateinfolinks()
+ . = ..()
+ update_size()
+
+/obj/item/paper/proc/update_size()
+ if(force_big || length(info) > small_paper_cap)
+ become_big()
+ else
+ reset_size()
+
+/obj/item/paper/proc/become_big()
+ paper_width = paper_width_big
+ paper_height = paper_height_big
+
+/obj/item/paper/proc/reset_size()
+ paper_width = initial(paper_width)
+ paper_height = initial(paper_height)
diff --git a/modular_ss220/bureaucracy/code/photocopier.dm b/modular_ss220/bureaucracy/code/photocopier.dm
new file mode 100644
index 000000000000..4392c1933085
--- /dev/null
+++ b/modular_ss220/bureaucracy/code/photocopier.dm
@@ -0,0 +1,113 @@
+/obj/machinery/photocopier
+ /// Selected form's category
+ var/category = ""
+ /// Selected form's id
+ var/form_id = ""
+ /// List of available forms
+ var/list/forms
+ /// Selected form's datum
+ var/datum/bureaucratic_form/form
+ /// Printing sound
+ var/print_sound = 'sound/goonstation/machines/printer_dotmatrix.ogg'
+
+/obj/machinery/photocopier/Initialize(mapload)
+ . = ..()
+ forms = new
+
+/obj/machinery/photocopier/attack_ai(mob/user)
+ add_hiddenprint(user)
+ parse_forms(user)
+ ui_interact(user)
+ return attack_hand(user)
+
+/obj/machinery/photocopier/attack_ghost(mob/user)
+ ui_interact(user)
+ return attack_hand(user)
+
+/obj/machinery/photocopier/attack_hand(mob/user)
+ if(..())
+ return TRUE
+
+ user.set_machine(src)
+ parse_forms(user)
+ ui_interact(user)
+
+/obj/machinery/photocopier/ui_act(action, list/params)
+ if(..())
+ return
+
+ switch(action)
+ if("print_form")
+ for(var/i in 1 to copies)
+ if(toner <= 0)
+ break
+ print_form(form)
+ use_power(active_power_consumption)
+ sleep(15)
+ . = TRUE
+ if("choose_form")
+ form = GLOB.bureaucratic_forms[params["path"]]
+ form_id = params["id"]
+ . = TRUE
+ if("choose_category")
+ category = params["category"]
+ . = TRUE
+ if("aipic")
+ aipic()
+ . = TRUE
+
+/obj/machinery/photocopier/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "Photocopier220", name, 650, 635, master_ui, state)
+ ui.open()
+
+/obj/machinery/photocopier/ui_data(mob/user)
+ if(!length(forms))
+ parse_forms(user)
+
+ var/list/data = list()
+
+ data["isAI"] = issilicon(user)
+ data["copynumber"] = copies
+ data["toner"] = toner
+ data["copyitem"] = (copyitem ? copyitem.name : null)
+ data["folder"] = (folder ? folder.name : null)
+ data["mob"] = (copymob ? copymob.name : null)
+ data["files"] = list()
+ data["form"] = form
+ data["category"] = category
+ data["form_id"] = form_id
+ data["forms"] = forms
+
+ if(LAZYLEN(saved_documents))
+ for(var/obj/item/O in saved_documents)
+ var/list/document_data = list(
+ name = O.name,
+ uid = O.UID()
+ )
+ data["files"] += list(document_data)
+ return data
+
+/obj/machinery/photocopier/proc/parse_forms(mob/user)
+ var/list/access = user.get_access()
+ forms.Cut()
+ for(var/path in GLOB.bureaucratic_forms)
+ var/datum/bureaucratic_form/F = GLOB.bureaucratic_forms[path]
+ var/req_access = F.req_access
+ if(req_access && !(req_access in access))
+ continue
+ var/form[0]
+ form["path"] = F.type
+ form["id"] = F.id
+ form["altername"] = F.altername
+ form["category"] = F.category
+ forms.Add(list(form))
+
+/obj/machinery/photocopier/proc/print_form(datum/bureaucratic_form/form)
+ playsound(loc, print_sound, 25, TRUE)
+ toner--
+ if(toner <= 0)
+ visible_message("На [src] мигает красная лампочка. Похоже, закончился тонер.")
+ var/obj/item/paper/paper = new(loc)
+ form.apply_to_paper(paper, usr)
diff --git a/modular_ss220/camera_nanomap/README.md b/modular_ss220/camera_nanomap/README.md
new file mode 100644
index 000000000000..a59c858b4fb3
--- /dev/null
+++ b/modular_ss220/camera_nanomap/README.md
@@ -0,0 +1,7 @@
+В этом модуле, для добавления наномапы в камеры, были затронуты НЕ модульно следующие файлы:
+"NanoMap.js"
+"NanoMap.scss"
+"CameraConsole.scss"
+"ByondUI.js"
+
+К сожалению, иной путь мог создать больше проблем в будущем чем такой топорный.
diff --git a/modular_ss220/camera_nanomap/camera.dm b/modular_ss220/camera_nanomap/camera.dm
new file mode 100644
index 000000000000..1d58dd99e9d4
--- /dev/null
+++ b/modular_ss220/camera_nanomap/camera.dm
@@ -0,0 +1,4 @@
+/datum/modpack/camera_nanomap
+ name = "Карта в терминале камер"
+ desc = "В названии всё сказано"
+ author = "Aylong220, RV666"
diff --git a/modular_ss220/camera_nanomap/camera.dme b/modular_ss220/camera_nanomap/camera.dme
new file mode 100644
index 000000000000..6c5dfc2f4a7c
--- /dev/null
+++ b/modular_ss220/camera_nanomap/camera.dme
@@ -0,0 +1,3 @@
+#include "camera.dm"
+
+#include "code/camera.dm"
diff --git a/modular_ss220/camera_nanomap/code/camera.dm b/modular_ss220/camera_nanomap/code/camera.dm
new file mode 100644
index 000000000000..ef291d602cd9
--- /dev/null
+++ b/modular_ss220/camera_nanomap/code/camera.dm
@@ -0,0 +1,54 @@
+/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ // Update UI
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ // Show static if can't use the camera
+ if(!active_camera?.can_use())
+ show_camera_static()
+ if(!ui)
+ var/user_uid = user.UID()
+ var/is_living = isliving(user)
+ // Ghosts shouldn't count towards concurrent users, which produces
+ // an audible terminal_on click.
+ if(is_living)
+ watchers += user_uid
+ // Turn on the console
+ if(length(watchers) == 1 && is_living)
+ if(!silent_console)
+ playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE)
+ use_power(active_power_consumption)
+ // Register map objects
+ user.client.register_map_obj(cam_screen)
+ for(var/plane in cam_plane_masters)
+ user.client.register_map_obj(plane)
+ user.client.register_map_obj(cam_background)
+ // Open UI
+ ui = new(user, src, ui_key, "CameraConsole220", name, 1170, 755, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/security/ui_data()
+ var/list/data = list()
+ data["network"] = network
+ data["activeCamera"] = null
+ if(active_camera)
+ data["activeCamera"] = list(
+ name = active_camera.c_tag,
+ status = active_camera.status,
+ )
+ var/list/cameras = get_available_cameras()
+ data["cameras"] = list()
+ for(var/i in cameras)
+ var/obj/machinery/camera/C = cameras[i]
+ data["cameras"] += list(list(
+ name = C.c_tag,
+ x = C.x,
+ y = C.y,
+ z = C.z,
+ status = C.status
+ ))
+ return data
+
+/obj/machinery/computer/security/ui_static_data()
+ var/list/data = list()
+ data["mapRef"] = map_name
+ data["stationLevel"] = level_name_to_num(MAIN_STATION)
+ return data
diff --git a/modular_ss220/chat_badges/_chat_badges.dm b/modular_ss220/chat_badges/_chat_badges.dm
new file mode 100644
index 000000000000..a343b6d9bda4
--- /dev/null
+++ b/modular_ss220/chat_badges/_chat_badges.dm
@@ -0,0 +1,4 @@
+/datum/modpack/chat_badges
+ name = "Chat badges"
+ desc = "Добавляет иконки в OOC для различных ролей"
+ author = "furior"
diff --git a/modular_ss220/chat_badges/_chat_badges.dme b/modular_ss220/chat_badges/_chat_badges.dme
new file mode 100644
index 000000000000..d6350942a883
--- /dev/null
+++ b/modular_ss220/chat_badges/_chat_badges.dme
@@ -0,0 +1,3 @@
+#include "_chat_badges.dm"
+
+#include "code/badges.dm"
diff --git a/modular_ss220/chat_badges/code/badges.dm b/modular_ss220/chat_badges/code/badges.dm
new file mode 100644
index 000000000000..10e3f4fe721a
--- /dev/null
+++ b/modular_ss220/chat_badges/code/badges.dm
@@ -0,0 +1,44 @@
+#define CHAT_BADGES_DMI 'modular_ss220/chat_badges/icons/chatbadges.dmi'
+
+/client/proc/get_ooc_badged_name()
+ . = key
+ if(donator_level && (prefs.toggles & PREFTOGGLE_DONATOR_PUBLIC))
+ var/icon/donator = icon(CHAT_BADGES_DMI, donator_level > 3 ? "Trusted" : "Paradise")
+ . = "[bicon(donator)][.]"
+
+ if(prefs.unlock_content)
+ if(prefs.toggles & PREFTOGGLE_MEMBER_PUBLIC)
+ var/icon/palm = icon(CHAT_BADGES_DMI, "Trusted")
+ . = "[bicon(palm)][.]"
+
+ if(!holder)
+ return
+
+ // Config disallows using Russian so this is the way
+ var/rank
+ switch(holder.rank)
+ if("Хост")
+ rank = "Host"
+ if("Ведущий Разработчик")
+ rank = "HeadDeveloper"
+ if("Старший Администратор")
+ rank = "HeadAdmin"
+ if("Банда")
+ rank = "Streamer"
+ if("Админ")
+ rank = "GameAdmin"
+ if("Триал Админ")
+ rank = "TrialAdmin"
+ if("Ментор")
+ rank = "Mentor"
+ if("Разработчик")
+ rank = "Developer"
+ if("Маппер")
+ rank = "Mapper"
+ if("Спрайтер")
+ rank = "Spriceter"
+
+ var/icon/rank_badge = icon(CHAT_BADGES_DMI, rank)
+ . = "[bicon(rank_badge)][.]"
+
+#undef CHAT_BADGES_DMI
diff --git a/modular_ss220/chat_badges/icons/chatbadges.dmi b/modular_ss220/chat_badges/icons/chatbadges.dmi
new file mode 100644
index 000000000000..b4ae6ba65827
Binary files /dev/null and b/modular_ss220/chat_badges/icons/chatbadges.dmi differ
diff --git a/modular_ss220/clothing/_clothing.dm b/modular_ss220/clothing/_clothing.dm
new file mode 100644
index 000000000000..0d6f0bb89547
--- /dev/null
+++ b/modular_ss220/clothing/_clothing.dm
@@ -0,0 +1,4 @@
+/datum/modpack/clothing
+ name = "Одежда"
+ desc = "Всё для модного приговора."
+ author = "Aylong220"
diff --git a/modular_ss220/clothing/_clothing.dme b/modular_ss220/clothing/_clothing.dme
new file mode 100644
index 000000000000..8a95fc46b2e7
--- /dev/null
+++ b/modular_ss220/clothing/_clothing.dme
@@ -0,0 +1,9 @@
+#include "_clothing.dm"
+
+#include "code/suits.dm"
+#include "code/shoes.dm"
+#include "code/gloves.dm"
+#include "code/helmet.dm"
+#include "code/under.dm"
+#include "code/cloaks.dm"
+#include "code/garment_bag.dm"
diff --git a/modular_ss220/clothing/code/cloaks.dm b/modular_ss220/clothing/code/cloaks.dm
new file mode 100644
index 000000000000..18b5c130f63f
--- /dev/null
+++ b/modular_ss220/clothing/code/cloaks.dm
@@ -0,0 +1,11 @@
+/obj/item/clothing/suit/mantle/armor/captain/black
+ name = "чёрная капитанская мантия"
+ desc = "Носится верховным лидером станции NSS Cyberiad."
+ icon = 'modular_ss220/clothing/icons/object/cloaks.dmi'
+ icon_state = "capcloak_black"
+ icon_override = 'modular_ss220/clothing/icons/mob/cloaks.dmi'
+ item_state = "capcloak_black"
+
+/obj/item/clothing/suit/mantle/armor/captain_black/Initialize(mapload)
+ . = ..()
+ desc = "Носится верховным лидером станции [station_name()]."
diff --git a/modular_ss220/clothing/code/garment_bag.dm b/modular_ss220/clothing/code/garment_bag.dm
new file mode 100644
index 000000000000..6caad62d50d5
--- /dev/null
+++ b/modular_ss220/clothing/code/garment_bag.dm
@@ -0,0 +1,4 @@
+/obj/item/storage/bag/garment/captain/populate_contents()
+ . = ..()
+ new /obj/item/clothing/head/caphat/beret_black(src)
+ new /obj/item/clothing/suit/mantle/armor/captain_black(src)
diff --git a/modular_ss220/clothing/code/gloves.dm b/modular_ss220/clothing/code/gloves.dm
new file mode 100644
index 000000000000..888291353e30
--- /dev/null
+++ b/modular_ss220/clothing/code/gloves.dm
@@ -0,0 +1,7 @@
+/obj/item/clothing/gloves/fingerless/biker_gloves
+ name = "байкерские перчатки"
+ desc = "Обычные черные перчатки с черепом."
+ icon = 'modular_ss220/clothing/icons/object/gloves.dmi'
+ icon_state = "bike_gloves"
+ icon_override = 'modular_ss220/clothing/icons/mob/hands.dmi'
+ item_state = "bike_gloves"
diff --git a/modular_ss220/clothing/code/helmet.dm b/modular_ss220/clothing/code/helmet.dm
new file mode 100644
index 000000000000..d1ec5e848e05
--- /dev/null
+++ b/modular_ss220/clothing/code/helmet.dm
@@ -0,0 +1,35 @@
+/obj/item/clothing/head/helmet/bike_helmet
+ name = "байкерский шлем"
+ desc = "Крутой шлем."
+ icon = 'modular_ss220/clothing/icons/object/hats.dmi'
+ icon_state = "bike_helmet"
+ icon_override = 'modular_ss220/clothing/icons/mob/hats.dmi'
+ item_state = "bike_helmet"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ toggle_message = "Вы опустили защитное стекло"
+ alt_toggle_message = "Вы подняли защитное стекло"
+ actions_types = list(/datum/action/item_action/toggle_helmet_mode)
+ can_toggle = TRUE
+ toggle_sound = 'sound/weapons/tap.ogg'
+ dog_fashion = null
+ sprite_sheets = list(
+ "Drask" = 'modular_ss220/clothing/icons/mob/species/drask/helmet.dmi',
+ "Skrell" = 'modular_ss220/clothing/icons/mob/species/skrell/helmet.dmi',
+ "Tajaran" = 'modular_ss220/clothing/icons/mob/species/tajaran/helmet.dmi',
+ "Unathi" = 'modular_ss220/clothing/icons/mob/species/unathi/helmet.dmi',
+ "Vox" = 'modular_ss220/clothing/icons/mob/species/vox/helmet.dmi',
+ "Vulpkanin" = 'modular_ss220/clothing/icons/mob/species/vulpkanin/helmet.dmi',
+ )
+
+/obj/item/clothing/head/helmet/bike_helmet/replica
+ desc = "Крутой шлем. На вид хлипкий..."
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+
+/obj/item/clothing/head/caphat/beret_black
+ name = "чёрный капитанский берет"
+ desc = "Хорошо быть королём."
+ icon = 'modular_ss220/clothing/icons/object/hats.dmi'
+ icon_state = "cap_beret_black"
+ icon_override = 'modular_ss220/clothing/icons/mob/hats.dmi'
+ item_state = "cap_beret_black"
diff --git a/modular_ss220/clothing/code/shoes.dm b/modular_ss220/clothing/code/shoes.dm
new file mode 100644
index 000000000000..9538dd2be850
--- /dev/null
+++ b/modular_ss220/clothing/code/shoes.dm
@@ -0,0 +1,68 @@
+/datum/action/item_action/change_color
+ name = "Change color"
+
+/obj/item/clothing/shoes/black/neon
+ name = "неоновые кросовки"
+ desc = "Пара чёрных кросовок с светодиодными вставками."
+ icon = 'modular_ss220/clothing/icons/object/shoes.dmi'
+ icon_state = "neon"
+ icon_override = 'modular_ss220/clothing/icons/mob/shoes.dmi'
+ item_color = "neon"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ actions_types = list(/datum/action/item_action/toggle_light, /datum/action/item_action/change_color)
+ dyeable = FALSE
+ color = null
+ var/glow_active = FALSE
+ var/brightness_on = 2
+
+/obj/item/clothing/shoes/black/neon/attack_self(mob/living/user as mob)
+ var/choice = input(user,"Neon shoes options") in list("Turn glow","Change Color")
+ switch(choice)
+ if("Turn glow")
+ turn_glow()
+ if("Change color")
+ change_color()
+
+/obj/item/clothing/shoes/black/neon/update_icon_state()
+ . = ..()
+
+/obj/item/clothing/shoes/black/neon/proc/turn_glow()
+ if(!glow_active)
+ set_light(brightness_on)
+ var/mutable_appearance/neon_overlay = mutable_appearance('modular_ss220/clothing/icons/mob/shoes.dmi',"neon_overlay")
+ neon_overlay.color = color
+ add_overlay(neon_overlay)
+ glow_active = TRUE
+ else
+ set_light(0)
+ cut_overlays()
+ glow_active = FALSE
+ update_icon_state()
+
+/obj/item/clothing/shoes/black/neon/proc/change_color(mob/living/user as mob)
+ var/temp = input(usr, "Пожалуйста, выберите цвет.", "Цвет кросовок") as color
+ color = temp
+ light_color = temp
+ update_icon_state()
+
+/obj/item/clothing/shoes/black/neon/ui_action_click(mob/user, actiontype)
+ if(actiontype == /datum/action/item_action/change_color)
+ change_color()
+ else if(actiontype == /datum/action/item_action/toggle_light)
+ turn_glow()
+
+/obj/item/clothing/shoes/shark
+ name = "акульи тапочки"
+ desc = "Эти тапочки сделаны из акульей кожи, или нет?"
+ icon = 'modular_ss220/clothing/icons/object/shoes.dmi'
+ icon_state = "shark"
+ icon_override = 'modular_ss220/clothing/icons/mob/shoes.dmi'
+ item_state = "shark"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+
+/obj/item/clothing/shoes/shark/light
+ name = "светло-голубые акульи тапочки"
+ icon_state = "shark_light"
+ item_state = "shark_light"
diff --git a/modular_ss220/clothing/code/suits.dm b/modular_ss220/clothing/code/suits.dm
new file mode 100644
index 000000000000..44f1a3a63e2a
--- /dev/null
+++ b/modular_ss220/clothing/code/suits.dm
@@ -0,0 +1,82 @@
+/obj/item/clothing/suit/v_jacket
+ name = "куртка V"
+ desc = "Куртка так называемого V."
+ icon = 'modular_ss220/clothing/icons/object/suits.dmi'
+ icon_state = "v_jacket"
+ icon_override = 'modular_ss220/clothing/icons/mob/suits.dmi'
+ item_state = "v_jacket"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ flags_inv = HIDEJUMPSUIT
+
+/obj/item/clothing/suit/takemura_jacket
+ name = "куртка Такэмуры"
+ desc = "Куртка так называемого Такэмуры."
+ icon = 'modular_ss220/clothing/icons/object/suits.dmi'
+ icon_state = "takemura_jacket"
+ icon_override = 'modular_ss220/clothing/icons/mob/suits.dmi'
+ item_state = "takemura_jacket"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ flags_inv = HIDEJUMPSUIT
+
+/obj/item/clothing/suit/katarina_jacket
+ name = "куртка Катарины"
+ desc = "Куртка так называемой Катарины."
+ icon = 'modular_ss220/clothing/icons/object/suits.dmi'
+ icon_state = "katarina_jacket"
+ icon_override = 'modular_ss220/clothing/icons/mob/suits.dmi'
+ item_state = "katarina_jacket"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ flags_inv = HIDEJUMPSUIT
+
+/obj/item/clothing/suit/katarina_cyberjacket
+ name = "киберкуртка Катарины"
+ desc = "Кибер-куртка так называемой Катарины."
+ icon = 'modular_ss220/clothing/icons/object/suits.dmi'
+ icon_state = "katarina_cyberjacket"
+ icon_override = 'modular_ss220/clothing/icons/mob/suits.dmi'
+ item_state = "katarina_cyberjacket"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ flags_inv = HIDEJUMPSUIT
+
+/obj/item/clothing/suit/hooded/shark_costume
+ name = "костюм акулы"
+ desc = "Костюм из 'синтетической' кожи акулы, пахнет."
+ icon = 'modular_ss220/clothing/icons/object/suits.dmi'
+ icon_state = "shark_casual"
+ icon_override = 'modular_ss220/clothing/icons/mob/suits.dmi'
+ item_state = "shark_casual"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS
+ cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ allowed = list(/obj/item/tank/internals/emergency_oxygen)
+ hoodtype = /obj/item/clothing/head/hooded/shark_hood
+
+/obj/item/clothing/head/hooded/shark_hood
+ name = "акулий капюшон"
+ desc = "Капюшон, прикрепленный к костюму акулы."
+ icon = 'modular_ss220/clothing/icons/object/hats.dmi'
+ icon_state = "shark_casual"
+ icon_override = 'modular_ss220/clothing/icons/mob/hats.dmi'
+ item_state = "shark_casual"
+ body_parts_covered = HEAD
+ cold_protection = HEAD
+ min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
+ flags = BLOCKHAIR
+ flags_inv = HIDEEARS
+
+/obj/item/clothing/suit/hooded/shark_costume/light
+ name = "светло-голубой костюм акулы"
+ icon_state = "shark_casual_light"
+ item_state = "shark_casual_light"
+ hoodtype = /obj/item/clothing/head/hooded/shark_hood/light
+
+/obj/item/clothing/head/hooded/shark_hood/light
+ name = "светло-голубой акулий капюшон"
+ icon_state = "shark_casual_light"
+ item_state = "shark_casual_light"
diff --git a/modular_ss220/clothing/code/under.dm b/modular_ss220/clothing/code/under.dm
new file mode 100644
index 000000000000..7f7c0e873af4
--- /dev/null
+++ b/modular_ss220/clothing/code/under.dm
@@ -0,0 +1,23 @@
+/obj/item/clothing/under/costume/katarina_cybersuit
+ name = "кибер-костюм Катарины"
+ desc = "Кибер-костюм так называемой Катарины."
+ icon = 'modular_ss220/clothing/icons/object/under.dmi'
+ icon_state = "katarina_cybersuit"
+ icon_override = 'modular_ss220/clothing/icons/mob/under.dmi'
+ item_state = "katarina_cybersuit"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ item_color = "katarina_cybersuit"
+ sprite_sheets = null
+
+/obj/item/clothing/under/costume/katarina_suit
+ name = "костюм Катарины"
+ desc = "Костюм так называемой Катарины."
+ icon = 'modular_ss220/clothing/icons/object/under.dmi'
+ icon_state = "katarina_suit"
+ icon_override = 'modular_ss220/clothing/icons/mob/under.dmi'
+ item_state = "katarina_suit"
+ lefthand_file = 'modular_ss220/clothing/icons/inhands/left_hand.dmi'
+ righthand_file = 'modular_ss220/clothing/icons/inhands/right_hand.dmi'
+ item_color = "katarina_suit"
+ sprite_sheets = null
diff --git a/modular_ss220/clothing/icons/inhands/left_hand.dmi b/modular_ss220/clothing/icons/inhands/left_hand.dmi
new file mode 100644
index 000000000000..1e974f6ca47e
Binary files /dev/null and b/modular_ss220/clothing/icons/inhands/left_hand.dmi differ
diff --git a/modular_ss220/clothing/icons/inhands/right_hand.dmi b/modular_ss220/clothing/icons/inhands/right_hand.dmi
new file mode 100644
index 000000000000..5c06d3b713e2
Binary files /dev/null and b/modular_ss220/clothing/icons/inhands/right_hand.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/cloaks.dmi b/modular_ss220/clothing/icons/mob/cloaks.dmi
new file mode 100644
index 000000000000..99933fa26275
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/cloaks.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/hands.dmi b/modular_ss220/clothing/icons/mob/hands.dmi
new file mode 100644
index 000000000000..4b4a10fbf5d7
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/hands.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/hats.dmi b/modular_ss220/clothing/icons/mob/hats.dmi
new file mode 100644
index 000000000000..b098e148d135
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/hats.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/shoes.dmi b/modular_ss220/clothing/icons/mob/shoes.dmi
new file mode 100644
index 000000000000..1bf793fcc972
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/shoes.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/species/drask/helmet.dmi b/modular_ss220/clothing/icons/mob/species/drask/helmet.dmi
new file mode 100644
index 000000000000..f44044f12a67
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/species/drask/helmet.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/species/skrell/helmet.dmi b/modular_ss220/clothing/icons/mob/species/skrell/helmet.dmi
new file mode 100644
index 000000000000..6635a7e9d60f
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/species/skrell/helmet.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/species/tajaran/helmet.dmi b/modular_ss220/clothing/icons/mob/species/tajaran/helmet.dmi
new file mode 100644
index 000000000000..9ad21ade1623
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/species/tajaran/helmet.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/species/unathi/helmet.dmi b/modular_ss220/clothing/icons/mob/species/unathi/helmet.dmi
new file mode 100644
index 000000000000..34ca762e9dc8
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/species/unathi/helmet.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/species/vox/helmet.dmi b/modular_ss220/clothing/icons/mob/species/vox/helmet.dmi
new file mode 100644
index 000000000000..f4aba5a68a3a
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/species/vox/helmet.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/species/vulpkanin/helmet.dmi b/modular_ss220/clothing/icons/mob/species/vulpkanin/helmet.dmi
new file mode 100644
index 000000000000..95b4c135d2c4
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/species/vulpkanin/helmet.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/suits.dmi b/modular_ss220/clothing/icons/mob/suits.dmi
new file mode 100644
index 000000000000..6e6bbff67d39
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/suits.dmi differ
diff --git a/modular_ss220/clothing/icons/mob/under.dmi b/modular_ss220/clothing/icons/mob/under.dmi
new file mode 100644
index 000000000000..e0d1cf7eea33
Binary files /dev/null and b/modular_ss220/clothing/icons/mob/under.dmi differ
diff --git a/modular_ss220/clothing/icons/object/cloaks.dmi b/modular_ss220/clothing/icons/object/cloaks.dmi
new file mode 100644
index 000000000000..e942515ed093
Binary files /dev/null and b/modular_ss220/clothing/icons/object/cloaks.dmi differ
diff --git a/modular_ss220/clothing/icons/object/gloves.dmi b/modular_ss220/clothing/icons/object/gloves.dmi
new file mode 100644
index 000000000000..6ddabb5d7327
Binary files /dev/null and b/modular_ss220/clothing/icons/object/gloves.dmi differ
diff --git a/modular_ss220/clothing/icons/object/hats.dmi b/modular_ss220/clothing/icons/object/hats.dmi
new file mode 100644
index 000000000000..e6615beaf08c
Binary files /dev/null and b/modular_ss220/clothing/icons/object/hats.dmi differ
diff --git a/modular_ss220/clothing/icons/object/shoes.dmi b/modular_ss220/clothing/icons/object/shoes.dmi
new file mode 100644
index 000000000000..f90f83b59f65
Binary files /dev/null and b/modular_ss220/clothing/icons/object/shoes.dmi differ
diff --git a/modular_ss220/clothing/icons/object/suits.dmi b/modular_ss220/clothing/icons/object/suits.dmi
new file mode 100644
index 000000000000..398edcbb19f3
Binary files /dev/null and b/modular_ss220/clothing/icons/object/suits.dmi differ
diff --git a/modular_ss220/clothing/icons/object/under.dmi b/modular_ss220/clothing/icons/object/under.dmi
new file mode 100644
index 000000000000..2e7c7891ac51
Binary files /dev/null and b/modular_ss220/clothing/icons/object/under.dmi differ
diff --git a/modular_ss220/crawl_speed/_crawl_speed.dm b/modular_ss220/crawl_speed/_crawl_speed.dm
new file mode 100644
index 000000000000..6c356c94c464
--- /dev/null
+++ b/modular_ss220/crawl_speed/_crawl_speed.dm
@@ -0,0 +1,4 @@
+/datum/modpack/crawl_speed
+ name = "Скорость ползания"
+ desc = "Ползание накладывает модификатор ходьбы."
+ author = "larentoun"
diff --git a/modular_ss220/crawl_speed/_crawl_speed.dme b/modular_ss220/crawl_speed/_crawl_speed.dme
new file mode 100644
index 000000000000..b81498fc1d27
--- /dev/null
+++ b/modular_ss220/crawl_speed/_crawl_speed.dme
@@ -0,0 +1,5 @@
+#include "_crawl_speed.dm"
+
+#include "code/_crawl_speed_defines.dm"
+#include "code/crawl_speed_mob.dm"
+#include "code/~crawl_speed_defines.dm"
diff --git a/modular_ss220/crawl_speed/code/_crawl_speed_defines.dm b/modular_ss220/crawl_speed/code/_crawl_speed_defines.dm
new file mode 100644
index 000000000000..a5484a523c35
--- /dev/null
+++ b/modular_ss220/crawl_speed/code/_crawl_speed_defines.dm
@@ -0,0 +1,2 @@
+#define CRAWL_SPEED_TRAIT "crawl-speed-trait"
+#define TRAIT_FORCE_WALK_SPEED "force_walk_speed"
diff --git a/modular_ss220/crawl_speed/code/crawl_speed_mob.dm b/modular_ss220/crawl_speed/code/crawl_speed_mob.dm
new file mode 100644
index 000000000000..dcfd2860433b
--- /dev/null
+++ b/modular_ss220/crawl_speed/code/crawl_speed_mob.dm
@@ -0,0 +1,13 @@
+/mob/living/carbon/set_body_position(new_value)
+ . = ..()
+ if(!.)
+ return
+ if(new_value == LYING_DOWN)
+ ADD_TRAIT(src, TRAIT_FORCE_WALK_SPEED, CRAWL_SPEED_TRAIT)
+ else
+ REMOVE_TRAIT(src, TRAIT_FORCE_WALK_SPEED, CRAWL_SPEED_TRAIT)
+
+/mob/living/carbon/movement_delay(ignorewalk)
+ . = ..()
+ if(m_intent != MOVE_INTENT_WALK && HAS_TRAIT(src, TRAIT_FORCE_WALK_SPEED))
+ . += GLOB.configuration.movement.base_walk_speed - GLOB.configuration.movement.base_run_speed
diff --git a/modular_ss220/crawl_speed/code/~crawl_speed_defines.dm b/modular_ss220/crawl_speed/code/~crawl_speed_defines.dm
new file mode 100644
index 000000000000..fa9dabba2795
--- /dev/null
+++ b/modular_ss220/crawl_speed/code/~crawl_speed_defines.dm
@@ -0,0 +1,2 @@
+#undef CRAWL_SPEED_TRAIT
+#undef TRAIT_FORCE_WALK_SPEED
diff --git a/modular_ss220/crit_rework/_crit_rework.dm b/modular_ss220/crit_rework/_crit_rework.dm
new file mode 100644
index 000000000000..49614c3bfae6
--- /dev/null
+++ b/modular_ss220/crit_rework/_crit_rework.dm
@@ -0,0 +1,4 @@
+/datum/modpack/crit_rework
+ name = "Переработка крита"
+ desc = "Если человек в крите, он ползает и не может держать что-либо в руках"
+ author = "larentoun"
diff --git a/modular_ss220/crit_rework/_crit_rework.dme b/modular_ss220/crit_rework/_crit_rework.dme
new file mode 100644
index 000000000000..3ad4c42e62b5
--- /dev/null
+++ b/modular_ss220/crit_rework/_crit_rework.dme
@@ -0,0 +1,4 @@
+#include "_crit_rework.dm"
+
+#include "code/crit_rework_component.dm"
+#include "code/crit_rework_mob.dm"
diff --git a/modular_ss220/crit_rework/code/crit_rework_component.dm b/modular_ss220/crit_rework/code/crit_rework_component.dm
new file mode 100644
index 000000000000..3b1b7774be0d
--- /dev/null
+++ b/modular_ss220/crit_rework/code/crit_rework_component.dm
@@ -0,0 +1,50 @@
+#define OLD_CRIT_TRAIT "old-crit"
+
+/datum/component/softcrit
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+
+/datum/component/softcrit/Initialize(...)
+ . = ..()
+ if(!ishuman(parent))
+ return COMPONENT_INCOMPATIBLE
+ softcrit_entered()
+
+/datum/component/softcrit/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(parent, COMSIG_LIVING_LIFE, PROC_REF(check_health))
+ RegisterSignal(parent, COMSIG_LIVING_HANDLE_MESSAGE_MODE, PROC_REF(force_whisper))
+
+/datum/component/softcrit/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_PARENT_EXAMINE)
+ UnregisterSignal(parent, COMSIG_LIVING_LIFE)
+ UnregisterSignal(parent, COMSIG_LIVING_HANDLE_MESSAGE_MODE)
+
+/datum/component/softcrit/proc/softcrit_entered()
+ SIGNAL_HANDLER
+ ADD_TRAIT(parent, TRAIT_FLOORED, OLD_CRIT_TRAIT)
+ ADD_TRAIT(parent, TRAIT_HANDS_BLOCKED, OLD_CRIT_TRAIT)
+
+/datum/component/softcrit/proc/softcrit_removed()
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(parent, TRAIT_FLOORED, OLD_CRIT_TRAIT)
+ REMOVE_TRAIT(parent, TRAIT_HANDS_BLOCKED, OLD_CRIT_TRAIT)
+ qdel(src)
+ return
+
+/datum/component/softcrit/proc/on_examine(atom/A, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+ var/mob/living/carbon/human/owner = parent
+ if(owner.stat == CONSCIOUS)
+ examine_list += span_warning("[owner] с трудом держится в сознании.\n")
+
+/datum/component/softcrit/proc/check_health()
+ SIGNAL_HANDLER
+ var/mob/living/carbon/human/owner = parent
+ if(owner.health > HEALTH_THRESHOLD_CRIT)
+ softcrit_removed()
+
+/datum/component/softcrit/proc/force_whisper(mob/source, message_mode, list/message_pieces, verb, used_radios)
+ SIGNAL_HANDLER
+ return COMPONENT_FORCE_WHISPER
+
+#undef OLD_CRIT_TRAIT
diff --git a/modular_ss220/crit_rework/code/crit_rework_mob.dm b/modular_ss220/crit_rework/code/crit_rework_mob.dm
new file mode 100644
index 000000000000..cce3430ac4e8
--- /dev/null
+++ b/modular_ss220/crit_rework/code/crit_rework_mob.dm
@@ -0,0 +1,9 @@
+/mob/living/carbon/human/handle_critical_condition()
+ if(status_flags & GODMODE)
+ return FALSE
+ . = ..()
+ if(health <= HEALTH_THRESHOLD_CRIT)
+ AddComponent(/datum/component/softcrit)
+
+/mob/living/carbon/human/check_death_method()
+ return FALSE
diff --git a/modular_ss220/debug/_debug.dm b/modular_ss220/debug/_debug.dm
new file mode 100644
index 000000000000..83a9cbb9044f
--- /dev/null
+++ b/modular_ss220/debug/_debug.dm
@@ -0,0 +1,4 @@
+/datum/modpack/debug
+ name = "Прочее"
+ desc = "Различные инструменты для отладки и щитспавна."
+ author = "furior"
diff --git a/modular_ss220/debug/_debug.dme b/modular_ss220/debug/_debug.dme
new file mode 100644
index 000000000000..040ec14d57ed
--- /dev/null
+++ b/modular_ss220/debug/_debug.dme
@@ -0,0 +1,3 @@
+#include "_debug.dm"
+
+#include "code/icons.dm"
diff --git a/modular_ss220/debug/code/icons.dm b/modular_ss220/debug/code/icons.dm
new file mode 100644
index 000000000000..409c2d41f413
--- /dev/null
+++ b/modular_ss220/debug/code/icons.dm
@@ -0,0 +1,3 @@
+/atom/proc/download_flaticon()
+ var/icon/I = getFlatIcon(src)
+ usr << ftp(I, "[name].png")
diff --git a/modular_ss220/devices/_devices.dm b/modular_ss220/devices/_devices.dm
new file mode 100644
index 000000000000..5d8dfa018c10
--- /dev/null
+++ b/modular_ss220/devices/_devices.dm
@@ -0,0 +1,4 @@
+/datum/modpack/devices
+ name = "Devices modpack"
+ desc = "Пак с различными устройствами."
+ author = "PhantomRU"
diff --git a/modular_ss220/devices/_devices.dme b/modular_ss220/devices/_devices.dme
new file mode 100644
index 000000000000..2aa9f1945e62
--- /dev/null
+++ b/modular_ss220/devices/_devices.dme
@@ -0,0 +1,4 @@
+#include "_devices.dm"
+
+#include "code/items/scanners.dm"
+#include "code/research_designs/equipment_designs.dm"
diff --git a/modular_ss220/devices/code/items/scanners.dm b/modular_ss220/devices/code/items/scanners.dm
new file mode 100644
index 000000000000..d010b9804d07
--- /dev/null
+++ b/modular_ss220/devices/code/items/scanners.dm
@@ -0,0 +1,109 @@
+// translate
+/obj/item/t_scanner
+ name = "T-ray сканнер"
+ desc = "Излучатель и сканер терагерцового излучения, используемый для обнаружения скрытых объектов под полом, таких как кабели и трубы."
+
+// debug
+/obj/item/t_scanner/mod
+ name = "Модификация T-ray сканнера"
+ desc = "Предмодифицированный сканнер, который не должен был попасть в ваши руки. Отнесите его в ближайший научный отдел \
+ \nдля изучения кодерами."
+ icon = 'modular_ss220/devices/icons/device.dmi'
+ icon_state = "t-ray0"
+ origin_tech = "magnets=3;engineering=3"
+ var/scan_range = 3
+ var/pulse_duration = 8
+
+/obj/item/t_scanner/mod/scan()
+ t_ray_scan(loc, pulse_duration, scan_range)
+
+// new scanners
+/obj/item/t_scanner/mod/extended_range
+ name = "Расширенный T-ray сканнер"
+ desc = "Излучатель и сканер терагерцового излучения, используемый для обнаружения скрытых объектов и объектов под полом, таких как кабели и трубы. \
+ \nОбразец с расширенным радиусов воздействия."
+ icon_state = "t-ray-range0"
+ scan_range = 5
+ origin_tech = "magnets=3;engineering=3"
+
+/obj/item/t_scanner/mod/pulse
+ name = "Пульсовой T-ray сканнер"
+ desc = "Излучатель и сканер терагерцового излучения, используемый для обнаружения скрытых объектов и объектов под полом, таких как кабели и трубы. \
+ \nОбразец с продолжительным пульсаром."
+ icon_state = "t-ray-pulse0"
+ pulse_duration = 20
+ origin_tech = "magnets=5;engineering=3"
+
+/obj/item/t_scanner/mod/advanced
+ name = "Продвинутый T-ray сканнер"
+ desc = "Излучатель и сканер терагерцового излучения, используемый для обнаружения скрытых объектов и объектов под полом, таких как кабели и трубы. \
+ \nОбразец с расширенным радиусом воздействия и продолжительным пульсаром."
+ icon_state = "t-ray-advanced0"
+ pulse_duration = 20
+ scan_range = 5
+ origin_tech = "magnets=7;engineering=3"
+
+/obj/item/t_scanner/mod/science
+ name = "Научный T-ray сканнер"
+ desc = "Излучатель и сканер терагерцового излучения, используемый для обнаружения скрытых объектов и объектов под полом, таких как кабели и трубы. \
+ \nНаучный образец сканнера с расширенным радиусом действия и продолжительным пульсаром."
+ icon_state = "t-ray-science0"
+ scan_range = 7
+ pulse_duration = 50
+ origin_tech = "magnets=8;engineering=5"
+ materials = list(MAT_METAL=500)
+
+/obj/item/t_scanner/mod/experimental //a high-risk that cannot be disassembled, since this garbage was invented by, well, you know who.
+ name = "Экспериментальный T-ray сканнер"
+ desc = "Излучатель и сканер терагерцового излучения, используемый для обнаружения скрытых объектов и объектов под полом, таких как кабели и трубы. \
+ \nЭкспериментальный образец сканнера с расширенным радиусом действия и продолжительным пульсаром. \
+ \nСудя по его виду, эта вещь изобретена безумными учеными, взятая буквально с экспериментами. Вы можете представить больное воображение ученого который это сделал? \
+ \nЦенная находка в практическом и научном пользовании. \
+ \nНо её не может изучить даже самый продвинутый разборщик, требуется тщательное исследование."
+ icon_state = "t-ray-experimental0"
+ scan_range = 5
+ pulse_duration = 80
+ origin_tech = null
+ materials = null
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+
+// /datum/theft_objective/experimental
+// name = "experimental T-ray scanner"
+// typepath = /obj/item/t_scanner/mod/experimental
+// protected_jobs = list("Research Director")
+// location_override = "кабинет Директора Исследований"
+
+/obj/item/t_scanner/mod/security
+ name = "Противо-маскировочное ТГц устройство"
+ desc = "Излучатель терагерцевого типа используемый для сканирования области на наличие замаскированных биоорганизмов. Устройство уязвимо для ЭМИ излучения."
+ icon = 'modular_ss220/devices/icons/device.dmi'
+ lefthand_file = 'modular_ss220/devices/icons/inhands/items_lefthand.dmi'
+ righthand_file = 'modular_ss220/devices/icons/inhands/items_righthand.dmi'
+ item_state = "sb_t-ray"
+ icon_state = "sb_t-ray0"
+ scan_range = 4
+ pulse_duration = 15
+ var/was_alerted = FALSE // Protection against spam alerts from this scanner
+ var/burnt = FALSE // Did emp break us?
+ var/datum/effect_system/spark_spread/spark_system //The spark system, used for generating... sparks?
+ origin_tech = "combat=3;magnets=5;biotech=5"
+
+/obj/item/t_scanner/mod/security/Initialize()
+ . = ..()
+ //Sets up a spark system
+ spark_system = new /datum/effect_system/spark_spread
+ spark_system.set_up(5, 0, src)
+ spark_system.attach(src)
+
+/obj/item/t_scanner/mod/security/toggle_on()
+ if(!burnt)
+ on = !on
+ icon_state = copytext(icon_state, 1, length(icon_state))+"[on]"
+ if(on)
+ START_PROCESSING(SSobj, src)
+
+/obj/item/t_scanner/mod/security/emp_act(severity)
+ . = ..()
+ if(prob(25) && !burnt)
+ burnt = TRUE
+ on = FALSE;
diff --git a/modular_ss220/devices/code/research_designs/equipment_designs.dm b/modular_ss220/devices/code/research_designs/equipment_designs.dm
new file mode 100644
index 000000000000..db6db4d45b7f
--- /dev/null
+++ b/modular_ss220/devices/code/research_designs/equipment_designs.dm
@@ -0,0 +1,49 @@
+/datum/design/tray_scanner_range
+ name = "Extended T-ray"
+ desc = "Расширенный по дальности Т-сканнер позволяющий визуально обнаружить скрытые объекты."
+ id = "tray_range"
+ req_tech = list("magnets" = 3, "engineering" = 3)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 500, MAT_DIAMOND = 200)
+ build_path = /obj/item/t_scanner/mod/extended_range
+ category = list("Equipment")
+
+/datum/design/tray_scanner_pulse
+ name = "Pulse T-ray"
+ desc = "Пульсовой Т-сканнер позволяющий гораздо дольше визуально обнаруживать скрытые объекты."
+ id = "tray_pulse"
+ req_tech = list("magnets" = 5, "engineering" = 3)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 500, MAT_DIAMOND = 200)
+ build_path = /obj/item/t_scanner/mod/pulse
+ category = list("Equipment")
+
+/datum/design/tray_scanner_advanced
+ name = "Advanced T-ray"
+ desc = "Расширенный по дальности Т-сканнер, более дольше удерживающий пульсар, позволяющий визуально обнаружить скрытые объекты."
+ id = "tray_advanced"
+ req_tech = list("magnets" = 7, "programming" = 5, "engineering" = 5)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1000, MAT_GLASS = 500, MAT_SILVER = 1000, MAT_DIAMOND = 500)
+ build_path = /obj/item/t_scanner/mod/advanced
+ category = list("Equipment")
+
+/datum/design/tray_scanner_science
+ name = "Science T-ray"
+ desc = "Научный Т-сканнер совмещающий в себя технологии пульсового и расширенного сканнера."
+ id = "tray_science"
+ req_tech = list("magnets" = 8, "programming" = 7, "engineering" = 7) // придется постараться чтобы найти 8-й уровень технологий
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1000, MAT_GLASS = 500, MAT_SILVER = 2000, MAT_DIAMOND = 1500)
+ build_path = /obj/item/t_scanner/mod/science
+ category = list("Equipment")
+
+/datum/design/sec_tray_scanner
+ name = "Security T-ray"
+ desc = "An advance use of a terahertz-ray to find any invisible biological creature nearby."
+ id = "sec_tray"
+ req_tech = list("magnets" = 7, "biotech" = 7, "engineering" = 3)
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_DIAMOND = 500)
+ build_path = /obj/item/t_scanner/mod/security
+ category = list("Equipment")
diff --git a/modular_ss220/devices/icons/device.dmi b/modular_ss220/devices/icons/device.dmi
new file mode 100644
index 000000000000..ebc6cb5eb044
Binary files /dev/null and b/modular_ss220/devices/icons/device.dmi differ
diff --git a/modular_ss220/devices/icons/inhands/items_lefthand.dmi b/modular_ss220/devices/icons/inhands/items_lefthand.dmi
new file mode 100644
index 000000000000..27c998c552d9
Binary files /dev/null and b/modular_ss220/devices/icons/inhands/items_lefthand.dmi differ
diff --git a/modular_ss220/devices/icons/inhands/items_righthand.dmi b/modular_ss220/devices/icons/inhands/items_righthand.dmi
new file mode 100644
index 000000000000..0de909884b39
Binary files /dev/null and b/modular_ss220/devices/icons/inhands/items_righthand.dmi differ
diff --git a/modular_ss220/discord_link/_discord_link.dm b/modular_ss220/discord_link/_discord_link.dm
new file mode 100644
index 000000000000..11f2d627109c
--- /dev/null
+++ b/modular_ss220/discord_link/_discord_link.dm
@@ -0,0 +1,4 @@
+/datum/modpack/discord_link
+ name = "Привязка Дискорд Аккаунта"
+ desc = "Привязка аккаунта и запрет играть без него."
+ author = "furior"
diff --git a/modular_ss220/discord_link/_discord_link.dme b/modular_ss220/discord_link/_discord_link.dme
new file mode 100644
index 000000000000..da6b51d8180b
--- /dev/null
+++ b/modular_ss220/discord_link/_discord_link.dme
@@ -0,0 +1,3 @@
+#include "_discord_link.dm"
+
+#include "code/discord.dm"
diff --git a/modular_ss220/discord_link/code/discord.dm b/modular_ss220/discord_link/code/discord.dm
new file mode 100644
index 000000000000..7210d01ef3bb
--- /dev/null
+++ b/modular_ss220/discord_link/code/discord.dm
@@ -0,0 +1,70 @@
+/datum/preferences
+ var/discord_id
+
+/client/verb/link_discord_account()
+ set name = "Привязка Discord"
+ set category = "Special Verbs"
+ set desc = "Привязать аккаунт Discord для удобного просмотра игровой статистики на нашем Discord-сервере."
+
+ if(!GLOB.configuration.url.discord_url)
+ return
+
+ if(IsGuestKey(key))
+ to_chat(usr, "Гостевой аккаунт не может быть связан.")
+ return
+
+ if(prefs?.discord_id)
+ to_chat(usr, span_darkmblue("Аккаунт Discord уже привязан! Чтобы отвязать используйте команду [span_boldannounce("/отвязать")] в канале #дом-бота в Discord-сообществе!"))
+ return
+
+ var/token = md5("[world.time+rand(1000,1000000)]")
+ if(SSdbcore.IsConnected())
+ var/datum/db_query/query_update_token = SSdbcore.NewQuery("UPDATE discord_links SET one_time_token=:token WHERE ckey =:ckey", list("token" = token, "ckey" = ckey))
+ if(!query_update_token.warn_execute())
+ to_chat(usr, span_warning("Ошибка записи токена в БД! Обратитесь к администрации."))
+ log_debug("link_discord_account: failed db update discord_id for ckey [ckey]")
+ qdel(query_update_token)
+ return
+ qdel(query_update_token)
+ to_chat(usr, span_darkmblue("Аккаунт Discord уже привязан! Чтобы отвязать используйте команду [span_boldannounce("/отвязать")] в канале #дом-бота в Discord-сообществе!"))
+ prefs?.load_preferences(usr)
+
+/mob/new_player/Topic(href, href_list)
+ if(src != usr)
+ return
+
+ if(!client)
+ return
+
+ if(href_list["observe"] || href_list["ready"] || href_list["late_join"])
+ if (GLOB.configuration.database.enabled && !client.prefs.discord_id)
+ to_chat(usr, span_danger("Вам необходимо привязать дискорд-профиль к аккаунту!"))
+ to_chat(usr, span_warning("Нажмите 'Привязка Discord' во вкладке 'Special Verbs' для получения инструкций."))
+ return FALSE
+
+ . = ..()
+
+/datum/preferences/proc/get_discord_id()
+ var/datum/db_query/discord_query = SSdbcore.NewQuery({"SELECT discord_id, valid FROM discord_links WHERE ckey=:ckey"}, list(
+ "ckey" = parent.ckey
+ ))
+
+ if(!discord_query.warn_execute())
+ qdel(discord_query)
+ return FALSE
+
+ while(discord_query.NextRow())
+ var/valid = discord_query.item[2]
+ if(valid)
+ discord_id = discord_query.item[1]
+ break
+
+ qdel(discord_query)
+ return TRUE
+
+/datum/preferences/load_preferences(datum/db_query/query)
+ . = ..()
+ if (!.)
+ return
+
+ return get_discord_id()
diff --git a/modular_ss220/emotes/_emotes.dm b/modular_ss220/emotes/_emotes.dm
new file mode 100644
index 000000000000..e562431422ad
--- /dev/null
+++ b/modular_ss220/emotes/_emotes.dm
@@ -0,0 +1,4 @@
+/datum/modpack/emotes
+ name = "Панель эмоций"
+ desc = "Добавляет панель эмоций в меню"
+ author = "furior"
diff --git a/modular_ss220/emotes/_emotes.dme b/modular_ss220/emotes/_emotes.dme
new file mode 100644
index 000000000000..331ad7a7f728
--- /dev/null
+++ b/modular_ss220/emotes/_emotes.dme
@@ -0,0 +1,7 @@
+#include "_emotes.dm"
+
+#include "code/emote.dm"
+#include "code/emote_verbs.dm"
+#include "code/emote_translations.dm"
+#include "code/species.dm"
+#include "code/racial_emotes.dm"
diff --git a/modular_ss220/emotes/audio/female/choke_female_1.ogg b/modular_ss220/emotes/audio/female/choke_female_1.ogg
new file mode 100644
index 000000000000..f33b775fd34e
Binary files /dev/null and b/modular_ss220/emotes/audio/female/choke_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/choke_female_2.ogg b/modular_ss220/emotes/audio/female/choke_female_2.ogg
new file mode 100644
index 000000000000..8bd7f74cfddc
Binary files /dev/null and b/modular_ss220/emotes/audio/female/choke_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/choke_female_3.ogg b/modular_ss220/emotes/audio/female/choke_female_3.ogg
new file mode 100644
index 000000000000..444b6688a481
Binary files /dev/null and b/modular_ss220/emotes/audio/female/choke_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/female/cough_female_1.ogg b/modular_ss220/emotes/audio/female/cough_female_1.ogg
new file mode 100644
index 000000000000..1888efa17d34
Binary files /dev/null and b/modular_ss220/emotes/audio/female/cough_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/cough_female_2.ogg b/modular_ss220/emotes/audio/female/cough_female_2.ogg
new file mode 100644
index 000000000000..cb2b32437d02
Binary files /dev/null and b/modular_ss220/emotes/audio/female/cough_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/cough_female_3.ogg b/modular_ss220/emotes/audio/female/cough_female_3.ogg
new file mode 100644
index 000000000000..f04e2800af2e
Binary files /dev/null and b/modular_ss220/emotes/audio/female/cough_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/female/cry_female_1.ogg b/modular_ss220/emotes/audio/female/cry_female_1.ogg
new file mode 100644
index 000000000000..1b415ea46a07
Binary files /dev/null and b/modular_ss220/emotes/audio/female/cry_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/cry_female_2.ogg b/modular_ss220/emotes/audio/female/cry_female_2.ogg
new file mode 100644
index 000000000000..95eea1260a51
Binary files /dev/null and b/modular_ss220/emotes/audio/female/cry_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/cry_female_3.ogg b/modular_ss220/emotes/audio/female/cry_female_3.ogg
new file mode 100644
index 000000000000..97b190d7994c
Binary files /dev/null and b/modular_ss220/emotes/audio/female/cry_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/female/gasp_female_1.ogg b/modular_ss220/emotes/audio/female/gasp_female_1.ogg
new file mode 100644
index 000000000000..5161bb364099
Binary files /dev/null and b/modular_ss220/emotes/audio/female/gasp_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/gasp_female_2.ogg b/modular_ss220/emotes/audio/female/gasp_female_2.ogg
new file mode 100644
index 000000000000..d9fc6919074d
Binary files /dev/null and b/modular_ss220/emotes/audio/female/gasp_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/gasp_female_3.ogg b/modular_ss220/emotes/audio/female/gasp_female_3.ogg
new file mode 100644
index 000000000000..5b39f41333c8
Binary files /dev/null and b/modular_ss220/emotes/audio/female/gasp_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/female/gasp_female_4.ogg b/modular_ss220/emotes/audio/female/gasp_female_4.ogg
new file mode 100644
index 000000000000..9d31687aa998
Binary files /dev/null and b/modular_ss220/emotes/audio/female/gasp_female_4.ogg differ
diff --git a/modular_ss220/emotes/audio/female/gasp_female_5.ogg b/modular_ss220/emotes/audio/female/gasp_female_5.ogg
new file mode 100644
index 000000000000..55509359c85f
Binary files /dev/null and b/modular_ss220/emotes/audio/female/gasp_female_5.ogg differ
diff --git a/modular_ss220/emotes/audio/female/gasp_female_6.ogg b/modular_ss220/emotes/audio/female/gasp_female_6.ogg
new file mode 100644
index 000000000000..02b61deef29c
Binary files /dev/null and b/modular_ss220/emotes/audio/female/gasp_female_6.ogg differ
diff --git a/modular_ss220/emotes/audio/female/gasp_female_7.ogg b/modular_ss220/emotes/audio/female/gasp_female_7.ogg
new file mode 100644
index 000000000000..48beb4ab30d7
Binary files /dev/null and b/modular_ss220/emotes/audio/female/gasp_female_7.ogg differ
diff --git a/modular_ss220/emotes/audio/female/giggle_female_1.ogg b/modular_ss220/emotes/audio/female/giggle_female_1.ogg
new file mode 100644
index 000000000000..73945321891b
Binary files /dev/null and b/modular_ss220/emotes/audio/female/giggle_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/giggle_female_2.ogg b/modular_ss220/emotes/audio/female/giggle_female_2.ogg
new file mode 100644
index 000000000000..953450e66a4e
Binary files /dev/null and b/modular_ss220/emotes/audio/female/giggle_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/giggle_female_3.ogg b/modular_ss220/emotes/audio/female/giggle_female_3.ogg
new file mode 100644
index 000000000000..bd1e99de7690
Binary files /dev/null and b/modular_ss220/emotes/audio/female/giggle_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/female/giggle_female_4.ogg b/modular_ss220/emotes/audio/female/giggle_female_4.ogg
new file mode 100644
index 000000000000..057a842adcbc
Binary files /dev/null and b/modular_ss220/emotes/audio/female/giggle_female_4.ogg differ
diff --git a/modular_ss220/emotes/audio/female/laugh_female_1.ogg b/modular_ss220/emotes/audio/female/laugh_female_1.ogg
new file mode 100644
index 000000000000..b4b464bb685c
Binary files /dev/null and b/modular_ss220/emotes/audio/female/laugh_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/laugh_female_2.ogg b/modular_ss220/emotes/audio/female/laugh_female_2.ogg
new file mode 100644
index 000000000000..3124eaf055cd
Binary files /dev/null and b/modular_ss220/emotes/audio/female/laugh_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/laugh_female_3.ogg b/modular_ss220/emotes/audio/female/laugh_female_3.ogg
new file mode 100644
index 000000000000..ed80538fa085
Binary files /dev/null and b/modular_ss220/emotes/audio/female/laugh_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/female/moan_female_1.ogg b/modular_ss220/emotes/audio/female/moan_female_1.ogg
new file mode 100644
index 000000000000..dcbac9ae6579
Binary files /dev/null and b/modular_ss220/emotes/audio/female/moan_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/moan_female_2.ogg b/modular_ss220/emotes/audio/female/moan_female_2.ogg
new file mode 100644
index 000000000000..88a1bf62b93d
Binary files /dev/null and b/modular_ss220/emotes/audio/female/moan_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/moan_female_3.ogg b/modular_ss220/emotes/audio/female/moan_female_3.ogg
new file mode 100644
index 000000000000..f283acbcac70
Binary files /dev/null and b/modular_ss220/emotes/audio/female/moan_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/female/sigh_female.ogg b/modular_ss220/emotes/audio/female/sigh_female.ogg
new file mode 100644
index 000000000000..d72c206cc306
Binary files /dev/null and b/modular_ss220/emotes/audio/female/sigh_female.ogg differ
diff --git a/modular_ss220/emotes/audio/female/sneeze_female.ogg b/modular_ss220/emotes/audio/female/sneeze_female.ogg
new file mode 100644
index 000000000000..4324415599a2
Binary files /dev/null and b/modular_ss220/emotes/audio/female/sneeze_female.ogg differ
diff --git a/modular_ss220/emotes/audio/female/sniff_female.ogg b/modular_ss220/emotes/audio/female/sniff_female.ogg
new file mode 100644
index 000000000000..6f4ce34b0b89
Binary files /dev/null and b/modular_ss220/emotes/audio/female/sniff_female.ogg differ
diff --git a/modular_ss220/emotes/audio/female/yawn_female_1.ogg b/modular_ss220/emotes/audio/female/yawn_female_1.ogg
new file mode 100644
index 000000000000..548be96b76e1
Binary files /dev/null and b/modular_ss220/emotes/audio/female/yawn_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/female/yawn_female_2.ogg b/modular_ss220/emotes/audio/female/yawn_female_2.ogg
new file mode 100644
index 000000000000..67b15056728e
Binary files /dev/null and b/modular_ss220/emotes/audio/female/yawn_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/female/yawn_female_3.ogg b/modular_ss220/emotes/audio/female/yawn_female_3.ogg
new file mode 100644
index 000000000000..12d42db2440e
Binary files /dev/null and b/modular_ss220/emotes/audio/female/yawn_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/cough_kidan.ogg b/modular_ss220/emotes/audio/kidan/cough_kidan.ogg
new file mode 100644
index 000000000000..fcb41df9b3a4
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/cough_kidan.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/cry_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/cry_kidan_1.ogg
new file mode 100644
index 000000000000..09baca1e8ce6
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/cry_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/cry_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/cry_kidan_2.ogg
new file mode 100644
index 000000000000..545c7cb3dd86
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/cry_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/deathsound_kidan.ogg b/modular_ss220/emotes/audio/kidan/deathsound_kidan.ogg
new file mode 100644
index 000000000000..373ab0bee75b
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/deathsound_kidan.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_1.ogg
new file mode 100644
index 000000000000..78de7bd6b5ca
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_2.ogg
new file mode 100644
index 000000000000..3a77eaa0ce1d
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_3.ogg b/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_3.ogg
new file mode 100644
index 000000000000..0b691233e365
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/dying_gasp_kidan_3.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/giggle_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/giggle_kidan_1.ogg
new file mode 100644
index 000000000000..960be0b59185
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/giggle_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/giggle_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/giggle_kidan_2.ogg
new file mode 100644
index 000000000000..e0a75f3fe65b
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/giggle_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/laugh_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/laugh_kidan_1.ogg
new file mode 100644
index 000000000000..8266231e230e
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/laugh_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/laugh_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/laugh_kidan_2.ogg
new file mode 100644
index 000000000000..675b84222e26
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/laugh_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/laugh_kidan_3.ogg b/modular_ss220/emotes/audio/kidan/laugh_kidan_3.ogg
new file mode 100644
index 000000000000..dfdd8278f602
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/laugh_kidan_3.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/laugh_kidan_4.ogg b/modular_ss220/emotes/audio/kidan/laugh_kidan_4.ogg
new file mode 100644
index 000000000000..93d39af1f80d
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/laugh_kidan_4.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/moan_kidan.ogg b/modular_ss220/emotes/audio/kidan/moan_kidan.ogg
new file mode 100644
index 000000000000..a6a05d070070
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/moan_kidan.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/scream_kidan.ogg b/modular_ss220/emotes/audio/kidan/scream_kidan.ogg
new file mode 100644
index 000000000000..20a361186586
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/scream_kidan.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/sigh_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/sigh_kidan_1.ogg
new file mode 100644
index 000000000000..bfb2b091e63c
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/sigh_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/sigh_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/sigh_kidan_2.ogg
new file mode 100644
index 000000000000..92326e36924b
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/sigh_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/sneeze_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/sneeze_kidan_1.ogg
new file mode 100644
index 000000000000..068c8deffb42
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/sneeze_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/sneeze_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/sneeze_kidan_2.ogg
new file mode 100644
index 000000000000..76e8bd363fb7
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/sneeze_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/sneeze_kidan_3.ogg b/modular_ss220/emotes/audio/kidan/sneeze_kidan_3.ogg
new file mode 100644
index 000000000000..5970e47646b4
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/sneeze_kidan_3.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/talk_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/talk_kidan_1.ogg
new file mode 100644
index 000000000000..3ce623ec34cb
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/talk_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/talk_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/talk_kidan_2.ogg
new file mode 100644
index 000000000000..42b76478a95a
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/talk_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/talk_kidan_3.ogg b/modular_ss220/emotes/audio/kidan/talk_kidan_3.ogg
new file mode 100644
index 000000000000..2d851bddb839
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/talk_kidan_3.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/waves_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/waves_kidan_1.ogg
new file mode 100644
index 000000000000..1a51d0baee21
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/waves_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/waves_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/waves_kidan_2.ogg
new file mode 100644
index 000000000000..baac80f59e03
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/waves_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/wiggles_kidan_1.ogg b/modular_ss220/emotes/audio/kidan/wiggles_kidan_1.ogg
new file mode 100644
index 000000000000..b89b6c11f829
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/wiggles_kidan_1.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/wiggles_kidan_2.ogg b/modular_ss220/emotes/audio/kidan/wiggles_kidan_2.ogg
new file mode 100644
index 000000000000..434d8cf6c4d1
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/wiggles_kidan_2.ogg differ
diff --git a/modular_ss220/emotes/audio/kidan/wiggles_kidan_3.ogg b/modular_ss220/emotes/audio/kidan/wiggles_kidan_3.ogg
new file mode 100644
index 000000000000..e68bc71799b7
Binary files /dev/null and b/modular_ss220/emotes/audio/kidan/wiggles_kidan_3.ogg differ
diff --git a/modular_ss220/emotes/audio/male/choke_male_1.ogg b/modular_ss220/emotes/audio/male/choke_male_1.ogg
new file mode 100644
index 000000000000..e0ddde118f3a
Binary files /dev/null and b/modular_ss220/emotes/audio/male/choke_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/choke_male_2.ogg b/modular_ss220/emotes/audio/male/choke_male_2.ogg
new file mode 100644
index 000000000000..8e7ef3734d80
Binary files /dev/null and b/modular_ss220/emotes/audio/male/choke_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/male/choke_male_3.ogg b/modular_ss220/emotes/audio/male/choke_male_3.ogg
new file mode 100644
index 000000000000..546ba45bff0f
Binary files /dev/null and b/modular_ss220/emotes/audio/male/choke_male_3.ogg differ
diff --git a/modular_ss220/emotes/audio/male/cough_male_1.ogg b/modular_ss220/emotes/audio/male/cough_male_1.ogg
new file mode 100644
index 000000000000..146beefdf873
Binary files /dev/null and b/modular_ss220/emotes/audio/male/cough_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/cough_male_2.ogg b/modular_ss220/emotes/audio/male/cough_male_2.ogg
new file mode 100644
index 000000000000..745fb50e19c4
Binary files /dev/null and b/modular_ss220/emotes/audio/male/cough_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/male/cough_male_3.ogg b/modular_ss220/emotes/audio/male/cough_male_3.ogg
new file mode 100644
index 000000000000..abfe70d27690
Binary files /dev/null and b/modular_ss220/emotes/audio/male/cough_male_3.ogg differ
diff --git a/modular_ss220/emotes/audio/male/cry_male_1.ogg b/modular_ss220/emotes/audio/male/cry_male_1.ogg
new file mode 100644
index 000000000000..6d4e35be168f
Binary files /dev/null and b/modular_ss220/emotes/audio/male/cry_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/cry_male_2.ogg b/modular_ss220/emotes/audio/male/cry_male_2.ogg
new file mode 100644
index 000000000000..b0437d3d9383
Binary files /dev/null and b/modular_ss220/emotes/audio/male/cry_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/male/gasp_male_1.ogg b/modular_ss220/emotes/audio/male/gasp_male_1.ogg
new file mode 100644
index 000000000000..5ce88dde766f
Binary files /dev/null and b/modular_ss220/emotes/audio/male/gasp_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/gasp_male_2.ogg b/modular_ss220/emotes/audio/male/gasp_male_2.ogg
new file mode 100644
index 000000000000..df311e2698e1
Binary files /dev/null and b/modular_ss220/emotes/audio/male/gasp_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/male/gasp_male_3.ogg b/modular_ss220/emotes/audio/male/gasp_male_3.ogg
new file mode 100644
index 000000000000..5614ae1b5735
Binary files /dev/null and b/modular_ss220/emotes/audio/male/gasp_male_3.ogg differ
diff --git a/modular_ss220/emotes/audio/male/gasp_male_4.ogg b/modular_ss220/emotes/audio/male/gasp_male_4.ogg
new file mode 100644
index 000000000000..3d29b276d22d
Binary files /dev/null and b/modular_ss220/emotes/audio/male/gasp_male_4.ogg differ
diff --git a/modular_ss220/emotes/audio/male/gasp_male_5.ogg b/modular_ss220/emotes/audio/male/gasp_male_5.ogg
new file mode 100644
index 000000000000..6cbc4001bfd7
Binary files /dev/null and b/modular_ss220/emotes/audio/male/gasp_male_5.ogg differ
diff --git a/modular_ss220/emotes/audio/male/gasp_male_6.ogg b/modular_ss220/emotes/audio/male/gasp_male_6.ogg
new file mode 100644
index 000000000000..a2417066a1fd
Binary files /dev/null and b/modular_ss220/emotes/audio/male/gasp_male_6.ogg differ
diff --git a/modular_ss220/emotes/audio/male/gasp_male_7.ogg b/modular_ss220/emotes/audio/male/gasp_male_7.ogg
new file mode 100644
index 000000000000..84059f30c441
Binary files /dev/null and b/modular_ss220/emotes/audio/male/gasp_male_7.ogg differ
diff --git a/modular_ss220/emotes/audio/male/giggle_male_1.ogg b/modular_ss220/emotes/audio/male/giggle_male_1.ogg
new file mode 100644
index 000000000000..06eb4abdfef7
Binary files /dev/null and b/modular_ss220/emotes/audio/male/giggle_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/giggle_male_2.ogg b/modular_ss220/emotes/audio/male/giggle_male_2.ogg
new file mode 100644
index 000000000000..befb7665723a
Binary files /dev/null and b/modular_ss220/emotes/audio/male/giggle_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/male/laugh_male_1.ogg b/modular_ss220/emotes/audio/male/laugh_male_1.ogg
new file mode 100644
index 000000000000..6b4f906ffe09
Binary files /dev/null and b/modular_ss220/emotes/audio/male/laugh_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/laugh_male_2.ogg b/modular_ss220/emotes/audio/male/laugh_male_2.ogg
new file mode 100644
index 000000000000..4033e04eac3b
Binary files /dev/null and b/modular_ss220/emotes/audio/male/laugh_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/male/laugh_male_3.ogg b/modular_ss220/emotes/audio/male/laugh_male_3.ogg
new file mode 100644
index 000000000000..dba2e80c257f
Binary files /dev/null and b/modular_ss220/emotes/audio/male/laugh_male_3.ogg differ
diff --git a/modular_ss220/emotes/audio/male/moan_male_1.ogg b/modular_ss220/emotes/audio/male/moan_male_1.ogg
new file mode 100644
index 000000000000..59cc27914305
Binary files /dev/null and b/modular_ss220/emotes/audio/male/moan_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/moan_male_2.ogg b/modular_ss220/emotes/audio/male/moan_male_2.ogg
new file mode 100644
index 000000000000..82a021b0a945
Binary files /dev/null and b/modular_ss220/emotes/audio/male/moan_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/male/moan_male_3.ogg b/modular_ss220/emotes/audio/male/moan_male_3.ogg
new file mode 100644
index 000000000000..78e8eb9c8f66
Binary files /dev/null and b/modular_ss220/emotes/audio/male/moan_male_3.ogg differ
diff --git a/modular_ss220/emotes/audio/male/sigh_male.ogg b/modular_ss220/emotes/audio/male/sigh_male.ogg
new file mode 100644
index 000000000000..1590bcdee605
Binary files /dev/null and b/modular_ss220/emotes/audio/male/sigh_male.ogg differ
diff --git a/modular_ss220/emotes/audio/male/sniff_male.ogg b/modular_ss220/emotes/audio/male/sniff_male.ogg
new file mode 100644
index 000000000000..d4b9ddf6cedb
Binary files /dev/null and b/modular_ss220/emotes/audio/male/sniff_male.ogg differ
diff --git a/modular_ss220/emotes/audio/male/yawn_male_1.ogg b/modular_ss220/emotes/audio/male/yawn_male_1.ogg
new file mode 100644
index 000000000000..193f59874584
Binary files /dev/null and b/modular_ss220/emotes/audio/male/yawn_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/male/yawn_male_2.ogg b/modular_ss220/emotes/audio/male/yawn_male_2.ogg
new file mode 100644
index 000000000000..4064029e1e34
Binary files /dev/null and b/modular_ss220/emotes/audio/male/yawn_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/moth/moth_chitter.ogg b/modular_ss220/emotes/audio/moth/moth_chitter.ogg
new file mode 100644
index 000000000000..842bcf8e533d
Binary files /dev/null and b/modular_ss220/emotes/audio/moth/moth_chitter.ogg differ
diff --git a/modular_ss220/emotes/audio/moth/moth_cough.ogg b/modular_ss220/emotes/audio/moth/moth_cough.ogg
new file mode 100644
index 000000000000..6222e84f6faf
Binary files /dev/null and b/modular_ss220/emotes/audio/moth/moth_cough.ogg differ
diff --git a/modular_ss220/emotes/audio/moth/moth_laugh.ogg b/modular_ss220/emotes/audio/moth/moth_laugh.ogg
new file mode 100644
index 000000000000..391d6c5aefe2
Binary files /dev/null and b/modular_ss220/emotes/audio/moth/moth_laugh.ogg differ
diff --git a/modular_ss220/emotes/audio/moth/moth_scream.ogg b/modular_ss220/emotes/audio/moth/moth_scream.ogg
new file mode 100644
index 000000000000..482086fb630d
Binary files /dev/null and b/modular_ss220/emotes/audio/moth/moth_scream.ogg differ
diff --git a/modular_ss220/emotes/audio/moth/moth_sneeze.ogg b/modular_ss220/emotes/audio/moth/moth_sneeze.ogg
new file mode 100644
index 000000000000..c5ac979aed11
Binary files /dev/null and b/modular_ss220/emotes/audio/moth/moth_sneeze.ogg differ
diff --git a/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_1.ogg b/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_1.ogg
new file mode 100644
index 000000000000..4cfc6ae65e6f
Binary files /dev/null and b/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_1.ogg differ
diff --git a/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_2.ogg b/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_2.ogg
new file mode 100644
index 000000000000..00ae761a239e
Binary files /dev/null and b/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_2.ogg differ
diff --git a/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_3.ogg b/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_3.ogg
new file mode 100644
index 000000000000..3868149c8e56
Binary files /dev/null and b/modular_ss220/emotes/audio/plasmaman/scream_plasmaman_3.ogg differ
diff --git a/modular_ss220/emotes/audio/scream_jelly.ogg b/modular_ss220/emotes/audio/scream_jelly.ogg
new file mode 100644
index 000000000000..97fb7e3c68e8
Binary files /dev/null and b/modular_ss220/emotes/audio/scream_jelly.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/giggle_female_1.ogg b/modular_ss220/emotes/audio/skrell/giggle_female_1.ogg
new file mode 100644
index 000000000000..1ea51a20362b
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/giggle_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/giggle_male_1.ogg b/modular_ss220/emotes/audio/skrell/giggle_male_1.ogg
new file mode 100644
index 000000000000..1b6e69ff0cf5
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/giggle_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/laugh_female_1.ogg b/modular_ss220/emotes/audio/skrell/laugh_female_1.ogg
new file mode 100644
index 000000000000..5a11708fa835
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/laugh_female_1.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/laugh_female_2.ogg b/modular_ss220/emotes/audio/skrell/laugh_female_2.ogg
new file mode 100644
index 000000000000..6357b3e56099
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/laugh_female_2.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/laugh_female_3.ogg b/modular_ss220/emotes/audio/skrell/laugh_female_3.ogg
new file mode 100644
index 000000000000..ca69d95a8d8d
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/laugh_female_3.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/laugh_male_1.ogg b/modular_ss220/emotes/audio/skrell/laugh_male_1.ogg
new file mode 100644
index 000000000000..384f8a797e92
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/laugh_male_1.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/laugh_male_2.ogg b/modular_ss220/emotes/audio/skrell/laugh_male_2.ogg
new file mode 100644
index 000000000000..8087bdcdbd02
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/laugh_male_2.ogg differ
diff --git a/modular_ss220/emotes/audio/skrell/laugh_male_3.ogg b/modular_ss220/emotes/audio/skrell/laugh_male_3.ogg
new file mode 100644
index 000000000000..2954bbcaa33f
Binary files /dev/null and b/modular_ss220/emotes/audio/skrell/laugh_male_3.ogg differ
diff --git a/modular_ss220/emotes/audio/snore_1.ogg b/modular_ss220/emotes/audio/snore_1.ogg
new file mode 100644
index 000000000000..f1afa4ecccab
Binary files /dev/null and b/modular_ss220/emotes/audio/snore_1.ogg differ
diff --git a/modular_ss220/emotes/audio/snore_2.ogg b/modular_ss220/emotes/audio/snore_2.ogg
new file mode 100644
index 000000000000..97b4b7a99cf5
Binary files /dev/null and b/modular_ss220/emotes/audio/snore_2.ogg differ
diff --git a/modular_ss220/emotes/audio/snore_3.ogg b/modular_ss220/emotes/audio/snore_3.ogg
new file mode 100644
index 000000000000..b95e4603cc11
Binary files /dev/null and b/modular_ss220/emotes/audio/snore_3.ogg differ
diff --git a/modular_ss220/emotes/audio/snore_4.ogg b/modular_ss220/emotes/audio/snore_4.ogg
new file mode 100644
index 000000000000..4e867039ea71
Binary files /dev/null and b/modular_ss220/emotes/audio/snore_4.ogg differ
diff --git a/modular_ss220/emotes/audio/snore_5.ogg b/modular_ss220/emotes/audio/snore_5.ogg
new file mode 100644
index 000000000000..8c0a300acd26
Binary files /dev/null and b/modular_ss220/emotes/audio/snore_5.ogg differ
diff --git a/modular_ss220/emotes/audio/snore_6.ogg b/modular_ss220/emotes/audio/snore_6.ogg
new file mode 100644
index 000000000000..bddccff92491
Binary files /dev/null and b/modular_ss220/emotes/audio/snore_6.ogg differ
diff --git a/modular_ss220/emotes/audio/snore_7.ogg b/modular_ss220/emotes/audio/snore_7.ogg
new file mode 100644
index 000000000000..7bfe7ca8b6b4
Binary files /dev/null and b/modular_ss220/emotes/audio/snore_7.ogg differ
diff --git a/modular_ss220/emotes/audio/tajaran/hiss_tajaran.ogg b/modular_ss220/emotes/audio/tajaran/hiss_tajaran.ogg
new file mode 100644
index 000000000000..8a14458ad24e
Binary files /dev/null and b/modular_ss220/emotes/audio/tajaran/hiss_tajaran.ogg differ
diff --git a/modular_ss220/emotes/audio/tajaran/purr_tajaran.ogg b/modular_ss220/emotes/audio/tajaran/purr_tajaran.ogg
new file mode 100644
index 000000000000..1125cb2bbd71
Binary files /dev/null and b/modular_ss220/emotes/audio/tajaran/purr_tajaran.ogg differ
diff --git a/modular_ss220/emotes/audio/tajaran/purr_tajaran_long.ogg b/modular_ss220/emotes/audio/tajaran/purr_tajaran_long.ogg
new file mode 100644
index 000000000000..c74b845250d0
Binary files /dev/null and b/modular_ss220/emotes/audio/tajaran/purr_tajaran_long.ogg differ
diff --git a/modular_ss220/emotes/audio/tajaran/scream_tajaran.ogg b/modular_ss220/emotes/audio/tajaran/scream_tajaran.ogg
new file mode 100644
index 000000000000..a9f3be40ddd8
Binary files /dev/null and b/modular_ss220/emotes/audio/tajaran/scream_tajaran.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/deathsound_unathi.ogg b/modular_ss220/emotes/audio/unathi/deathsound_unathi.ogg
new file mode 100644
index 000000000000..6fe30f0f1f9f
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/deathsound_unathi.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/roar_unathi_1.ogg b/modular_ss220/emotes/audio/unathi/roar_unathi_1.ogg
new file mode 100644
index 000000000000..f286ebd35e09
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/roar_unathi_1.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/roar_unathi_2.ogg b/modular_ss220/emotes/audio/unathi/roar_unathi_2.ogg
new file mode 100644
index 000000000000..1674bb756eba
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/roar_unathi_2.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/roar_unathi_3.ogg b/modular_ss220/emotes/audio/unathi/roar_unathi_3.ogg
new file mode 100644
index 000000000000..24d212007b40
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/roar_unathi_3.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/rumble_unathi_1.ogg b/modular_ss220/emotes/audio/unathi/rumble_unathi_1.ogg
new file mode 100644
index 000000000000..f9e218f2ce72
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/rumble_unathi_1.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/rumble_unathi_2.ogg b/modular_ss220/emotes/audio/unathi/rumble_unathi_2.ogg
new file mode 100644
index 000000000000..18e353878f9b
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/rumble_unathi_2.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/scream_female.ogg b/modular_ss220/emotes/audio/unathi/scream_female.ogg
new file mode 100644
index 000000000000..a8a8fb45ce8d
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/scream_female.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/scream_male.ogg b/modular_ss220/emotes/audio/unathi/scream_male.ogg
new file mode 100644
index 000000000000..30255bf49c4f
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/scream_male.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/sneeze_female.ogg b/modular_ss220/emotes/audio/unathi/sneeze_female.ogg
new file mode 100644
index 000000000000..cb62b78dea96
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/sneeze_female.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/sneeze_male.ogg b/modular_ss220/emotes/audio/unathi/sneeze_male.ogg
new file mode 100644
index 000000000000..bc2cb785836f
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/sneeze_male.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/talk_unathi_1.ogg b/modular_ss220/emotes/audio/unathi/talk_unathi_1.ogg
new file mode 100644
index 000000000000..c48027b52ae1
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/talk_unathi_1.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/talk_unathi_2.ogg b/modular_ss220/emotes/audio/unathi/talk_unathi_2.ogg
new file mode 100644
index 000000000000..3ee114f6ec86
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/talk_unathi_2.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/talk_unathi_3.ogg b/modular_ss220/emotes/audio/unathi/talk_unathi_3.ogg
new file mode 100644
index 000000000000..64a494d0b0cc
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/talk_unathi_3.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/threat_unathi_1.ogg b/modular_ss220/emotes/audio/unathi/threat_unathi_1.ogg
new file mode 100644
index 000000000000..e101bab110a0
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/threat_unathi_1.ogg differ
diff --git a/modular_ss220/emotes/audio/unathi/threat_unathi_2.ogg b/modular_ss220/emotes/audio/unathi/threat_unathi_2.ogg
new file mode 100644
index 000000000000..9560561ba74e
Binary files /dev/null and b/modular_ss220/emotes/audio/unathi/threat_unathi_2.ogg differ
diff --git a/modular_ss220/emotes/audio/whistle.ogg b/modular_ss220/emotes/audio/whistle.ogg
new file mode 100644
index 000000000000..e8c1a6c90f46
Binary files /dev/null and b/modular_ss220/emotes/audio/whistle.ogg differ
diff --git a/modular_ss220/emotes/code/emote.dm b/modular_ss220/emotes/code/emote.dm
new file mode 100644
index 000000000000..f550ff266f70
--- /dev/null
+++ b/modular_ss220/emotes/code/emote.dm
@@ -0,0 +1,410 @@
+/datum/emote
+ cooldown = 1.5 SECONDS
+ audio_cooldown = 3 SECONDS
+
+/datum/emote/living/carbon/human/gasp
+ message = "задыхается!"
+ message_mime = "кажется, задыхается!"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+ unintentional_stat_allowed = UNCONSCIOUS
+
+/datum/emote/living/carbon/human/gasp/get_sound(mob/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+
+ if(H.is_muzzled())
+ // If you're muzzled you're not making noise
+ return
+
+ if(H.health > 0)
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_gasp_sound)
+ else
+ return pick(H.dna.species.gasp_sound)
+
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_dying_gasp_sounds)
+ else
+ return pick(H.dna.species.male_dying_gasp_sounds)
+
+/datum/emote/living/carbon/human/scream
+ message = "кричит!"
+ message_mime = "как будто кричит!"
+ message_simple = "скулит."
+ message_alien = "рычит!"
+ message_postfix = "на %t!"
+ muzzled_noises = list("очень громко")
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH
+
+/datum/emote/living/carbon/human/scream/select_message_type(mob/user, msg, intentional)
+ . = ..()
+ var/mob/living/carbon/human/H = user
+ if(H.dna.species?.scream_verb)
+ if(H.mind?.miming)
+ return "как будто [H.dna.species?.scream_verb]!"
+ else
+ return "[H.dna.species?.scream_verb]!"
+
+/datum/emote/living/carbon/human/scream/get_sound(mob/living/user)
+ var/mob/living/carbon/human/human = user
+ if(human.mind?.miming || !istype(human))
+ return
+ if(human.gender == FEMALE)
+ return pick(human.dna.species.female_scream_sound)
+ else
+ return pick(human.dna.species.male_scream_sound)
+
+/datum/emote/living/carbon/human/salute
+ message = "салютует."
+ message_param = "салютует %t."
+ emote_type = EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/salute/get_sound(mob/living/user)
+ var/mob/living/carbon/human/H = user
+ if(!is_type_in_list(H.shoes, funny_shoes))
+ return 'sound/effects/salute.ogg'
+ if(is_type_in_list(H.shoes, funny_shoes))
+ return 'sound/items/toysqueak1.ogg'
+
+/datum/emote/living/carbon/human/cry
+ message = "плачет."
+ muzzled_noises = list("слабо", "жалко", "грустно")
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH | EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/cry/get_sound(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_cry_sound)
+ else
+ return pick(H.dna.species.male_cry_sound)
+
+/datum/emote/living/carbon/giggle
+ message = "хихикает."
+ message_mime = "бесшумно хихикает!"
+ muzzled_noises = list("булькающе")
+
+/datum/emote/living/carbon/giggle/get_sound(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_giggle_sound)
+ else
+ return pick(H.dna.species.male_giggle_sound)
+
+/datum/emote/living/carbon/moan
+ message = "стонет!"
+ message_mime = "как будто стонет!"
+ muzzled_noises = list("болезненно")
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/moan/get_sound(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_moan_sound)
+ else
+ return pick(H.dna.species.male_moan_sound)
+
+/datum/emote/living/carbon/laugh
+ message = "смеется."
+ message_mime = "бесшумно смеется!"
+ message_param = "смеется над %t."
+ muzzled_noises = list("счастливо", "весело")
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH | EMOTE_VISIBLE
+
+/datum/emote/living/carbon/laugh/get_sound(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_laugh_sound)
+ else
+ return pick(H.dna.species.male_laugh_sound)
+
+/datum/emote/living/carbon/yawn
+ message = "зевает."
+ muzzled_noises = list("устало", "медленно", "сонно")
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH | EMOTE_SOUND
+
+/datum/emote/living/carbon/yawn/get_sound(mob/living/user)
+ . = ..()
+ if(user.gender == FEMALE)
+ return pick(
+ "modular_ss220/emotes/audio/female/yawn_female_1.ogg",
+ "modular_ss220/emotes/audio/female/yawn_female_2.ogg",
+ "modular_ss220/emotes/audio/female/yawn_female_3.ogg")
+ else
+ return pick(
+ "modular_ss220/emotes/audio/male/yawn_male_1.ogg",
+ "modular_ss220/emotes/audio/male/yawn_male_2.ogg")
+
+/datum/emote/living/carbon/human/sneeze
+ message = "чихает."
+ muzzled_noises = list("странно", "остро")
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH
+
+/datum/emote/living/carbon/human/sneeze/get_sound(mob/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_sneeze_sound)
+ else
+ return pick(H.dna.species.male_sneeze_sound)
+
+/datum/emote/living/sigh
+ message = "вздыхает."
+ message_mime = "беззвучно вздыхает."
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH
+
+/datum/emote/living/sigh/get_sound(mob/living/user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+
+ if(H.gender == FEMALE)
+ return pick(H.dna.species.female_sigh_sound)
+ else
+ return pick(H.dna.species.male_sigh_sound)
+
+/datum/emote/living/choke
+ message = "подавился!"
+ message_mime = "отчаянно хватается за горло!"
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH
+
+/datum/emote/living/choke/get_sound(mob/living/user)
+ . = ..()
+ if(user.gender == FEMALE)
+ return pick(
+ "modular_ss220/emotes/audio/female/choke_female_1.ogg",
+ "modular_ss220/emotes/audio/female/choke_female_2.ogg",
+ "modular_ss220/emotes/audio/female/choke_female_3.ogg")
+ else
+ return pick(
+ "modular_ss220/emotes/audio/male/choke_male_1.ogg",
+ "modular_ss220/emotes/audio/male/choke_male_2.ogg",
+ "modular_ss220/emotes/audio/male/choke_male_3.ogg")
+
+/datum/emote/living/carbon/human/sniff
+ message = "нюхает."
+ message_mime = "бесшумно нюхнул."
+ emote_type = EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/human/sniff/get_sound(mob/living/user)
+ . = ..()
+ if(user.gender == FEMALE)
+ return "modular_ss220/emotes/audio/female/sniff_female.ogg"
+ else
+ return "modular_ss220/emotes/audio/male/sniff_male.ogg"
+
+/datum/emote/living/sniff
+ message = "нюхает."
+ message_mime = "бесшумно нюхнул."
+ emote_type = EMOTE_AUDIBLE
+
+/datum/emote/living/sniff/get_sound(mob/living/user)
+ . = ..()
+ if(user.gender == FEMALE)
+ return "modular_ss220/emotes/audio/female/sniff_female.ogg"
+ else
+ return "modular_ss220/emotes/audio/male/sniff_male.ogg"
+
+/datum/emote/living/snore
+ message = "храпит."
+ message_mime = "крепко спит."
+ message_simple = "ворочается во сне."
+ message_robot = "мечтает об электроовцах"
+ emote_type = EMOTE_AUDIBLE | EMOTE_SOUND
+ stat_allowed = CONSCIOUS
+ max_stat_allowed = CONSCIOUS
+ unintentional_stat_allowed = UNCONSCIOUS
+ max_unintentional_stat_allowed = UNCONSCIOUS
+
+/datum/emote/living/snore/get_sound(mob/living/user)
+ . = ..()
+ if(iscarbon(user))
+ return pick(
+ 'modular_ss220/emotes/audio/snore_1.ogg',
+ 'modular_ss220/emotes/audio/snore_2.ogg',
+ 'modular_ss220/emotes/audio/snore_3.ogg',
+ 'modular_ss220/emotes/audio/snore_4.ogg',
+ 'modular_ss220/emotes/audio/snore_5.ogg',
+ 'modular_ss220/emotes/audio/snore_6.ogg',
+ 'modular_ss220/emotes/audio/snore_7.ogg')
+
+/datum/emote/living/dance
+ message = "радостно танцует."
+ cooldown = 5 SECONDS
+ var/dance_time = 3 SECONDS
+
+/datum/emote/living/dance/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ user.spin(dance_time, pick(0.1 SECONDS, 0.2 SECONDS))
+ user.do_jitter_animation(rand(8 SECONDS, 16 SECONDS), dance_time / 4)
+
+/datum/emote/living/carbon/human/roar
+ key = "roar"
+ key_third_person = "roar"
+ message = "рычит."
+ message_mime = "бесшумно рычит."
+ message_param = "рычит на %t."
+ species_type_whitelist_typecache = list(/datum/species/unathi)
+ volume = 50
+ muzzled_noises = list("раздражённый")
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH | EMOTE_AUDIBLE
+ age_based = TRUE
+
+/datum/emote/living/carbon/human/roar/get_sound(mob/living/user)
+ return pick(
+ 'modular_ss220/emotes/audio/unathi/roar_unathi_1.ogg',
+ 'modular_ss220/emotes/audio/unathi/roar_unathi_2.ogg',
+ 'modular_ss220/emotes/audio/unathi/roar_unathi_3.ogg')
+
+/datum/emote/living/carbon/human/rumble
+ key = "rumble"
+ key_third_person = "rumble"
+ message = "урчит."
+ message_param = "урчит на %t."
+ species_type_whitelist_typecache = list(/datum/species/unathi)
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH | EMOTE_AUDIBLE
+ age_based = TRUE
+ volume = 50
+ muzzled_noises = list("слабо урчащий")
+
+/datum/emote/living/carbon/human/rumble/get_sound(mob/living/user)
+ return pick(
+ 'modular_ss220/emotes/audio/unathi/rumble_unathi_1.ogg',
+ 'modular_ss220/emotes/audio/unathi/rumble_unathi_2.ogg')
+
+/datum/emote/living/carbon/human/threat
+ key = "threat"
+ key_third_person = "threat"
+ message = "угрожающе рычит."
+ message_param = "угрожающе рычит на %t."
+ species_type_whitelist_typecache = list(/datum/species/unathi)
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH | EMOTE_AUDIBLE
+ age_based = TRUE
+ volume = 80
+ muzzled_noises = list("очень раздражённый")
+
+/datum/emote/living/carbon/human/threat/get_sound(mob/living/user)
+ return pick(
+ 'modular_ss220/emotes/audio/unathi/threat_unathi_1.ogg',
+ 'modular_ss220/emotes/audio/unathi/threat_unathi_2.ogg')
+
+/datum/emote/living/carbon/human/purr
+ key = "purr"
+ key_third_person = "purr"
+ message = "мурчит."
+ message_param = "мурчит из-за %t."
+ species_type_whitelist_typecache = list(/datum/species/tajaran)
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+ age_based = TRUE
+ sound = 'modular_ss220/emotes/audio/tajaran/purr_tajaran.ogg'
+ volume = 80
+ muzzled_noises = list("слабо")
+
+/datum/emote/living/carbon/human/purrl
+ key = "purrl"
+ key_third_person = "purrl"
+ message = "мурчит."
+ message_param = "мурчит из-за %t."
+ cooldown = 5 SECONDS
+ species_type_whitelist_typecache = list(/datum/species/tajaran)
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+ age_based = TRUE
+ sound = 'modular_ss220/emotes/audio/tajaran/purr_tajaran_long.ogg'
+ volume = 80
+ muzzled_noises = list("слабо")
+
+/datum/emote/living/carbon/human/waves_k
+ key = "waves_k"
+ key_third_person = "waves_k"
+ message = "взмахивает усиками."
+ message_param = "взмахивает усиками из-за %t."
+ species_type_whitelist_typecache = list(/datum/species/kidan)
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_MOUTH
+ age_based = TRUE
+ volume = 80
+ muzzled_noises = list("слабо")
+
+/datum/emote/living/carbon/human/waves_k/get_sound(mob/living/user)
+ return pick(
+ 'modular_ss220/emotes/audio/kidan/waves_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/waves_kidan_2.ogg')
+
+/datum/emote/living/carbon/human/wiggles
+ key = "wiggles"
+ key_third_person = "wiggles"
+ message = "шевелит усиками."
+ message_param = "шевелит усиками из-за %t."
+ cooldown = 5 SECONDS
+ species_type_whitelist_typecache = list(/datum/species/kidan)
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_MOUTH
+ age_based = TRUE
+ volume = 80
+ muzzled_noises = list("слабо")
+
+/datum/emote/living/carbon/human/wiggles/get_sound(mob/living/user)
+ return pick(
+ 'modular_ss220/emotes/audio/kidan/wiggles_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/wiggles_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/wiggles_kidan_3.ogg')
+
+
+////////////////////
+/// Keybindings ///
+//////////////////
+/datum/keybinding/emote/carbon/human/hem
+ linked_emote = /datum/emote/living/carbon/human/hem
+ name = "Хныкать"
+
+/datum/keybinding/emote/carbon/human/scratch
+ linked_emote = /datum/emote/living/carbon/human/scratch
+ name = "Чесаться"
+
+/datum/keybinding/emote/carbon/human/whistle
+ linked_emote = /datum/emote/living/carbon/human/whistle
+ name = "Свистеть"
+
+/datum/keybinding/emote/carbon/human/snuffle
+ linked_emote = /datum/emote/living/carbon/human/snuffle
+ name = "Шмыгать носом"
+
+/datum/keybinding/emote/carbon/human/roar
+ linked_emote = /datum/emote/living/carbon/human/roar
+ name = "Рычать"
+
+/datum/keybinding/emote/carbon/human/rumble
+ linked_emote = /datum/emote/living/carbon/human/rumble
+ name = "Урчать"
+
+/datum/keybinding/emote/carbon/human/threat
+ linked_emote = /datum/emote/living/carbon/human/threat
+ name = "Угрожающе рычать"
+
+/datum/keybinding/emote/carbon/human/purr
+ linked_emote = /datum/emote/living/carbon/human/purr
+ name = "Мурчать"
+
+/datum/keybinding/emote/carbon/human/purrl
+ linked_emote = /datum/emote/living/carbon/human/purrl
+ name = "Мурчать подольше"
+
+/datum/keybinding/emote/carbon/human/waves
+ linked_emote = /datum/emote/living/carbon/human/waves_k
+ name = "Взмахнуть усиками"
+
+/datum/keybinding/emote/carbon/human/wiggles
+ linked_emote = /datum/emote/living/carbon/human/wiggles
+ name = "Шевелить усиками"
diff --git a/modular_ss220/emotes/code/emote_translations.dm b/modular_ss220/emotes/code/emote_translations.dm
new file mode 100644
index 000000000000..8d55b20b08d9
--- /dev/null
+++ b/modular_ss220/emotes/code/emote_translations.dm
@@ -0,0 +1,462 @@
+//////////////////////
+/// Living Emotes ///
+////////////////////
+/datum/emote/living/scream
+ message = "кричит!"
+ message_mime = "как будто кричит!"
+ message_simple = "скулит."
+ message_alien = "рычит!"
+ message_postfix = "на %t!"
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH
+
+/datum/emote/flip
+ message = "делает кувырок!"
+
+/datum/emote/spin
+ key_third_person = "крутится"
+
+/datum/emote/living/blush
+ message = "краснеет."
+
+/datum/emote/living/bow
+ message = "кланяется."
+ message_param = "кланяется %t."
+ message_postfix = "%t."
+
+/datum/emote/living/burp
+ message = "отрыгивает."
+ message_mime = "довольно противно открывает рот."
+
+/datum/emote/living/collapse
+ message = "падает!"
+
+/datum/emote/living/deathgasp
+ message = "цепенеет и расслабляется, взгляд становится пустым и безжизненным..."
+ message_alien = "цепенеет и расслабляется, взгляд становится пустым и безжизненным..."
+ message_robot = "на мгновение вздрагивает и замирает, глаза медленно темнеют."
+ message_AI = "скрипит, экран мерцает, пока системы медленно выключаются."
+ message_alien = "издает ослабевающий крик, зеленая кровь пузырится из пасти..."
+ message_larva = "с тошнотворным шипением выдыхает воздух и падает на пол..."
+ message_monkey = "издает слабый звон, когда рушится и перестает двигаться..."
+ message_simple = "перестает двигаться..."
+
+/datum/emote/living/drool
+ message = "несет чепуху."
+
+/datum/emote/living/quiver
+ message = "дрожит."
+
+/datum/emote/living/frown
+ message = "хмурится."
+ message_param = "хмурится, смотря на %t."
+
+/datum/emote/living/gag
+ message = "выворачивает содержимое желудка."
+ message_mime = "будто бы выворачивает содержимое желудка."
+ message_param = "выворачивает содержимое желудка на %t."
+
+/datum/emote/living/glare
+ message = "смотрит с ненавистью."
+ message_param = "с ненавистью смотрит на %t."
+
+/datum/emote/living/grin
+ message = "скалится в улыбке."
+
+/datum/emote/living/grimace
+ message = "корчит рожицу."
+
+/datum/emote/living/look
+ message = "смотрит."
+ message_param = "смотрит на %t."
+
+/datum/emote/living/bshake
+ message = "трясется."
+
+/datum/emote/living/shudder
+ message = "вздрагивает."
+
+/datum/emote/living/point
+ message = "показывает пальцем."
+ message_param = "показывает пальцем на %t."
+
+/datum/emote/living/pout
+ message = "надувает губы."
+
+/datum/emote/living/shake
+ message = "трясет головой."
+
+/datum/emote/living/shiver
+ message = "дрожит."
+
+/datum/emote/living/sigh/happy
+ message = "Удовлетворённо вздыхает."
+ message_mime = "кажется, удовлетворенно вздыхает"
+ muzzled_noises = list("расслабленный", "довольный")
+
+/datum/emote/living/sit
+ message = "садится."
+
+/datum/emote/living/smile
+ message = "улыбается."
+ message_param = "улыбается, смотря на %t."
+
+/datum/emote/living/smug
+ message = "самодовольно ухмыляется."
+ message_param = "самодовольно ухмыляется, смотря на %t."
+
+/datum/emote/living/nightmare
+ message = "ворочается во сне."
+
+/datum/emote/living/stare
+ message = "пялится."
+ message_param = "пялится на %t."
+
+/datum/emote/living/strech
+ message = "разминает руки."
+ message_robot = "проверяет приводы."
+
+/datum/emote/living/sulk
+ message = "печально опускает руки."
+
+/datum/emote/living/sway
+ message = "качается на месте."
+
+/datum/emote/living/swear
+ message = "ругается!"
+ message_param = "говорит нелестное слово %t!"
+ message_mime = "показывает оскорбительный жест!"
+ message_simple = "издает недовольный звук!"
+ message_robot = "издает особенно оскорбительную серию звуковых сигналов!"
+ message_postfix = ", обращаясь к %t!"
+
+/datum/emote/living/tilt
+ message = "наклоняет голову в сторону."
+
+/datum/emote/living/tremble
+ message = "дрожит от страха!"
+
+/datum/emote/living/twitch
+ message = "сильно дёргается."
+
+/datum/emote/living/twitch_s
+ message = "дергается."
+
+/datum/emote/living/whimper
+ message = "хнычет."
+ message_mime = "кажется, поранился."
+ muzzled_noises = list("слабый", "жалкий")
+
+/datum/emote/living/wsmile
+ message = "слабо улыбается."
+
+//////////////////////
+/// Carbon Emotes ///
+////////////////////
+/datum/emote/living/carbon/blink
+ message = "моргает."
+
+/datum/emote/living/carbon/blink_r
+ key = "blink_r"
+ message = "быстро моргает."
+
+/datum/emote/living/carbon/clap
+ message = "хлопает."
+ message_mime = "бесшумно хлопает."
+ message_param = "хлопает %t."
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
+
+/datum/emote/living/carbon/chuckle
+ message = "усмехается."
+ message_mime = "бесшумно усмехается."
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH | EMOTE_VISIBLE
+ muzzled_noises = list("радостно", "оптимистично")
+
+/datum/emote/living/carbon/gurgle
+ message = "издает неприятное бульканье."
+ muzzled_noises = list("недовольно", "гортанно")
+
+/datum/emote/living/carbon/inhale
+ message = "вдыхает."
+ muzzled_noises = list("воздушно")
+
+/datum/emote/living/carbon/inhale/sharp
+ message = "глубоко вдыхает!"
+
+/datum/emote/living/carbon/kiss
+ message = "целует."
+ message_param = "целует %t!"
+ muzzled_noises = list("smooching")
+
+/datum/emote/living/carbon/wave
+ message = "машет."
+ message_param = "машет %t."
+
+/datum/emote/living/carbon/exhale
+ message = "выдыхает."
+
+/datum/emote/living/carbon/scowl
+ message = "хмурится."
+
+/datum/emote/living/groan
+ message = "болезненно вздыхает!"
+ message_mime = "как будто болезненно вздыхает!"
+ message_param = "болезненно вздыхает на %t."
+ muzzled_noises = list("болезненно")
+
+/datum/emote/living/carbon/sign
+ message = "показывает несколько пальцев."
+ message_param = "показывает %t пальцев."
+ param_desc = "число(0-10)"
+
+/datum/emote/living/carbon/faint
+ message = "падает в обморок."
+
+/datum/emote/living/carbon/cough
+ message = "кашляет!"
+ message_mime = "бесшумно кашляет!"
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH | EMOTE_AUDIBLE
+
+/////////////////////
+/// Human Emotes ///
+///////////////////
+/datum/emote/living/carbon/human/airguitar
+ message = "натягивает струны и бьет головой, как шимпанзе в сафари."
+
+/datum/emote/living/carbon/human/crack
+ message = "хрустит пальцами."
+
+/datum/emote/living/carbon/human/eyebrow
+ message = "приподнимает бровь."
+ message_param = "приподнимает бровь на %t."
+
+/datum/emote/living/carbon/human/grumble
+ message = "ворчит!"
+ message_mime = "как будто ворчит!"
+ message_postfix = "на %t!"
+ muzzled_noises = list("беспокойно")
+
+/datum/emote/living/carbon/human/hug
+ message = "обнимает себя."
+ message_param = "обнимает %t."
+
+/datum/emote/living/carbon/human/mumble
+ message = "бормочет!"
+ message_mime = "кажется, что он говорит приятное ничто!"
+ message_postfix = "на %t!"
+
+/datum/emote/living/carbon/human/nod
+ message = "кивает."
+ message_param = "кивает, обращаясь к %t."
+
+/datum/emote/living/carbon/human/shake
+ message = "трясет головой."
+ message_param = "трясет головой, обращяясь к %t."
+
+/datum/emote/living/carbon/human/pale
+ message = "на секунду бледнеет."
+
+/datum/emote/living/carbon/human/raise
+ message = "поднимает руку."
+
+/datum/emote/living/carbon/human/shrug
+ message = "пожимает плечами."
+
+/datum/emote/living/carbon/human/johnny
+ message = "затягивается сигаретой и выдыхает дым в форме своего имени."
+ message_param = "тупо"
+
+/datum/emote/living/carbon/human/wink
+ message = "подмигивает."
+
+/datum/emote/living/carbon/human/snap
+ message = "щелкает пальцами."
+ message_param = "щелкает пальцами на %t."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/human/fart
+ message = "пердит."
+ message_param = "пердит в направлении %t."
+
+/datum/emote/living/carbon/sign/signal
+ message_param = "показывает %t пальцев."
+ param_desc = "число(0-10)"
+
+/datum/emote/living/carbon/human/wag
+ message = "начинает вилять хвостом."
+
+/datum/emote/living/carbon/human/wag/stop
+ message = "перестает вилять хвостом."
+
+/datum/emote/living/carbon/human/scream/screech
+ message = "визжит!"
+ message_param = "визжит на %t!"
+
+/datum/emote/living/carbon/human/scream/screech/roar
+ message = "ревет!"
+ message_param = "ревет на %t!"
+
+/datum/emote/living/carbon/human/monkey/roll
+ message = "кружится."
+
+/datum/emote/living/carbon/human/monkey/scratch
+ message = "чешется."
+
+/datum/emote/living/carbon/human/monkey/tail
+ message = "машет хвостом."
+
+/datum/emote/living/carbon/human/flap
+ message = "хлопает крыльями."
+
+/datum/emote/living/carbon/human/flutter
+ message = "зло хлопает крыльями."
+
+/datum/emote/living/carbon/human/flutter
+ message = "трепещет крыльями."
+
+/datum/emote/living/carbon/human/quill
+ message = "шелестят перьями."
+ message_param = "шелестят перьями на %t."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/human/clack
+ message = "клацает своими мандибулами."
+ message_param = "клацает своими мандибулами на %t."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_MOUTH
+
+/datum/emote/living/carbon/human/clack/click
+ key = "click"
+ key_third_person = "clicks"
+ message = "щелкает своими мандибулами."
+ message_param = "щелкает своими мандибулами на %t."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_MOUTH
+
+/datum/emote/living/carbon/human/drask_talk/drone
+ message = "жужжит."
+ message_param = "жужжит на %t."
+
+
+/datum/emote/living/carbon/human/drask_talk/hum
+ message = "напевает."
+ message_param = "напевает %t."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_MOUTH
+
+/datum/emote/living/carbon/human/drask_talk/rumble
+ message = "урчит."
+ message_param = "урчит на %t."
+
+/datum/emote/living/carbon/human/hiss
+ message = "шипит."
+ message_param = "шипит на %t."
+ muzzled_noises = list("слабое шипение")
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH | EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/human/creak
+ message = "скрипит."
+ message_param = "скрипит на %t."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/human/howl
+ message = "воет."
+ message_mime = "беззвучно воет."
+ message_param = "воет на %t."
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH | EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/human/growl
+ message = "рычит."
+ message_mime = "бесшумно рычит."
+ message_param = "рычит на %t."
+ muzzled_noises = list("раздражённо")
+ emote_type = EMOTE_VISIBLE | EMOTE_MOUTH | EMOTE_AUDIBLE
+
+/datum/emote/living/carbon/human/rattle
+ message = "гремит костями."
+ message_param = "гремит костями на %t."
+
+/datum/emote/living/carbon/human/crack/slime
+ message = "сминает костяшки пальцев!"
+
+/datum/emote/living/carbon/human/crack/diona
+ message = "трещит ветками!"
+
+/datum/emote/living/carbon/human/snuffle
+ key = "snuffle"
+ key_third_person = "snuffles"
+ message = "шмыгает носом."
+ message_param = "шмыгает носом на %t."
+
+/datum/emote/living/carbon/human/hem
+ key = "hem"
+ key_third_person = "hems"
+ message = "хмыкает."
+ message_param = "хмыкает %t."
+
+/datum/emote/living/carbon/human/scratch
+ key = "scratch"
+ key_third_person = "scratch"
+ message = "чешется."
+ message_param = "чешет %t."
+
+/datum/emote/living/carbon/human/whistle
+ key = "whistle"
+ key_third_person = "whistles"
+ message = "свистит."
+ message_param = "свистит на %t."
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH | EMOTE_VISIBLE
+ sound = "modular_ss220/emotes/audio/whistle.ogg"
+
+/datum/emote/living/carbon/human/warble
+ message = "издаёт трель."
+ message_param = "издаёт трель для %t."
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/slime/squish
+ message = "хлюпает."
+ message_param = "хлюпает на %t."
+ emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
+
+/////////////////////
+/// Alien Emotes ///
+///////////////////
+/datum/emote/living/carbon/alien/humanoid/roar
+ message = "ревет!"
+ message_param = "ревет на %t!"
+
+/datum/emote/living/carbon/alien/humanoid/hiss
+ message = "шипит!"
+ message_param = "шипит %t!"
+
+/datum/emote/living/carbon/alien/humanoid/gnarl
+ message = "оскаливается и показывает зубы."
+ message_param = "оскаливается и показывает зубы %t."
+
+/////////////////////
+/// Brain Emotes ///
+///////////////////
+/datum/emote/living/carbon/brain/alarm
+ message = "подает сигнал тревоги."
+ self_message = "Вы подаете сигнал тревоги."
+
+/datum/emote/living/carbon/brain/alert
+ message = "издаёт страдальческий звук."
+ self_message = "Вы издаёте страдальческий звук."
+
+/datum/emote/living/carbon/brain/notice
+ message = "воспроизводит громкий звук."
+ self_message = "Вы воспроизводите громкий звук."
+
+/datum/emote/living/carbon/brain/flash
+ message = "начинает быстро мигать лампочками!"
+
+/datum/emote/living/carbon/brain/whistle
+ message = "свистит."
+ self_message = "Вы свистите."
+ emote_type = EMOTE_AUDIBLE | EMOTE_MOUTH | EMOTE_SOUND
+ sound = "modular_ss220/emotes/audio/whistle.ogg"
+
+/datum/emote/living/carbon/brain/beep
+ message = "бипает."
+ self_message = "Вы бипаете."
+
+/datum/emote/living/carbon/brain/boop
+ message = "бупает."
+ self_message = "вы бупаете."
diff --git a/modular_ss220/emotes/code/emote_verbs.dm b/modular_ss220/emotes/code/emote_verbs.dm
new file mode 100644
index 000000000000..ddd56f887fef
--- /dev/null
+++ b/modular_ss220/emotes/code/emote_verbs.dm
@@ -0,0 +1,487 @@
+/// Sound Emotes ///
+/mob/living/carbon/human/verb/emote_laugh()
+ set name = "▷ Смеяться "
+ set category = "Эмоции"
+ emote("laugh", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_cry()
+ set name = "▷ Плакать "
+ set category = "Эмоции"
+ emote("cry", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_giggle()
+ set name = "▷ Хихикать "
+ set category = "Эмоции"
+ emote("giggle", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_cough()
+ set name = "▷ Кашлять "
+ set category = "Эмоции"
+ emote("cough", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_scream()
+ set name = "▷ Кричать "
+ set category = "Эмоции"
+ emote("scream", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_choke()
+ set name = "▷ Подавиться "
+ set category = "Эмоции"
+ emote("choke", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_moan()
+ set name = "▷ Стонать "
+ set category = "Эмоции"
+ emote("moan", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_gasp()
+ set name = "▷ Задыхаться "
+ set category = "Эмоции"
+ emote("gasp", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_sigh()
+ set name = "▷ Вздыхать "
+ set category = "Эмоции"
+ emote("sigh", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_sneeze()
+ set name = "▷ Чихнуть "
+ set category = "Эмоции"
+ emote("sneeze", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_sniff()
+ set name = "▷ Понюхать "
+ set category = "Эмоции"
+ emote("sniff", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_snore()
+ set name = "▷ Храпеть "
+ set category = "Эмоции"
+ emote("snore", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_whistle()
+ set name = "▷ Свистеть "
+ set category = "Эмоции"
+ emote("whistle", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_yawn()
+ set name = "▷ Зевать "
+ set category = "Эмоции"
+ emote("yawn", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_salute()
+ set name = "▷ Салютовать "
+ set category = "Эмоции"
+ emote("salute", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_snap()
+ set name = "▷ Щелкнуть пальцами "
+ set category = "Эмоции"
+ emote("snap", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_clap()
+ set name = "▷ Хлопать "
+ set category = "Эмоции"
+ emote("clap", intentional = TRUE)
+
+/// Action Emotes ///
+/mob/living/carbon/human/verb/emote_collapse()
+ set name = "○ Рухнуть "
+ set category = "Эмоции"
+ emote("collapse", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_faint()
+ set name = "○ Потерять сознание "
+ set category = "Эмоции"
+ emote("faint", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_highfive()
+ set name = "○ Дать пять "
+ set category = "Эмоции"
+ emote("highfive", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_handshake()
+ set name = "○ Пожать руку "
+ set category = "Эмоции"
+ emote("handshake", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_flip()
+ set name = "○ Сделать кувырок "
+ set category = "Эмоции"
+ emote("flip", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_dance()
+ set name = "○ Танцевать "
+ set category = "Эмоции"
+ emote("dance", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_slap()
+ set name = "○ Шлёпнуть "
+ set category = "Эмоции"
+ emote("slap", intentional = TRUE)
+
+/// ME Emotes ///
+/mob/living/carbon/human/verb/emote_scratch()
+ set name = "◦ Почесаться "
+ set category = "Эмоции"
+ emote("scratch", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_blush()
+ set name = "◦ Краснеть "
+ set category = "Эмоции"
+ emote("blush", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_blink()
+ set name = "◦ Моргать "
+ set category = "Эмоции"
+ emote("blink", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_blink_r()
+ set name = "◦ Моргать быстро "
+ set category = "Эмоции"
+ emote("blink_r", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_bow()
+ set name = "◦ Поклониться "
+ set category = "Эмоции"
+ emote("bow", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_chuckle()
+ set name = "◦ Усмехнуться "
+ set category = "Эмоции"
+ emote("chuckle", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_drool()
+ set name = "◦ Нести чепуху "
+ set category = "Эмоции"
+ emote("drool", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_frown()
+ set name = "◦ Хмуриться "
+ set category = "Эмоции"
+ emote("frown", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_glare()
+ set name = "◦ Смотреть с ненавистью "
+ set category = "Эмоции"
+ emote("glare", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_groan()
+ set name = "◦ Болезненный вздох "
+ set category = "Эмоции"
+ emote("groan", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_grin()
+ set name = "◦ Оскалиться в улыбке "
+ set category = "Эмоции"
+ emote("grin", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_shake()
+ set name = "◦ Трясти головой "
+ set category = "Эмоции"
+ emote("shake", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_smile()
+ set name = "◦ Улыбнуться "
+ set category = "Эмоции"
+ emote("smile", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_grunt()
+ set name = "◦ Ворчать "
+ set category = "Эмоции"
+ emote("grumble", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_snuffle()
+ set name = "◦ Шмыгать носом "
+ set category = "Эмоции"
+ emote("snuffle", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_shrug()
+ set name = "◦ Пожать плечами "
+ set category = "Эмоции"
+ emote("shrug", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_stare()
+ set name = "◦ Пялиться "
+ set category = "Эмоции"
+ emote("stare", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_tremble()
+ set name = "◦ Дрожать в ужасе "
+ set category = "Эмоции"
+ emote("tremble", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_twitch_v()
+ set name = "◦ Сильно дёргаться "
+ set category = "Эмоции"
+ emote("twitch", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_twitch()
+ set name = "◦ Дёргаться "
+ set category = "Эмоции"
+ emote("twitch_s", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_eyebrow()
+ set name = "◦ Приподнять бровь "
+ set category = "Эмоции"
+ emote("eyebrow", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_fart()
+ set name = "◦ Пернуть "
+ set category = "Эмоции"
+ emote("fart", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_airguitar()
+ set name = "◦ Воображаемая гитара "
+ set category = "Эмоции"
+ emote("airguitar", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_burp()
+ set name = "◦ Рыгнуть "
+ set category = "Эмоции"
+ emote("burp", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_quiver()
+ set name = "◦ Трепетать "
+ set category = "Эмоции"
+ emote("quiver", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_mumble()
+ set name = "◦ Бормотать "
+ set category = "Эмоции"
+ emote("mumble", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_raise()
+ set name = "◦ Поднять руку "
+ set category = "Эмоции"
+ emote("raise", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_pale()
+ set name = "◦ Бледнеть "
+ set category = "Эмоции"
+ emote("pale", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_shudder()
+ set name = "◦ Содрогаться "
+ set category = "Эмоции"
+ emote("shudder", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_bshake()
+ set name = "◦ Трястись "
+ set category = "Эмоции"
+ emote("bshake", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_flap()
+ set name = "◦ Махать крыльями "
+ set category = "Эмоции"
+ emote("flap", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_aflap()
+ set name = "◦ Махать крыльями агрессивно "
+ set category = "Эмоции"
+ emote("aflap", intentional = TRUE)
+
+/// Racial Emotes ///
+/mob/living/carbon/human/proc/emote_wag()
+ set name = "< Махать хвостом >"
+ set category = "Эмоции"
+ emote("wag", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_swag()
+ set name = "< Перестать махать хвостом >"
+ set category = "Эмоции"
+ emote("swag", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_howl()
+ set name = "< Выть >"
+ set category = "Эмоции"
+ emote("howl", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_growl()
+ set name = "< Рычать >"
+ set category = "Эмоции"
+ emote("growl", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_purr()
+ set name = "< Мурчать >"
+ set category = "Эмоции"
+ emote("purr", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_purrl()
+ set name = "< Мурчать дольше >"
+ set category = "Эмоции"
+ emote("purrl", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_hiss()
+ set name = "< Шипеть >"
+ set category = "Эмоции"
+ emote("hiss", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_roar()
+ set name = "< Рычать >"
+ set category = "Эмоции"
+ emote("roar", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_threat()
+ set name = "< Угрожать >"
+ set category = "Эмоции"
+ emote("threat", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_whip()
+ set name = "< Ударить хвостом >"
+ set category = "Эмоции"
+ emote("whip", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_whips()
+ set name = "< Хлестать хвостом >"
+ set category = "Эмоции"
+ emote("whips", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_rumble()
+ set name = "< Урчать >"
+ set category = "Эмоции"
+ emote("rumble", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_hisses()
+ set name = "< Шипеть >"
+ set category = "Эмоции"
+ emote("hisses", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_quill()
+ set name = "< Шуршать перьями >"
+ set category = "Эмоции"
+ emote("quill", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_creak()
+ set name = "< Скрипеть >"
+ set category = "Эмоции"
+ emote("creak", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_warble()
+ set name = "< Трель >"
+ set category = "Эмоции"
+ emote("warble", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_click()
+ set name = "< Щелкать >"
+ set category = "Эмоции"
+ emote("click", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_clack()
+ set name = "< Трещать >"
+ set category = "Эмоции"
+ emote("clack", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_hum()
+ set name = "< Гудеть >"
+ set category = "Эмоции"
+ emote("hum", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_squish()
+ set name = "< Хлюпать >"
+ set category = "Эмоции"
+ emote("squish", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_ping()
+ set name = "< Звенеть >"
+ set category = "Эмоции"
+ emote("ping", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_beep()
+ set name = "< Пищать >"
+ set category = "Эмоции"
+ emote("beep", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_buzz()
+ set name = "< Жужжать >"
+ set category = "Эмоции"
+ emote("buzz", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_buzz2()
+ set name = "< Жужжать раздраженно >"
+ set category = "Эмоции"
+ emote("buzz2", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_yes()
+ set name = "< Утвердительно >"
+ set category = "Эмоции"
+ emote("yes", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_no()
+ set name = "< Отрицательно >"
+ set category = "Эмоции"
+ emote("no", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_waves_k()
+ set name = "< Взмахнуть усиками >"
+ set category = "Эмоции"
+ emote("waves_k", intentional = TRUE)
+
+/mob/living/carbon/human/proc/emote_wiggles()
+ set name = "< Шевелить усиками >"
+ set category = "Эмоции"
+ emote("wiggles", intentional = TRUE)
+
+/// Слишком переполненное меню, убираю то что легче написать чем искать. ///
+/*
+/mob/living/carbon/human/verb/emote_deathgasp()
+ set name = "▷ Предсмертный вздох "
+ set category = "Эмоции"
+ emote("deathgasp", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_dap()
+ set name = "◦ dap "
+ set category = "Эмоции"
+ emote("dap", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_wave()
+ set name = "◦ Махать "
+ set category = "Эмоции"
+ emote("wave", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_whimper()
+ set name = "◦ Хныкать "
+ set category = "Эмоции"
+ emote("whimper", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_look()
+ set name = "◦ Посмотреть "
+ set category = "Эмоции"
+ emote("look", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_nod()
+ set name = "◦ Кивнуть "
+ set category = "Эмоции"
+ emote("nod", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_wink()
+ set name = "◦ Подмигнуть "
+ set category = "Эмоции"
+ emote("wink", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_point()
+ set name = "◦ Показать пальцем " // Куда блять показывать...
+ set category = "Эмоции"
+ emote("point", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_shiver()
+ set name = "◦ Дрожать "
+ set category = "Эмоции"
+ emote("shiver", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_hug()
+ set name = "◦ Обнимать " // Обнимать себя конечно смешно, но бесполезно.
+ set category = "Эмоции"
+ emote("hug", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_hem()
+ set name = "◦ Хмыкнуть "
+ set category = "Эмоции"
+ emote("hem", intentional = TRUE)
+
+/mob/living/carbon/human/verb/emote_signal()
+ set name = "◦ Показать несколько пальцев "
+ set category = "Эмоции"
+ var/Cnt = input("Руки должны быть свободны", "Показать несколько пальцев", 1) in list(1,2,3,4,5,6,7,8,9,10)
+ emote("sign", message = Cnt)
+*/
diff --git a/modular_ss220/emotes/code/racial_emotes.dm b/modular_ss220/emotes/code/racial_emotes.dm
new file mode 100644
index 000000000000..c8fdd0a0db0e
--- /dev/null
+++ b/modular_ss220/emotes/code/racial_emotes.dm
@@ -0,0 +1,135 @@
+/datum/species/vulpkanin/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_wag
+ H.verbs |= /mob/living/carbon/human/proc/emote_swag
+ H.verbs |= /mob/living/carbon/human/proc/emote_howl
+ H.verbs |= /mob/living/carbon/human/proc/emote_growl
+
+/datum/species/vulpkanin/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_wag
+ H.verbs -= /mob/living/carbon/human/proc/emote_swag
+ H.verbs -= /mob/living/carbon/human/proc/emote_howl
+ H.verbs -= /mob/living/carbon/human/proc/emote_growl
+
+/datum/species/diona/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_creak
+
+/datum/species/diona/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_creak
+
+/datum/species/drask/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_hum
+
+/datum/species/drask/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_hum
+
+/datum/species/kidan/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_click
+ H.verbs |= /mob/living/carbon/human/proc/emote_clack
+ H.verbs |= /mob/living/carbon/human/proc/emote_waves_k
+ H.verbs |= /mob/living/carbon/human/proc/emote_wiggles
+
+/datum/species/kidan/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_click
+ H.verbs -= /mob/living/carbon/human/proc/emote_clack
+ H.verbs -= /mob/living/carbon/human/proc/emote_waves_k
+ H.verbs -= /mob/living/carbon/human/proc/emote_wiggles
+
+/datum/species/machine/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_ping
+ H.verbs |= /mob/living/carbon/human/proc/emote_beep
+ H.verbs |= /mob/living/carbon/human/proc/emote_buzz
+ H.verbs |= /mob/living/carbon/human/proc/emote_buzz2
+ H.verbs |= /mob/living/carbon/human/proc/emote_yes
+ H.verbs |= /mob/living/carbon/human/proc/emote_no
+
+/datum/species/machine/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_ping
+ H.verbs -= /mob/living/carbon/human/proc/emote_beep
+ H.verbs -= /mob/living/carbon/human/proc/emote_buzz
+ H.verbs -= /mob/living/carbon/human/proc/emote_buzz2
+ H.verbs -= /mob/living/carbon/human/proc/emote_yes
+ H.verbs -= /mob/living/carbon/human/proc/emote_no
+
+/datum/species/moth/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_flap
+
+/datum/species/moth/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_flap
+
+/datum/species/skrell/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_warble
+
+/datum/species/skrell/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_warble
+
+/datum/species/slime/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_squish
+
+/datum/species/slime/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_squish
+
+/datum/species/tajaran/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_wag
+ H.verbs |= /mob/living/carbon/human/proc/emote_swag
+ H.verbs |= /mob/living/carbon/human/proc/emote_purr
+ H.verbs |= /mob/living/carbon/human/proc/emote_purrl
+ H.verbs |= /mob/living/carbon/human/proc/emote_hisses
+
+/datum/species/tajaran/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_wag
+ H.verbs -= /mob/living/carbon/human/proc/emote_swag
+ H.verbs -= /mob/living/carbon/human/proc/emote_purr
+ H.verbs -= /mob/living/carbon/human/proc/emote_purrl
+ H.verbs -= /mob/living/carbon/human/proc/emote_hisses
+
+/datum/species/unathi/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_wag
+ H.verbs |= /mob/living/carbon/human/proc/emote_swag
+ H.verbs |= /mob/living/carbon/human/proc/emote_hiss
+ H.verbs |= /mob/living/carbon/human/proc/emote_roar
+ H.verbs |= /mob/living/carbon/human/proc/emote_threat
+ H.verbs |= /mob/living/carbon/human/proc/emote_whip
+ H.verbs |= /mob/living/carbon/human/proc/emote_whips
+ H.verbs |= /mob/living/carbon/human/proc/emote_rumble
+
+/datum/species/unathi/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_wag
+ H.verbs -= /mob/living/carbon/human/proc/emote_swag
+ H.verbs -= /mob/living/carbon/human/proc/emote_hiss
+ H.verbs -= /mob/living/carbon/human/proc/emote_roar
+ H.verbs -= /mob/living/carbon/human/proc/emote_threat
+ H.verbs -= /mob/living/carbon/human/proc/emote_whip
+ H.verbs -= /mob/living/carbon/human/proc/emote_whips
+ H.verbs -= /mob/living/carbon/human/proc/emote_rumble
+
+/datum/species/vox/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ H.verbs |= /mob/living/carbon/human/proc/emote_wag
+ H.verbs |= /mob/living/carbon/human/proc/emote_swag
+ H.verbs |= /mob/living/carbon/human/proc/emote_quill
+
+/datum/species/vox/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ H.verbs -= /mob/living/carbon/human/proc/emote_wag
+ H.verbs -= /mob/living/carbon/human/proc/emote_swag
+ H.verbs -= /mob/living/carbon/human/proc/emote_quill
diff --git a/modular_ss220/emotes/code/species.dm b/modular_ss220/emotes/code/species.dm
new file mode 100644
index 000000000000..589eaed5f6e4
--- /dev/null
+++ b/modular_ss220/emotes/code/species.dm
@@ -0,0 +1,319 @@
+/datum/species
+ scream_verb = "кричит"
+ var/male_cry_sound = list(
+ 'modular_ss220/emotes/audio/male/cry_male_1.ogg',
+ 'modular_ss220/emotes/audio/male/cry_male_2.ogg')
+ var/female_cry_sound = list(
+ 'modular_ss220/emotes/audio/female/cry_female_1.ogg',
+ 'modular_ss220/emotes/audio/female/cry_female_2.ogg',
+ 'modular_ss220/emotes/audio/female/cry_female_3.ogg')
+ var/male_giggle_sound = list(
+ 'modular_ss220/emotes/audio/male/giggle_male_1.ogg',
+ 'modular_ss220/emotes/audio/male/giggle_male_2.ogg')
+ var/female_giggle_sound = list(
+ 'modular_ss220/emotes/audio/female/giggle_female_1.ogg',
+ 'modular_ss220/emotes/audio/female/giggle_female_2.ogg',
+ 'modular_ss220/emotes/audio/female/giggle_female_3.ogg',
+ 'modular_ss220/emotes/audio/female/giggle_female_4.ogg')
+ var/male_laugh_sound = list(
+ 'modular_ss220/emotes/audio/male/laugh_male_1.ogg',
+ 'modular_ss220/emotes/audio/male/laugh_male_2.ogg',
+ 'modular_ss220/emotes/audio/male/laugh_male_3.ogg')
+ var/female_laugh_sound = list(
+ 'modular_ss220/emotes/audio/female/laugh_female_1.ogg',
+ 'modular_ss220/emotes/audio/female/laugh_female_2.ogg',
+ 'modular_ss220/emotes/audio/female/laugh_female_3.ogg')
+ var/male_sigh_sound = list('modular_ss220/emotes/audio/male/sigh_male.ogg')
+ var/female_sigh_sound = list('modular_ss220/emotes/audio/female/sigh_female.ogg')
+ var/male_moan_sound = list(
+ 'modular_ss220/emotes/audio/male/moan_male_1.ogg',
+ 'modular_ss220/emotes/audio/male/moan_male_2.ogg',
+ 'modular_ss220/emotes/audio/male/moan_male_3.ogg')
+ var/female_moan_sound = list(
+ 'modular_ss220/emotes/audio/female/moan_female_1.ogg',
+ 'modular_ss220/emotes/audio/female/moan_female_2.ogg',
+ 'modular_ss220/emotes/audio/female/moan_female_3.ogg')
+ var/female_gasp_sound = list(
+ 'modular_ss220/emotes/audio/female/gasp_female_1.ogg',
+ 'modular_ss220/emotes/audio/female/gasp_female_2.ogg',
+ 'modular_ss220/emotes/audio/female/gasp_female_3.ogg',
+ 'modular_ss220/emotes/audio/female/gasp_female_4.ogg',
+ 'modular_ss220/emotes/audio/female/gasp_female_5.ogg',
+ 'modular_ss220/emotes/audio/female/gasp_female_6.ogg',
+ 'modular_ss220/emotes/audio/female/gasp_female_7.ogg')
+ gasp_sound = list(
+ 'modular_ss220/emotes/audio/male/gasp_male_1.ogg',
+ 'modular_ss220/emotes/audio/male/gasp_male_2.ogg',
+ 'modular_ss220/emotes/audio/male/gasp_male_3.ogg',
+ 'modular_ss220/emotes/audio/male/gasp_male_4.ogg',
+ 'modular_ss220/emotes/audio/male/gasp_male_5.ogg',
+ 'modular_ss220/emotes/audio/male/gasp_male_6.ogg',
+ 'modular_ss220/emotes/audio/male/gasp_male_7.ogg')
+ male_cough_sounds = list(
+ 'modular_ss220/emotes/audio/male/cough_male_1.ogg',
+ 'modular_ss220/emotes/audio/male/cough_male_2.ogg',
+ 'modular_ss220/emotes/audio/male/cough_male_3.ogg')
+ female_cough_sounds = list(
+ 'modular_ss220/emotes/audio/female/cough_female_1.ogg',
+ 'modular_ss220/emotes/audio/female/cough_female_2.ogg',
+ 'modular_ss220/emotes/audio/female/cough_female_3.ogg')
+ female_sneeze_sound = 'modular_ss220/emotes/audio/female/sneeze_female.ogg'
+ suicide_messages = list(
+ "пытается откусить себе язык!",
+ "выдавливает свои глазницы большими пальцами!",
+ "сворачивает себе шею!",
+ "задерживает дыхание!")
+
+/datum/species/diona
+ suicide_messages = list(
+ "теряет ветви!",
+ "вытаскивает из тайника бутыль с гербицидом и делает большой глоток!",
+ "разваливается на множество нимф!")
+
+/datum/species/drask
+ suicide_messages = list(
+ "трёт себя до возгорания!",
+ "давит пальцами на свои большие глаза!",
+ "втягивает теплый воздух!",
+ "задерживает дыхание!")
+
+/datum/species/golem
+ suicide_messages = list(
+ "рассыпается в прах!",
+ "разбивает своё тело на части!")
+
+/datum/species/kidan
+ autohiss_basic_map = list(
+ "z" = list("zz", "zzz", "zzzz"),
+ "v" = list("vv", "vvv", "vvvv"),
+ "з" = list("зз", "ззз", "зззз"),
+ "в" = list("вв", "ввв", "вввв"))
+ autohiss_extra_map = list(
+ "s" = list("z", "zs", "zzz", "zzsz"),
+ "с" = list("з", "зс", "ззз", "ззсз"))
+ autohiss_exempt = list("Chittin")
+
+ scream_verb = "визжит"
+ speech_sounds = list(
+ 'modular_ss220/emotes/audio/kidan/talk_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/talk_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/talk_kidan_3.ogg')
+ speech_chance = 20
+ male_scream_sound = 'modular_ss220/emotes/audio/kidan/scream_kidan.ogg'
+ female_scream_sound = 'modular_ss220/emotes/audio/kidan/scream_kidan.ogg'
+ male_cry_sound = list(
+ 'modular_ss220/emotes/audio/kidan/cry_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/cry_kidan_2.ogg')
+ female_cry_sound = list(
+ 'modular_ss220/emotes/audio/kidan/cry_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/cry_kidan_2.ogg')
+ male_giggle_sound = list(
+ 'modular_ss220/emotes/audio/kidan/giggle_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/giggle_kidan_2.ogg')
+ female_giggle_sound = list(
+ 'modular_ss220/emotes/audio/kidan/giggle_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/giggle_kidan_2.ogg')
+ male_laugh_sound = list(
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_3.ogg',
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_4.ogg')
+ female_laugh_sound = list(
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_3.ogg',
+ 'modular_ss220/emotes/audio/kidan/laugh_kidan_4.ogg')
+ male_sigh_sound = list(
+ 'modular_ss220/emotes/audio/kidan/sigh_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/sigh_kidan_2.ogg')
+ female_sigh_sound = list(
+ 'modular_ss220/emotes/audio/kidan/sigh_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/sigh_kidan_2.ogg')
+ male_moan_sound = list('modular_ss220/emotes/audio/kidan/moan_kidan.ogg')
+ female_moan_sound = list('modular_ss220/emotes/audio/kidan/moan_kidan.ogg')
+ male_cough_sounds = list('modular_ss220/emotes/audio/kidan/cough_kidan.ogg')
+ female_cough_sounds = list('modular_ss220/emotes/audio/kidan/cough_kidan.ogg')
+ male_sneeze_sound = list(
+ 'modular_ss220/emotes/audio/kidan/sneeze_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/sneeze_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/sneeze_kidan_3.ogg')
+ female_sneeze_sound = list(
+ 'modular_ss220/emotes/audio/kidan/sneeze_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/sneeze_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/sneeze_kidan_3.ogg')
+ male_dying_gasp_sounds = list(
+ 'modular_ss220/emotes/audio/kidan/dying_gasp_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/dying_gasp_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/dying_gasp_kidan_3.ogg')
+ female_dying_gasp_sounds = list(
+ 'modular_ss220/emotes/audio/kidan/dying_gasp_kidan_1.ogg',
+ 'modular_ss220/emotes/audio/kidan/dying_gasp_kidan_2.ogg',
+ 'modular_ss220/emotes/audio/kidan/dying_gasp_kidan_3.ogg')
+ death_sounds = 'modular_ss220/emotes/audio/kidan/deathsound_kidan.ogg'
+ suicide_messages = list(
+ "пытается откусить себе усики!",
+ "вонзает когти в свои глазницы!",
+ "сворачивает себе шею!",
+ "разбивает себе панцирь",
+ "протыкает себя челюстями!",
+ "задерживает дыхание!")
+
+/datum/species/machine
+ suicide_messages = list(
+ "отключает питание!",
+ "разбивает свой монитор!",
+ "выкручивает себе шею!",
+ "загружает дополнительную оперативную память!",
+ "замыкает свои микросхемы!",
+ "блокирует свой вентиляционный порт!")
+
+/datum/species/moth
+ scream_verb = "жужжит"
+ female_giggle_sound = 'modular_ss220/emotes/audio/moth/moth_chitter.ogg'
+ male_giggle_sound = 'modular_ss220/emotes/audio/moth/moth_chitter.ogg'
+ male_scream_sound = 'modular_ss220/emotes/audio/moth/moth_scream.ogg'
+ female_scream_sound = 'modular_ss220/emotes/audio/moth/moth_scream.ogg'
+ male_sneeze_sound = 'modular_ss220/emotes/audio/moth/moth_sneeze.ogg'
+ female_sneeze_sound = 'modular_ss220/emotes/audio/moth/moth_sneeze.ogg'
+ female_laugh_sound = 'modular_ss220/emotes/audio/moth/moth_laugh.ogg'
+ male_laugh_sound = 'modular_ss220/emotes/audio/moth/moth_laugh.ogg'
+ female_cough_sounds = 'modular_ss220/emotes/audio/moth/moth_cough.ogg'
+ male_cough_sounds = 'modular_ss220/emotes/audio/moth/moth_cough.ogg'
+ suicide_messages = list(
+ "откусывает свои усики!",
+ "вспарывает себе живот!",
+ "отрывает себе крылья!",
+ "заддерживает своё дыхание!")
+
+/datum/species/plasmaman
+ autohiss_basic_map = list(
+ "s" = list("ss", "sss", "ssss"),
+ "с" = list("сс", "ссс", "сссс"))
+
+ male_scream_sound = list(
+ 'modular_ss220/emotes/audio/plasmaman/scream_plasmaman_1.ogg',
+ 'modular_ss220/emotes/audio/plasmaman/scream_plasmaman_2.ogg',
+ 'modular_ss220/emotes/audio/plasmaman/scream_plasmaman_3.ogg')
+ female_scream_sound = list(
+ 'modular_ss220/emotes/audio/plasmaman/scream_plasmaman_1.ogg',
+ 'modular_ss220/emotes/audio/plasmaman/scream_plasmaman_2.ogg',
+ 'modular_ss220/emotes/audio/plasmaman/scream_plasmaman_3.ogg')
+ suicide_messages = list(
+ "сворачивает себе шею!",
+ "впускает себе немного O2!",
+ "осознает экзистенциальную проблему быть рождённым из плазмы!",
+ "показывает свою истинную природу, которая оказывается плазмой!")
+
+/datum/species/shadow
+ suicide_messages = list(
+ "пытается откусить себе язык!",
+ "выдавливает большими пальцами себе глазницы!",
+ "сворачивает себе шею!",
+ "пялится на ближайший источник света!")
+
+/datum/species/skeleton
+ suicide_messages = list(
+ "ломает себе кости!",
+ "сваливается в кучу!",
+ "разваливается!",
+ "откручивает себе череп!")
+
+/datum/species/skrell
+ male_giggle_sound = 'modular_ss220/emotes/audio/skrell/giggle_male_1.ogg'
+ female_giggle_sound = 'modular_ss220/emotes/audio/skrell/giggle_female_1.ogg'
+ male_laugh_sound = list(
+ 'modular_ss220/emotes/audio/skrell/laugh_male_1.ogg',
+ 'modular_ss220/emotes/audio/skrell/laugh_male_2.ogg',
+ 'modular_ss220/emotes/audio/skrell/laugh_male_3.ogg')
+ female_laugh_sound = list(
+ 'modular_ss220/emotes/audio/skrell/laugh_female_1.ogg',
+ 'modular_ss220/emotes/audio/skrell/laugh_female_2.ogg',
+ 'modular_ss220/emotes/audio/skrell/laugh_female_3.ogg')
+ suicide_messages = list(
+ "пытается откусить себе язык!",
+ "выдавливает большими пальцами свои глазницы!",
+ "сворачивает себе шею!",
+ "задыхается словно рыба!",
+ "душит себя собственными усиками!")
+
+/datum/species/slime
+ male_scream_sound = 'modular_ss220/emotes/audio/scream_jelly.ogg'
+ female_scream_sound = 'modular_ss220/emotes/audio/scream_jelly.ogg'
+ suicide_messages = list(
+ "тает в лужу!",
+ "растекается в лужу!",
+ "становится растаявшим желе!",
+ "вырывает собственное ядро!",
+ "становится коричневым, тусклым и растекается в лужу!")
+
+/datum/species/tajaran
+ autohiss_basic_map = list(
+ "r" = list("rr", "rrr", "rrrr"),
+ "р" = list("рр", "ррр", "рррр"))
+ autohiss_exempt = list("Siik'tajr")
+
+ male_scream_sound = 'modular_ss220/emotes/audio/tajaran/scream_tajaran.ogg'
+ female_scream_sound = 'modular_ss220/emotes/audio/tajaran/scream_tajaran.ogg'
+ suicide_messages = list(
+ "пытается откусить себе язык!",
+ "вонзает когти себе в глазницы!",
+ "сворачивает себе шею!",
+ "задерживает дыхание!")
+
+/datum/species/unathi
+ autohiss_basic_map = list(
+ "s" = list("ss", "sss", "ssss"),
+ "с" = list("сс", "ссс", "сссс"))
+ autohiss_extra_map = list(
+ "x" = list("ks", "kss", "ksss"),
+ "ш" = list("шш", "шшш", "шшшш"),
+ "ч" = list("щ", "щщ", "щщщ"))
+ autohiss_exempt = list("Sinta'unathi")
+
+ speech_sounds = list(
+ 'modular_ss220/emotes/audio/unathi/talk_unathi_1.ogg',
+ 'modular_ss220/emotes/audio/unathi/talk_unathi_2.ogg',
+ 'modular_ss220/emotes/audio/unathi/talk_unathi_3.ogg')
+ speech_chance = 20
+ male_scream_sound = 'modular_ss220/emotes/audio/unathi/scream_male.ogg'
+ female_scream_sound = 'modular_ss220/emotes/audio/unathi/scream_female.ogg'
+ male_sneeze_sound = 'modular_ss220/emotes/audio/unathi/sneeze_male.ogg'
+ female_sneeze_sound = 'modular_ss220/emotes/audio/unathi/sneeze_female.ogg'
+ death_sounds = 'modular_ss220/emotes/audio/unathi/deathsound_unathi.ogg'
+ suicide_messages = list(
+ "пытается откусить себе язык!",
+ "вонзает когти себе в глазницы!",
+ "сворачивает себе шею!",
+ "задерживает дыхание!")
+
+/datum/species/vox
+ autohiss_basic_map = list(
+ "ch" = list("ch", "chch", "chich"),
+ "k" = list("k", "kk", "kik"),
+ "ч" = list("ч", "чч", "чич"),
+ "к" = list("к", "кк", "кик"))
+ autohiss_exempt = list("Vox-pidgin")
+
+ scream_verb = "скрипит"
+ suicide_messages = list(
+ "пытается откусить себе язык!",
+ "вонзает когти себе в глазницы!",
+ "сворачивает себе шею!",
+ "задерживает дыхание!",
+ "глубоко вдыхает кислород!")
+
+/datum/species/vulpkanin
+ autohiss_basic_map = list(
+ "r" = list("r", "rr", "rrr"),
+ "р" = list("р", "рр", "ррр"))
+ autohiss_exempt = list("Canilunzt")
+
+ scream_verb = "скулит"
+ suicide_messages = list(
+ "пытается откусить себе язык!",
+ "выдавливает когтями свои глазницы!",
+ "сворачивает себе шею!",
+ "задерживает дыхание!")
+
+/datum/species/monkey
+ scream_verb = "визжит"
diff --git a/modular_ss220/events/_events.dm b/modular_ss220/events/_events.dm
new file mode 100644
index 000000000000..9e38e87f6379
--- /dev/null
+++ b/modular_ss220/events/_events.dm
@@ -0,0 +1,4 @@
+/datum/modpack/events
+ name = "Кастомные ивенты"
+ desc = "Добавление новых ивентов"
+ author = "dj-34"
diff --git a/modular_ss220/events/_events.dme b/modular_ss220/events/_events.dme
new file mode 100644
index 000000000000..967093b11acb
--- /dev/null
+++ b/modular_ss220/events/_events.dme
@@ -0,0 +1,3 @@
+#include "_events.dm"
+
+#include "code/new_space_laws.dm"
diff --git a/modular_ss220/events/code/new_space_laws.dm b/modular_ss220/events/code/new_space_laws.dm
new file mode 100644
index 000000000000..fb92dd071542
--- /dev/null
+++ b/modular_ss220/events/code/new_space_laws.dm
@@ -0,0 +1,10 @@
+/datum/event/new_space_law
+ announceWhen = 1
+
+/datum/event/new_space_law/announce()
+ var/list/new_space_laws = file2list("strings/new_space_laws.txt")
+ GLOB.major_announcement.Announce("В связи с последними событиями в космической политике, [pick(new_space_laws)] теперь признается (или признаются) незаконным(-ыми) по кодовому номеру «1xx» Космического Закона. Вы обязаны незамедлительно исправить ситуацию в течение 15 минут. Мы настоятельно рекомендуем вам поторопиться, чтобы избежать возможных негативных последствий.", "Юридический отдел Нанотрейзен")
+
+/datum/event_container/mundane/New()
+ . = ..()
+ available_events |= new /datum/event_meta(EVENT_LEVEL_MUNDANE, "New Space Law", /datum/event/new_space_law, 80, TRUE)
diff --git a/modular_ss220/example/_example.dm b/modular_ss220/example/_example.dm
new file mode 100644
index 000000000000..234336718403
--- /dev/null
+++ b/modular_ss220/example/_example.dm
@@ -0,0 +1,16 @@
+/datum/modpack/example
+ /// A string name for the modpack. Used for looking up other modpacks in init.
+ name = "Example modpack"
+ /// A string desc for the modpack. Can be used for modpack verb list as description.
+ desc = "its useless"
+ /// A string with authors of this modpack.
+ author = "furior"
+
+/datum/modpack/example/pre_initialize()
+ . = ..()
+
+/datum/modpack/example/initialize()
+ . = ..()
+
+/datum/modpack/example/post_initialize()
+ . = ..()
diff --git a/modular_ss220/example/_example.dme b/modular_ss220/example/_example.dme
new file mode 100644
index 000000000000..5540c273b03b
--- /dev/null
+++ b/modular_ss220/example/_example.dme
@@ -0,0 +1,3 @@
+#include "_example.dm"
+
+#include "code/example.dm"
diff --git a/modular_ss220/example/code/example.dm b/modular_ss220/example/code/example.dm
new file mode 100644
index 000000000000..4dd5a3a28bc0
--- /dev/null
+++ b/modular_ss220/example/code/example.dm
@@ -0,0 +1,2 @@
+/turf/simulated/wall/example
+ name = "Example wall"
diff --git a/modular_ss220/food/_food.dm b/modular_ss220/food/_food.dm
new file mode 100644
index 000000000000..63dae63cf48d
--- /dev/null
+++ b/modular_ss220/food/_food.dm
@@ -0,0 +1,4 @@
+/datum/modpack/food
+ name = "Новая еда/напитики"
+ desc = "Различная новая еда и напитки."
+ author = "furior"
diff --git a/modular_ss220/food/_food.dme b/modular_ss220/food/_food.dme
new file mode 100644
index 000000000000..52b2b79af838
--- /dev/null
+++ b/modular_ss220/food/_food.dme
@@ -0,0 +1,4 @@
+#include "_food.dm"
+
+#include "code/drinks.dm"
+#include "code/food.dm"
diff --git a/modular_ss220/food/code/drinks.dm b/modular_ss220/food/code/drinks.dm
new file mode 100644
index 000000000000..3d941f3dbbdc
--- /dev/null
+++ b/modular_ss220/food/code/drinks.dm
@@ -0,0 +1,54 @@
+/obj/item/reagent_containers/food/drinks/drinkingglass/on_reagent_change()
+ . = ..()
+ if(!reagents.reagent_list.len)
+ icon = initial(icon)
+ return
+ var/datum/reagent/reagent = reagents.get_master_reagent()
+ if(!istype(reagent, /datum/reagent/consumable/ethanol))
+ return
+ var/datum/reagent/consumable/ethanol/booze = reagent
+ icon = booze.drinking_glass_icon
+
+/datum/reagent/consumable/ethanol
+ var/drinking_glass_icon = 'icons/obj/drinks.dmi'
+
+/obj/machinery/chem_dispenser/beer/Initialize(mapload)
+ . = ..()
+ dispensable_reagents |= "sambuka"
+
+/obj/item/handheld_chem_dispenser/booze/Initialize(mapload)
+ . = ..()
+ dispensable_reagents |= "sambuka"
+
+/datum/reagent/consumable/ethanol/sambuka
+ name = "Sambuka"
+ id = "sambuka"
+ description = "Flying into space, many thought that they had grasped fate."
+ color = "#e0e0e0"
+ alcohol_perc = 0.45
+ dizzy_adj = 1
+ drink_icon = "sambukaglass"
+ drinking_glass_icon = 'modular_ss220/food/icons/drinks.dmi'
+ drink_name = "Glass of Sambuka"
+ drink_desc = "Flying into space, many thought that they had grasped fate."
+ taste_description = "twirly fire"
+
+/datum/reagent/consumable/ethanol/innocent_erp
+ name = "Innocent ERP"
+ id = "innocent_erp"
+ description = "Remember that big brother sees everything."
+ color = "#746463"
+ alcohol_perc = 0.5
+ drink_icon = "innocent_erp"
+ drinking_glass_icon = 'modular_ss220/food/icons/drinks.dmi'
+ drink_name = "Innocent ERP"
+ drink_desc = "Remember that big brother sees everything."
+ taste_description = "loss of flirtatiousness"
+
+/datum/chemical_reaction/innocent_erp
+ name = "Innocent ERP"
+ id = "innocent_erp"
+ result = "innocent_erp"
+ required_reagents = list("sambuka" = 3, "triple_citrus" = 1, "irishcream" = 1)
+ result_amount = 5
+ mix_sound = 'sound/goonstation/misc/drinkfizz.ogg'
diff --git a/modular_ss220/food/code/food.dm b/modular_ss220/food/code/food.dm
new file mode 100644
index 000000000000..4e9bfdd83197
--- /dev/null
+++ b/modular_ss220/food/code/food.dm
@@ -0,0 +1,46 @@
+// Reagent Grinder
+/obj/machinery/reagentgrinder/Initialize(mapload)
+ . = ..()
+ blend_items = list(/obj/item/reagent_containers/food/snacks/grown/buckwheat = list("buckwheat" = -5)) + blend_items
+
+// Buckwheat
+/datum/reagent/consumable/buckwheat
+ name = "Гречка"
+ id = "buckwheat"
+ description = "Ходят слухи, что советские люди жрут только водку и... это?"
+ reagent_state = SOLID
+ nutriment_factor = 3 * REAGENTS_METABOLISM
+ color = "#8E633C"
+ taste_description = "сухая гречка"
+
+/obj/item/reagent_containers/food/snacks/boiledbuckwheat
+ name = "варённая гречка"
+ desc = "Это просто варённая гречка, ничего необычного."
+ icon = 'modular_ss220/food/icons/food.dmi'
+ icon_state = "boiledbuckwheat"
+ trash = /obj/item/trash/plate
+ filling_color = "#8E633C"
+ list_reagents = list("nutriment" = 5, "vitamin" = 1)
+ tastes = list("гречка" = 1)
+
+/datum/recipe/microwave/boiledbuckwheat
+ reagents = list("water" = 5, "buckwheat" = 10)
+ result = /obj/item/reagent_containers/food/snacks/boiledbuckwheat
+
+/obj/item/reagent_containers/food/snacks/buckwheat_merchant
+ name = "гречка по-купечески"
+ desc = "Тушённая гречка с овощами и мясом."
+ icon = 'modular_ss220/food/icons/food.dmi'
+ icon_state = "buckwheat_merchant"
+ trash = /obj/item/trash/plate
+ filling_color = "#8E633C"
+ list_reagents = list("nutriment" = 5, "protein" = 2, "vitamin" = 3)
+ tastes = list("гречка" = 2, "мясо" = 2, "томатный соус" = 1)
+
+/datum/recipe/microwave/buckwheat_merchant
+ reagents = list("water" = 5, "buckwheat" = 10)
+ items = list(
+ /obj/item/reagent_containers/food/snacks/grown/tomato,
+ /obj/item/reagent_containers/food/snacks/grown/carrot,
+ /obj/item/reagent_containers/food/snacks/meat)
+ result = /obj/item/reagent_containers/food/snacks/buckwheat_merchant
diff --git a/modular_ss220/food/icons/drinks.dmi b/modular_ss220/food/icons/drinks.dmi
new file mode 100644
index 000000000000..22399084a1a4
Binary files /dev/null and b/modular_ss220/food/icons/drinks.dmi differ
diff --git a/modular_ss220/food/icons/food.dmi b/modular_ss220/food/icons/food.dmi
new file mode 100644
index 000000000000..bc2517a312cf
Binary files /dev/null and b/modular_ss220/food/icons/food.dmi differ
diff --git a/modular_ss220/fullscreen/_fullscreen.dm b/modular_ss220/fullscreen/_fullscreen.dm
new file mode 100644
index 000000000000..1c3a2a238f44
--- /dev/null
+++ b/modular_ss220/fullscreen/_fullscreen.dm
@@ -0,0 +1,4 @@
+/datum/modpack/fullscreen
+ name = "Fullscreen"
+ desc = "Добавляет полноэкранный режим"
+ author = "larentoun"
diff --git a/modular_ss220/fullscreen/_fullscreen.dme b/modular_ss220/fullscreen/_fullscreen.dme
new file mode 100644
index 000000000000..37df23aee29b
--- /dev/null
+++ b/modular_ss220/fullscreen/_fullscreen.dme
@@ -0,0 +1,5 @@
+#include "_fullscreen.dm"
+
+#include "code/client_defines.dm"
+#include "code/client_procs.dm"
+#include "code/client_keybindings.dm"
diff --git a/modular_ss220/fullscreen/code/client_defines.dm b/modular_ss220/fullscreen/code/client_defines.dm
new file mode 100644
index 000000000000..508ea2e46b9f
--- /dev/null
+++ b/modular_ss220/fullscreen/code/client_defines.dm
@@ -0,0 +1,2 @@
+/client
+ var/fullscreen = FALSE
diff --git a/modular_ss220/fullscreen/code/client_keybindings.dm b/modular_ss220/fullscreen/code/client_keybindings.dm
new file mode 100644
index 000000000000..4f5a431547db
--- /dev/null
+++ b/modular_ss220/fullscreen/code/client_keybindings.dm
@@ -0,0 +1,7 @@
+/datum/keybinding/client/t_fullscreen
+ name = "Переключить Fullscreen"
+ keys = list("F11")
+
+/datum/keybinding/client/t_fullscreen/down(client/C)
+ . = ..()
+ C.toggle_fullscreen()
diff --git a/modular_ss220/fullscreen/code/client_procs.dm b/modular_ss220/fullscreen/code/client_procs.dm
new file mode 100644
index 000000000000..85890f0e2f70
--- /dev/null
+++ b/modular_ss220/fullscreen/code/client_procs.dm
@@ -0,0 +1,21 @@
+/client/verb/toggle_fullscreen()
+ set name = "Toggle Fullscreen"
+ set category = "OOC"
+
+ fullscreen = !fullscreen
+
+ if (fullscreen)
+ winset(usr, "mainwindow", "on-size=")
+ winset(usr, "mainwindow", "titlebar=false")
+ winset(usr, "mainwindow", "can-resize=false")
+ winset(usr, "mainwindow", "menu=")
+ winset(usr, "mainwindow", "is-maximized=false")
+ winset(usr, "mainwindow", "is-maximized=true")
+ else
+ winset(usr, "mainwindow", "titlebar=true")
+ winset(usr, "mainwindow", "can-resize=true")
+ winset(usr, "mainwindow", "menu=menu")
+ winset(usr, "mainwindow", "is-maximized=false")
+ winset(usr, "mainwindow", "on-size=fitviewport")
+
+ fit_viewport()
diff --git a/modular_ss220/gateway/_gateway.dm b/modular_ss220/gateway/_gateway.dm
new file mode 100644
index 000000000000..ead08d91197c
--- /dev/null
+++ b/modular_ss220/gateway/_gateway.dm
@@ -0,0 +1,4 @@
+/datum/modpack/gateway
+ name = "Гейт"
+ desc = "Возвращает гейт, зачем..."
+ author = "Aylong220"
diff --git a/modular_ss220/gateway/_gateway.dme b/modular_ss220/gateway/_gateway.dme
new file mode 100644
index 000000000000..a9385ad57749
--- /dev/null
+++ b/modular_ss220/gateway/_gateway.dme
@@ -0,0 +1,6 @@
+#include "_gateway.dm"
+
+#include "code/gateway.dm"
+#include "code/gateway_config.dm"
+#include "code/gateway_initialize.dm"
+#include "code/jobs.dm"
diff --git a/modular_ss220/gateway/code/gateway.dm b/modular_ss220/gateway/code/gateway.dm
new file mode 100644
index 000000000000..d4304ab3ab9c
--- /dev/null
+++ b/modular_ss220/gateway/code/gateway.dm
@@ -0,0 +1,300 @@
+/* Now you can click through gateways while observing. */
+/obj/machinery/gateway/centerstation/attack_ghost(mob/user as mob)
+ if(awaygate)
+ user.forceMove(awaygate.loc)
+ else
+ to_chat(user, "[src] не имеет пункта назначения.")
+
+/obj/machinery/gateway/centeraway/attack_ghost(mob/user as mob)
+ if(stationgate)
+ user.forceMove(stationgate.loc)
+ else
+
+ to_chat(user, "[src] не имеет пункта назначения.")
+
+GLOBAL_DATUM_INIT(the_gateway, /obj/machinery/gateway/centerstation, null)
+/obj/machinery/gateway
+ name = "gateway"
+ desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations."
+ icon = 'icons/obj/machines/gateway.dmi'
+ icon_state = "off"
+ density = TRUE
+ anchored = TRUE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ flags_2 = NO_MALF_EFFECT_2
+ var/active = FALSE
+
+/obj/machinery/gateway/Initialize()
+ ..()
+ update_icon(UPDATE_ICON_STATE)
+ update_density_from_dir()
+
+/obj/machinery/gateway/proc/update_density_from_dir()
+ if(dir in list(SOUTH, SOUTHEAST, SOUTHWEST))
+ density = FALSE
+
+/obj/machinery/gateway/update_icon_state()
+ icon_state = active ? "on" : "off"
+
+
+// This is da important part wot makes things go
+/obj/machinery/gateway/centerstation
+ density = TRUE
+ icon_state = "offcenter"
+ power_state = IDLE_POWER_USE
+
+
+ // Warping vars
+ var/list/linked = list()
+ var/ready = FALSE // Have we got all the parts for a gateway?
+ var/obj/machinery/gateway/centeraway/awaygate = null
+
+/obj/machinery/gateway/centerstation/Initialize(mapload)
+ . = ..()
+ if(!GLOB.the_gateway)
+ GLOB.the_gateway = src
+
+ update_icon(UPDATE_ICON_STATE)
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/gateway/centerstation/LateInitialize()
+ awaygate = locate(/obj/machinery/gateway/centeraway) in GLOB.machines
+
+/obj/machinery/gateway/centerstation/update_density_from_dir()
+ return
+
+/obj/machinery/gateway/centerstation/Destroy()
+ if(GLOB.the_gateway == src)
+ GLOB.the_gateway = null
+ return ..()
+
+/obj/machinery/gateway/centerstation/update_icon_state()
+ icon_state = active ? "oncenter" : "offcenter"
+
+
+/obj/machinery/gateway/centerstation/process()
+ if(stat & (NOPOWER))
+ if(active) toggleoff()
+ return
+
+ if(active)
+ use_power(5000)
+
+
+/obj/machinery/gateway/centerstation/proc/detect()
+ linked = list() //clear the list
+ var/turf/T = loc
+
+ for(var/i in GLOB.alldirs)
+ T = get_step(loc, i)
+ var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T
+ if(G)
+ linked.Add(G)
+ continue
+
+ // This is only done if we fail to find a part
+ ready = FALSE
+ toggleoff()
+ break
+
+ if(linked.len == 8)
+ ready = TRUE
+
+
+/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob)
+ if(!ready)
+ return
+ if(linked.len != 8)
+ return
+ if(!has_power())
+ return
+ if(!awaygate)
+ awaygate = locate(/obj/machinery/gateway/centeraway) in GLOB.machines
+ if(!awaygate)
+ to_chat(user, span_notice("Error: No destination found."))
+ return
+ var/wait = GLOB.configuration.gateway.away_mission_delay + SSticker.round_start_time
+ if(wait > world.time)
+ to_chat(user, span_notice("Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes."))
+ return
+
+ for(var/obj/machinery/gateway/G in linked)
+ G.active = TRUE
+ G.update_icon(UPDATE_ICON_STATE)
+ active = TRUE
+ update_icon(UPDATE_ICON_STATE)
+
+
+/obj/machinery/gateway/centerstation/proc/toggleoff()
+ for(var/obj/machinery/gateway/G in linked)
+ G.active = FALSE
+ G.update_icon(UPDATE_ICON_STATE)
+ active = FALSE
+ update_icon(UPDATE_ICON_STATE)
+
+
+/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob)
+ if(!ready)
+ detect()
+ return
+ if(!active)
+ toggleon(user)
+ return
+ toggleoff()
+
+
+// Okay, here's the good teleporting stuff
+/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj)
+ if(!ready)
+ return
+ if(!active)
+ return
+ if(!awaygate)
+ return
+
+ if(awaygate.calibrated)
+ M.forceMove(get_step(awaygate.loc, SOUTH))
+ M.dir = SOUTH
+ return
+ else
+ var/obj/effect/landmark/dest = pick(GLOB.awaydestinations)
+ if(dest)
+ M.forceMove(dest.loc)
+ M.dir = SOUTH
+ use_power(5000)
+ return
+
+
+/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob, params)
+ if(istype(W,/obj/item/multitool))
+ to_chat(user, "The gate is already calibrated, there is no work for you to do here.")
+ return
+ return ..()
+
+/////////////////////////////////////Away////////////////////////
+
+
+/obj/machinery/gateway/centeraway
+ density = TRUE
+ icon_state = "offcenter"
+ power_state = NO_POWER_USE
+
+ var/calibrated = TRUE
+ var/list/linked = list() // A list of the connected gateway chunks
+ var/ready = FALSE
+ var/obj/machinery/gateway/centeraway/stationgate = null
+
+
+/obj/machinery/gateway/centeraway/Initialize()
+ ..()
+ update_icon(UPDATE_ICON_STATE)
+ stationgate = locate(/obj/machinery/gateway/centerstation) in GLOB.machines
+
+
+/obj/machinery/gateway/centeraway/update_density_from_dir()
+ return
+
+/obj/machinery/gateway/centeraway/update_icon_state()
+ icon_state = active ? "oncenter" : "offcenter"
+
+
+/obj/machinery/gateway/centeraway/proc/detect()
+ linked.Cut()
+ var/turf/T = loc
+
+ for(var/i in GLOB.alldirs)
+ T = get_step(loc, i)
+ var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T
+ if(G)
+ linked.Add(G)
+ continue
+
+ // This is only done if we fail to find a part
+ ready = FALSE
+ toggleoff()
+ break
+
+ if(length(linked) == 8)
+ ready = TRUE
+
+
+/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob)
+ if(!ready)
+ return
+ if(length(linked) != 8)
+ return
+ if(!stationgate)
+ stationgate = locate(/obj/machinery/gateway/centerstation) in GLOB.machines
+ if(!stationgate)
+ to_chat(user, span_notice("Error: No destination found."))
+ return
+
+ for(var/obj/machinery/gateway/G in linked)
+ G.active = TRUE
+ G.update_icon(UPDATE_ICON_STATE)
+ active = TRUE
+ update_icon(UPDATE_ICON_STATE)
+
+
+/obj/machinery/gateway/centeraway/proc/toggleoff()
+ for(var/obj/machinery/gateway/G in linked)
+ G.active = FALSE
+ G.update_icon(UPDATE_ICON_STATE)
+ active = FALSE
+ update_icon(UPDATE_ICON_STATE)
+
+
+/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob)
+ if(!ready)
+ detect()
+ return
+ if(!active)
+ toggleon(user)
+ return
+ toggleoff()
+
+
+/obj/machinery/gateway/centeraway/Bumped(atom/movable/AM)
+ if(!ready)
+ return
+ if(!active)
+ return
+ if(!stationgate || QDELETED(stationgate))
+ return
+ if(isliving(AM))
+ if(exilecheck(AM))
+ return
+ else
+ for(var/mob/living/L in AM.contents)
+ if(exilecheck(L))
+ atom_say("Rejecting [AM]: Exile bio-chip detected in contained lifeform.")
+ return
+ if(AM.has_buckled_mobs())
+ for(var/mob/living/L in AM.buckled_mobs)
+ if(exilecheck(L))
+ atom_say("Rejecting [AM]: Exile bio-chip detected in close proximity lifeform.")
+ return
+ AM.forceMove(get_step(stationgate.loc, SOUTH))
+ AM.setDir(SOUTH)
+ if(ismob(AM))
+ var/mob/M = AM
+ if(M.client)
+ M.client.move_delay = max(world.time + 5, M.client.move_delay)
+
+/obj/machinery/gateway/centeraway/proc/exilecheck(mob/living/carbon/M)
+ for(var/obj/item/implant/exile/E in M) // Checking that there is an exile bio-chip in the contents
+ if(E.imp_in == M) // Checking that it's actually implanted vs just in their pocket
+ to_chat(M, span_notice("The station gate has detected your exile bio-chip and is blocking your entry."))
+ return TRUE
+ return FALSE
+
+/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob, params)
+ if(istype(W,/obj/item/multitool))
+ if(calibrated)
+ to_chat(user, span_notice("The gate is already calibrated, there is no work for you to do here."))
+ return
+ else
+ to_chat(user, span_boldannounce("Recalibration successful!") + span_notice(": This gate's systems have been fine tuned. Travel to this gate will now be on target."))
+ calibrated = TRUE
+ return
+ return ..()
diff --git a/modular_ss220/gateway/code/gateway_config.dm b/modular_ss220/gateway/code/gateway_config.dm
new file mode 100644
index 000000000000..a3e5d8b46e7f
--- /dev/null
+++ b/modular_ss220/gateway/code/gateway_config.dm
@@ -0,0 +1,22 @@
+/datum/server_configuration
+ /// Holder for the gateway configuration datum
+ var/datum/configuration_section/gateway_configuration/gateway
+
+/datum/server_configuration/load_all_sections()
+ . = ..()
+ gateway = new()
+ safe_load(gateway, "gateway_configuration")
+
+/datum/configuration_section/gateway_configuration
+ /// Do we want to enable away missions or not
+ var/enable_away_mission = TRUE
+ /// Delay (in deciseconds) before the gateway is usable
+ var/away_mission_delay = 6000
+ /// List of all available away missions
+ var/list/enabled_away_missions = list()
+
+/datum/configuration_section/gateway_configuration/load_data(list/data)
+ // Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
+ CONFIG_LOAD_BOOL(enable_away_mission, data["enable_away_mission"])
+ CONFIG_LOAD_NUM(away_mission_delay, data["away_mission_delay"])
+ CONFIG_LOAD_LIST(enabled_away_missions, data["enabled_away_missions"])
diff --git a/modular_ss220/gateway/code/gateway_initialize.dm b/modular_ss220/gateway/code/gateway_initialize.dm
new file mode 100644
index 000000000000..789d86ee3300
--- /dev/null
+++ b/modular_ss220/gateway/code/gateway_initialize.dm
@@ -0,0 +1,30 @@
+/datum/controller/subsystem/mapping/Initialize()
+ . = ..()
+ // Pick a random away mission.
+ if(GLOB.configuration.gateway.enable_away_mission)
+ load_away_mission()
+ else
+ log_startup_progress("Skipping away mission...")
+
+/datum/controller/subsystem/mapping/proc/load_away_mission()
+ if(!length(GLOB.configuration.gateway.enabled_away_missions))
+ log_startup_progress("No away missions found.")
+ return
+
+ var/watch = start_watch()
+ log_startup_progress("Loading away mission...")
+
+ var/map = pick(GLOB.configuration.gateway.enabled_away_missions)
+ var/file = wrap_file(map)
+ if(!isfile(file))
+ log_startup_progress("Picked away mission doesnt exist.")
+ return
+
+ var/zlev = GLOB.space_manager.add_new_zlevel(AWAY_MISSION, linkage = UNAFFECTED, traits = list(AWAY_LEVEL, BLOCK_TELEPORT))
+ GLOB.space_manager.add_dirt(zlev)
+ GLOB.maploader.load_map(file, z_offset = zlev)
+ late_setup_level(block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev)))
+ GLOB.space_manager.remove_dirt(zlev)
+ log_world("Away mission loaded: [map]")
+
+ log_startup_progress("Away mission loaded in [stop_watch(watch)]s.")
diff --git a/modular_ss220/gateway/code/jobs.dm b/modular_ss220/gateway/code/jobs.dm
new file mode 100644
index 000000000000..1c8b25bd7e97
--- /dev/null
+++ b/modular_ss220/gateway/code/jobs.dm
@@ -0,0 +1,3 @@
+/datum/job/explorer
+ spawn_positions = 0
+ total_positions = 0
diff --git a/modular_ss220/gunhud/_gunhud.dm b/modular_ss220/gunhud/_gunhud.dm
new file mode 100644
index 000000000000..9c735d459bfc
--- /dev/null
+++ b/modular_ss220/gunhud/_gunhud.dm
@@ -0,0 +1,4 @@
+/datum/modpack/gunhud
+ name = "Счетчик патронов"
+ desc = "Добавляет счетчик патронов"
+ author = "larentoun"
diff --git a/modular_ss220/gunhud/_gunhud.dme b/modular_ss220/gunhud/_gunhud.dme
new file mode 100644
index 000000000000..df823a3dd7e8
--- /dev/null
+++ b/modular_ss220/gunhud/_gunhud.dme
@@ -0,0 +1,6 @@
+#include "_gunhud.dm"
+
+#include "code/_gunhud_defines.dm"
+#include "code/gunhud_component.dm"
+#include "code/gunhud_gun.dm"
+#include "code/gunhud_hud.dm"
diff --git a/modular_ss220/gunhud/code/_gunhud_defines.dm b/modular_ss220/gunhud/code/_gunhud_defines.dm
new file mode 100644
index 000000000000..36f1cf8182b0
--- /dev/null
+++ b/modular_ss220/gunhud/code/_gunhud_defines.dm
@@ -0,0 +1,5 @@
+// Ammo counter
+#define ui_ammocounter "RIGHT-1:28,CENTER-5:28"
+
+///The gun needs to update the gun hud!
+#define COMSIG_UPDATE_GUNHUD "update_gunhud"
diff --git a/modular_ss220/gunhud/code/gunhud_component.dm b/modular_ss220/gunhud/code/gunhud_component.dm
new file mode 100644
index 000000000000..57296fdfdddf
--- /dev/null
+++ b/modular_ss220/gunhud/code/gunhud_component.dm
@@ -0,0 +1,171 @@
+/datum/component/gunhud
+ var/obj/screen/ammo_counter/hud
+
+/datum/component/gunhud/Initialize()
+ . = ..()
+ if(!istype(parent, /obj/item/gun) && !istype(parent, /obj/item/weldingtool) || istype(parent, /obj/item/gun/projectile/revolver))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(wake_up))
+
+/datum/component/gunhud/Destroy()
+ turn_off()
+ return ..()
+
+/datum/component/gunhud/proc/wake_up(datum/source, mob/user, slot)
+ SIGNAL_HANDLER
+
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(H.get_active_hand() == parent || H.get_inactive_hand() == parent)
+ if(H.hud_used)
+ hud = H.hud_used.ammo_counter
+ turn_on()
+ else
+ turn_off()
+
+/datum/component/gunhud/proc/turn_on()
+ SIGNAL_HANDLER
+
+ RegisterSignal(parent, COMSIG_PARENT_PREQDELETED, PROC_REF(turn_off))
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(turn_off))
+ RegisterSignal(parent, COMSIG_UPDATE_GUNHUD, PROC_REF(update_hud))
+
+ hud.turn_on()
+ update_hud()
+
+/datum/component/gunhud/proc/turn_off()
+ SIGNAL_HANDLER
+
+ UnregisterSignal(parent, COMSIG_PARENT_PREQDELETED)
+ UnregisterSignal(parent, COMSIG_ITEM_DROPPED)
+ UnregisterSignal(parent, COMSIG_UPDATE_GUNHUD)
+
+ if(hud)
+ hud.turn_off()
+ hud = null
+
+/datum/component/gunhud/proc/update_hud()
+ SIGNAL_HANDLER
+ if(istype(parent, /obj/item/gun/projectile))
+ var/obj/item/gun/projectile/pew = parent
+ hud.maptext = null
+ hud.icon_state = "backing"
+ var/backing_color = COLOR_CYAN
+ if(!pew.magazine)
+ hud.set_hud(backing_color, "oe", "te", "he", "no_mag")
+ return
+ if(!pew.get_ammo())
+ hud.set_hud(backing_color, "oe", "te", "he", "empty_flash")
+ return
+
+ var/indicator
+ var/rounds = num2text(pew.get_ammo(TRUE))
+ var/oth_o
+ var/oth_t
+ var/oth_h
+
+ switch(length(rounds))
+ if(1)
+ oth_o = "o[rounds[1]]"
+ if(2)
+ oth_o = "o[rounds[2]]"
+ oth_t = "t[rounds[1]]"
+ if(3)
+ oth_o = "o[rounds[3]]"
+ oth_t = "t[rounds[2]]"
+ oth_h = "h[rounds[1]]"
+ else
+ oth_o = "o9"
+ oth_t = "t9"
+ oth_h = "h9"
+ hud.set_hud(backing_color, oth_o, oth_t, oth_h, indicator)
+ return
+
+ if(istype(parent, /obj/item/gun/energy))
+ var/obj/item/gun/energy/pew = parent
+ hud.icon_state = "eammo_counter"
+ hud.cut_overlays()
+ hud.maptext_x = -12
+ var/obj/item/ammo_casing/energy/shot = pew.ammo_type[pew.select]
+ var/batt_percent = FLOOR(clamp(pew.cell.charge / pew.cell.maxcharge, 0, 1) * 100, 1)
+ var/shot_cost_percent = FLOOR(clamp(shot.e_cost / pew.cell.maxcharge, 0, 1) * 100, 1)
+ if(batt_percent > 99 || shot_cost_percent > 99)
+ hud.maptext_x = -12
+ else
+ hud.maptext_x = -8
+ if(!pew.can_shoot())
+ hud.icon_state = "eammo_counter_empty"
+ hud.maptext = MAPTEXT("[batt_percent]% [shot_cost_percent]% ")
+ return
+ if(batt_percent <= 25)
+ hud.maptext = MAPTEXT("[batt_percent]% [shot_cost_percent]% ")
+ return
+ hud.maptext = MAPTEXT("[batt_percent]% [shot_cost_percent]% ")
+ return
+
+ if(istype(parent, /obj/item/weldingtool))
+ var/obj/item/weldingtool/welder = parent
+ hud.maptext = null
+ var/backing_color = "#FF7B00"
+ hud.icon_state = "backing"
+
+ if(welder.GET_FUEL < 1)
+ hud.set_hud(backing_color, "oe", "te", "he", "empty_flash")
+ return
+
+ var/indicator
+ var/fuel
+ var/oth_o
+ var/oth_t
+ var/oth_h
+
+ if(welder.tool_enabled)
+ indicator = "flame_on"
+ else
+ indicator = "flame_off"
+
+ fuel = num2text(round(welder.GET_FUEL))
+
+ switch(length(fuel))
+ if(1)
+ oth_o = "o[fuel[1]]"
+ if(2)
+ oth_o = "o[fuel[2]]"
+ oth_t = "t[fuel[1]]"
+ if(3)
+ oth_o = "o[fuel[3]]"
+ oth_t = "t[fuel[2]]"
+ oth_h = "h[fuel[1]]"
+ else
+ oth_o = "o9"
+ oth_t = "t9"
+ oth_h = "h9"
+ hud.set_hud(backing_color, oth_o, oth_t, oth_h, indicator)
+
+/obj/item/proc/add_gunhud()
+ return
+
+/obj/item/gun/projectile/add_gunhud()
+ AddComponent(/datum/component/gunhud)
+
+/obj/item/gun/projectile/revolver/add_gunhud()
+ return
+
+/obj/item/gun/energy/add_gunhud()
+ AddComponent(/datum/component/gunhud)
+
+/obj/item/weldingtool/add_gunhud()
+ AddComponent(/datum/component/gunhud)
+
+/obj/item/gun/projectile/Initialize(mapload)
+ . = ..()
+ add_gunhud()
+
+/obj/item/gun/energy/Initialize(mapload)
+ . = ..()
+ add_gunhud()
+
+/obj/item/weldingtool/Initialize(mapload)
+ . = ..()
+ add_gunhud()
+
diff --git a/modular_ss220/gunhud/code/gunhud_gun.dm b/modular_ss220/gunhud/code/gunhud_gun.dm
new file mode 100644
index 000000000000..49ed03a46aea
--- /dev/null
+++ b/modular_ss220/gunhud/code/gunhud_gun.dm
@@ -0,0 +1,60 @@
+/obj/item/gun/projectile/attackby(obj/item/A, mob/user, params)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/projectile/attack_self(mob/living/user)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/energy/on_recharge()
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/energy/select_fire(mob/living/user)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/energy/emp_act(severity)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/energy/process_chamber()
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/projectile/process_chamber(eject_casing, empty_chamber)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/projectile/shotgun/pump(mob/M)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/projectile/revolver/attackby(obj/item/A, mob/user, params)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/gun/projectile/revolver/attack_self(mob/living/user)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/weldingtool/toggle_welder(turn_off)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/weldingtool/remove_fuel(amount)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/weldingtool/refill(mob/user, atom/A, amount)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/weldingtool/use(amount)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
+
+/obj/item/weldingtool/process()
+ . = ..()
+ if(refills_over_time && GET_FUEL != maximum_fuel)
+ SEND_SIGNAL(src, COMSIG_UPDATE_GUNHUD)
diff --git a/modular_ss220/gunhud/code/gunhud_hud.dm b/modular_ss220/gunhud/code/gunhud_hud.dm
new file mode 100644
index 000000000000..e4e5a728e60a
--- /dev/null
+++ b/modular_ss220/gunhud/code/gunhud_hud.dm
@@ -0,0 +1,95 @@
+/datum/hud
+ var/obj/screen/ammo_counter
+
+/datum/hud/human/New(mob/living/carbon/human/owner, ui_style, ui_color, ui_alpha)
+ . = ..()
+ ammo_counter = new /obj/screen/ammo_counter()
+ ammo_counter.hud = src
+ infodisplay += ammo_counter
+
+/*
+* Customizable ammo hud
+*/
+
+/*
+* This hud is controlled namely by the gunhud component. Generally speaking this is inactive much like all other hud components until it's needed.
+* It does not do any calculations of it's own, you must do this externally.
+* If you wish to use this hud, use the gunhud component or create another one which interacts with it via the below procs.
+* proc/turn_off
+* proc/turn_on
+* proc/set_hud
+* Check the gunhud.dmi for all available icons you can use.
+*/
+
+/obj/screen/ammo_counter
+ name = "ammo counter"
+ icon = 'modular_ss220/gunhud/icons/gunhud.dmi'
+ icon_state = "backing"
+ screen_loc = ui_ammocounter
+ invisibility = INVISIBILITY_ABSTRACT
+
+ ///This is the color assigned to the OTH backing, numbers and indicator.
+ var/backing_color = COLOR_RED
+ ///This is the "backlight" of the numbers, and only the numbers. Generally you should leave this alone if you aren't making some mutant project.
+ var/oth_backing = "oth_light"
+
+ //Below are the OTH numbers, these are assigned by oX, tX and hX, x being the number you wish to display(0-9)
+ ///OTH position X00
+ var/oth_o
+ ///OTH position 0X0
+ var/oth_t
+ ///OTH position 00X
+ var/oth_h
+ ///This is the custom indicator sprite that will appear in the box at the bottom of the ammo hud, use this for something like semi/auto toggle on a gun.
+ var/indicator
+
+///This proc simply resets the hud to standard and removes it from the players visible hud.
+/obj/screen/ammo_counter/proc/turn_off()
+ invisibility = INVISIBILITY_ABSTRACT
+ maptext = null
+ backing_color = COLOR_RED
+ oth_backing = ""
+ oth_o = ""
+ oth_t = ""
+ oth_h = ""
+ indicator = ""
+ update_appearance()
+
+///This proc turns the hud on, but does not set it to anything other than the currently set values
+/obj/screen/ammo_counter/proc/turn_on()
+ invisibility = 0
+
+///This is the main proc for altering the hud's appeareance, it controls the setting of the overlays. Use the OTH and below variables to set it accordingly.
+/obj/screen/ammo_counter/proc/set_hud(_backing_color, _oth_o, _oth_t, _oth_h, _indicator, _oth_backing = "oth_light")
+ backing_color = _backing_color
+ oth_backing = _oth_backing
+ oth_o = _oth_o
+ oth_t = _oth_t
+ oth_h = _oth_h
+ indicator = _indicator
+
+ update_appearance()
+
+/obj/screen/ammo_counter/update_overlays()
+ . = ..()
+ if(oth_backing)
+ var/mutable_appearance/oth_backing_overlay = mutable_appearance(icon, oth_backing)
+ oth_backing_overlay.color = backing_color
+ . += oth_backing_overlay
+ if(oth_o)
+ var/mutable_appearance/o_overlay = mutable_appearance(icon, oth_o)
+ o_overlay.color = backing_color
+ . += o_overlay
+ if(oth_t)
+ var/mutable_appearance/t_overlay = mutable_appearance(icon, oth_t)
+ t_overlay.color = backing_color
+ . += t_overlay
+ if(oth_h)
+ var/mutable_appearance/h_overlay = mutable_appearance(icon, oth_h)
+ h_overlay.color = backing_color
+ . += h_overlay
+ if(indicator)
+ var/mutable_appearance/indicator_overlay = mutable_appearance(icon, indicator)
+ indicator_overlay.color = backing_color
+ . += indicator_overlay
+
diff --git a/modular_ss220/gunhud/icons/gunhud.dmi b/modular_ss220/gunhud/icons/gunhud.dmi
new file mode 100644
index 000000000000..6bd861100e2e
Binary files /dev/null and b/modular_ss220/gunhud/icons/gunhud.dmi differ
diff --git a/modular_ss220/hairs/_hairs.dm b/modular_ss220/hairs/_hairs.dm
new file mode 100644
index 000000000000..9f082c589cac
--- /dev/null
+++ b/modular_ss220/hairs/_hairs.dm
@@ -0,0 +1,4 @@
+/datum/modpack/hairs
+ name = "Новые волосы"
+ desc = "Добавляет новые типы волос"
+ author = "oricyUwU, FlutterSnedDraw"
diff --git a/modular_ss220/hairs/_hairs.dme b/modular_ss220/hairs/_hairs.dme
new file mode 100644
index 000000000000..873e1c645e17
--- /dev/null
+++ b/modular_ss220/hairs/_hairs.dme
@@ -0,0 +1,11 @@
+#ifndef MODPACK_HAIRS
+#define MODPACK_HAIRS
+
+#endif
+
+// BEGIN INCLUDE
+#include "_hairs.dm"
+#include "code/vulpkanin_hair.dm"
+#include "code/human_hair.dm"
+// END_INCLUDE
+
diff --git a/modular_ss220/hairs/code/human_hair.dm b/modular_ss220/hairs/code/human_hair.dm
new file mode 100644
index 000000000000..f481eb0cf9e1
--- /dev/null
+++ b/modular_ss220/hairs/code/human_hair.dm
@@ -0,0 +1,7 @@
+/*
+== Human Hair Definitions ==
+*/
+
+/datum/sprite_accessory/hair/crew
+ icon = 'modular_ss220/hairs/icons/human_hair.dmi'
+ icon_state = "crewcut"
diff --git a/modular_ss220/hairs/code/vulpkanin_hair.dm b/modular_ss220/hairs/code/vulpkanin_hair.dm
new file mode 100644
index 000000000000..b7b46fbecea7
--- /dev/null
+++ b/modular_ss220/hairs/code/vulpkanin_hair.dm
@@ -0,0 +1,242 @@
+/datum/sprite_accessory/hair/vulpkanin/hair_ponytailf
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Ponytailf"
+ icon_state = "ponytailf"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_pigtailss
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Pigtailss"
+ icon_state = "pigtailss"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_dave
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Dave"
+ icon_state = "dave"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_ziegler
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Ziegler"
+ icon_state = "ziegler"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_hightight
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Hightight"
+ icon_state = "hightight"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_sergeant
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Sergeant"
+ icon_state = "sergeant"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_grande
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Grande"
+ icon_state = "grande"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_beehive2
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Beehive"
+ icon_state = "beehive2"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_longeralt
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Longeralt"
+ icon_state = "longeralt"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_curly
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Curly"
+ icon_state = "curly"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_bunhead3
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Bunhead3"
+ icon_state = "bunhead3"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_ponytailalt
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Ponytail alt."
+ icon_state = "ponytailalt"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_emolong
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Emolong"
+ icon_state = "emolong"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_volajupompless
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Volajupompless"
+ icon_state = "volajupompless"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_bunhead4
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Bunhead4"
+ icon_state = "bunhead4"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_toriyama
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Toriyama"
+ icon_state = "toriyama"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_unkempt
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Unkempt"
+ icon_state = "unkempt"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_nia
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Nia"
+ icon_state = "nia"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_twintail_floor
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Twintail floor"
+ icon_state = "twintail_floor"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_nia_alt
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Nia Alt."
+ icon_state = "nia_alt"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_fluffy_short
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Fluffy Short"
+ icon_state = "fluffy_short"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_simple_ponytail
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Simple Ponytail"
+ icon_state = "simple_ponytail"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_halfshave_snout
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Halfshave"
+ icon_state = "halfshave_snout"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_bluntbangs
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Bluntbangs"
+ icon_state = "bluntbangs"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_bluntbangs_alt
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Bluntbangs Alt."
+ icon_state = "bluntbangs_alt"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_slightymessy
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Slightymessy"
+ icon_state = "slightymessy"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_loghair5
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Loghair 5"
+ icon_state = "loghair5"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_halfshaved
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Halfshaved"
+ icon_state = "halfshaved"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_geisha
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Geisha"
+ icon_state = "geisha"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_cotton
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Cotton"
+ icon_state = "cotton"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_africanpigtails
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "African pigtails"
+ icon_state = "africanpigtails"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_froofylong
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Froofylong"
+ icon_state = "froofylong"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_glammetal
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Glammetal"
+ icon_state = "glammetal"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_astolfo
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Astolfo"
+ icon_state = "astolfo"
+
+/datum/sprite_accessory/hair/vulpkanin/hair_newyou
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "New you"
+ icon_state = "newyou"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_sabitsuki
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Sabitsuki"
+ icon_state = "sabitsuki"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_jessica
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Jessica"
+ icon_state = "jessica"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_country
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Country"
+ icon_state = "country"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_beachwave
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Beachwave"
+ icon_state = "beachwave"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_emoshort
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Emoshort"
+ icon_state = "emoshort"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_shortovereyealt
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Short over eye"
+ icon_state = "shortovereyealt"
+ gender = FEMALE
+
+/datum/sprite_accessory/hair/vulpkanin/hair_spicy
+ icon = 'modular_ss220/hairs/icons/vulpkanin_hair.dmi'
+ name = "Spicy"
+ icon_state = "spicy"
+
diff --git a/modular_ss220/hairs/icons/human_hair.dmi b/modular_ss220/hairs/icons/human_hair.dmi
new file mode 100644
index 000000000000..b614f229a8f0
Binary files /dev/null and b/modular_ss220/hairs/icons/human_hair.dmi differ
diff --git a/modular_ss220/hairs/icons/vulpkanin_hair.dmi b/modular_ss220/hairs/icons/vulpkanin_hair.dmi
new file mode 100644
index 000000000000..d8c135186d6b
Binary files /dev/null and b/modular_ss220/hairs/icons/vulpkanin_hair.dmi differ
diff --git a/modular_ss220/hydroponics/code/biogenerator_designs.dm b/modular_ss220/hydroponics/code/biogenerator_designs.dm
new file mode 100644
index 000000000000..ddd86d5faf65
--- /dev/null
+++ b/modular_ss220/hydroponics/code/biogenerator_designs.dm
@@ -0,0 +1,15 @@
+/datum/design/rollingpapers
+ name = "Бумага для самокруток"
+ id = "rolling_paper_pack"
+ build_type = BIOGENERATOR
+ materials = list(MAT_BIOMASS = 100)
+ build_path = /obj/item/storage/fancy/rollingpapers
+ category = list("initial","Organic Materials")
+
+/datum/design/strange_seeds
+ name = "Странные семена"
+ id = "strange_seeds"
+ build_type = BIOGENERATOR
+ materials = list(MAT_BIOMASS = 10000)
+ build_path = /obj/item/seeds/random
+ category = list("initial","Organic Materials")
diff --git a/modular_ss220/hydroponics/code/dispenser.dm b/modular_ss220/hydroponics/code/dispenser.dm
new file mode 100644
index 000000000000..a967991a4508
--- /dev/null
+++ b/modular_ss220/hydroponics/code/dispenser.dm
@@ -0,0 +1,45 @@
+/obj/machinery/chem_dispenser/botanical
+ name = "ботанический химический раздатчик"
+ desc = "Химический раздатчик, разработанный специально для ботаников."
+ ui_title = "Ботанический Хим. Раздатчик"
+ dispensable_reagents = list("mutagen", "saltpetre", "ammonia", "water")
+ upgrade_reagents = list("atrazine", "glyphosate", "pestkiller", "diethylamine", "ash")
+
+/obj/machinery/chem_dispenser/botanical/Initialize(mapload)
+ . = ..()
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/chem_dispenser/botanical(null)
+ component_parts += new /obj/item/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/stock_parts/capacitor(null)
+ component_parts += new /obj/item/stock_parts/manipulator(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ component_parts += new cell_type(null)
+ RefreshParts()
+ dispensable_reagents = sortList(dispensable_reagents)
+
+/obj/machinery/chem_dispenser/botanical/upgraded/Initialize(mapload)
+ . = ..()
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/chem_dispenser/botanical(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/super(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/super(null)
+ component_parts += new /obj/item/stock_parts/capacitor/super(null)
+ component_parts += new /obj/item/stock_parts/manipulator/pico(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ component_parts += new /obj/item/stack/cable_coil(null)
+ RefreshParts()
+
+/obj/item/circuitboard/chem_dispenser/botanical
+ name = "печатная плата (Ботанический Хим. Раздатчик)"
+ build_path = /obj/machinery/chem_dispenser/botanical
+
+/datum/design/botanical_dispenser
+ name = "Machine Board (Ботанический Раздатчик)"
+ desc = "Плата для ботанического хим. раздатчика."
+ id = "botanical_dispenser"
+ req_tech = list("programming" = 5, "biotech" = 3, "materials" = 4, "plasmatech" = 4)
+ build_type = IMPRINTER
+ materials = list(MAT_GLASS = 1000)
+ build_path = /obj/item/circuitboard/chem_dispenser/botanical
+ category = list("Misc. Machinery")
diff --git a/modular_ss220/hydroponics/code/plants.dm b/modular_ss220/hydroponics/code/plants.dm
new file mode 100644
index 000000000000..cb34a689d454
--- /dev/null
+++ b/modular_ss220/hydroponics/code/plants.dm
@@ -0,0 +1,27 @@
+// Buckwheat
+/obj/item/seeds/wheat/oat
+ mutatelist = list(/obj/item/seeds/wheat/buckwheat)
+
+/obj/item/seeds/wheat/buckwheat
+ name = "пачка семян гречки"
+ desc = "Из этого может получиться гречка, а может и нет."
+ icon = 'modular_ss220/hydroponics/icons/seeds.dmi'
+ icon_state = "seed-buckwheat"
+ growing_icon = 'modular_ss220/hydroponics/icons/growing.dmi'
+ species = "buckwheat"
+ icon_dead = "buckwheat-dead"
+ plantname = "Стебли Гречки"
+ product = /obj/item/reagent_containers/food/snacks/grown/buckwheat
+ mutatelist = list()
+
+/obj/item/reagent_containers/food/snacks/grown/buckwheat
+ seed = /obj/item/seeds/wheat/buckwheat
+ name = "гречка"
+ desc = "Finally, гречка."
+ gender = PLURAL
+ icon = 'modular_ss220/hydroponics/icons/plants.dmi'
+ icon_state = "buckwheat"
+ filling_color = "#8E633C"
+ bitesize_mod = 2
+ tastes = list("гречка" = 1)
+ can_distill = FALSE
diff --git a/modular_ss220/hydroponics/hydroponics.dm b/modular_ss220/hydroponics/hydroponics.dm
new file mode 100644
index 000000000000..28246543525a
--- /dev/null
+++ b/modular_ss220/hydroponics/hydroponics.dm
@@ -0,0 +1,4 @@
+/datum/modpack/hydroponics
+ name = "Ботаника"
+ desc = "Растения, приборы и прочие улучшения для ботаники."
+ author = "Aylong220"
diff --git a/modular_ss220/hydroponics/hydroponics.dme b/modular_ss220/hydroponics/hydroponics.dme
new file mode 100644
index 000000000000..945c91e274fe
--- /dev/null
+++ b/modular_ss220/hydroponics/hydroponics.dme
@@ -0,0 +1,5 @@
+#include "hydroponics.dm"
+
+#include "code/dispenser.dm"
+#include "code/biogenerator_designs.dm"
+#include "code/plants.dm"
diff --git a/modular_ss220/hydroponics/icons/growing.dmi b/modular_ss220/hydroponics/icons/growing.dmi
new file mode 100644
index 000000000000..edb3d4dce5c5
Binary files /dev/null and b/modular_ss220/hydroponics/icons/growing.dmi differ
diff --git a/modular_ss220/hydroponics/icons/plants.dmi b/modular_ss220/hydroponics/icons/plants.dmi
new file mode 100644
index 000000000000..c6e99988af6c
Binary files /dev/null and b/modular_ss220/hydroponics/icons/plants.dmi differ
diff --git a/modular_ss220/hydroponics/icons/seeds.dmi b/modular_ss220/hydroponics/icons/seeds.dmi
new file mode 100644
index 000000000000..97329a6eef30
Binary files /dev/null and b/modular_ss220/hydroponics/icons/seeds.dmi differ
diff --git a/modular_ss220/jobs/_jobs.dm b/modular_ss220/jobs/_jobs.dm
new file mode 100644
index 000000000000..cd20e61a4ff3
--- /dev/null
+++ b/modular_ss220/jobs/_jobs.dm
@@ -0,0 +1,4 @@
+/datum/modpack/jobs
+ name = "Работы"
+ desc = "Новые джобки и изменения старых"
+ author = "furior"
diff --git a/modular_ss220/jobs/_jobs.dme b/modular_ss220/jobs/_jobs.dme
new file mode 100644
index 000000000000..c8c3a6f2b56a
--- /dev/null
+++ b/modular_ss220/jobs/_jobs.dme
@@ -0,0 +1,3 @@
+#include "_jobs.dm"
+
+#include "code/access.dm"
diff --git a/modular_ss220/jobs/code/access.dm b/modular_ss220/jobs/code/access.dm
new file mode 100644
index 000000000000..a89870b43200
--- /dev/null
+++ b/modular_ss220/jobs/code/access.dm
@@ -0,0 +1,3 @@
+/datum/job/scientist/New()
+ . = ..()
+ access |= ACCESS_MAINT_TUNNELS
diff --git a/modular_ss220/keybindings/_keybindings.dm b/modular_ss220/keybindings/_keybindings.dm
new file mode 100644
index 000000000000..289a15b6bdf5
--- /dev/null
+++ b/modular_ss220/keybindings/_keybindings.dm
@@ -0,0 +1,4 @@
+/datum/modpack/keybindings
+ name = "Keybindings"
+ desc = "Заменяет значения по-умолчанию хоткеев на SS220"
+ author = "larentoun"
diff --git a/modular_ss220/keybindings/_keybindings.dme b/modular_ss220/keybindings/_keybindings.dme
new file mode 100644
index 000000000000..189bd64129c5
--- /dev/null
+++ b/modular_ss220/keybindings/_keybindings.dme
@@ -0,0 +1,12 @@
+#include "_keybindings.dm"
+
+#include "code/admin_keybinds.dm"
+#include "code/carbon_keybinds.dm"
+#include "code/client.dm"
+#include "code/emote_keybinds.dm"
+#include "code/human_keybinds.dm"
+#include "code/living_keybinds.dm"
+#include "code/mob_keybinds.dm"
+#include "code/movement_keybinds.dm"
+#include "code/robot_keybinds.dm"
+#include "code/silicon_keybinds.dm"
diff --git a/modular_ss220/keybindings/code/admin_keybinds.dm b/modular_ss220/keybindings/code/admin_keybinds.dm
new file mode 100644
index 000000000000..b3cf2eb7a6c2
--- /dev/null
+++ b/modular_ss220/keybindings/code/admin_keybinds.dm
@@ -0,0 +1,30 @@
+/datum/keybinding/admin/mc_debug
+ keys = null
+
+/datum/keybinding/admin/msay
+ name = "Msay"
+ keys = list("ShiftF5")
+
+/datum/keybinding/admin/asay
+ name = "Asay"
+ keys = list("F5")
+
+/datum/keybinding/admin/aghost
+ name = "Aghost"
+ keys = list("F6")
+
+/datum/keybinding/admin/player_panel
+ name = "Player Panel"
+ keys = list("F7")
+
+/datum/keybinding/admin/apm
+ name = "Admin PM"
+ keys = list("F8")
+
+/datum/keybinding/admin/invisimin
+ name = "Invisimin"
+ keys = list("F9")
+
+/datum/keybinding/admin/dsay
+ name = "Dsay"
+ keys = list("F10")
diff --git a/modular_ss220/keybindings/code/carbon_keybinds.dm b/modular_ss220/keybindings/code/carbon_keybinds.dm
new file mode 100644
index 000000000000..ebc8f3735b11
--- /dev/null
+++ b/modular_ss220/keybindings/code/carbon_keybinds.dm
@@ -0,0 +1,35 @@
+/datum/keybinding/carbon/throw_mode
+ name = "Режим броска (переключить)"
+ keys = list("R", "Southwest")
+
+/datum/keybinding/carbon/throw_mode/hold
+ name = "Режим броска (зажать)"
+ keys = null
+
+/datum/keybinding/carbon/give_item
+ name = "Передать вещь (переключить)"
+ keys = list("V")
+
+/datum/keybinding/carbon/intent/help
+ name = "Help Intent (переключить)"
+
+/datum/keybinding/carbon/intent/disarm
+ name = "Disarm Intent (переключить)"
+
+/datum/keybinding/carbon/intent/grab
+ name = "Grab Intent (переключить)"
+
+/datum/keybinding/carbon/intent/harm
+ name = "Harm Intent (переключить)"
+
+/datum/keybinding/carbon/intent/hold/help
+ name = "Help Intent (переключить)"
+
+/datum/keybinding/carbon/intent/hold/disarm
+ name = "Disarm Intent (зажать)"
+
+/datum/keybinding/carbon/intent/hold/grab
+ name = "Grab Intent (зажать)"
+
+/datum/keybinding/carbon/intent/hold/harm
+ name = "Harm Intent (зажать)"
diff --git a/modular_ss220/keybindings/code/client.dm b/modular_ss220/keybindings/code/client.dm
new file mode 100644
index 000000000000..1cf32d8c4fb7
--- /dev/null
+++ b/modular_ss220/keybindings/code/client.dm
@@ -0,0 +1,23 @@
+/datum/keybinding/client/admin_help
+ name = "Admin Help"
+ keys = list("F1")
+
+/datum/keybinding/client/ooc
+ name = "OOC"
+ keys = list("F2", "O")
+
+/datum/keybinding/client/looc
+ name = "Локальный OOC"
+ keys = list("L")
+
+/datum/keybinding/client/say
+ name = "Say"
+ keys = list("F3", "T")
+
+/datum/keybinding/client/me
+ name = "Me"
+ keys = list("F4", "M")
+
+/datum/keybinding/client/toggle_min_hud
+ name = "Переключить минимальный HUD"
+ keys = list("F12")
diff --git a/modular_ss220/keybindings/code/emote_keybinds.dm b/modular_ss220/keybindings/code/emote_keybinds.dm
new file mode 100644
index 000000000000..09f19a0c3adb
--- /dev/null
+++ b/modular_ss220/keybindings/code/emote_keybinds.dm
@@ -0,0 +1,459 @@
+/datum/keybinding/emote/flip
+ name = "Кувырок"
+
+/datum/keybinding/emote/spin
+ name = "Крутиться"
+
+/datum/keybinding/emote/blush
+ name = "Краснеть"
+
+/datum/keybinding/emote/bow
+ name = "Поклониться"
+
+/datum/keybinding/emote/burp
+ name = "Рыгнуть"
+
+/datum/keybinding/emote/choke
+ name = "Подавиться"
+
+/datum/keybinding/emote/collapse
+ name = "Рухнуть"
+
+/datum/keybinding/emote/dance
+ name = "Танцевать"
+
+/datum/keybinding/emote/jump
+ name = "Прыгать"
+
+/datum/keybinding/emote/deathgasp
+ name = "Предсмертное дыхание"
+
+/datum/keybinding/emote/drool
+ name = "Нести чепуху"
+
+/datum/keybinding/emote/quiver
+ name = "Трепетать"
+
+/datum/keybinding/emote/frown
+ name = "Хмуриться"
+
+/datum/keybinding/emote/glare
+ name = "Недовольно смотреть"
+
+/datum/keybinding/emote/gag
+ name = "Подавиться"
+
+/datum/keybinding/emote/grin
+ name = "Оскалиться в улыбке"
+
+/datum/keybinding/emote/grimace
+ name = "Корчиться"
+
+/datum/keybinding/emote/groan
+ name = "Болезненно вздохнуть"
+
+/datum/keybinding/emote/look
+ name = "Смотреть"
+
+/datum/keybinding/emote/bshake
+ name = "Трястись"
+
+/datum/keybinding/emote/shudder
+ name = "Содрогаться"
+
+/datum/keybinding/emote/point
+ name = "Указать пальцем"
+
+/datum/keybinding/emote/pout
+ name = "Надуть губы"
+
+/datum/keybinding/emote/scream
+ name = "Кричать"
+
+/datum/keybinding/emote/shake
+ name = "Трясти головой"
+
+/datum/keybinding/emote/shiver
+ name = "Дрожать"
+
+/datum/keybinding/emote/sigh
+ name = "Вздыхать"
+
+/datum/keybinding/emote/happy
+ name = "Вздыхать (счастливо)"
+
+/datum/keybinding/emote/sit
+ name = "Сесть"
+
+/datum/keybinding/emote/smile
+ name = "Улыбнуться"
+
+/datum/keybinding/emote/smug
+ name = "Самодовольно"
+
+/datum/keybinding/emote/sniff
+ name = "Нюхать"
+
+/datum/keybinding/emote/snore
+ name = "Храпеть"
+
+/datum/keybinding/emote/nightmare
+ name = "Кошмар"
+
+/datum/keybinding/emote/stare
+ name = "Пялиться"
+
+/datum/keybinding/emote/stretch
+ name = "Растянуться"
+
+/datum/keybinding/emote/sulk
+ name = "Дуться"
+
+/datum/keybinding/emote/sway
+ name = "Покачиваться"
+
+/datum/keybinding/emote/swear
+ name = "Ругаться"
+
+/datum/keybinding/emote/tilt
+ name = "Наклонить голову"
+
+/datum/keybinding/emote/tremble
+ name = "Дрожать в ужасе"
+
+/datum/keybinding/emote/twitch
+ name = "Дёргаться (сильно)"
+
+/datum/keybinding/emote/twitch_s
+ name = "Дёргаться"
+
+/datum/keybinding/emote/whimper
+ name = "Хныкать"
+
+/datum/keybinding/emote/wsmile
+ name = "Улыбаться (слабо)"
+
+/datum/keybinding/emote/carbon/blink
+ name = "Моргать"
+
+/datum/keybinding/emote/carbon/blink_r
+ name = "Моргать (быстро)"
+
+/datum/keybinding/emote/carbon/clap
+ name = "Хлопать"
+
+/datum/keybinding/emote/carbon/cross
+ name = "Скрестить руки"
+
+/datum/keybinding/emote/carbon/chuckle
+ name = "Усмехнуться"
+
+/datum/keybinding/emote/carbon/cough
+ name = "Кашлять"
+
+/datum/keybinding/emote/carbon/moan
+ name = "Стонать"
+
+/datum/keybinding/emote/carbon/giggle
+ name = "Хихикать"
+
+/datum/keybinding/emote/carbon/gurgle
+ name = "Булькать"
+
+/datum/keybinding/emote/carbon/inhale
+ name = "Вдохнуть"
+
+/datum/keybinding/emote/carbon/inhale/sharp
+ name = "Вдохнуть (резко)"
+
+/datum/keybinding/emote/carbon/kiss
+ name = "Поцеловать"
+
+/datum/keybinding/emote/carbon/wave
+ name = "Махать"
+
+/datum/keybinding/emote/carbon/yawn
+ name = "Зевать"
+
+/datum/keybinding/emote/carbon/exhale
+ name = "Выдохнуть"
+
+/datum/keybinding/emote/carbon/laugh
+ name = "Смеяться"
+
+/datum/keybinding/emote/carbon/scowl
+ name = "Хмуриться"
+
+/datum/keybinding/emote/carbon/faint
+ name = "Потерять сознание"
+
+/datum/keybinding/emote/carbon/sign
+ name = "Знак"
+
+/datum/keybinding/emote/carbon/alien/humanoid/roar
+ name = "Рычать"
+
+/datum/keybinding/emote/carbon/alien/humanoid/hiss
+ name = "Шипеть"
+
+/datum/keybinding/emote/carbon/alien/humanoid/gnarl
+
+/datum/keybinding/emote/carbon/brain/alarm
+ name = "Тревога"
+
+/datum/keybinding/emote/carbon/brain/alert
+ name = "Предупреждение"
+
+/datum/keybinding/emote/carbon/brain/notice
+ name = "Оповещение"
+
+/datum/keybinding/emote/carbon/brain/flash
+ name = "Моргать"
+
+/datum/keybinding/emote/carbon/brain/whistle
+ name = "Свист"
+
+/datum/keybinding/emote/carbon/brain/beep
+ name = "Бип"
+
+/datum/keybinding/emote/carbon/brain/boop
+ name = "Буп"
+
+/datum/keybinding/emote/carbon/human/airguitar
+ name = "Запил на гитаре"
+
+/datum/keybinding/emote/carbon/human/cry
+ name = "Плакать"
+
+/datum/keybinding/emote/carbon/human/dap
+
+/datum/keybinding/emote/carbon/human/eyebrow
+ name = "Приподнять бровь"
+
+/datum/keybinding/emote/carbon/human/grumble
+ name = "Ворчать"
+
+/datum/keybinding/emote/carbon/human/hug
+ name = "Обнимать"
+
+/datum/keybinding/emote/carbon/human/mumble
+ name = "Бормотать"
+
+/datum/keybinding/emote/carbon/human/nod
+ name = "Кивнуть"
+
+/datum/keybinding/emote/carbon/human/scream
+ name = "Кричать"
+
+/datum/keybinding/emote/carbon/human/gasp
+ name = "Задыхаться"
+
+/datum/keybinding/emote/carbon/human/shake
+ name = "Трясти головой"
+
+/datum/keybinding/emote/carbon/human/pale
+ name = "Бледнеть"
+
+/datum/keybinding/emote/carbon/human/raise
+ name = "Поднять руку"
+
+/datum/keybinding/emote/carbon/human/salute
+ name = "Салютовать"
+
+/datum/keybinding/emote/carbon/human/sign/signal
+ name = "Сигналить"
+
+/datum/keybinding/emote/carbon/human/shrug
+ name = "Пожать плечами"
+
+/datum/keybinding/emote/carbon/human/sniff
+ name = "Понюхать"
+
+/datum/keybinding/emote/carbon/human/johnny
+ name = "Джонни"
+
+/datum/keybinding/emote/carbon/human/sneeze
+ name = "Чихнуть"
+
+/datum/keybinding/emote/carbon/human/slap
+ name = "Шлёпнуть"
+
+/datum/keybinding/emote/carbon/human/wink
+ name = "Подмигнуть"
+
+/datum/keybinding/emote/carbon/human/highfive
+ name = "Дать Пять"
+
+/datum/keybinding/emote/carbon/human/handshake
+ name = "Пожать руку"
+
+/datum/keybinding/emote/carbon/human/snap
+ name = "Щёлкнуть пальцами"
+
+/datum/keybinding/emote/carbon/human/crack
+ name = "Хрустеть"
+
+/datum/keybinding/emote/carbon/human/fart
+ name = "Пёрнуть"
+
+/datum/keybinding/emote/carbon/human/wag
+ name = "Махать хвостом"
+
+/datum/keybinding/emote/carbon/human/wag/stop
+ name = "Перестать махать хвостом"
+
+/datum/keybinding/emote/carbon/human/flap
+ name = "Махать крыльями"
+
+/datum/keybinding/emote/carbon/human/flap/angry
+ name = "Агрессивно махать крыльями"
+
+/datum/keybinding/emote/carbon/human/flutter
+ name = "Трепетать"
+
+/datum/keybinding/emote/carbon/human/quill
+ name = "Шуршать перьями"
+
+/datum/keybinding/emote/carbon/human/warble
+ name = "Трель"
+
+/datum/keybinding/emote/carbon/human/clack
+ name = "Трещать"
+
+/datum/keybinding/emote/carbon/human/clack/click
+ name = "Щёлкать"
+
+/datum/keybinding/emote/carbon/human/drask_talk/drone
+ name = "Гудеть"
+
+/datum/keybinding/emote/carbon/human/drask_talk/hum
+ name = "Жужжать"
+
+/datum/keybinding/emote/carbon/human/drask_talk/rumble
+ name = "Урчать"
+
+/datum/keybinding/emote/carbon/human/hiss
+ name = "Шипеть"
+
+/datum/keybinding/emote/carbon/human/creak
+ name = "Скрипеть"
+
+/datum/keybinding/emote/carbon/human/squish
+ name = "Хлюпать"
+
+/datum/keybinding/emote/carbon/human/howl
+ name = "Выть"
+
+/datum/keybinding/emote/carbon/human/growl
+ name = "Рычать"
+
+/datum/keybinding/emote/carbon/human/rattle
+ name = "Греметь"
+
+/datum/keybinding/emote/carbon/human/monkey/gnarl
+
+/datum/keybinding/emote/carbon/human/monkey/roll
+ name = "Крутиться (мартышка)"
+
+/datum/keybinding/emote/carbon/human/monkey/scratch
+ name = "Почесаться"
+
+/datum/keybinding/emote/carbon/human/monkey/tail
+ name = "Хвост (мартышка)"
+
+/datum/keybinding/emote/carbon/human/monkey/scream/screech
+ name = "Визжать (мартышка)"
+
+/datum/keybinding/emote/carbon/human/monkey/scream/screech/roar
+ name = "Рычать (мартышка)"
+
+/datum/keybinding/emote/silicon/scream
+ name = "Кричать"
+
+/datum/keybinding/emote/silicon/ping
+ name = "Звенеть"
+
+/datum/keybinding/emote/silicon/buzz
+ name = "Жужжать"
+
+/datum/keybinding/emote/silicon/buzz2
+ name = "Жужжать раздражённо"
+
+/datum/keybinding/emote/silicon/beep
+ name = "Пищать"
+
+/datum/keybinding/emote/silicon/boop
+ name = "Буп"
+
+/datum/keybinding/emote/silicon/yes
+ name = "Утвердительно"
+
+/datum/keybinding/emote/silicon/no
+ name = "Отрицательно"
+
+/datum/keybinding/emote/silicon/law
+ name = "Law"
+
+/datum/keybinding/emote/silicon/halt
+ name = "Halt"
+
+/datum/keybinding/emote/simple_animal/diona_chirp
+
+/datum/keybinding/emote/simple_animal/gorilla_ooga
+
+/datum/keybinding/emote/simple_animal/pet/dog/bark
+ name = "Лаять (пёс)"
+
+/datum/keybinding/emote/simple_animal/pet/dog/yelp
+ name = "Визг (пёс)"
+
+/datum/keybinding/emote/simple_animal/pet/dog/growl
+ name = "Рычать (пёс)"
+
+/datum/keybinding/emote/simple_animal/mouse/squeak
+ name = "Пищать (мышь)"
+
+/datum/keybinding/emote/simple_animal/pet/cat/meow
+ name = "Мяукать (кот)"
+
+/datum/keybinding/emote/simple_animal/pet/cat/hiss
+ name = "Шипеть (кот)"
+
+/datum/keybinding/emote/simple_animal/pet/cat/purr
+ name = "Мурчать (кот)"
+
+/datum/keybinding/emote/simple_animal/pet/cat/sit
+ name = "Сесть/встать (кот)"
+
+/datum/keybinding/custom
+ default_emote_text = "Введите текст вашей эмоции"
+
+/datum/keybinding/custom/one
+ name = "Своя эмоция 1"
+
+/datum/keybinding/custom/two
+ name = "Своя эмоция 2"
+
+/datum/keybinding/custom/three
+ name = "Своя эмоция 3"
+
+/datum/keybinding/custom/four
+ name = "Своя эмоция 4"
+
+/datum/keybinding/custom/five
+ name = "Своя эмоцияe 5"
+
+/datum/keybinding/custom/six
+ name = "Своя эмоция 6"
+
+/datum/keybinding/custom/seven
+ name = "Своя эмоция 7"
+
+/datum/keybinding/custom/eight
+ name = "Своя эмоция 8"
+
+/datum/keybinding/custom/nine
+ name = "Своя эмоция 9"
+
+/datum/keybinding/custom/ten
+ name = "Своя эмоция 10"
diff --git a/modular_ss220/keybindings/code/human_keybinds.dm b/modular_ss220/keybindings/code/human_keybinds.dm
new file mode 100644
index 000000000000..a9de087e40fb
--- /dev/null
+++ b/modular_ss220/keybindings/code/human_keybinds.dm
@@ -0,0 +1,19 @@
+/datum/keybinding/human/bag_equip
+ name = "Быстрая экипировка сумки"
+ keys = list("ShiftV")
+
+/datum/keybinding/human/belt_equip
+ name = "Быстрая экипировка пояса"
+ keys = list("ShiftE")
+
+/datum/keybinding/human/suit_equip
+ name = "Быстрая экипировка хранилища костюма"
+ keys = list("ShiftQ")
+
+/datum/keybinding/human/toggle_holster
+ name = "Использовать кобуру"
+ keys = list("H")
+
+/datum/keybinding/human/parry
+ name = "Паррировать"
+ keys = list("Space")
diff --git a/modular_ss220/keybindings/code/living_keybinds.dm b/modular_ss220/keybindings/code/living_keybinds.dm
new file mode 100644
index 000000000000..0536602528f1
--- /dev/null
+++ b/modular_ss220/keybindings/code/living_keybinds.dm
@@ -0,0 +1,11 @@
+/datum/keybinding/living/rest
+ name = "Лечь/встать"
+ keys = list("ShiftB")
+
+/datum/keybinding/living/resist
+ name = "Сопротивляться"
+ keys = list("B")
+
+/datum/keybinding/living/whisper
+ name = "Шептать"
+ keys = list("ShiftT")
diff --git a/modular_ss220/keybindings/code/mob_keybinds.dm b/modular_ss220/keybindings/code/mob_keybinds.dm
new file mode 100644
index 000000000000..421b6cafeaf9
--- /dev/null
+++ b/modular_ss220/keybindings/code/mob_keybinds.dm
@@ -0,0 +1,111 @@
+/datum/keybinding/mob/use_held_object
+ name = "Использовать вещь в руке"
+ keys = list("Y", "Z", "Southeast")
+
+/datum/keybinding/mob/equip_held_object
+ name = "Экипировать вещь"
+ keys = list("E")
+
+/datum/keybinding/mob/drop_held_object
+ name = "Выложить вещь в руке"
+ keys = list("Q", "Northwest")
+
+/datum/keybinding/mob/swap_hands
+ name = "Поменять руки"
+ keys = list("X", "Northeast")
+
+/datum/keybinding/mob/prev_intent
+ name = "Предыдущий Intent"
+ keys = list("F")
+
+/datum/keybinding/mob/next_intent
+ name = "Следующий Intent"
+ keys = list("G", "Insert")
+
+/datum/keybinding/mob/walk_hold
+ name = "Идти (зажать)"
+ keys = list("Alt")
+
+/datum/keybinding/mob/walk_toggle
+ name = "Идти (переключить)"
+
+/datum/keybinding/mob/stop_pulling
+ name = "Перестать тащить"
+ keys = list("C")
+
+/datum/keybinding/mob/face_dir/north
+ name = "Смотреть наверх"
+ keys = list("CtrlW", "CtrlNorth")
+
+/datum/keybinding/mob/face_dir/south
+ name = "Смотреть вниз"
+ keys = list("CtrlS", "CtrlSouth")
+
+/datum/keybinding/mob/face_dir/east
+ name = "Смотреть вправо"
+ keys = list("CtrlD", "CtrlEast")
+
+/datum/keybinding/mob/face_dir/west
+ name = "Смотреть влево"
+ keys = list("CtrlA", "CtrlWest")
+
+/datum/keybinding/mob/target_cycle/head
+ name = "Выбрать голову/глаза/рот"
+ keys = list("Numpad8")
+
+/datum/keybinding/mob/target_cycle/r_arm
+ name = "Выбрать правую руку/кисть"
+ keys = list("Numpad4")
+
+/datum/keybinding/mob/target_cycle/l_arm
+ name = "Выбрать левую руку/кисть"
+ keys = list("Numpad6")
+
+/datum/keybinding/mob/target_cycle/r_leg
+ name = "Выбрать правую ногу/ступню"
+ keys = list("Numpad1")
+
+/datum/keybinding/mob/target_cycle/l_leg
+ name = "Выбрать левую ногу/ступню"
+ keys = list("Numpad3")
+
+/datum/keybinding/mob/target/head
+ name = "Выбрать голову"
+
+/datum/keybinding/mob/target/eyes
+ name = "Выбрать глаза"
+
+/datum/keybinding/mob/target/mouth
+ name = "Выбрать рот"
+
+/datum/keybinding/mob/target/chest
+ name = "Выбрать грудь"
+ keys = list("Numpad5")
+
+/datum/keybinding/mob/target/groin
+ name = "Выбрать пах"
+ keys = list("Numpad2")
+
+/datum/keybinding/mob/target/r_arm
+ name = "Выбрать правую руку"
+
+/datum/keybinding/mob/target/r_hand
+ name = "Выбрать правую кисть"
+
+/datum/keybinding/mob/target/l_arm
+ name = "Выбрать левую руку"
+
+/datum/keybinding/mob/target/l_hand
+ name = "Выбрать левую кисть"
+
+/datum/keybinding/mob/target/r_leg
+ name = "Выбрать правую ногу"
+
+/datum/keybinding/mob/target/r_foot
+ name = "Выбрать правую ступню"
+
+/datum/keybinding/mob/target/l_leg
+ name = "Выбрать левую ногу"
+
+/datum/keybinding/mob/target/l_foot
+ name = "Выбрать левую ступню"
diff --git a/modular_ss220/keybindings/code/movement_keybinds.dm b/modular_ss220/keybindings/code/movement_keybinds.dm
new file mode 100644
index 000000000000..3cbd50add0d6
--- /dev/null
+++ b/modular_ss220/keybindings/code/movement_keybinds.dm
@@ -0,0 +1,14 @@
+/datum/keybinding/movement/north
+ name = "Идти наверх"
+
+/datum/keybinding/movement/south
+ name = "Идти вниз"
+
+/datum/keybinding/movement/east
+ name = "Идти вправо"
+
+/datum/keybinding/movement/west
+ name = "Идти влево"
+
+/datum/keybinding/lock
+ name = "Остановиться (зажать)"
diff --git a/modular_ss220/keybindings/code/robot_keybinds.dm b/modular_ss220/keybindings/code/robot_keybinds.dm
new file mode 100644
index 000000000000..fef019449105
--- /dev/null
+++ b/modular_ss220/keybindings/code/robot_keybinds.dm
@@ -0,0 +1,19 @@
+/datum/keybinding/robot/module/slot_1
+ name = "Ячейка 1"
+ keys = list("1")
+
+/datum/keybinding/robot/module/slot_2
+ name = "Ячейка 2"
+ keys = list("2")
+
+/datum/keybinding/robot/module/slot_3
+ name = "Ячейка 3"
+ keys = list("3")
+
+/datum/keybinding/robot/cycle_modules
+ name = "Смена ячеек"
+ keys = list("X")
+
+/datum/keybinding/robot/drop_held_object
+ name = "Выложить в хранилище"
+ keys = list("Q", "Northwest")
diff --git a/modular_ss220/keybindings/code/silicon_keybinds.dm b/modular_ss220/keybindings/code/silicon_keybinds.dm
new file mode 100644
index 000000000000..5bcf75b2f13c
--- /dev/null
+++ b/modular_ss220/keybindings/code/silicon_keybinds.dm
@@ -0,0 +1,3 @@
+/datum/keybinding/silicon/switch_intent
+ name = "Смена Intents"
+ keys = list("4")
diff --git a/modular_ss220/loadout/_loadout.dm b/modular_ss220/loadout/_loadout.dm
new file mode 100644
index 000000000000..da08ac56ba43
--- /dev/null
+++ b/modular_ss220/loadout/_loadout.dm
@@ -0,0 +1,4 @@
+/datum/modpack/loadout
+ name = "Loadout"
+ desc = "Добавляет новые вещи в Loadout."
+ author = "Aylong220"
diff --git a/modular_ss220/loadout/_loadout.dme b/modular_ss220/loadout/_loadout.dme
new file mode 100644
index 000000000000..9853029c9d8d
--- /dev/null
+++ b/modular_ss220/loadout/_loadout.dme
@@ -0,0 +1,5 @@
+#include "_loadout.dm"
+
+#include "code/donor.dm"
+#include "code/shoes.dm"
+#include "code/suit.dm"
diff --git a/modular_ss220/loadout/code/donor.dm b/modular_ss220/loadout/code/donor.dm
new file mode 100644
index 000000000000..7715f3b26361
--- /dev/null
+++ b/modular_ss220/loadout/code/donor.dm
@@ -0,0 +1,53 @@
+/datum/gear/donor/neon_shoes
+ display_name = "Неоновые кросовки"
+ path = /obj/item/clothing/shoes/black/neon
+ donator_tier = 1
+ cost = 1
+
+/datum/gear/donor/biker_gloves
+ display_name = "Байкерские перчатки"
+ path = /obj/item/clothing/gloves/fingerless/biker_gloves
+ donator_tier = 1
+ cost = 1
+
+/datum/gear/donor/bike_helmet
+ display_name = "Байкерский шлем"
+ path = /obj/item/clothing/head/helmet/bike_helmet/replica
+ donator_tier = 2
+ cost = 2
+
+/datum/gear/donor/v_jacket
+ display_name = "Куртка V"
+ path = /obj/item/clothing/suit/v_jacket
+ donator_tier = 3
+ cost = 2
+
+/datum/gear/donor/takemura_jacket
+ display_name = "Куртка Такэмуры"
+ path = /obj/item/clothing/suit/takemura_jacket
+ donator_tier = 3
+ cost = 2
+
+/datum/gear/donor/katarina_jacket
+ display_name = "Куртка Катарины"
+ path = /obj/item/clothing/suit/katarina_jacket
+ donator_tier = 4
+ cost = 2
+
+/datum/gear/donor/katarina_suit
+ display_name = "Костюм Катарины"
+ path = /obj/item/clothing/under/costume/katarina_suit
+ donator_tier = 4
+ cost = 1
+
+/datum/gear/donor/katarina_cyberjacket
+ display_name = "Кибер-куртка Катарины"
+ path = /obj/item/clothing/suit/katarina_cyberjacket
+ donator_tier = 4
+ cost = 2
+
+/datum/gear/donor/katarina_cybersuit
+ display_name = "Кибер-костюм Катарины"
+ path = /obj/item/clothing/under/costume/katarina_cybersuit
+ donator_tier = 4
+ cost = 1
diff --git a/modular_ss220/loadout/code/shoes.dm b/modular_ss220/loadout/code/shoes.dm
new file mode 100644
index 000000000000..cfed2cb32deb
--- /dev/null
+++ b/modular_ss220/loadout/code/shoes.dm
@@ -0,0 +1,7 @@
+/datum/gear/shoes/shark
+ display_name = "Акульи тапочки"
+ path = /obj/item/clothing/shoes/shark
+
+/datum/gear/shoes/shark_light
+ display_name = "Акульи тапочки (светло-голубые)"
+ path = /obj/item/clothing/shoes/shark/light
diff --git a/modular_ss220/loadout/code/suit.dm b/modular_ss220/loadout/code/suit.dm
new file mode 100644
index 000000000000..e3e2cea89cfe
--- /dev/null
+++ b/modular_ss220/loadout/code/suit.dm
@@ -0,0 +1,7 @@
+/datum/gear/suit/shark
+ display_name = "Костюм акулы"
+ path = /obj/item/clothing/suit/hooded/shark_costume
+
+/datum/gear/suit/shark_light
+ display_name = "Костюм акулы (светло-голубой)"
+ path = /obj/item/clothing/suit/hooded/shark_costume/light
diff --git a/modular_ss220/logs/_logs.dm b/modular_ss220/logs/_logs.dm
new file mode 100644
index 000000000000..f5fa60ebaf9c
--- /dev/null
+++ b/modular_ss220/logs/_logs.dm
@@ -0,0 +1,4 @@
+/datum/modpack/logs
+ name = "Улучшенное логирование"
+ desc = "Добавляет логирование профессий, антажек, ЕРП."
+ author = "furior"
diff --git a/modular_ss220/logs/_logs.dme b/modular_ss220/logs/_logs.dme
new file mode 100644
index 000000000000..1efb87b09833
--- /dev/null
+++ b/modular_ss220/logs/_logs.dme
@@ -0,0 +1,3 @@
+#include "_logs.dm"
+
+#include "code/logging.dm"
diff --git a/modular_ss220/logs/code/logging.dm b/modular_ss220/logs/code/logging.dm
new file mode 100644
index 000000000000..f37cb56f6cba
--- /dev/null
+++ b/modular_ss220/logs/code/logging.dm
@@ -0,0 +1,24 @@
+/datum/controller/subsystem/jobs/AssignRole(mob/new_player/player, rank, latejoin)
+ . = ..()
+ if(!.)
+ return
+
+ var/datum/job/job = GetJob(rank)
+ log_game("Игрок [player.mind.key] вошел в раунд с профессией [rank] ([job.current_positions]/[job.total_positions])")
+
+/datum/mind/proc/log_antag_objectives()
+ if(length(objectives))
+ log_game("GAME: Start objective log for [html_decode(key)]/[html_decode(name)]")
+ var/count = 1
+ for(var/datum/objective/objective in objectives)
+ log_game("GAME: Objective #[count]: [objective.explanation_text]")
+ count++
+ log_game("GAME: End objective log for [html_decode(key)]/[html_decode(name)]")
+
+/datum/scoreboard/log_antags()
+ . = ..()
+ for(var/mind in SSticker.minds)
+ var/datum/mind/M = mind
+ var/role = M.special_role
+ if(role)
+ M.log_antag_objectives()
diff --git a/modular_ss220/maps220/_maps220.dm b/modular_ss220/maps220/_maps220.dm
new file mode 100644
index 000000000000..f1bb7a01302a
--- /dev/null
+++ b/modular_ss220/maps220/_maps220.dm
@@ -0,0 +1,4 @@
+/datum/modpack/ss220maps
+ name = "SS220 Maps"
+ desc = "Наши карты, код к ним и вспомогательные ресурсы."
+ author = "Aylong220, dj-34"
diff --git a/modular_ss220/maps220/_maps220.dme b/modular_ss220/maps220/_maps220.dme
new file mode 100644
index 000000000000..2ca1c8702986
--- /dev/null
+++ b/modular_ss220/maps220/_maps220.dme
@@ -0,0 +1,15 @@
+#include "_maps220.dm"
+
+#include "code/Areas/station.dm"
+#include "code/Areas/away.dm"
+#include "code/Areas/gateway.dm"
+#include "code/RandomRuins/lavaland_ruins.dm"
+#include "code/RandomRuins/space_ruins.dm"
+#include "code/Station/cyberiad.dm"
+#include "code/Station/delta.dm"
+#include "code/corpses.dm"
+#include "code/helpers.dm"
+#include "code/misc.dm"
+#include "code/mobs.dm"
+#include "code/spawners.dm"
+#include "code/walls.dm"
diff --git a/modular_ss220/maps220/code/Areas/away.dm b/modular_ss220/maps220/code/Areas/away.dm
new file mode 100644
index 000000000000..1277776aa97f
--- /dev/null
+++ b/modular_ss220/maps220/code/Areas/away.dm
@@ -0,0 +1,7 @@
+/area/ruin/space/unpowered/unpowered_structures
+ always_unpowered = TRUE
+ report_alerts = FALSE
+
+/area/ruin/space/powered/requires_power_space
+ requires_power = TRUE
+ report_alerts = FALSE
diff --git a/modular_ss220/maps220/code/Areas/gateway.dm b/modular_ss220/maps220/code/Areas/gateway.dm
new file mode 100644
index 000000000000..04e879a7d51c
--- /dev/null
+++ b/modular_ss220/maps220/code/Areas/gateway.dm
@@ -0,0 +1,214 @@
+/* Wild West */
+/area/awaymission/wildwest
+ name = "Wild West"
+ report_alerts = FALSE
+ icon_state = "away"
+ requires_power = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/area/awaymission/wildwest/wildwest_mines
+ name = "\improper Wild West Mines"
+ icon_state = "awaycontent1"
+
+/area/awaymission/wildwest/wildwest_vaultdoors
+ name = "\improper Wild West Vault Doors"
+ icon_state = "awaycontent2"
+
+/area/awaymission/wildwest/wildwest_refine
+ name = "\improper Wild West Refinery"
+ icon_state = "awaycontent3"
+
+/area/awaymission/wildwest/wildwest_vault
+ name = "\improper Wild West Vault"
+ icon_state = "awaycontent3"
+
+/* Terror Spiders */
+/area/awaymission/UO71
+ name = "UO71"
+ icon_state = "away"
+ report_alerts = FALSE
+ tele_proof = TRUE
+
+
+/area/awaymission/UO71/plaza
+ name = "UO71 Plaza"
+ icon_state = "awaycontent1"
+ fire = TRUE
+
+/area/awaymission/UO71/centralhall
+ name = "UO71 Central"
+ icon_state = "awaycontent2"
+ fire = TRUE
+
+/area/awaymission/UO71/eng
+ name = "UO71 Engineering"
+ icon_state = "awaycontent3"
+ fire = TRUE
+
+/area/awaymission/UO71/mining
+ name = "UO71 Mining"
+ icon_state = "awaycontent4"
+ fire = TRUE
+
+/area/awaymission/UO71/science
+ name = "UO71 Science"
+ icon_state = "awaycontent5"
+ fire = TRUE
+
+/area/awaymission/UO71/medical
+ name = "UO71 Medical"
+ icon_state = "awaycontent6"
+ fire = TRUE
+
+/area/awaymission/UO71/gateway
+ name = "UO71 Gateway"
+ icon_state = "awaycontent7"
+ fire = TRUE
+
+/area/awaymission/UO71/outside
+ name = "UO71 Outside"
+ icon_state = "awaycontent8"
+
+/area/awaymission/UO71/bridge
+ name = "UO71 Bridge"
+ icon_state = "awaycontent21"
+ fire = TRUE
+ requires_power = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/area/awaymission/UO71/queen
+ name = "UO71 Queen Lair"
+ icon_state = "awaycontent9"
+ fire = TRUE
+ requires_power = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/area/awaymission/UO71/prince
+ name = "UO71 Prince Containment"
+ icon_state = "awaycontent10"
+ fire = TRUE
+ requires_power = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/area/awaymission/UO71/loot
+ name = "UO71 Loot Vault"
+ icon_state = "awaycontent11"
+ requires_power = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/* Black Market Packers */
+/area/awaymission/BMPship
+ name = "BMP Asteroids"
+ icon_state = "away"
+ report_alerts = FALSE
+ requires_power = FALSE
+ ambientsounds = list('sound/music/space.ogg', 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambigen11.ogg', 'sound/ambience/ambispace.ogg', 'sound/ambience/ambispace2.ogg', 'modular_ss220/aesthetics_sounds/sound/music/Traitor.ogg')
+
+/area/awaymission/BMPship/Engines
+ name = "BMP Engine Block"
+ icon_state = "awaycontent1"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/Containment
+ name = "BMP Containment Block"
+ icon_state = "awaycontent2"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambicave.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/Fore
+ name = "BMP Fore Block"
+ icon_state = "awaycontent3"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambigen12.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/Gate
+ name = "BMP Gate"
+ icon_state = "awaycontent4"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/Armory
+ name = "BMP Armory"
+ icon_state = "awaycontent5"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/CommonArea
+ name = "BMP Common Area"
+ icon_state = "awaycontent6"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambigen4.ogg', 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/MedBay
+ name = "BMP MedBay Block"
+ icon_state = "awaycontent7"
+ requires_power = TRUE
+ ambientsounds = list('sound/ambience/ambigen6.ogg', 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/ChemLab
+ name = "BMP Chem Lab"
+ icon_state = "awaycontent8"
+ requires_power = TRUE
+ ambientsounds = "sound/ambience/ambifailure.ogg"
+
+/area/awaymission/BMPship/Shelter
+ name = "BMP Shelter"
+ icon_state = "awaycontent9"
+ requires_power = TRUE
+ ambientsounds = "sound/ambience/ambifailure.ogg"
+
+/area/awaymission/BMPship/Dormitories
+ name = "BMP Dormitories"
+ icon_state = "awaycontent10"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambigen3.ogg', 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/TurretsNorth
+ name = "BMP Turrets North"
+ icon_state = "awaycontent11"
+ requires_power = TRUE
+
+/area/awaymission/BMPship/TurretsSouth
+ name = "BMP Turrets South"
+ icon_state = "awaycontent12"
+ requires_power = TRUE
+
+/area/awaymission/BMPship/Bath
+ name = "Bath"
+ icon_state = "awaycontent13"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/Kitchen
+ name = "BMP Kitchen"
+ icon_state = "awaycontent14"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/Buffer
+ name = "BMP Buffer"
+ icon_state = "awaycontent15"
+ requires_power = TRUE
+ fire = TRUE
+ ambientsounds = list('sound/ambience/ambigen5.ogg', 'sound/ambience/ambilava1.ogg', 'sound/ambience/ambilava3.ogg', 'sound/ambience/ambimo2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg', 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg')
+
+/area/awaymission/BMPship/TraderShuttle
+ name = "BMP Trader Shuttle"
+ icon_state = "awaycontent16"
+ requires_power = TRUE
+ ambientsounds = "sound/spookoween/ghost_whisper.ogg"
+
+/area/awaymission/BMPship/Mining
+ name = "BMP Mining"
+ icon_state = "awaycontent17"
+ requires_power = TRUE
diff --git a/modular_ss220/maps220/code/Areas/station.dm b/modular_ss220/maps220/code/Areas/station.dm
new file mode 100644
index 000000000000..522b2da44088
--- /dev/null
+++ b/modular_ss220/maps220/code/Areas/station.dm
@@ -0,0 +1,87 @@
+/* Station */
+/area/security/checkpoint/south
+ name = "\improper Escape Security Checkpoint"
+
+/area/bridge/checkpoint
+ name = "\improper Command Checkpoint"
+
+/area/bridge/checkpoint/north
+ name = "\improper North Command Checkpoint"
+
+/area/bridge/checkpoint/south
+ name = "\improper South Command Checkpoint"
+
+/area/engine/aitransit
+ name = "\improper AI Satellite Transfer Point"
+ icon_state = "engi"
+
+/area/engine/hallway
+ name = "\improper Engineering Hallway"
+ icon_state = "engine_hallway"
+
+/area/engine/dronefabricator
+ name = "\improper Engineering Drone Fabricator Room"
+ icon_state = "engi"
+
+/area/engine/emergency
+ name = "\improper Engineering Emergency Supplies"
+ icon_state = "emergencystorage"
+
+/area/engine/supermatter_room
+ name = "\improper Supermatter Room"
+ icon_state = "engi"
+
+/area/engine/utility
+ name = "\improper Engineering Utility Room"
+ icon_state = "engimaint"
+
+/area/atmos/storage
+ name = "\improper Atmospherics Storage"
+ icon_state = "atmos"
+
+/* CentCom */
+/area/centcom220
+ name = "\improper ЦК"
+ icon_state = "centcom"
+ requires_power = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
+ nad_allowed = TRUE
+
+/area/centcom220/evac
+ name = "\improper ЦК - Эвакуационный шаттл"
+ icon_state = "centcom_evac"
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/area/centcom220/park
+ name = "\improper ЦК - Парк"
+ icon_state ="centcom"
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/area/centcom220/bar
+ name = "\improper ЦК - Бар"
+ icon_state ="centcom"
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+
+/area/centcom220/general
+ name = "\improper ЦК - Зона персонала"
+ icon_state ="centcom"
+
+/area/centcom220/supply
+ name = "\improper ЦК - Доставка"
+ icon_state ="centcom"
+
+/area/centcom220/admin1
+ name = "\improper ЦК - Коридоры ЦК"
+ icon_state ="centcom"
+
+/area/centcom220/admin2
+ name = "\improper ЦК - Офисы"
+ icon_state ="centcom"
+
+/area/centcom220/admin3
+ name = "\improper ЦК - ОБР"
+ icon_state ="centcom"
+
+/area/centcom220/jail
+ name = "\improper ЦК - Тюрьма"
+ icon_state ="centcom"
diff --git a/modular_ss220/maps220/code/RandomRuins/lavaland_ruins.dm b/modular_ss220/maps220/code/RandomRuins/lavaland_ruins.dm
new file mode 100644
index 000000000000..29fcdaa4d2dd
--- /dev/null
+++ b/modular_ss220/maps220/code/RandomRuins/lavaland_ruins.dm
@@ -0,0 +1,14 @@
+// Пример добавления руины.
+/datum/map_template/ruin/lavaland/example // Вместо "example" писать название руины.
+ name = "example" // Имя руины
+ id = "example_id" // ID руины
+ description = "Пример описания" // Описание руины. Видно только админам.
+ prefix = "_maps/map_files220/RandomRuins/LavaRuins/" // Путь до карты, обязательно оставлять таким.
+ suffix = "" // .dmm файл руины, вписывать название полностью, пример: suffix = "example.dmm". Саму карту закидывать в "_maps\map_files\RandomRuins\LavaRuins"
+ cost = 5 // Вес руины, чем он больше, тем меньше шанс что она заспавнится
+ allow_duplicates = FALSE // Разрешает/Запрещает дубликаты руины. TRUE - могут быть дубликаты. FALSE - дубликатов не будет.
+ always_place = TRUE // Если вписать эту строчку, руина будет спавнится всегда.
+ ci_exclude = /datum/map_template/ruin/lavaland/example // Это не использовать.
+
+// Добавлять свои руины под этими комментариями. Делать это по примеру выше!
+// Комментарии УДАЛИТЬ если копируешь пример.
diff --git a/modular_ss220/maps220/code/RandomRuins/space_ruins.dm b/modular_ss220/maps220/code/RandomRuins/space_ruins.dm
new file mode 100644
index 000000000000..fd603264df91
--- /dev/null
+++ b/modular_ss220/maps220/code/RandomRuins/space_ruins.dm
@@ -0,0 +1,68 @@
+// Пример добавления руины.
+/datum/map_template/ruin/space/example // Вместо "example" писать название руины.
+ name = "example" // Имя руины
+ id = "example_id" // ID руины
+ description = "Пример описания" // Описание руины. Видно только админам.
+ prefix = "_maps/map_files220/RandomRuins/SpaceRuins/" // Путь до карты, обязательно оставлять таким.
+ suffix = "" // .dmm файл руины, вписывать название полностью, пример: suffix = "example.dmm". Саму карту закидывать в путь префикса.
+ cost = 5 // Вес руины, чем он больше, тем меньше шанс что она заспавнится
+ allow_duplicates = FALSE // Разрешает/Запрещает дубликаты руины. TRUE - могут быть дубликаты. FALSE - дубликатов не будет.
+ always_place = TRUE // Если вписать эту строчку, руина будет спавнится всегда. Использовать ТОЛЬКО для теста! После удалить.
+ ci_exclude = /datum/map_template/ruin/space/example // Это не использовать.
+
+// Добавлять свои руины под этими комментариями. Делать это по примеру выше!
+// Комментарии УДАЛИТЬ если копируешь пример.
+/datum/map_template/ruin/space/mechtransport_new
+ name = "Mechtransport"
+ id = "mechtransport_new"
+ description = "An abandoned unarmed transport ship, a perfect target for the bandit scum."
+ prefix = "_maps/map_files220/RandomRuins/SpaceRuins/"
+ suffix = "mechtransport_new.dmm"
+ cost = 3
+ allow_duplicates = FALSE
+
+/datum/map_template/ruin/space/destroyed_infiltrator
+ name = "Destroyed Infiltrator Ship"
+ id = "destroyed_infiltrator"
+ description = "They're loading BSA! But why? Ah, they're going to sho-..."
+ prefix = "_maps/map_files220/RandomRuins/SpaceRuins/"
+ suffix = "destroyed_infiltrator.dmm"
+ cost = 3
+ allow_duplicates = FALSE
+
+/datum/map_template/ruin/space/transit_bar
+ name = "Transit Bar"
+ id = "transit_bar"
+ description = "One of the trillion bars in this galaxy, this one looks especially homey and comfy."
+ prefix = "_maps/map_files220/RandomRuins/SpaceRuins/"
+ suffix = "transit_bar.dmm"
+ cost = 1
+ allow_duplicates = FALSE
+
+/datum/map_template/ruin/space/infected_ship
+ name = "Infected Ship"
+ id = "infected_ship"
+ description = "A lonely drifting ship showing no signs of life... What kind of black rubber substance is weaving around its shell?"
+ prefix = "_maps/map_files220/RandomRuins/SpaceRuins/"
+ suffix = "infected_ship.dmm"
+ cost = 3
+ allow_duplicates = FALSE
+
+/datum/map_template/ruin/space/convoy_ambush
+ name = "Convoy Ambush"
+ id = "convoy_ambush"
+ description = "I've been waiting for this for twuh years!"
+ prefix = "_maps/map_files220/RandomRuins/SpaceRuins/"
+ suffix = "convoy_ambush.dmm"
+ cost = 3
+ allow_duplicates = FALSE
+
+/datum/map_template/ruin/space/whiteship
+ name = "NT Medical Ship"
+ id = "whiteship"
+ prefix = "_maps/map_files220/RandomRuins/SpaceRuins/"
+ suffix = "whiteship.dmm"
+ description = "An old, abandoned NT medical ship. Its computer can navigate to other landmarks within space with ease."
+ allow_duplicates = FALSE
+ always_place = TRUE
+ cost = 0
diff --git a/modular_ss220/maps220/code/Station/cyberiad.dm b/modular_ss220/maps220/code/Station/cyberiad.dm
new file mode 100644
index 000000000000..e3d60416372c
--- /dev/null
+++ b/modular_ss220/maps220/code/Station/cyberiad.dm
@@ -0,0 +1,5 @@
+/datum/map/cyberiad
+ fluff_name = "ИСН Кибериада"
+ technical_name = "Кибериада"
+ map_path = "_maps/map_files220/cyberiad/cyberiad.dmm"
+ webmap_url = "https://affectedarc07.github.io/SS13WebMap/Paradise/Cyberiad/"
diff --git a/modular_ss220/maps220/code/Station/delta.dm b/modular_ss220/maps220/code/Station/delta.dm
new file mode 100644
index 000000000000..898988a68922
--- /dev/null
+++ b/modular_ss220/maps220/code/Station/delta.dm
@@ -0,0 +1,5 @@
+/datum/map/delta
+ fluff_name = "ИСН Керберос"
+ technical_name = "Керберос"
+ map_path = "_maps/map_files220/delta/delta.dmm"
+ webmap_url = "https://affectedarc07.github.io/SS13WebMap/Paradise/Delta/"
diff --git a/modular_ss220/maps220/code/corpses.dm b/modular_ss220/maps220/code/corpses.dm
new file mode 100644
index 000000000000..5a75f8da0a86
--- /dev/null
+++ b/modular_ss220/maps220/code/corpses.dm
@@ -0,0 +1,46 @@
+/* For Black Market Packers gateway */
+/obj/effect/mob_spawn/human/corpse/tacticool
+ mob_type = /mob/living/carbon/human
+ name = "Tacticool corpse"
+ icon = 'icons/obj/clothing/under/syndicate.dmi'
+ icon_state = "tactifool"
+ mob_name = "Unknown"
+ random = TRUE
+ death = TRUE
+ disable_sensors = TRUE
+ outfit = /datum/outfit/packercorpse
+
+/datum/outfit/packercorpse
+ name = "Packer Corpse"
+ uniform = /obj/item/clothing/under/syndicate/tacticool
+ shoes = /obj/item/clothing/shoes/combat
+ back = /obj/item/storage/backpack
+ l_ear = /obj/item/radio/headset
+ gloves = /obj/item/clothing/gloves/color/black
+
+/obj/effect/mob_spawn/human/corpse/tacticool/Initialize()
+ brute_damage = rand(0, 400)
+ burn_damage = rand(0, 400)
+ return ..()
+
+/obj/effect/mob_spawn/human/corpse/syndicatesoldier/trader
+ name = "Syndi trader corpse"
+ icon = 'icons/obj/clothing/under/syndicate.dmi'
+ icon_state = "tactifool"
+ random = TRUE
+ disable_sensors = TRUE
+ outfit = /datum/outfit/syndicatetrader
+
+/datum/outfit/syndicatetrader
+ uniform = /obj/item/clothing/under/syndicate/tacticool
+ shoes = /obj/item/clothing/shoes/combat
+ back = /obj/item/storage/backpack
+ gloves = /obj/item/clothing/gloves/color/black/forensics
+ belt = /obj/item/gun/projectile/automatic/pistol
+ mask = /obj/item/clothing/mask/balaclava
+ suit = /obj/item/clothing/suit/armor/vest/combat
+
+/obj/effect/mob_spawn/human/corpse/syndicatesoldier/trader/Initialize()
+ brute_damage = rand(150, 500)
+ burn_damage = rand(100, 300)
+ return ..()
diff --git a/modular_ss220/maps220/code/helpers.dm b/modular_ss220/maps220/code/helpers.dm
new file mode 100644
index 000000000000..9eb2566efdb2
--- /dev/null
+++ b/modular_ss220/maps220/code/helpers.dm
@@ -0,0 +1,101 @@
+/obj/effect/mapping_helpers
+ icon = 'modular_ss220/maps220/icons/mapping_helpers.dmi'
+
+/obj/effect/mapping_helpers/light
+ icon_state = "sunlight_helper"
+ light_color = null
+ light_power = 1
+ light_range = 10
+
+/obj/effect/mapping_helpers/light/New()
+ var/turf/T = get_turf(src)
+ T.light_color = light_color
+ T.light_power = light_power
+ T.light_range = light_range
+ . = ..()
+
+//Machinery helpers
+/obj/effect/mapping_helpers/machinery
+ layer = BELOW_MOB_LAYER
+ late = TRUE
+
+/obj/effect/mapping_helpers/machinery/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("[src] spawned outside of mapload!")
+ return
+
+ if(!(locate(/obj/machinery) in get_turf(src)))
+ log_world("[src] failed to find any machinery [AREACOORD(src)]")
+
+ for(var/obj/machinery/M in get_turf(src))
+ payload(M)
+
+ return INITIALIZE_HINT_QDEL
+
+/obj/effect/mapping_helpers/machinery/proc/payload(obj/machinery/payload)
+ return
+
+/obj/effect/mapping_helpers/machinery/damaged
+ name = "damaged machinery helper"
+ icon_state = "damaged_machine"
+
+/obj/effect/mapping_helpers/machinery/destroyed
+ name = "destroyed machinery helper"
+ icon_state = "broken_machine"
+
+/obj/effect/mapping_helpers/machinery/damaged/payload(obj/machinery/M)
+ M.take_damage(M.obj_integrity - M.integrity_failure)
+
+/obj/effect/mapping_helpers/machinery/destroyed/payload(obj/machinery/M)
+ M.take_damage(M.obj_integrity)
+
+//Window helpers
+///Deals random damage to the first window found on a tile to appear cracked
+/obj/effect/mapping_helpers/damaged_window
+ name = "damaged window helper"
+ icon_state = "damaged_window"
+ layer = ABOVE_OBJ_LAYER
+ late = TRUE
+ /// Minimum roll of integrity damage in percents needed to show cracks
+ var/integrity_damage_min = 0.25
+ /// Maximum roll of integrity damage in percents needed to show cracks
+ var/integrity_damage_max = 0.85
+
+/obj/effect/mapping_helpers/damaged_window/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("[src] spawned outside of mapload!")
+ return INITIALIZE_HINT_QDEL
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/effect/mapping_helpers/damaged_window/LateInitialize()
+ . = ..()
+ var/obj/structure/window/target = locate(/obj/structure/window) in loc
+
+ if(isnull(target))
+ var/area/target_area = get_area(src)
+ log_world("[src] failed to find a window at [AREACOORD(src)] ([target_area.type]).")
+ qdel(src)
+ return
+ else
+ payload(target)
+
+ target.update_appearance()
+ qdel(src)
+
+/obj/effect/mapping_helpers/damaged_window/proc/payload(obj/structure/window/target)
+ if(target.obj_integrity < target.max_integrity)
+ var/area/area = get_area(target)
+ log_world("[src] at [AREACOORD(src)] [(area.type)] tried to damage [target] but it's already damaged!")
+ target.take_damage(rand(target.max_integrity * integrity_damage_min, target.max_integrity * integrity_damage_max))
+
+//Airlock helpers
+/obj/effect/mapping_helpers/airlock/welded
+ name = "airlock welded helper"
+ icon_state = "airlock_welded"
+
+/obj/effect/mapping_helpers/airlock/welded/payload(obj/machinery/door/airlock/airlock)
+ if(airlock.welded)
+ log_world("[src] at [AREACOORD(src)] tried to make [airlock] welded but it's already welded closed!")
+ airlock.welded = TRUE
diff --git a/modular_ss220/maps220/code/misc.dm b/modular_ss220/maps220/code/misc.dm
new file mode 100644
index 000000000000..e0f4dc3ca429
--- /dev/null
+++ b/modular_ss220/maps220/code/misc.dm
@@ -0,0 +1,105 @@
+/obj/machinery/wish_granter_dark
+ name = "Исполнитель Желаний"
+ desc = "Вы уже не уверены в этом..."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "syndbeacon"
+
+ anchored = TRUE
+ density = TRUE
+ power_state = NO_POWER_USE
+
+ var/power_mutations
+ var/charges = 1
+ var/insisting = FALSE
+
+/obj/machinery/wish_granter_dark/Initialize(mapload)
+ . = ..()
+ power_mutations = list(/datum/mutation/meson_vision, /datum/mutation/night_vision, /datum/mutation/cold_resist, /datum/mutation/grant_spell/cryo)
+
+/obj/machinery/wish_granter_dark/attack_hand(mob/living/carbon/human/user as mob)
+ usr.set_machine(src)
+
+ if(!charges)
+ to_chat(user, "[name] никак не реагирует.")
+ return
+
+ else if(!ishuman(user))
+ to_chat(user, "Вы чувствуете темное движение внутри [name], которого опасаются ваши инстинкты.")
+ return
+
+ else if(is_special_character(user))
+ to_chat(user, "Что-то инстинктивно заставляет вас отстраниться.")
+ return
+
+ else if(!insisting)
+ to_chat(user, "Ваше первое прикосновение заставляет [name] зашевелиться, прислушиваясь к вам. Вы действительно уверены, что хотите это сделать?")
+ insisting = TRUE
+ return
+
+ insisting = FALSE
+ var/wish = input("Вы хотите...","Желание") as null|anything in list("Сила", "Богатство", "Бессмертие", "Покой")
+ if(!wish)
+ return
+ charges--
+
+ var/mob/living/carbon/human/human = user
+ var/become_shadow = TRUE
+ var/list/output = list()
+ switch(wish)
+ if("Сила")
+ for(var/mutation_type in power_mutations)
+ var/datum/mutation/mutation = GLOB.dna_mutations[mutation_type]
+ mutation.activate(human)
+
+ if("Богатство")
+ new /obj/structure/closet/syndicate/resources/everything(loc)
+
+ if("Бессмертие")
+ user.verbs += /mob/living/carbon/human/verb/immortality
+
+ if("Покой")
+ for(var/mob/living/simple_animal/hostile/faithless/F in GLOB.mob_living_list)
+ F.death()
+ become_shadow = FALSE
+
+ if(become_shadow && !isshadowperson(human))
+ output += "Ваше желание исполнено, но какой ценой..."
+ output += "[name] наказывает вас за ваш эгоизм, забирая вашу душу и деформируя ваше тело, чтобы оно соответствовало тьме в вашем сердце."
+ output += span_warning("Ваша плоть темнеет!")
+ output += "Вы теперь Тень, раса живущих во тьме гуманоидов."
+ output += span_warning("Ваше тело бурно реагирует на свет.") + span_notice("Однако естественным образом исцеляется в темноте..")
+ output += "Помимо ваших новых качеств, вы психически не изменились и сохраняете свою прежнюю личность."
+ human.set_species(/datum/species/shadow)
+ user.regenerate_icons()
+ else
+ output += "Вы чувствуете как избежали горькой судьбы..."
+ output += "Каким бы инопланетным разумом ни обладал [name], оно удовлетворяет ваше желание. Наступает тишина..."
+
+ to_chat(user, output.Join(" "))
+
+#define TRAIT_REVIVAL_IN_PROGRESS "revival_in_progress"
+
+/mob/living/carbon/human/verb/immortality()
+ set category = "Бессмертие"
+ set name = "Возрождение"
+
+ if(stat != DEAD)
+ to_chat(src, span_notice("Вы еще живы!"))
+ return
+
+ if(HAS_TRAIT(src, TRAIT_REVIVAL_IN_PROGRESS))
+ to_chat(src, span_notice("Вы уже восстаёте из мертвых!"))
+ return
+
+ ADD_TRAIT(src, TRAIT_REVIVAL_IN_PROGRESS, "Immortality")
+ to_chat(src, span_notice("Смерть - ещё не конец!"))
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/carbon/human, resurrect)), rand(80 SECONDS, 120 SECONDS))
+
+/mob/living/carbon/human/proc/resurrect()
+ // Stolen from ling stasis
+ revive()
+ REMOVE_TRAIT(src, TRAIT_REVIVAL_IN_PROGRESS, "Immortality")
+ to_chat(src, span_notice("Вы вернулись из небытия."))
+ visible_message(span_warning("[name] восстаёт из мертвых, исцелив все свои раны"))
+
+#undef TRAIT_REVIVAL_IN_PROGRESS
diff --git a/modular_ss220/maps220/code/mobs.dm b/modular_ss220/maps220/code/mobs.dm
new file mode 100644
index 000000000000..0af1c446771a
--- /dev/null
+++ b/modular_ss220/maps220/code/mobs.dm
@@ -0,0 +1,190 @@
+//Scavengers
+/mob/living/simple_animal/hostile/scavengers
+ name = "Scavenger"
+ desc = "One of the many random looters or bandits of the frontiers."
+ icon = 'modular_ss220/maps220/icons/simple_human.dmi'
+ icon_state = "scav"
+ icon_living = "scav"
+ icon_dead = "scavdead"
+ mob_biotypes = MOB_ORGANIC | MOB_HUMANOID
+ sentience_type = SENTIENCE_OTHER
+ speak_chance = 0
+ turns_per_move = 5
+ speed = 0
+ stat_attack = UNCONSCIOUS
+ robust_searching = 1
+ maxHealth = 75
+ health = 75
+ harm_intent_damage = 8
+ melee_damage_lower = 10
+ melee_damage_upper = 10
+ attacktext = "punches"
+ attack_sound = 'sound/weapons/punch1.ogg'
+ a_intent = INTENT_HARM
+ unsuitable_atmos_damage = 10
+ faction = list("scavengers")
+ check_friendly_fire = TRUE
+ status_flags = CANPUSH
+ del_on_death = TRUE
+ footstep_type = FOOTSTEP_MOB_SHOE
+
+/mob/living/simple_animal/hostile/scavengers/meele
+ name = "Scrapper Scavenger"
+ desc = "One of the many random looters or bandits of the frontiers. This one is carrying a pipe."
+ icon_state = "scavmeelepipe"
+ icon_living = "scavmeelepipe"
+ icon_dead = "scavdead"
+ maxHealth = 90
+ health = 90
+ harm_intent_damage = 8
+ melee_damage_lower = 15
+ melee_damage_upper = 15
+ rapid_melee = 2
+ attack_sound = 'sound/weapons/genhit1.ogg'
+ attacktext = "bashing"
+
+/mob/living/simple_animal/hostile/scavengers/meele/crusher
+ name = "Heavy Scavenger"
+ desc = "One of the many random looters or bandits of the frontiers. This one is carrying a KC."
+ icon_state = "scavmeelecrush"
+ icon_living = "scavmeelecrush"
+ icon_dead = "scavdead"
+ maxHealth = 100
+ health = 100
+ rapid_melee = 0
+ harm_intent_damage = 8
+ melee_damage_lower = 20
+ melee_damage_upper = 20
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ attacktext = "smashes"
+
+/mob/living/simple_animal/hostile/scavengers/meele/axe
+ name = "Shipbreaker Scavenger"
+ desc = "A shipbreaker scavenger. This one is carrying a axe."
+ icon_state = "scavmeeleaxe"
+ icon_living = "scavmeeleaxe"
+ icon_dead = "scavdead"
+ maxHealth = 120
+ health = 120
+ rapid_melee = 0
+ harm_intent_damage = 8
+ melee_damage_lower = 20
+ melee_damage_upper = 25
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ attacktext = "cuts"
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ wander = FALSE
+
+/mob/living/simple_animal/hostile/scavengers/laser
+ name = " Scavenger Gunslinger"
+ desc = "A bandit scum, who has learned to shoot accurately and quickly."
+ icon_state = "scavpistol"
+ icon_living = "scavpistol"
+ icon_dead = "scavdead"
+ maxHealth = 100
+ health = 100
+ ranged = 1
+ retreat_distance = 5
+ minimum_distance = 5
+ rapid = 2
+ melee_damage_lower = 10
+ melee_damage_upper = 10
+ projectiletype = /obj/item/projectile/beam/laser
+ projectilesound = 'sound/weapons/laser.ogg'
+
+/mob/living/simple_animal/hostile/scavengers/laser/spacelaser
+ name = "Spacetrooper Scavenger"
+ desc = "A shipbreaker scavenger. This one is carrying a laser gun."
+ icon_state = "scavlaser"
+ icon_living = "scavlaser"
+ icon_dead = "scavdead"
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ rapid = 3
+ wander = FALSE
+
+/mob/living/simple_animal/hostile/scavengers/gun
+ name = "Scavenger Gunman"
+ desc = "A bandit scum with a shotgun."
+ icon_state = "scavshotgun"
+ icon_living = "scavshotgun"
+ icon_dead = "scavdead"
+ maxHealth = 100
+ health = 100
+ ranged = 1
+ retreat_distance = 5
+ minimum_distance = 5
+ rapid = 0
+ melee_damage_lower = 10
+ melee_damage_upper = 10
+ casingtype = /obj/item/ammo_casing/shotgun
+ projectilesound = 'sound/weapons/gunshots/gunshot_shotgun.ogg'
+
+/mob/living/simple_animal/hostile/scavengers/gun/spacegun
+ name = "Spacetrooper Scavenger"
+ desc = "A shipbreaker scavenger. This one is carrying a submachine gun."
+ icon_state = "scavm90"
+ icon_living = "scavm90"
+ icon_dead = "scavdead"
+ casingtype = /obj/item/ammo_casing/a556
+ rapid = 2
+ projectilesound = 'sound/weapons/gunshots/gunshot_smg.ogg'
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ wander = FALSE
+
+// Undead
+/mob/living/simple_animal/hostile/undead
+ name = "zombie"
+ icon = 'icons/mob/human.dmi'
+ icon_state = "zombie_s"
+ icon_living = "zombie_s"
+ icon_dead = "zombie_l"
+ mob_biotypes = MOB_UNDEAD | MOB_HUMANOID
+ speak_chance = 0
+ turns_per_move = 10
+ response_help = "gently prods"
+ response_disarm = "shoves"
+ response_harm = "hits"
+ speed = -1
+ maxHealth = 50
+ health = 50
+ faction = list("zombie")
+
+ harm_intent_damage = 10
+ melee_damage_lower = 5
+ melee_damage_upper = 10
+ attacktext = "claws"
+ attack_sound = 'sound/hallucinations/growl1.ogg'
+
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+
+ faction = list("undead")
+ loot = list(/obj/effect/decal/cleanable/blood/gibs)
+ del_on_death = TRUE
+
+//whiteship undead
+/mob/living/simple_animal/hostile/undead/zombie
+ speak = list("RAWR!","Rawr!","GRR!","Growl!")
+ speak_chance = 1
+ speak_emote = list("growls","roars")
+
+ icon_living = "zombie2_s"
+ icon_state = "zombie2_s"
+ maxHealth = 100
+ health = 100
+ speed = 0
+
+/mob/living/simple_animal/hostile/undead/zombie/fast
+ name = "fast zombie"
+ icon = 'icons/mob/human.dmi'
+ icon_living = "zombie_s"
+ icon_state = "zombie_s"
+ maxHealth = 75
+ health = 75
+ melee_damage_lower = 15
+ melee_damage_upper = 30
+ speed = -1
+
diff --git a/modular_ss220/maps220/code/spawners.dm b/modular_ss220/maps220/code/spawners.dm
new file mode 100644
index 000000000000..612632e1466d
--- /dev/null
+++ b/modular_ss220/maps220/code/spawners.dm
@@ -0,0 +1,77 @@
+/* FOOD SPAWNERS */
+/obj/effect/spawner/lootdrop/CCfood
+
+/obj/effect/spawner/lootdrop/CCfood/desert
+ lootcount = 5
+ loot = list(
+ /obj/item/reagent_containers/food/snacks/baguette=10,
+ /obj/item/reagent_containers/food/snacks/applepie=10,
+ /obj/item/reagent_containers/food/snacks/bananabreadslice=10,
+ /obj/item/reagent_containers/food/snacks/bananacakeslice=10,
+ /obj/item/reagent_containers/food/snacks/carrotcakeslice=10,
+ /obj/item/reagent_containers/food/snacks/croissant=10,
+ /obj/item/reagent_containers/food/drinks/cans/cola=10,""=70)
+
+/obj/effect/spawner/lootdrop/CCfood/meat
+ lootcount = 5
+ loot = list(
+ /obj/item/reagent_containers/food/snacks/lasagna=10,
+ /obj/item/reagent_containers/food/snacks/bigbiteburger=10,
+ /obj/item/reagent_containers/food/snacks/fishandchips=10,
+ /obj/item/reagent_containers/food/snacks/fishburger=10,
+ /obj/item/reagent_containers/food/snacks/hotdog=10,
+ /obj/item/reagent_containers/food/snacks/meatpie=10,
+ /obj/item/reagent_containers/food/drinks/cans/cola=10,""=70)
+
+/obj/effect/spawner/lootdrop/CCfood/alcohol
+ lootcount = 1
+ loot = list(
+ /obj/item/reagent_containers/food/drinks/flask/detflask=10,
+ /obj/item/reagent_containers/food/drinks/cans/tonic=10,
+ /obj/item/reagent_containers/food/drinks/cans/thirteenloko=10,
+ /obj/item/reagent_containers/food/drinks/cans/synthanol=10,
+ /obj/item/reagent_containers/food/drinks/cans/space_mountain_wind=10,
+ /obj/item/reagent_containers/food/drinks/cans/lemon_lime=10,""=70)
+
+/* LOOTDROP */
+/obj/effect/spawner/lootdrop/maintenance
+ icon = 'modular_ss220/maps220/icons/spawner_icons.dmi'
+
+/obj/effect/spawner/lootdrop/maintenance/three
+ icon_state = "trippleloot"
+
+/obj/effect/spawner/lootdrop/maintenance/five
+ name = "maintenance loot spawner (5 items)"
+ icon_state = "moreloot"
+ lootcount = 5
+
+/obj/effect/spawner/lootdrop/trash
+ name = "trash spawner"
+ icon = 'modular_ss220/maps220/icons/spawner_icons.dmi'
+ icon_state = "trash"
+ loot = list(
+ /obj/item/trash/bowl,
+ /obj/item/trash/can,
+ /obj/item/trash/candle,
+ /obj/item/trash/candy,
+ /obj/item/trash/cheesie,
+ /obj/item/trash/chips,
+ /obj/item/trash/fried_vox,
+ /obj/item/trash/gum,
+ /obj/item/trash/liquidfood,
+ /obj/item/trash/pistachios,
+ /obj/item/trash/plate,
+ /obj/item/trash/popcorn,
+ /obj/item/trash/raisins,
+ /obj/item/trash/semki,
+ /obj/item/trash/snack_bowl,
+ /obj/item/trash/sosjerky,
+ /obj/item/trash/spacetwinkie,
+ /obj/item/trash/spentcasing,
+ /obj/item/trash/syndi_cakes,
+ /obj/item/trash/tapetrash,
+ /obj/item/trash/tastybread,
+ /obj/item/trash/tray,
+ /obj/item/trash/waffles,
+ ""=20
+ )
diff --git a/modular_ss220/maps220/code/walls.dm b/modular_ss220/maps220/code/walls.dm
new file mode 100644
index 000000000000..77a7983a99bf
--- /dev/null
+++ b/modular_ss220/maps220/code/walls.dm
@@ -0,0 +1,115 @@
+/* Indestructible */
+/turf/simulated/wall/indestructible/rock/mineral
+ name = "dense rock"
+ desc = "An extremely densely-packed rock, Most mining tools or explosives would never get through this."
+ icon = 'icons/turf/walls//smoothrocks.dmi'
+ icon_state = "smoothrocks-0"
+ base_icon_state = "smoothrocks"
+ color = COLOR_ANCIENT_ROCK
+ smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
+ smoothing_groups = list(SMOOTH_GROUP_SIMULATED_TURFS, SMOOTH_GROUP_MINERAL_WALLS)
+ canSmoothWith = list(SMOOTH_GROUP_MINERAL_WALLS)
+
+/turf/simulated/wall/indestructible/cult
+ name = "runed metal wall"
+ icon = 'icons/turf/walls/cult_wall.dmi'
+ icon_state = "cult_wall-0"
+ base_icon_state = "cult_wall"
+ smoothing_flags = SMOOTH_BITMASK
+ canSmoothWith = list(SMOOTH_GROUP_WALLS, SMOOTH_GROUP_CULT_WALLS)
+
+/* White Shuttle */
+/turf/simulated/wall/indestructible/whiteshuttle
+ name = "wall"
+ desc = "A light-weight titanium wall used in shuttles."
+ icon = 'icons/turf/walls/plastinum_wall.dmi'
+ icon_state = "plastinum_wall-0"
+ base_icon_state = "plastinum_wall"
+ explosion_block = 3
+ flags_2 = CHECK_RICOCHET_2
+ sheet_type = /obj/item/stack/sheet/mineral/titanium
+ smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
+ smoothing_groups = list(SMOOTH_GROUP_TITANIUM_WALLS, SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE)
+ canSmoothWith = list(SMOOTH_GROUP_TITANIUM_WALLS, SMOOTH_GROUP_AIRLOCK, SMOOTH_GROUP_SHUTTLE_PARTS, SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE)
+
+/turf/simulated/wall/indestructible/whiteshuttle/nodiagonal
+ icon_state = "map-shuttle_nd"
+ smoothing_flags = SMOOTH_BITMASK
+
+/turf/simulated/wall/indestructible/whiteshuttle/nosmooth
+ smoothing_flags = NONE
+
+/turf/simulated/wall/indestructible/whiteshuttle/overspace
+ icon_state = "map-overspace"
+ smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
+ fixed_underlay = list("space" = TRUE)
+
+// sub-type to be used for interior shuttle walls
+// won't get an underlay of the destination turf on shuttle move
+/turf/simulated/wall/indestructible/whiteshuttle/interior/copyTurf(turf/T)
+ if(T.type != type)
+ T.ChangeTurf(type)
+ if(underlays.len)
+ T.underlays = underlays
+ if(T.icon_state != icon_state)
+ T.icon_state = icon_state
+ if(T.icon != icon)
+ T.icon = icon
+ if(color)
+ T.atom_colours = atom_colours.Copy()
+ T.update_atom_colour()
+ if(T.dir != dir)
+ T.setDir(dir)
+ T.transform = transform
+ return T
+
+/turf/simulated/wall/indestructible/whiteshuttle/copyTurf(turf/T)
+ . = ..()
+ T.transform = transform
+
+/* Syndie Shuttle */
+/turf/simulated/wall/indestructible/syndishuttle
+ name = "wall"
+ desc = "An evil wall of plasma and titanium."
+ icon = 'icons/turf/walls/plastitanium_wall.dmi'
+ icon_state = "plastitanium_wall-0"
+ base_icon_state = "plastitanium_wall"
+ explosion_block = 4
+ sheet_type = /obj/item/stack/sheet/mineral/plastitanium
+ smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
+ smoothing_groups = list(SMOOTH_GROUP_PLASTITANIUM_WALLS)
+ canSmoothWith = list(SMOOTH_GROUP_PLASTITANIUM_WALLS, SMOOTH_GROUP_AIRLOCK, SMOOTH_GROUP_SHUTTLE_PARTS)
+
+/turf/simulated/wall/indestructible/syndishuttle/nodiagonal
+ icon_state = "map-shuttle_nd"
+ base_icon_state = "plastitanium_wall"
+ smoothing_flags = SMOOTH_BITMASK
+
+/turf/simulated/wall/indestructible/syndishuttle/nosmooth
+ smoothing_flags = NONE
+
+/turf/simulated/wall/indestructible/syndishuttle/overspace
+ icon_state = "map-overspace"
+ smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
+ fixed_underlay = list("space" = TRUE)
+
+/turf/simulated/wall/indestructible/syndishuttle/interior/copyTurf(turf/T)
+ if(T.type != type)
+ T.ChangeTurf(type)
+ if(underlays.len)
+ T.underlays = underlays
+ if(T.icon_state != icon_state)
+ T.icon_state = icon_state
+ if(T.icon != icon)
+ T.icon = icon
+ if(color)
+ T.atom_colours = atom_colours.Copy()
+ T.update_atom_colour()
+ if(T.dir != dir)
+ T.setDir(dir)
+ T.transform = transform
+ return T
+
+/turf/simulated/wall/indestructible/syndishuttle/copyTurf(turf/T)
+ . = ..()
+ T.transform = transform
diff --git a/modular_ss220/maps220/icons/mapping_helpers.dmi b/modular_ss220/maps220/icons/mapping_helpers.dmi
new file mode 100644
index 000000000000..558e8d2e63ca
Binary files /dev/null and b/modular_ss220/maps220/icons/mapping_helpers.dmi differ
diff --git a/modular_ss220/maps220/icons/simple_human.dmi b/modular_ss220/maps220/icons/simple_human.dmi
new file mode 100644
index 000000000000..36dc1148eb78
Binary files /dev/null and b/modular_ss220/maps220/icons/simple_human.dmi differ
diff --git a/modular_ss220/maps220/icons/spawner_icons.dmi b/modular_ss220/maps220/icons/spawner_icons.dmi
new file mode 100644
index 000000000000..68cb57f6b571
Binary files /dev/null and b/modular_ss220/maps220/icons/spawner_icons.dmi differ
diff --git a/modular_ss220/modular_ss220.dme b/modular_ss220/modular_ss220.dme
new file mode 100644
index 000000000000..527551d862e3
--- /dev/null
+++ b/modular_ss220/modular_ss220.dme
@@ -0,0 +1,68 @@
+#include "_modpack.dm"
+#include "_modpacks.dm"
+
+// #include "example/_example.dme"
+
+// --- MAINTENANCE --- //
+#include "_defines220/_defines220.dme"
+#include "_signals220/_signals220.dme"
+#include "_misc/_misc.dme"
+#include "_span/_span.dme"
+#include "maps220/_maps220.dme"
+
+// --- ICONS --- //
+#include "aesthetics/_aesthetics.dme"
+#ifdef MODPACK_CHAT_BADGES
+#include "chat_badges/_chat_badges.dme"
+#endif
+#include "hairs/_hairs.dme"
+
+// --- OBJECTS --- //
+#include "awaymission_gun/_awaymission_gun.dme"
+#include "barsigns/_barsigns.dme"
+#include "clothing/_clothing.dme"
+#include "devices/_devices.dme"
+#include "food/_food.dme"
+#include "gateway/_gateway.dme"
+#include "hydroponics/hydroponics.dme"
+#include "objects/_objects.dme"
+#include "sechailer/sechailer.dme"
+#include "unique_objects/_unique_objects.dme"
+#include "vending/vending.dme"
+#include "wire_splicing/wiresplicing.dme"
+
+// --- MISC --- //
+#include "aesthetics_sounds/_aesthetics_sounds.dme"
+#include "balance/_balance.dme"
+#include "bureaucracy/_bureaucracy.dme"
+#include "camera_nanomap/camera.dme"
+#include "crawl_speed/_crawl_speed.dme"
+#include "debug/_debug.dme"
+#include "discord_link/_discord_link.dme"
+#include "emotes/_emotes.dme"
+#include "events/_events.dme"
+#include "fullscreen/_fullscreen.dme"
+#include "gunhud/_gunhud.dme"
+#include "jobs/_jobs.dme"
+#include "keybindings/_keybindings.dme"
+#include "loadout/_loadout.dme"
+#include "logs/_logs.dme"
+#include "pixel_shift/_pixel_shift.dme"
+#include "radio_sound/radio_sound.dme"
+#include "screentip_change/_screentip_change.dme"
+#include "smart_equip_targeted/_smart_equip_targeted.dme"
+#include "text_to_speech/_tts.dme"
+#include "title_screen/_title_screen.dme"
+#include "whitelist/_whitelist.dme"
+
+// --- UNUSED MODS --- //
+
+ /*------------------------------------------------------------------
+ Почему UNUSED MODS стоит хранить?
+ Потому что никто не проверяет использование тех или иных файлов
+ в коде, и мод просто исчезнет из поля зрения, когда находясь здесь
+ он всегда напоминает о своём существовании. Небольшая библиотека,
+ если так вообще можно выразиться.
+ ---------------------------------------------------------------------*/
+
+// #include "crit_rework/_crit_rework.dme"
diff --git a/modular_ss220/objects/_objects.dm b/modular_ss220/objects/_objects.dm
new file mode 100644
index 000000000000..1d263c97905c
--- /dev/null
+++ b/modular_ss220/objects/_objects.dm
@@ -0,0 +1,16 @@
+/datum/modpack/objects
+ name = "Объекты"
+ desc = "В основном включает в себя портированные объекты и всякие мелочи, которым не нужен отдельный модпак."
+ author = "dj-34"
+
+// Maybe it would be better, if i didn't make it modular, because i can't change order in the recipe list :catDespair:
+/datum/modpack/objects/initialize()
+ GLOB.metal_recipes += list(
+ new /datum/stack_recipe("metal platform", /obj/structure/platform, 4, time = 30,one_per_turf = TRUE, on_floor = TRUE),
+ new /datum/stack_recipe("metal platform corner", /obj/structure/platform/corner, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE)
+ )
+
+ GLOB.plasteel_recipes += list(
+ new /datum/stack_recipe("reinforced plasteel platform", /obj/structure/platform/reinforced, 4, time = 40,one_per_turf = TRUE, on_floor = TRUE),
+ new /datum/stack_recipe("reinforced plasteel platform corner", /obj/structure/platform/reinforced/corner, 2, time = 30,one_per_turf = TRUE, on_floor = TRUE)
+ )
diff --git a/modular_ss220/objects/_objects.dme b/modular_ss220/objects/_objects.dme
new file mode 100644
index 000000000000..327876c078aa
--- /dev/null
+++ b/modular_ss220/objects/_objects.dme
@@ -0,0 +1,3 @@
+#include "_objects.dm"
+
+#include "code/platform.dm"
diff --git a/modular_ss220/objects/code/platform.dm b/modular_ss220/objects/code/platform.dm
new file mode 100644
index 000000000000..c3782feaeb5b
--- /dev/null
+++ b/modular_ss220/objects/code/platform.dm
@@ -0,0 +1,208 @@
+// Platform Code by Danaleja2005
+/obj/structure/platform
+ name = "platform"
+ icon = 'modular_ss220/objects/icons/platform.dmi'
+ icon_state = "metal"
+ desc = "A metal platform."
+ flags = ON_BORDER
+ anchored = FALSE
+ climbable = TRUE
+ max_integrity = 200
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 50, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30)
+ var/corner = FALSE
+ var/material_type = /obj/item/stack/sheet/metal
+ var/material_amount = 4
+ var/decon_speed
+
+/obj/structure/platform/proc/CheckLayer()
+ if(dir == SOUTH)
+ layer = ABOVE_MOB_LAYER
+ else if(corner || dir == NORTH)
+ layer = BELOW_MOB_LAYER
+
+/obj/structure/platform/setDir(newdir)
+ . = ..()
+ CheckLayer()
+
+/obj/structure/platform/Initialize()
+ . = ..()
+ CheckLayer()
+
+/obj/structure/platform/New()
+ ..()
+ if(corner)
+ decon_speed = 20
+ density = FALSE
+ climbable = FALSE
+ else
+ decon_speed = 30
+ CheckLayer()
+
+/obj/structure/platform/examine(mob/user)
+ . = ..()
+ . += span_notice("[src] is [anchored == TRUE ? "screwed" : "unscrewed"] [anchored == TRUE ? "to" : "from"] the floor.")
+
+/obj/structure/platform/verb/rotate()
+ set name = "Rotate Platform Counter-Clockwise"
+ set category = "Object"
+ set src in oview(1)
+
+ if(usr.incapacitated())
+ return
+
+ if(anchored)
+ to_chat(usr, span_warning("[src] cannot be rotated while it is screwed to the floor!"))
+ return FALSE
+
+ var/target_dir = turn(dir, 90)
+
+ setDir(target_dir)
+ air_update_turf(1)
+ add_fingerprint(usr)
+ return TRUE
+
+/obj/structure/platform/verb/revrotate()
+ set name = "Rotate Platform Clockwise"
+ set category = "Object"
+ set src in oview(1)
+
+ if(usr.incapacitated())
+ return
+
+ if(anchored)
+ to_chat(usr, span_warning("[src] cannot be rotated while it is screwed to the floor!"))
+ return FALSE
+
+ var/target_dir = turn(dir, 270)
+
+ setDir(target_dir)
+ air_update_turf(1)
+ add_fingerprint(usr)
+ return TRUE
+
+/obj/structure/platform/AltClick(mob/user)
+ rotate()
+
+// Construction
+/obj/structure/platform/screwdriver_act(mob/user, obj/item/I)
+ . = TRUE
+ to_chat(user, span_notice("You begin [anchored == TRUE ? "unscrewing" : "screwing"] [src] [anchored == TRUE ? "from" : "to"] the floor."))
+ if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume))
+ return
+ to_chat(user, span_notice("You [anchored == TRUE ? "unscrew" : "screw"] [src] [anchored == TRUE ? "from" : "to"] the floor."))
+ anchored = !anchored
+
+/obj/structure/platform/wrench_act(mob/user, obj/item/I)
+ if(user.a_intent != INTENT_HELP)
+ return
+ . = TRUE
+ if(anchored)
+ to_chat(user, span_notice("You cannot disassemble [src], unscrew it first!"))
+ return
+ TOOL_ATTEMPT_DISMANTLE_MESSAGE
+ if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume))
+ return
+ var/obj/item/stack/sheet/G = new material_type(user.loc, material_amount)
+ G.add_fingerprint(user)
+ playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
+ TOOL_DISMANTLE_SUCCESS_MESSAGE
+ qdel(src)
+
+
+/obj/structure/platform/CheckExit(atom/movable/O, turf/target)
+ if(!anchored)
+ CheckLayer()
+ if(istype(O, /obj/structure/platform))
+ return FALSE
+ if(istype(O, /obj/item/projectile))
+ return TRUE
+ if(corner)
+ return !density
+ if(O && O.throwing)
+ return TRUE
+ if(((flags & ON_BORDER) && get_dir(loc, target) == dir))
+ return FALSE
+ else
+ return TRUE
+
+/obj/structure/platform/CanPass(atom/movable/mover, turf/target)
+ if(!anchored)
+ CheckLayer()
+ if(istype(mover, /obj/structure/platform))
+ return FALSE
+ if(istype(mover, /obj/item/projectile))
+ return TRUE
+ if(corner)
+ return !density
+ if(mover && mover.throwing)
+ return TRUE
+ var/obj/structure/S = locate(/obj/structure) in get_turf(mover)
+ if(S && S.climbable && !(S.flags & ON_BORDER) && climbable && isliving(mover))// Climbable objects allow you to universally climb over others
+ return TRUE
+ if(!(flags & ON_BORDER) || get_dir(loc, target) == dir)
+ return FALSE
+ else
+ return TRUE
+
+/obj/structure/platform/do_climb(mob/living/user)
+ if(!can_touch(user) || !climbable)
+ return
+ var/blocking_object = density_check()
+ if(blocking_object)
+ to_chat(user, span_warning("You cannot climb over [src], as it is blocked by \a [blocking_object]!"))
+ return
+
+ var/destination_climb = get_step(src, dir)
+ if(is_blocked_turf(destination_climb))
+ to_chat(user, span_warning("You cannot climb over [src], the path is blocked!"))
+ return
+ var/turf/T = src.loc
+ if(!T || !istype(T)) return
+
+ if(get_turf(user) == get_turf(src))
+ usr.visible_message(span_warning("[user] starts climbing over \the [src]!"))
+ else
+ usr.visible_message(span_warning("[user] starts getting off \the [src]!"))
+ climber = user
+ if(!do_after(user, 50, target = src))
+ climber = null
+ return
+
+ if(!can_touch(user) || !climbable)
+ climber = null
+ return
+
+ if(get_turf(user) == get_turf(src))
+ usr.loc = get_step(src, dir)
+ usr.visible_message(span_warning("[user] leaves \the [src]!"))
+ else
+ usr.loc = get_turf(src)
+ usr.visible_message(span_warning("[user] starts climbing over \the [src]!"))
+ climber = null
+
+/obj/structure/platform/CanAtmosPass()
+ return TRUE
+
+// Platform types
+/obj/structure/platform/reinforced
+ name = "reinforced platform"
+ desc = "A robust platform made of plasteel, more resistance for hazard sites."
+ icon_state = "plasteel"
+ material_type = /obj/item/stack/sheet/plasteel
+ max_integrity = 300
+ armor = list("melee" = 20, "bullet" = 30, "laser" = 30, "energy" = 100, "bomb" = 50, "bio" = 0, "rad" = 75, "fire" = 100, "acid" = 100)
+
+// Platform corners
+/obj/structure/platform/corner
+ name = "platform corner"
+ desc = "A metal platform corner."
+ icon_state = "metalcorner"
+ corner = TRUE
+ material_amount = 2
+
+/obj/structure/platform/reinforced/corner
+ name = "reinforced platform corner"
+ desc = "A robust platform corner made of plasteel, more resistance for hazard sites."
+ icon_state = "plasteelcorner"
+ corner = TRUE
+ material_amount = 2
diff --git a/modular_ss220/objects/icons/platform.dmi b/modular_ss220/objects/icons/platform.dmi
new file mode 100644
index 000000000000..5bbf75c31b12
Binary files /dev/null and b/modular_ss220/objects/icons/platform.dmi differ
diff --git a/modular_ss220/pixel_shift/_pixel_shift.dm b/modular_ss220/pixel_shift/_pixel_shift.dm
new file mode 100644
index 000000000000..477168b29ce6
--- /dev/null
+++ b/modular_ss220/pixel_shift/_pixel_shift.dm
@@ -0,0 +1,4 @@
+/datum/modpack/pixel_shift
+ name = "Pixel Shift"
+ desc = "Позволяет двигаться по-пиксельно в пределах турфа"
+ author = "larentoun"
diff --git a/modular_ss220/pixel_shift/_pixel_shift.dme b/modular_ss220/pixel_shift/_pixel_shift.dme
new file mode 100644
index 000000000000..358bedbd1246
--- /dev/null
+++ b/modular_ss220/pixel_shift/_pixel_shift.dme
@@ -0,0 +1,8 @@
+#include "_pixel_shift.dm"
+
+#include "code/_pixel_shift_defines.dm"
+#include "code/layer_shift.dm"
+#include "code/pixel_shift_component.dm"
+#include "code/pixel_shift_keybind.dm"
+#include "code/pixel_shift_mob.dm"
+#include "code/~pixel_shift_defines.dm"
diff --git a/modular_ss220/pixel_shift/code/_pixel_shift_defines.dm b/modular_ss220/pixel_shift/code/_pixel_shift_defines.dm
new file mode 100644
index 000000000000..23bcff0827dc
--- /dev/null
+++ b/modular_ss220/pixel_shift/code/_pixel_shift_defines.dm
@@ -0,0 +1,2 @@
+#define COMSIG_KB_MOB_PIXEL_SHIFT_DOWN "keybinding_mob_pixel_shift_down"
+#define COMSIG_KB_MOB_PIXEL_SHIFT_UP "keybinding_mob_pixel_shift_up"
diff --git a/modular_ss220/pixel_shift/code/layer_shift.dm b/modular_ss220/pixel_shift/code/layer_shift.dm
new file mode 100644
index 000000000000..3be3824c06fd
--- /dev/null
+++ b/modular_ss220/pixel_shift/code/layer_shift.dm
@@ -0,0 +1,36 @@
+#define MOB_LAYER_SHIFT_INCREMENT 0.01
+#define MOB_LAYER_SHIFT_MIN 3.95
+//#define MOB_LAYER 4 // This is a byond standard define
+#define MOB_LAYER_SHIFT_MAX 4.05
+
+/mob/living/verb/layershift_up()
+ set name = "Shift Layer Upwards"
+ set category = "IC"
+
+ if(incapacitated())
+ to_chat(src, span_warning("You can't do that right now!"))
+ return
+
+ if(layer >= MOB_LAYER_SHIFT_MAX)
+ to_chat(src, span_warning("You cannot increase your layer priority any further."))
+ return
+
+ layer += MOB_LAYER_SHIFT_INCREMENT
+ var/layer_priority = round((layer - MOB_LAYER) * 100, 1) // Just for text feedback
+ to_chat(src, span_notice("Your layer priority is now [layer_priority]."))
+
+/mob/living/verb/layershift_down()
+ set name = "Shift Layer Downwards"
+ set category = "IC"
+
+ if(incapacitated())
+ to_chat(src, span_warning("You can't do that right now!"))
+ return
+
+ if(layer <= MOB_LAYER_SHIFT_MIN)
+ to_chat(src, span_warning("You cannot decrease your layer priority any further."))
+ return
+
+ layer -= MOB_LAYER_SHIFT_INCREMENT
+ var/layer_priority = round((layer - MOB_LAYER) * 100, 1) // Just for text feedback
+ to_chat(src, span_notice("Your layer priority is now [layer_priority]."))
diff --git a/modular_ss220/pixel_shift/code/pixel_shift_component.dm b/modular_ss220/pixel_shift/code/pixel_shift_component.dm
new file mode 100644
index 000000000000..ad2055219623
--- /dev/null
+++ b/modular_ss220/pixel_shift/code/pixel_shift_component.dm
@@ -0,0 +1,99 @@
+#define MAXIMUM_PIXEL_SHIFT 12
+#define PASSABLE_SHIFT_THRESHOLD 8
+
+/datum/component/pixel_shift
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ /// Whether the mob is pixel shifted or not
+ var/is_shifted = FALSE
+ /// If we are in the shifting setting.
+ var/shifting = TRUE
+ /// Takes the four cardinal direction defines. Any atoms moving into this atom's tile will be allowed to from the added directions.
+ var/passthroughable = NONE
+
+/datum/component/pixel_shift/Initialize(...)
+ . = ..()
+ if(!isliving(parent) || isAI(parent))
+ return COMPONENT_INCOMPATIBLE
+
+/datum/component/pixel_shift/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_DOWN, PROC_REF(pixel_shift_down))
+ RegisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_UP, PROC_REF(pixel_shift_up))
+ RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(unpixel_shift))
+ RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_IMMOBILIZED), PROC_REF(unpixel_shift))
+ RegisterSignal(parent, COMSIG_LIVING_PROCESS_SPACEMOVE, PROC_REF(pre_move_check))
+ RegisterSignal(parent, COMSIG_LIVING_CAN_PASS, PROC_REF(check_passable))
+
+/datum/component/pixel_shift/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_DOWN)
+ UnregisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_UP)
+ UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_IMMOBILIZED))
+ UnregisterSignal(parent, COMSIG_LIVING_PROCESS_SPACEMOVE)
+ UnregisterSignal(parent, COMSIG_LIVING_CAN_PASS)
+
+/datum/component/pixel_shift/proc/pre_move_check(mob/source, movement_dir)
+ SIGNAL_HANDLER
+ if(shifting)
+ pixel_shift(source, movement_dir)
+ return COMPONENT_BLOCK_SPACEMOVE
+
+/datum/component/pixel_shift/proc/check_passable(mob/source, atom/movable/mover, target, height)
+ SIGNAL_HANDLER
+ var/mob/living/carbon/human/owner = parent
+ if(!istype(mover, /obj/item/projectile) && !mover.throwing && passthroughable & get_dir(owner, mover))
+ return COMPONENT_LIVING_PASSABLE
+
+/datum/component/pixel_shift/proc/pixel_shift_down()
+ SIGNAL_HANDLER
+ shifting = TRUE
+ return COMSIG_KB_ACTIVATED
+
+/datum/component/pixel_shift/proc/pixel_shift_up()
+ SIGNAL_HANDLER
+ shifting = FALSE
+
+/datum/component/pixel_shift/proc/unpixel_shift()
+ SIGNAL_HANDLER
+ passthroughable = NONE
+ if(is_shifted)
+ var/mob/living/owner = parent
+ owner.pixel_x = owner.get_standard_pixel_x_offset()
+ owner.pixel_y = owner.get_standard_pixel_y_offset()
+ qdel(src)
+
+/datum/component/pixel_shift/proc/pixel_shift(mob/target, direction)
+ var/mob/living/owner = parent
+ if(HAS_TRAIT(owner, TRAIT_RESTRAINED) || HAS_TRAIT(owner, TRAIT_IMMOBILIZED) || length(owner.grabbed_by) || owner.stat != CONSCIOUS)
+ return
+ passthroughable = NONE
+ switch(direction)
+ if(NORTH)
+ if(owner.pixel_y < MAXIMUM_PIXEL_SHIFT + initial(owner.pixel_y))
+ owner.pixel_y++
+ is_shifted = TRUE
+ if(EAST)
+ if(owner.pixel_x < MAXIMUM_PIXEL_SHIFT + initial(owner.pixel_x))
+ owner.pixel_x++
+ is_shifted = TRUE
+ if(SOUTH)
+ if(owner.pixel_y > -MAXIMUM_PIXEL_SHIFT + initial(owner.pixel_y))
+ owner.pixel_y--
+ is_shifted = TRUE
+ if(WEST)
+ if(owner.pixel_x > -MAXIMUM_PIXEL_SHIFT + initial(owner.pixel_x))
+ owner.pixel_x--
+ is_shifted = TRUE
+
+ // Yes, I know this sets it to true for everything if more than one is matched.
+ // Movement doesn't check diagonals, and instead just checks EAST or WEST, depending on where you are for those.
+ if(owner.pixel_y - initial(owner.pixel_y) > PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= EAST | SOUTH | WEST
+ else if(owner.pixel_y - initial(owner.pixel_y) < -PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= NORTH | EAST | WEST
+ if(owner.pixel_x - initial(owner.pixel_x) > PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= NORTH | SOUTH | WEST
+ else if(owner.pixel_x - initial(owner.pixel_x) < -PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= NORTH | EAST | SOUTH
+
+#undef MAXIMUM_PIXEL_SHIFT
+#undef PASSABLE_SHIFT_THRESHOLD
diff --git a/modular_ss220/pixel_shift/code/pixel_shift_keybind.dm b/modular_ss220/pixel_shift/code/pixel_shift_keybind.dm
new file mode 100644
index 000000000000..b845107ae2ef
--- /dev/null
+++ b/modular_ss220/pixel_shift/code/pixel_shift_keybind.dm
@@ -0,0 +1,14 @@
+/datum/keybinding/living/pixel_shift
+ keys = list("B")
+ name = "Pixel Shift"
+ category = KB_CATEGORY_MOVEMENT
+
+/datum/keybinding/living/pixel_shift/down(client/user)
+ . = ..()
+ if(SEND_SIGNAL(user.mob, COMSIG_KB_MOB_PIXEL_SHIFT_DOWN) & COMSIG_KB_ACTIVATED)
+ return
+ user.mob.add_pixel_shift_component()
+
+/datum/keybinding/living/pixel_shift/up(client/user)
+ . = ..()
+ SEND_SIGNAL(user.mob, COMSIG_KB_MOB_PIXEL_SHIFT_UP)
diff --git a/modular_ss220/pixel_shift/code/pixel_shift_mob.dm b/modular_ss220/pixel_shift/code/pixel_shift_mob.dm
new file mode 100644
index 000000000000..97d1b61f4592
--- /dev/null
+++ b/modular_ss220/pixel_shift/code/pixel_shift_mob.dm
@@ -0,0 +1,8 @@
+/mob/proc/add_pixel_shift_component()
+ return
+
+/mob/living/add_pixel_shift_component()
+ AddComponent(/datum/component/pixel_shift)
+
+/mob/living/silicon/ai/add_pixel_shift_component()
+ return
diff --git a/modular_ss220/pixel_shift/code/~pixel_shift_defines.dm b/modular_ss220/pixel_shift/code/~pixel_shift_defines.dm
new file mode 100644
index 000000000000..bb4bb97b64dd
--- /dev/null
+++ b/modular_ss220/pixel_shift/code/~pixel_shift_defines.dm
@@ -0,0 +1,2 @@
+#undef COMSIG_KB_MOB_PIXEL_SHIFT_DOWN
+#undef COMSIG_KB_MOB_PIXEL_SHIFT_UP
diff --git a/modular_ss220/radio_sound/code/radiosound.dm b/modular_ss220/radio_sound/code/radiosound.dm
new file mode 100644
index 000000000000..a14449df95d7
--- /dev/null
+++ b/modular_ss220/radio_sound/code/radiosound.dm
@@ -0,0 +1,16 @@
+/obj/item/radio/headset
+ var/radiosound = 'modular_ss220/radio_sound/sound/common.ogg'
+
+/obj/item/radio/headset/syndicate //disguised to look like a normal headset for stealth ops
+ radiosound = 'modular_ss220/radio_sound/sound/syndie.ogg'
+
+/obj/item/radio/headset/headset_sec
+ radiosound = 'modular_ss220/radio_sound/sound/security.ogg'
+
+/obj/item/radio/headset/talk_into(mob/living/M as mob, list/message_pieces, channel, verbage = "says")
+ if(!on)
+ return FALSE // the device has to be on
+
+ if(radiosound && listening)
+ playsound(M, radiosound, rand(20, 30))
+ . = ..()
diff --git a/modular_ss220/radio_sound/radio_sound.dme b/modular_ss220/radio_sound/radio_sound.dme
new file mode 100644
index 000000000000..f296671ade56
--- /dev/null
+++ b/modular_ss220/radio_sound/radio_sound.dme
@@ -0,0 +1,3 @@
+// BEGIN INCLUDE
+#include "code/radiosound.dm"
+// END_INCLUDE
diff --git a/modular_ss220/radio_sound/sound/common.ogg b/modular_ss220/radio_sound/sound/common.ogg
new file mode 100644
index 000000000000..20a1d1da0a06
Binary files /dev/null and b/modular_ss220/radio_sound/sound/common.ogg differ
diff --git a/modular_ss220/radio_sound/sound/security.ogg b/modular_ss220/radio_sound/sound/security.ogg
new file mode 100644
index 000000000000..e905d018a81e
Binary files /dev/null and b/modular_ss220/radio_sound/sound/security.ogg differ
diff --git a/modular_ss220/radio_sound/sound/syndie.ogg b/modular_ss220/radio_sound/sound/syndie.ogg
new file mode 100644
index 000000000000..818ec11c1561
Binary files /dev/null and b/modular_ss220/radio_sound/sound/syndie.ogg differ
diff --git a/modular_ss220/screentip_change/_screentip_change.dm b/modular_ss220/screentip_change/_screentip_change.dm
new file mode 100644
index 000000000000..f58f36c3dd42
--- /dev/null
+++ b/modular_ss220/screentip_change/_screentip_change.dm
@@ -0,0 +1,4 @@
+/datum/modpack/screentip_change
+ name = "Сдвиг Screentip вверх"
+ desc = "Меняет местоположение Screentip, чтобы они были выше."
+ author = "larentoun"
diff --git a/modular_ss220/screentip_change/_screentip_change.dme b/modular_ss220/screentip_change/_screentip_change.dme
new file mode 100644
index 000000000000..5a38ceff2ee6
--- /dev/null
+++ b/modular_ss220/screentip_change/_screentip_change.dme
@@ -0,0 +1,3 @@
+#include "_screentip_change.dm"
+
+#include "code/screentip.dm"
diff --git a/modular_ss220/screentip_change/code/screentip.dm b/modular_ss220/screentip_change/code/screentip.dm
new file mode 100644
index 000000000000..3a4ba41f16b1
--- /dev/null
+++ b/modular_ss220/screentip_change/code/screentip.dm
@@ -0,0 +1,2 @@
+/obj/screen/screentip
+ maptext_y = 0
diff --git a/modular_ss220/sechailer/code/sechailer.dm b/modular_ss220/sechailer/code/sechailer.dm
new file mode 100644
index 000000000000..217f009b64aa
--- /dev/null
+++ b/modular_ss220/sechailer/code/sechailer.dm
@@ -0,0 +1,65 @@
+GLOBAL_LIST_EMPTY(sechailers)
+
+/datum/action/item_action/dispatch
+ name = "Signal dispatch"
+ desc = "Opens up a quick select wheel for reporting crimes, including your current location, to your fellow security officers."
+ button_icon_state = "dispatch"
+ icon_icon = 'modular_ss220/sechailer/icons/sechailer.dmi'
+ use_itemicon = FALSE
+
+/obj/item/clothing/mask/gas/sechailer
+ name = "\improper security gas mask"
+ var/obj/item/radio/radio //For engineering alerts.
+ var/dispatch_cooldown = 250
+ var/last_dispatch = 0
+ actions_types = list(/datum/action/item_action/dispatch, /datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase)
+
+/obj/item/clothing/mask/gas/sechailer/hos
+ name = "\improper head of security's SWAT mask"
+ actions_types = list(/datum/action/item_action/dispatch, /datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase)
+
+/obj/item/clothing/mask/gas/sechailer/warden
+ name = "\improper warden's SWAT mask"
+ actions_types = list(/datum/action/item_action/dispatch, /datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase)
+
+/obj/item/clothing/mask/gas/sechailer/swat
+ actions_types = list(/datum/action/item_action/dispatch, /datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase)
+
+/obj/item/clothing/mask/gas/sechailer/blue
+ actions_types = list(/datum/action/item_action/dispatch, /datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase)
+
+/obj/item/clothing/mask/gas/sechailer/Destroy()
+ qdel(radio)
+ GLOB.sechailers -= src
+ . = ..()
+
+/obj/item/clothing/mask/gas/sechailer/Initialize()
+ . = ..()
+ GLOB.sechailers += src
+ radio = new /obj/item/radio(src)
+ radio.listening = FALSE
+ radio.config(list("Security" = 0))
+ radio.follow_target = src
+
+
+/obj/item/clothing/mask/gas/sechailer/proc/dispatch(mob/user)
+ var/area/A = get_area(src)
+ if(world.time < last_dispatch + dispatch_cooldown)
+ to_chat(user, "Dispatch radio broadcasting systems are recharging.")
+ return FALSE
+ var/list/options = list()
+ for(var/option in list("502 (Убийство)", "101 (Сопротивление Аресту)", "308 (Вторжение)", "305 (Мятеж)", "402 (Нападение на Офицера)")) //Just hardcoded for now!
+ options[option] = image(icon = 'modular_ss220/sechailer/icons/menu.dmi', icon_state = option)
+ var/message = show_radial_menu(user, src, options)
+ if(!message)
+ return FALSE
+ radio.autosay("Диспетчер, [user], код [message] в [A], запрашивается помощь.", src, "Security", list(z))
+ last_dispatch = world.time
+ for(var/atom/movable/hailer in GLOB.sechailers)
+ if(hailer.loc && ismob(hailer.loc))
+ playsound(hailer.loc, "modular_ss220/sechailer/sound/dispatch_please_respond.ogg", 55, FALSE)
+
+/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype)
+ . = ..()
+ if(actiontype == /datum/action/item_action/dispatch)
+ dispatch(user)
diff --git a/modular_ss220/sechailer/icons/menu.dmi b/modular_ss220/sechailer/icons/menu.dmi
new file mode 100644
index 000000000000..aee00ca952b2
Binary files /dev/null and b/modular_ss220/sechailer/icons/menu.dmi differ
diff --git a/modular_ss220/sechailer/icons/sechailer.dmi b/modular_ss220/sechailer/icons/sechailer.dmi
new file mode 100644
index 000000000000..444b3fb5d2a1
Binary files /dev/null and b/modular_ss220/sechailer/icons/sechailer.dmi differ
diff --git a/modular_ss220/sechailer/sechailer.dme b/modular_ss220/sechailer/sechailer.dme
new file mode 100644
index 000000000000..94e707c5220e
--- /dev/null
+++ b/modular_ss220/sechailer/sechailer.dme
@@ -0,0 +1,9 @@
+#ifndef MODPACK_HAILER
+#define MODPACK_HAILER
+
+#endif
+
+// BEGIN INCLUDE
+#include "code/sechailer.dm"
+// END_INCLUDE
+
diff --git a/modular_ss220/sechailer/sound/dispatch_please_respond.ogg b/modular_ss220/sechailer/sound/dispatch_please_respond.ogg
new file mode 100644
index 000000000000..83ea06d476a3
Binary files /dev/null and b/modular_ss220/sechailer/sound/dispatch_please_respond.ogg differ
diff --git a/modular_ss220/smart_equip_targeted/_smart_equip_targeted.dm b/modular_ss220/smart_equip_targeted/_smart_equip_targeted.dm
new file mode 100644
index 000000000000..4170f5430799
--- /dev/null
+++ b/modular_ss220/smart_equip_targeted/_smart_equip_targeted.dm
@@ -0,0 +1,4 @@
+/datum/modpack/smart_equip_targeted
+ name = "Smart Equip"
+ desc = "Позволяет хоткеям на сумку брать предмет из выбранного слота"
+ author = "larentoun"
diff --git a/modular_ss220/smart_equip_targeted/_smart_equip_targeted.dme b/modular_ss220/smart_equip_targeted/_smart_equip_targeted.dme
new file mode 100644
index 000000000000..f9ec7b8bc011
--- /dev/null
+++ b/modular_ss220/smart_equip_targeted/_smart_equip_targeted.dme
@@ -0,0 +1,3 @@
+#include "_smart_equip_targeted.dm"
+
+#include "code/smart_equip_targeted.dm"
diff --git a/modular_ss220/smart_equip_targeted/code/smart_equip_targeted.dm b/modular_ss220/smart_equip_targeted/code/smart_equip_targeted.dm
new file mode 100644
index 000000000000..e33727d90f13
--- /dev/null
+++ b/modular_ss220/smart_equip_targeted/code/smart_equip_targeted.dm
@@ -0,0 +1,31 @@
+/// take the most recent item out of a slot or place held item in a slot
+/mob/living/carbon/human/proc/smart_equip_targeted(slot_item = slot_belt)
+ var/obj/item/thing = get_active_hand()
+ var/obj/item/item_in_slot = get_item_by_slot(slot_item)
+ var/obj/item/storage/equipped_item
+ if(isstorage(item_in_slot))
+ equipped_item = item_in_slot
+ if(ismecha(loc) || HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
+ return
+ if(!istype(equipped_item))
+ if(thing)
+ equip_to_slot_if_possible(thing, slot_item)
+ else
+ if(istype(item_in_slot))
+ item_in_slot.attack_hand(src)
+ return
+ if(thing && equipped_item.can_be_inserted(thing))
+ equipped_item.handle_item_insertion(thing)
+ playsound(loc, "rustle", 50, 1, -5)
+ return
+ if(thing)
+ return
+ if(!length(equipped_item.contents))
+ return
+ var/obj/item/stored = equipped_item.contents[length(equipped_item.contents)]
+ if(!stored || stored.on_found(src))
+ return
+ stored.attack_hand(src) // take out thing from item in storage slot
+
+/mob/living/carbon/human/quick_equip_item(slot_item)
+ smart_equip_targeted(slot_item)
diff --git a/modular_ss220/text_to_speech/_tts.dm b/modular_ss220/text_to_speech/_tts.dm
new file mode 100644
index 000000000000..0d82e9c7d564
--- /dev/null
+++ b/modular_ss220/text_to_speech/_tts.dm
@@ -0,0 +1,4 @@
+/datum/modpack/example
+ name = "Text-To-Speech"
+ desc = "Озвучка фраз."
+ author = "furior"
diff --git a/modular_ss220/text_to_speech/_tts.dme b/modular_ss220/text_to_speech/_tts.dme
new file mode 100644
index 000000000000..602fdfd9829c
--- /dev/null
+++ b/modular_ss220/text_to_speech/_tts.dme
@@ -0,0 +1,24 @@
+#include "_tts.dm"
+
+#include "code/_defines.dm"
+#include "code/configuration.dm"
+#include "code/hear.dm"
+#include "code/numbers.dm"
+#include "code/providers/silero.dm"
+#include "code/rust_g_ss220.dm"
+#include "code/seeds/silero.dm"
+#include "code/sound.dm"
+#include "code/tts_preferences.dm"
+#include "code/tts_provider.dm"
+#include "code/tts_seed.dm"
+#include "code/tts_subsystem.dm"
+
+#include "code/base_seeds/mobs/_base.dm"
+#include "code/base_seeds/mobs/alien.dm"
+#include "code/base_seeds/mobs/guardian.dm"
+#include "code/base_seeds/objs/objs.dm"
+#include "code/base_seeds/mobs/other.dm"
+#include "code/base_seeds/mobs/lavaland.dm"
+#include "code/base_seeds/mobs/pets.dm"
+
+#include "code/~undefs/~undefs.dm"
diff --git a/modular_ss220/text_to_speech/code/_defines.dm b/modular_ss220/text_to_speech/code/_defines.dm
new file mode 100644
index 000000000000..9e860347cb01
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/_defines.dm
@@ -0,0 +1,53 @@
+#define SOUND_EFFECT_NONE 0
+#define SOUND_EFFECT_RADIO 1
+#define SOUND_EFFECT_ROBOT 2
+#define SOUND_EFFECT_RADIO_ROBOT 3
+#define SOUND_EFFECT_MEGAPHONE 4
+#define SOUND_EFFECT_MEGAPHONE_ROBOT 5
+
+#define TTS_TRAIT_PITCH_WHISPER (1<<1)
+#define TTS_TRAIT_RATE_FASTER (1<<2)
+#define TTS_TRAIT_RATE_MEDIUM (1<<3)
+
+#define TTS_CATEGORY_OTHER "Другое"
+#define TTS_CATEGORY_WARCRAFT3 "WarCraft 3"
+#define TTS_CATEGORY_HALFLIFE2 "Half-Life 2"
+#define TTS_CATEGORY_STARCRAFT "StarCraft"
+#define TTS_CATEGORY_PORTAL2 "Portal 2"
+#define TTS_CATEGORY_STALKER "STALKER"
+#define TTS_CATEGORY_DOTA2 "Dota 2"
+#define TTS_CATEGORY_LOL "League of Legends"
+#define TTS_CATEGORY_FALLOUT "Fallout"
+#define TTS_CATEGORY_FALLOUT2 "Fallout 2"
+#define TTS_CATEGORY_POSTAL2 "Postal 2"
+#define TTS_CATEGORY_TEAMFORTRESS2 "Team Fortress 2"
+#define TTS_CATEGORY_ATOMIC_HEART "Atomic Heart"
+#define TTS_CATEGORY_OVERWATCH "Overwatch"
+#define TTS_CATEGORY_SKYRIM "Skyrim"
+#define TTS_CATEGORY_RITA "Rita"
+#define TTS_CATEGORY_METRO "Metro"
+#define TTS_CATEGORY_HEROESOFTHESTORM "Heroes of the Storm"
+#define TTS_CATEGORY_HEARTHSTONE "Hearthstone"
+#define TTS_CATEGORY_VALORANT "Valorant"
+#define TTS_CATEGORY_EVILISLANDS "Evil Islands"
+
+#define TTS_GENDER_ANY "Любой"
+#define TTS_GENDER_MALE "Мужской"
+#define TTS_GENDER_FEMALE "Женский"
+
+#define TTS_PHRASES list(\
+ "Так звучит мой голос.",\
+ "Так я звучу.",\
+ "Я.",\
+ "Поставьте свою подпись.",\
+ "Пора за работу.",\
+ "Дело сделано.",\
+ "Станция Нанотрейзен.",\
+ "Офицер СБ.",\
+ "Капитан.",\
+ "Вульпканин.",\
+ "Съешь же ещё этих мягких французских булок, да выпей чаю.",\
+ "Клоун, прекрати разбрасывать банановые кожурки офицерам под ноги!",\
+ "Капитан, вы уверены что хотите назначить клоуна на должность главы персонала?",\
+ )
+
diff --git a/modular_ss220/text_to_speech/code/base_seeds/mobs/_base.dm b/modular_ss220/text_to_speech/code/base_seeds/mobs/_base.dm
new file mode 100644
index 000000000000..1008faa16277
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/base_seeds/mobs/_base.dm
@@ -0,0 +1,5 @@
+//Fallback values for TTS voices
+
+/mob/living
+ tts_seed = "Kleiner"
+
diff --git a/modular_ss220/text_to_speech/code/base_seeds/mobs/alien.dm b/modular_ss220/text_to_speech/code/base_seeds/mobs/alien.dm
new file mode 100644
index 000000000000..e961a000fac1
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/base_seeds/mobs/alien.dm
@@ -0,0 +1,11 @@
+//Aliens!
+
+/mob/living/carbon/alien
+ tts_seed = "Ladyvashj"
+
+/mob/living/carbon/alien/larva
+ tts_seed = "Templar"
+
+/mob/living/carbon/alien/adult/royal/queen
+ tts_seed = "Queen"
+
diff --git a/modular_ss220/text_to_speech/code/base_seeds/mobs/guardian.dm b/modular_ss220/text_to_speech/code/base_seeds/mobs/guardian.dm
new file mode 100644
index 000000000000..e7f87b9b51d0
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/base_seeds/mobs/guardian.dm
@@ -0,0 +1,16 @@
+//Guardians
+
+/mob/living/simple_animal/hostile/guardian
+ tts_seed = "Earth"
+
+/mob/living/simple_animal/hostile/guardian/assassin
+ tts_seed = "Spy"
+
+/mob/living/simple_animal/hostile/guardian/lightning
+ tts_seed = "Archmage"
+
+/mob/living/simple_animal/hostile/guardian/protector
+ tts_seed = "Caime"
+
+/mob/living/simple_animal/hostile/guardian/standard
+ tts_seed = "Heavy"
diff --git a/modular_ss220/text_to_speech/code/base_seeds/mobs/lavaland.dm b/modular_ss220/text_to_speech/code/base_seeds/mobs/lavaland.dm
new file mode 100644
index 000000000000..75228b23f518
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/base_seeds/mobs/lavaland.dm
@@ -0,0 +1,37 @@
+//Lavaland mobs and megafauna
+
+/mob/living/simple_animal/hostile/megafauna
+ tts_seed = "Mannoroth"
+
+/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner
+ tts_seed = "Chen"
+
+/mob/living/simple_animal/hostile/asteroid/basilisk
+ tts_seed = "Antimage"
+
+/mob/living/simple_animal/hostile/asteroid/goliath
+ tts_seed = "Bloodseeker"
+
+/mob/living/simple_animal/hostile/asteroid/hivelord
+ tts_seed = "Ladyvashj"
+
+/mob/living/simple_animal/hostile/asteroid/hivelordbrood
+ tts_seed = "Ladyvashj"
+
+/mob/living/simple_animal/hostile/asteroid/hivelord/legion
+ tts_seed = "Bloodseeker"
+
+/mob/living/simple_animal/hostile/big_legion
+ tts_seed = "Mannoroth"
+
+/mob/living/simple_animal/hostile/asteroid/elite/broodmother
+ tts_seed = "Azalina"
+
+/mob/living/simple_animal/hostile/asteroid/elite/herald
+ tts_seed = "Abathur"
+
+/mob/living/simple_animal/hostile/asteroid/elite/legionnaire
+ tts_seed = "Volibear"
+
+/mob/living/simple_animal/hostile/asteroid/elite/pandora
+ tts_seed = "Zyra"
diff --git a/modular_ss220/text_to_speech/code/base_seeds/mobs/other.dm b/modular_ss220/text_to_speech/code/base_seeds/mobs/other.dm
new file mode 100644
index 000000000000..07757a188d6e
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/base_seeds/mobs/other.dm
@@ -0,0 +1,22 @@
+//Uncategorized mobs
+
+/mob/living/silicon/ai
+ tts_seed = "Glados"
+
+/obj/item/nullrod/scythe/talking
+ tts_seed = "Sylvanas"
+
+/mob/living/simple_animal/shade
+ tts_seed = "Kelthuzad"
+
+/mob/living/simple_animal/bot
+ tts_seed = null
+
+/mob/living/simple_animal/slime
+ tts_seed = "Chen"
+
+/mob/living/carbon/human/species/monkey
+ tts_seed = "Sniper"
+
+/mob/living/carbon/human/species/monkey/punpun
+ tts_seed = "Chen"
diff --git a/modular_ss220/text_to_speech/code/base_seeds/mobs/pets.dm b/modular_ss220/text_to_speech/code/base_seeds/mobs/pets.dm
new file mode 100644
index 000000000000..0ec65ab066ee
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/base_seeds/mobs/pets.dm
@@ -0,0 +1,7 @@
+//All named pets
+
+/mob/living/basic/giant_spider/sgt_araneus
+ tts_seed = "Anubarak"
+
+/mob/living/simple_animal/parrot/poly
+ tts_seed = "Gyro"
diff --git a/modular_ss220/text_to_speech/code/base_seeds/objs/objs.dm b/modular_ss220/text_to_speech/code/base_seeds/objs/objs.dm
new file mode 100644
index 000000000000..f65e9be6188d
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/base_seeds/objs/objs.dm
@@ -0,0 +1,17 @@
+/obj/machinery
+ tts_seed = "Glados"
+
+/obj/machinery/computer
+ tts_seed = null
+
+/obj/machinery/autolathe
+ tts_seed = null
+
+/obj/machinery/mecha_part_fabricator
+ tts_seed = null
+
+/obj/item/taperecorder
+ tts_seed = "Xenia"
+
+/obj/item/ttsdevice
+ tts_seed = "Xenia"
diff --git a/modular_ss220/text_to_speech/code/configuration.dm b/modular_ss220/text_to_speech/code/configuration.dm
new file mode 100644
index 000000000000..30d862197bfb
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/configuration.dm
@@ -0,0 +1,34 @@
+/datum/server_configuration
+ /// Holder for the tts configuration datum
+ var/datum/configuration_section/tts_configuration/tts
+
+/datum/server_configuration/load_all_sections()
+ . = ..()
+ tts = new()
+ safe_load(tts, "tts_configuration")
+
+/datum/configuration_section/tts_configuration
+ protection_state = PROTECTION_PRIVATE
+ /// Is TTS enabled
+ var/tts_enabled = FALSE
+ /// TTS API token for silero provider
+ var/tts_token_silero = ""
+ /// Should oggs be cached
+ var/tts_cache_enabled = FALSE
+ /// What cpu threads should ffmpeg use
+ var/ffmpeg_cpuaffinity
+
+/datum/configuration_section/tts_configuration/load_data(list/data)
+ CONFIG_LOAD_BOOL(tts_enabled, data["tts_enabled"])
+ CONFIG_LOAD_STR(tts_token_silero, data["tts_token_silero"])
+ CONFIG_LOAD_BOOL(tts_cache_enabled, data["tts_cache_enabled"])
+ CONFIG_LOAD_STR(ffmpeg_cpuaffinity, data["ffmpeg_cpuaffinity"])
+
+ tts_enabled = tts_token_silero && tts_enabled
+ var/sanitized = regex(@"[^0-9,-]", "g").Replace(ffmpeg_cpuaffinity, "")
+ if(ffmpeg_cpuaffinity != sanitized)
+ log_config("Wrong value for ffmpeg_cpuaffinity. Check out taskset man page.")
+
+/datum/http_request/vv_get_var(var_name)
+ if(var_name == "body")
+ return FALSE
diff --git a/modular_ss220/text_to_speech/code/hear.dm b/modular_ss220/text_to_speech/code/hear.dm
new file mode 100644
index 000000000000..bc248aa9fd6e
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/hear.dm
@@ -0,0 +1,78 @@
+/mob/proc/combine_message_tts(list/message_pieces, mob/speaker, always_stars = FALSE)
+ var/iteration_count = 0
+ var/msg = ""
+ for(var/datum/multilingual_say_piece/say_piece in message_pieces)
+ iteration_count++
+ var/piece = say_piece.message
+ if(piece == "")
+ continue
+
+ if(say_piece.speaking?.flags & INNATE) // TTS should not read emotes like "laughts"
+ return ""
+
+ if(always_stars)
+ continue
+
+ if(iteration_count == 1)
+ piece = capitalize(piece)
+
+ if(!say_understands(speaker, say_piece.speaking))
+ if(isanimal(speaker))
+ var/mob/living/simple_animal/S = speaker
+ if(!LAZYLEN(S.speak))
+ continue
+ piece = pick(S.speak)
+ else if(say_piece.speaking)
+ piece = say_piece.speaking.scramble(piece)
+ else
+ continue
+ msg += (piece + " ")
+ return trim(msg)
+
+
+/mob/hear_say(list/message_pieces, verb, italics, mob/speaker, sound/speech_sound, sound_vol, sound_frequency, use_voice)
+ . = ..()
+ if(!can_hear())
+ return
+
+ var/message_tts = combine_message_tts(message_pieces, speaker)
+ var/effect = isrobot(speaker) ? SOUND_EFFECT_ROBOT : SOUND_EFFECT_NONE
+ var/traits = TTS_TRAIT_RATE_FASTER
+ var/is_whisper = verb == "whispers"
+ if(is_whisper)
+ traits |= TTS_TRAIT_PITCH_WHISPER
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), speaker, src, message_tts, speaker.tts_seed, TRUE, effect, traits)
+
+/mob/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target, check_name_against)
+ . = ..()
+ if(!can_hear())
+ return
+
+ if(src != speaker || isrobot(src) || isAI(src))
+ var/effect = isrobot(speaker) ? SOUND_EFFECT_RADIO_ROBOT : SOUND_EFFECT_RADIO
+ var/message_tts = combine_message_tts(message_pieces, speaker, always_stars = hard_to_hear)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), src, src, message_tts, speaker.tts_seed, FALSE, effect, null, null, 'modular_ss220/text_to_speech/code/sound/radio_chatter.ogg')
+
+/mob/hear_holopad_talk(list/message_pieces, verb, mob/speaker, obj/effect/overlay/holo_pad_hologram/H)
+ . = ..()
+ if(!can_hear())
+ return
+ var/message_tts = combine_message_tts(message_pieces, speaker)
+ var/effect = isrobot(speaker) ? SOUND_EFFECT_RADIO_ROBOT : SOUND_EFFECT_RADIO
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), H, src, message_tts, speaker.tts_seed, TRUE, effect)
+
+/datum/announcer/Message(message, garbled_message, receivers, garbled_receivers)
+ var/tts_seed = "Glados"
+ if(GLOB.ai_list.len)
+ var/mob/living/silicon/ai/AI = pick(GLOB.ai_list)
+ tts_seed = AI.tts_seed
+ var/message_tts = message
+ var/garbled_message_tts = garbled_message
+ message = replace_characters(message, list("+"))
+ garbled_message = replace_characters(garbled_message, list("+"))
+ . = ..()
+ for(var/mob/M in receivers)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), null, M, message_tts, tts_seed, FALSE, SOUND_EFFECT_NONE, TTS_TRAIT_RATE_MEDIUM)
+ for(var/mob/M in garbled_receivers)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), null, M, garbled_message_tts, tts_seed, FALSE, SOUND_EFFECT_NONE, TTS_TRAIT_RATE_MEDIUM)
+
diff --git a/modular_ss220/text_to_speech/code/numbers.dm b/modular_ss220/text_to_speech/code/numbers.dm
new file mode 100644
index 000000000000..ed41e245dfcf
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/numbers.dm
@@ -0,0 +1,161 @@
+/proc/num_in_words(n)
+ return get_num_in_words(n)
+
+/proc/dec_in_words(n)
+ return get_num_in_words(n, TRUE)
+
+/proc/get_num_in_words(n, decimal = FALSE)
+ var/static/datum/number/num
+ if(!num)
+ num = new /datum/number
+
+ if(num.cache["[n]"])
+ return num.cache["[n]"]
+
+ var/result = decimal ? num.decimal2words(n) : num.int2words(n)
+
+ result = " [result] "
+ num.cache["[n]"] = result
+ return result
+
+/datum/number
+ var/static/list/units = list(
+ "ноль",
+
+ list("один", "одна"),
+ list("два", "две"),
+
+ "три", "четыре", "пять",
+ "шесть", "семь", "восемь", "девять"
+ )
+
+ var/static/list/teens = list(
+ "десять", "одиннадцать",
+ "двенадцать", "тринадцать",
+ "четырнадцать", "пятнадцать",
+ "шестнадцать", "семнадцать",
+ "восемнадцать", "девятнадцать"
+ )
+
+ var/static/list/tens = list(
+ "десять",
+ "двадцать", "тридцать",
+ "сорок", "пятьдесят",
+ "шестьдесят", "семьдесят",
+ "восемьдесят", "девяносто"
+ )
+
+ var/static/list/hundreds = list(
+ "сто", "двести",
+ "триста", "четыреста",
+ "пятьсот", "шестьсот",
+ "семьсот", "восемьсот",
+ "девятьсот"
+ )
+
+ var/static/list/orders = list(
+ list(list("тысяча", "тысячи", "тысяч"), "f"),
+ list(list("миллион", "миллиона", "миллионов"), "m"),
+ list(list("миллиард", "миллиарда", "миллиардов"), "m"),
+ list(list("триллион", "триллиона", "триллионов"), "m"),
+ list(list("квадриллион", "квадриллиона", "квадриллионов"), "m"),
+ list(list("квинтиллион", "квинтиллиона", "квинтиллионов"), "m"),
+ )
+
+ var/static/list/decimal_int_units = list(list("целая", "целых", "целых"), "f")
+
+ var/static/list/decimal_exp_units = list(
+ list(list("десятая", "десятых", "десятых"), "f"),
+ list(list("сотая", "сотых", "сотых"), "f"),
+ list(list("тысячная", "тысячных", "тысячных"), "f"),
+ )
+
+ var/static/minus = "минус"
+
+ var/static/cache = list()
+
+/datum/number/proc/thousand(rest, sex)
+// """Converts numbers from 19 to 999"""
+ var/prev = 0
+ var/plural = 3
+ var/list/name = list()
+ var/use_teens = (rest % 100 >= 10) && (rest % 100 <= 19)
+ var/list/data = use_teens ? list(list(teens, 10), list(hundreds, 1000)) : list(list(units, 10), list(tens, 100), list(hundreds, 1000))
+ for(var/list in data)
+
+ var/names = list[1]
+ var/x = list[2]
+
+ var/cur = round(((rest - prev) % x) * 10 / x) + 1
+ prev = rest % x
+
+ if(x == 10 && use_teens)
+ plural = 3
+ name += teens[cur]
+ else if(cur == 1)
+ continue
+ else if(x == 10)
+ var/name_ = names[cur]
+ if(islist(name_))
+ name_ = name_[sex == "m" ? 1 : 2]
+ name += name_
+ if(cur >= 3 && cur <= 5)
+ plural = 2
+ else if(cur == 2)
+ plural = 1
+ else
+ plural = 3
+ else
+ name += names[cur-1]
+
+ return list(plural, name)
+
+/datum/number/proc/int2words(textnum, list/main_units = list(list("", "", ""), "m"))
+// http://ru.wikipedia.org/wiki/Gettext#.D0.9C.D0.BD.D0.BE.D0.B6.D0.B5.D1.81.D1.82.D0.B2.D0.B5.D0.BD.D0.BD.D1.8B.D0.B5_.D1.87.D0.B8.D1.81.D0.BB.D0.B0_2
+
+ var/list/_orders = list(main_units) + orders
+
+ var/num = text2num(textnum)
+ if(num == 0)
+ return trim(jointext(list(units[1], _orders[1][1][3]), " "))
+
+ var/negative = FALSE
+ if(num < 0)
+ negative = TRUE
+ textnum = copytext_char(textnum, 2, 0)
+
+ var/ord = 1
+ var/list/name = list()
+
+ while(textnum)
+ var/next_thousand = text2num(copytext_char(textnum, -3, 0))
+ var/list/thousand_result = thousand(next_thousand, _orders[ord][2])
+ var/plural = thousand_result[1]
+ var/list/nme = thousand_result[2]
+
+ if(length(nme) || ord == 1)
+ name += _orders[ord][1][plural]
+
+ name += nme
+ textnum = copytext_char(textnum, 1, -3)
+ ord += 1
+
+ if(negative)
+ name += minus
+
+ var/temp_name = name
+ name = list()
+ for(var/i = length_char(temp_name), i >= 1, i--)
+ name += temp_name[i]
+
+ var/result = trim(jointext(name, " "))
+ return result
+
+/datum/number/proc/decimal2words(textvalue, places = 3)
+ var/pieces = splittext_char(textvalue, ".")
+ var/integral = pieces[1]
+ var/exp = copytext_char(pieces[2], 1, places + 1)
+ var/list/exp_units = decimal_exp_units[length_char(exp)]
+
+ var/result = trim("[int2words(integral, decimal_int_units)] [int2words(exp, exp_units)]")
+ return result
diff --git a/modular_ss220/text_to_speech/code/providers/silero.dm b/modular_ss220/text_to_speech/code/providers/silero.dm
new file mode 100644
index 000000000000..6c924b0a8ad6
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/providers/silero.dm
@@ -0,0 +1,54 @@
+/datum/tts_provider/silero
+ name = "Silero"
+ is_enabled = TRUE
+ api_url = "http://s2.ss220.club:9999/voice"
+
+/datum/tts_provider/silero/vv_edit_var(var_name, var_value)
+ . = ..()
+ if(var_name == "api_url")
+ return FALSE
+
+/datum/tts_provider/silero/request(text, datum/tts_seed/silero/seed, datum/callback/proc_callback)
+ if(throttle_check())
+ return FALSE
+
+ var/ssml_text = {"[text]"}
+
+ var/list/req_body = list()
+ req_body["api_token"] = GLOB.configuration.tts.tts_token_silero
+ req_body["text"] = ssml_text
+ req_body["sample_rate"] = 24000
+ req_body["ssml"] = TRUE
+ req_body["speaker"] = seed.value
+ req_body["lang"] = "ru"
+ req_body["remote_id"] = "[world.port]"
+ req_body["put_accent"] = TRUE
+ req_body["put_yo"] = FALSE
+ req_body["symbol_durs"] = list()
+ req_body["format"] = "ogg"
+ req_body["word_ts"] = FALSE
+
+ SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, api_url, json_encode(req_body), list("content-type" = "application/json"), proc_callback)
+
+ return TRUE
+
+/datum/tts_provider/silero/process_response(datum/http_response/response)
+ var/data = json_decode(response.body)
+ // log_debug(response.body)
+
+ if(data["timings"]["003_tts_time"] > 3)
+ is_throttled = TRUE
+ throttled_until = world.time + 15 SECONDS
+
+ return data["results"][1]["audio"]
+
+ //var/sha1 = data["original_sha1"]
+
+/datum/tts_provider/silero/pitch_whisper(text)
+ return {"[text]"}
+
+/datum/tts_provider/silero/rate_faster(text)
+ return {"[text]"}
+
+/datum/tts_provider/silero/rate_medium(text)
+ return {"[text]"}
diff --git a/modular_ss220/text_to_speech/code/rust_g_ss220.dm b/modular_ss220/text_to_speech/code/rust_g_ss220.dm
new file mode 100644
index 000000000000..dffceaafc9bd
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/rust_g_ss220.dm
@@ -0,0 +1,57 @@
+// rust_g_ss220.dm - DM API for rust_g_ss220 extension library
+//
+// To configure, create a `rust_g_ss220.config.dm` and set what you care about from
+// the following options:
+//
+// #define RUST_G_SS220 "path/to/rust_g_ss220"
+// Override the .dll/.so detection logic with a fixed path or with detection
+// logic of your own.
+//
+// #define RUSTG_OVERRIDE_BUILTINS
+// Enable replacement rust-g functions for certain builtins. Off by default.
+
+#ifndef RUST_G_SS220
+// Default automatic RUST_G_SS220 detection.
+// On Windows, looks in the standard places for `rust_g_ss220.dll`.
+// On Linux, looks in `.`, `$LD_LIBRARY_PATH`, and `~/.byond/bin` for either of
+// `librust_g_ss220.so` (preferred) or `rust_g_ss220` (old).
+
+/* This comment bypasses grep checks */ /var/__rust_g_ss220
+
+/proc/__detect_rust_g_ss220()
+ if (world.system_type == UNIX)
+ if (fexists("./librust_g_ss220.so"))
+ // No need for LD_LIBRARY_PATH badness.
+ return __rust_g_ss220 = "./librust_g_ss220.so"
+ else if (fexists("./rust_g_ss220"))
+ // Old dumb filename.
+ return __rust_g_ss220 = "./rust_g_ss220"
+ else if (fexists("[world.GetConfig("env", "HOME")]/.byond/bin/rust_g_ss220"))
+ // Old dumb filename in `~/.byond/bin`.
+ return __rust_g_ss220 = "rust_g_ss220"
+ else
+ // It's not in the current directory, so try others
+ return __rust_g_ss220 = "librust_g_ss220.so"
+ else
+ return __rust_g_ss220 = "rust_g_ss220"
+
+#define RUST_G_SS220 (__rust_g_ss220 || __detect_rust_g_ss220())
+#endif
+
+/// Gets the version of rust_g
+/proc/rustgss220_get_version() return RUSTG_CALL(RUST_G_SS220, "get_version")()
+
+#define rustgss220_file_write_b64decode(text, fname) RUSTG_CALL(RUST_G_SS220, "file_write")(text, fname, "true")
+
+// Hashing Operations //
+#define rustgss220_hash_string(algorithm, text) RUSTG_CALL(RUST_G_SS220, "hash_string")(algorithm, text)
+#define rustgss220_hash_file(algorithm, fname) RUSTG_CALL(RUST_G_SS220, "hash_file")(algorithm, fname)
+
+
+#ifdef RUSTG_OVERRIDE_BUILTINS
+ #define md5(thing) (isfile(thing) ? rustgss220_hash_file(RUSTG_HASH_MD5, "[thing]") : rustgss220_hash_string(RUSTG_HASH_MD5, thing))
+#endif
+
+// Text Operations //
+#define rustgss220_cyrillic_to_latin(text) RUSTG_CALL(RUST_G_SS220, "cyrillic_to_latin")("[text]")
+#define rustgss220_latin_to_cyrillic(text) RUSTG_CALL(RUST_G_SS220, "latin_to_cyrillic")("[text]")
diff --git a/modular_ss220/text_to_speech/code/seeds/silero.dm b/modular_ss220/text_to_speech/code/seeds/silero.dm
new file mode 100644
index 000000000000..dd941a5a09ff
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/seeds/silero.dm
@@ -0,0 +1,3581 @@
+/datum/tts_seed/silero
+ provider = /datum/tts_provider/silero
+
+/datum/tts_seed/silero/arthas
+ name = "Arthas"
+ value = "arthas"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/kelthuzad
+ name = "Kelthuzad"
+ value = "kelthuzad"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/anubarak
+ name = "Anubarak"
+ value = "anubarak"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/thrall
+ name = "Thrall"
+ value = "thrall"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/grunt
+ name = "Grunt"
+ value = "grunt"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/cairne
+ name = "Cairne"
+ value = "cairne"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/rexxar
+ name = "Rexxar"
+ value = "rexxar"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/uther
+ name = "Uther"
+ value = "uther"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/jaina
+ name = "Jaina"
+ value = "jaina"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/kael
+ name = "Kael"
+ value = "kael"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/garithos
+ name = "Garithos"
+ value = "garithos"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/maiev
+ name = "Maiev"
+ value = "maiev"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/naisha
+ name = "Naisha"
+ value = "naisha"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/tyrande
+ name = "Tyrande"
+ value = "tyrande"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/furion
+ name = "Furion"
+ value = "furion"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/illidan
+ name = "Illidan"
+ value = "illidan"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/ladyvashj
+ name = "Ladyvashj"
+ value = "ladyvashj"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/narrator
+ name = "Narrator"
+ value = "narrator"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/medivh
+ name = "Medivh"
+ value = "medivh"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/villagerm
+ name = "Villagerm"
+ value = "villagerm"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/xenia
+ name = "Xenia"
+ value = "xenia"
+ category = TTS_CATEGORY_OTHER
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/illidan_f
+ name = "Illidan_f"
+ value = "illidan_f"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/peon
+ name = "Peon"
+ value = "peon"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/chen
+ name = "Chen"
+ value = "chen"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/dread_bm
+ name = "Dread_bm"
+ value = "dread_bm"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/sylvanas
+ name = "Sylvanas"
+ value = "sylvanas"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/priest
+ name = "Priest"
+ value = "priest"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/acolyte
+ name = "Acolyte"
+ value = "acolyte"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/muradin
+ name = "Muradin"
+ value = "muradin"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/dread_t
+ name = "Dread_t"
+ value = "dread_t"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/mannoroth
+ name = "Mannoroth"
+ value = "mannoroth"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/sorceress
+ name = "Sorceress"
+ value = "sorceress"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/peasant
+ name = "Peasant"
+ value = "peasant"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/alyx
+ name = "Alyx"
+ value = "alyx"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/glados
+ name = "Glados"
+ value = "glados"
+ category = TTS_CATEGORY_PORTAL2
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/announcer
+ name = "Announcer"
+ value = "announcer"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/wheatley
+ name = "Wheatley"
+ value = "wheatley"
+ category = TTS_CATEGORY_PORTAL2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/barney
+ name = "Barney"
+ value = "barney"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/raynor
+ name = "Raynor"
+ value = "raynor"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kerrigan
+ name = "Kerrigan"
+ value = "kerrigan"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/tusk
+ name = "Tusk"
+ value = "tusk"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/earth
+ name = "Earth"
+ value = "earth"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/wraith
+ name = "Wraith"
+ value = "wraith"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/meepo
+ name = "Meepo"
+ value = "meepo"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/lina
+ name = "Lina"
+ value = "lina"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/bristle
+ name = "Bristle"
+ value = "bristle"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/gyro
+ name = "Gyro"
+ value = "gyro"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/treant
+ name = "Treant"
+ value = "treant"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/lancer
+ name = "Lancer"
+ value = "lancer"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/clockwerk
+ name = "Clockwerk"
+ value = "clockwerk"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/batrider
+ name = "Batrider"
+ value = "batrider"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/kotl
+ name = "Kotl"
+ value = "kotl"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/kunkka
+ name = "Kunkka"
+ value = "kunkka"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/pudge
+ name = "Pudge"
+ value = "pudge"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/juggernaut
+ name = "Juggernaut"
+ value = "juggernaut"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/vort_e2
+ name = "Vort_e2"
+ value = "vort_e2"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/luna
+ name = "Luna"
+ value = "luna"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/omni
+ name = "Omni"
+ value = "omni"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/sniper
+ name = "Sniper"
+ value = "sniper"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/skywrath
+ name = "Skywrath"
+ value = "skywrath"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/bounty
+ name = "Bounty"
+ value = "bounty"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/huskar
+ name = "Huskar"
+ value = "huskar"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/windranger
+ name = "Windranger"
+ value = "windranger"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/bloodseeker
+ name = "Bloodseeker"
+ value = "bloodseeker"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/templar
+ name = "Templar"
+ value = "templar"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/ranger
+ name = "Ranger"
+ value = "ranger"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/shaker
+ name = "Shaker"
+ value = "shaker"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/mortred
+ name = "Mortred"
+ value = "mortred"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/queen
+ name = "Queen"
+ value = "queen"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/storm
+ name = "Storm"
+ value = "storm"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/tide
+ name = "Tide"
+ value = "tide"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/evelynn
+ name = "Evelynn"
+ value = "evelynn"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/riki
+ name = "Riki"
+ value = "riki"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/antimage
+ name = "Antimage"
+ value = "antimage"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/witchdoctor
+ name = "Witchdoctor"
+ value = "witchdoctor"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/doom
+ name = "Doom"
+ value = "doom"
+ category = TTS_CATEGORY_DOTA2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/yuumi
+ name = "Yuumi"
+ value = "yuumi"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/bandit
+ name = "Bandit"
+ value = "bandit"
+ category = TTS_CATEGORY_STALKER
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/pantheon
+ name = "pantheon"
+ value = "pantheon"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tychus
+ name = "Tychus"
+ value = "tychus"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/breen
+ name = "Breen"
+ value = "breen"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/kleiner
+ name = "Kleiner"
+ value = "kleiner"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/father
+ name = "Father"
+ value = "father"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/tosh
+ name = "Tosh"
+ value = "tosh"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/stetmann
+ name = "Stetmann"
+ value = "stetmann"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/hanson
+ name = "Hanson"
+ value = "hanson"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/swann
+ name = "Swann"
+ value = "swann"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/hill
+ name = "Hill"
+ value = "hill"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/gman_e2
+ name = "Gman_e2"
+ value = "gman_e2"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/valerian
+ name = "Valerian"
+ value = "valerian"
+ category = TTS_CATEGORY_STARCRAFT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/gman
+ name = "Gman"
+ value = "gman"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/vort
+ name = "Vort"
+ value = "vort"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/aradesh
+ name = "Aradesh"
+ value = "aradesh"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/dornan
+ name = "Dornan"
+ value = "dornan"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/elder
+ name = "Elder"
+ value = "elder"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/harris
+ name = "Harris"
+ value = "harris"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/cabbot
+ name = "Cabbot"
+ value = "cabbot"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/decker
+ name = "Decker"
+ value = "decker"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/dick
+ name = "Dick"
+ value = "dick"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/officer
+ name = "Officer"
+ value = "officer"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/frank
+ name = "Frank"
+ value = "frank"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/gizmo
+ name = "Gizmo"
+ value = "gizmo"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/hakunin
+ name = "Hakunin"
+ value = "hakunin"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/harold
+ name = "Harold"
+ value = "harold"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/harry
+ name = "Harry"
+ value = "harry"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/jain
+ name = "Jain"
+ value = "jain"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/maxson
+ name = "Maxson"
+ value = "maxson"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/killian
+ name = "Killian"
+ value = "killian"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/laura
+ name = "Laura"
+ value = "laura"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/lieutenant
+ name = "Lieutenant"
+ value = "lieutenant"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/loxley
+ name = "Loxley"
+ value = "loxley"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/lynette
+ name = "Lynette"
+ value = "lynette"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/marcus
+ name = "Marcus"
+ value = "marcus"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/master
+ name = "Master"
+ value = "master"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/morpheus
+ name = "Morpheus"
+ value = "morpheus"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/myron
+ name = "Myron"
+ value = "myron"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/nicole
+ name = "Nicole"
+ value = "nicole"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/overseer
+ name = "Overseer"
+ value = "overseer"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/rhombus
+ name = "Rhombus"
+ value = "rhombus"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/_set
+ name = "Set"
+ value = "set"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/sulik
+ name = "Sulik"
+ value = "sulik"
+ category = TTS_CATEGORY_FALLOUT2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/tandi
+ name = "Tandi"
+ value = "tandi"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/vree
+ name = "Vree"
+ value = "vree"
+ category = TTS_CATEGORY_FALLOUT
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/dude
+ name = "Dude"
+ value = "dude"
+ category = TTS_CATEGORY_POSTAL2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/archmage
+ name = "Archmage"
+ value = "archmage"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/demoman
+ name = "Demoman"
+ value = "demoman"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/engineer
+ name = "Engineer"
+ value = "engineer"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/heavy
+ name = "Heavy"
+ value = "heavy"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/medic
+ name = "Medic"
+ value = "medic"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/scout
+ name = "Scout"
+ value = "scout"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/sniper_tf
+ name = "Sniper_tf"
+ value = "sniper_tf"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/soldier
+ name = "Soldier"
+ value = "soldier"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/spy
+ name = "Spy"
+ value = "spy"
+ category = TTS_CATEGORY_TEAMFORTRESS2
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/admiral
+ name = "Admiral"
+ value = "admiral"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/alchemist
+ name = "Alchemist"
+ value = "alchemist"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/archimonde
+ name = "Archimonde"
+ value = "archimonde"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/breaker
+ name = "Breaker"
+ value = "breaker"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/captain
+ name = "Captain"
+ value = "captain"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/dryad
+ name = "Dryad"
+ value = "dryad"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/elf_eng
+ name = "Elf_eng"
+ value = "elf_eng"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/footman
+ name = "Footman"
+ value = "footman"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/grom
+ name = "Grom"
+ value = "grom"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/hh
+ name = "Hh"
+ value = "hh"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/huntress
+ name = "Huntress"
+ value = "huntress"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/keeper
+ name = "Keeper"
+ value = "keeper"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/naga_m
+ name = "Naga_m"
+ value = "naga_m"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/naga_rg
+ name = "Naga_rg"
+ value = "naga_rg"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/peasant_w
+ name = "Peasant_w"
+ value = "peasant_w"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/rifleman
+ name = "Rifleman"
+ value = "rifleman"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/satyr
+ name = "Satyr"
+ value = "satyr"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/sylvanas_w
+ name = "Sylvanas_w"
+ value = "sylvanas_w"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/voljin
+ name = "Voljin"
+ value = "voljin"
+ category = TTS_CATEGORY_WARCRAFT3
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/sidorovich
+ name = "Sidorovich"
+ value = "sidorovich"
+ category = TTS_CATEGORY_STALKER
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/p3
+ name = "P3"
+ value = "p3"
+ category = TTS_CATEGORY_ATOMIC_HEART
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hraz
+ name = "Hraz"
+ value = "hraz"
+ category = TTS_CATEGORY_ATOMIC_HEART
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tereshkova
+ name = "Tereshkova"
+ value = "tereshkova"
+ category = TTS_CATEGORY_ATOMIC_HEART
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/babazina
+ name = "Babazina"
+ value = "babazina"
+ category = TTS_CATEGORY_ATOMIC_HEART
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/darius
+ name = "Darius"
+ value = "darius"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/trundle
+ name = "Trundle"
+ value = "trundle"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/garen
+ name = "Garen"
+ value = "garen"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kled
+ name = "Kled"
+ value = "kled"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ekko
+ name = "Ekko"
+ value = "ekko"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/volibear
+ name = "Volibear"
+ value = "volibear"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/samira
+ name = "Samira"
+ value = "samira"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/swain
+ name = "Swain"
+ value = "swain"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/udyr
+ name = "Udyr"
+ value = "udyr"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dr_mundo
+ name = "Dr_mundo"
+ value = "dr_mundo"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/graves
+ name = "Graves"
+ value = "graves"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/rakan
+ name = "Rakan"
+ value = "rakan"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/renata_glasc
+ name = "Renata_glasc"
+ value = "renata_glasc"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/gangplank
+ name = "Gangplank"
+ value = "gangplank"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/riven
+ name = "Riven"
+ value = "riven"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/katarina
+ name = "Katarina"
+ value = "katarina"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ahri
+ name = "Ahri"
+ value = "ahri"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ornn
+ name = "Ornn"
+ value = "ornn"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/braum
+ name = "Braum"
+ value = "braum"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/fizz
+ name = "Fizz"
+ value = "fizz"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/draven
+ name = "Draven"
+ value = "draven"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/qiyana
+ name = "Qiyana"
+ value = "qiyana"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ksante
+ name = "Ksante"
+ value = "ksante"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/talon
+ name = "Talon"
+ value = "talon"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/shyvana
+ name = "Shyvana"
+ value = "shyvana"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zenyatta
+ name = "Zenyatta"
+ value = "zenyatta"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kiriko
+ name = "Kiriko"
+ value = "kiriko"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hanzo
+ name = "Hanzo"
+ value = "hanzo"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/roadhog
+ name = "Roadhog"
+ value = "roadhog"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sigma
+ name = "Sigma"
+ value = "sigma"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/soldier_76
+ name = "Soldier_76"
+ value = "soldier_76"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/junkrat
+ name = "Junkrat"
+ value = "junkrat"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tracer
+ name = "Tracer"
+ value = "tracer"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/genji
+ name = "Genji"
+ value = "genji"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/echo
+ name = "Echo"
+ value = "echo"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sojourn
+ name = "Sojourn"
+ value = "sojourn"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/winston
+ name = "Winston"
+ value = "winston"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/reaper
+ name = "Reaper"
+ value = "reaper"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/training_robot
+ name = "Training_robot"
+ value = "training_robot"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_darkelf
+ name = "M_darkelf"
+ value = "m_darkelf"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/esbern
+ name = "Esbern"
+ value = "esbern"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_argo
+ name = "M_argo"
+ value = "m_argo"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_khajiit
+ name = "M_khajiit"
+ value = "m_khajiit"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_coward
+ name = "M_coward"
+ value = "m_coward"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/farkas
+ name = "Farkas"
+ value = "farkas"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_drunk
+ name = "M_drunk"
+ value = "m_drunk"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_khajiit
+ name = "F_khajiit"
+ value = "f_khajiit"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_citizen
+ name = "M_citizen"
+ value = "m_citizen"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_orc
+ name = "M_orc"
+ value = "m_orc"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/odahviing
+ name = "Odahviing"
+ value = "odahviing"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kodlak
+ name = "Kodlak"
+ value = "kodlak"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_child
+ name = "M_child"
+ value = "m_child"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/emperor
+ name = "Emperor"
+ value = "emperor"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hagraven
+ name = "Hagraven"
+ value = "hagraven"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/nazir
+ name = "Nazir"
+ value = "nazir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dremora
+ name = "Dremora"
+ value = "dremora"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/alduin
+ name = "Alduin"
+ value = "alduin"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/malkoran
+ name = "Malkoran"
+ value = "malkoran"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/barbas
+ name = "Barbas"
+ value = "barbas"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hermaeus
+ name = "Hermaeus"
+ value = "hermaeus"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hakon
+ name = "Hakon"
+ value = "hakon"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/rita
+ name = "Rita"
+ value = "rita"
+ category = TTS_CATEGORY_RITA
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/barman
+ name = "Barman"
+ value = "barman"
+ category = TTS_CATEGORY_STALKER
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/bridger2
+ name = "Bridger2"
+ value = "bridger2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/bridger3
+ name = "Bridger3"
+ value = "bridger3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cannibal3
+ name = "Cannibal3"
+ value = "cannibal3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/bridger1
+ name = "Bridger1"
+ value = "bridger1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cannibal2
+ name = "Cannibal2"
+ value = "cannibal2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/slave1
+ name = "Slave1"
+ value = "slave1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/slave3
+ name = "Slave3"
+ value = "slave3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/mira
+ name = "Mira"
+ value = "mira"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/valeera
+ name = "Valeera"
+ value = "valeera"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/rehgar
+ name = "Rehgar"
+ value = "rehgar"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/yrel
+ name = "Yrel"
+ value = "yrel"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/volskaya
+ name = "Volskaya"
+ value = "volskaya"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/necromancer
+ name = "Necromancer"
+ value = "necromancer"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zuljin
+ name = "Zuljin"
+ value = "zuljin"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/samuro
+ name = "Samuro"
+ value = "samuro"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tyrael
+ name = "Tyrael"
+ value = "tyrael"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/athena
+ name = "Athena"
+ value = "athena"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/default
+ name = "Default"
+ value = "default"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/chromie
+ name = "Chromie"
+ value = "chromie"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/orphea
+ name = "Orphea"
+ value = "orphea"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/adjutant
+ name = "Adjutant"
+ value = "adjutant"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/vanndara
+ name = "Vanndara"
+ value = "vanndara"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/mechatassadar
+ name = "Mechatassadar"
+ value = "mechatassadar"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/blackheart
+ name = "Blackheart"
+ value = "blackheart"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/olaf
+ name = "Olaf"
+ value = "olaf"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/alarak
+ name = "Alarak"
+ value = "alarak"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dva
+ name = "Dva"
+ value = "dva"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/toy18
+ name = "Toy18"
+ value = "toy18"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/witchdoctor_h
+ name = "Witchdoctor_h"
+ value = "witchdoctor_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lucio
+ name = "Lucio"
+ value = "lucio"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/angel
+ name = "Angel"
+ value = "angel"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/thunderking
+ name = "Thunderking"
+ value = "thunderking"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dr_boom
+ name = "Dr_boom"
+ value = "dr_boom"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hooktusk
+ name = "Hooktusk"
+ value = "hooktusk"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sinclari
+ name = "Sinclari"
+ value = "sinclari"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kazakus
+ name = "Kazakus"
+ value = "kazakus"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ol_toomba
+ name = "Ol_toomba"
+ value = "ol_toomba"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/moroes
+ name = "Moroes"
+ value = "moroes"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/maiev_hs
+ name = "Maiev_hs"
+ value = "maiev_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zentimo
+ name = "Zentimo"
+ value = "zentimo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/rastakhan
+ name = "Rastakhan"
+ value = "rastakhan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/innkeeper
+ name = "Innkeeper"
+ value = "innkeeper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/togwaggle
+ name = "Togwaggle"
+ value = "togwaggle"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/biggs
+ name = "Biggs"
+ value = "biggs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/brann
+ name = "Brann"
+ value = "brann"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tekahn_boss
+ name = "Tekahn_boss"
+ value = "tekahn_boss"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/siamat
+ name = "Siamat"
+ value = "siamat"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/omnotron
+ name = "Omnotron"
+ value = "omnotron"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/putricide
+ name = "Putricide"
+ value = "putricide"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/khadgar
+ name = "Khadgar"
+ value = "khadgar"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zoie
+ name = "Zoie"
+ value = "zoie"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/azalina
+ name = "Azalina"
+ value = "azalina"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/chu
+ name = "Chu"
+ value = "chu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tekahn
+ name = "Tekahn"
+ value = "tekahn"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sthara
+ name = "Sthara"
+ value = "sthara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dovo
+ name = "Dovo"
+ value = "dovo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/shaw
+ name = "Shaw"
+ value = "shaw"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/greymane
+ name = "Greymane"
+ value = "greymane"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/willow
+ name = "Willow"
+ value = "willow"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/haro
+ name = "Haro"
+ value = "haro"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hagatha
+ name = "Hagatha"
+ value = "hagatha"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/reno
+ name = "Reno"
+ value = "reno"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ozara
+ name = "Ozara"
+ value = "ozara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/loti
+ name = "Loti"
+ value = "loti"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tarkus
+ name = "Tarkus"
+ value = "tarkus"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/voone
+ name = "Voone"
+ value = "voone"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tala
+ name = "Tala"
+ value = "tala"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/edra
+ name = "Edra"
+ value = "edra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/myra
+ name = "Myra"
+ value = "myra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/smiggs
+ name = "Smiggs"
+ value = "smiggs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/timothy
+ name = "Timothy"
+ value = "timothy"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/wendy
+ name = "Wendy"
+ value = "wendy"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hannigan
+ name = "Hannigan"
+ value = "hannigan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/vargoth
+ name = "Vargoth"
+ value = "vargoth"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/jolene
+ name = "Jolene"
+ value = "jolene"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kyriss
+ name = "Kyriss"
+ value = "kyriss"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/saurfang
+ name = "Saurfang"
+ value = "saurfang"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kizi
+ name = "Kizi"
+ value = "kizi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/slate
+ name = "Slate"
+ value = "slate"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hesutu
+ name = "Hesutu"
+ value = "hesutu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hancho
+ name = "Hancho"
+ value = "hancho"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/gnomenapper
+ name = "Gnomenapper"
+ value = "gnomenapper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/valdera
+ name = "Valdera"
+ value = "valdera"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/disidra
+ name = "Disidra"
+ value = "disidra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/omu
+ name = "Omu"
+ value = "omu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/floop
+ name = "Floop"
+ value = "floop"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/belloc
+ name = "Belloc"
+ value = "belloc"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/xurios
+ name = "Xurios"
+ value = "xurios"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/wagtoggle
+ name = "Wagtoggle"
+ value = "wagtoggle"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/belnaara
+ name = "Belnaara"
+ value = "belnaara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lilayell
+ name = "Lilayell"
+ value = "lilayell"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/candlebeard
+ name = "Candlebeard"
+ value = "candlebeard"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/awilo
+ name = "Awilo"
+ value = "awilo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/marei
+ name = "Marei"
+ value = "marei"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/applebough
+ name = "Applebough"
+ value = "applebough"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lazul
+ name = "Lazul"
+ value = "lazul"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/arwyn
+ name = "Arwyn"
+ value = "arwyn"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/glowtron
+ name = "Glowtron"
+ value = "glowtron"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cardish
+ name = "Cardish"
+ value = "cardish"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/robold
+ name = "Robold"
+ value = "robold"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/malfurion
+ name = "Malfurion"
+ value = "malfurion"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/deathwhisper
+ name = "Deathwhisper"
+ value = "deathwhisper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/janna
+ name = "Janna"
+ value = "janna"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cassiopeia
+ name = "Cassiopeia"
+ value = "cassiopeia"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/taliyah
+ name = "Taliyah"
+ value = "taliyah"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/neeko
+ name = "Neeko"
+ value = "neeko"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/taric
+ name = "Taric"
+ value = "taric"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/akshan
+ name = "Akshan"
+ value = "akshan"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tristana
+ name = "Tristana"
+ value = "tristana"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sylas
+ name = "Sylas"
+ value = "sylas"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sejuani
+ name = "Sejuani"
+ value = "sejuani"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/anivia
+ name = "Anivia"
+ value = "anivia"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/vayne
+ name = "Vayne"
+ value = "vayne"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/karma
+ name = "Karma"
+ value = "karma"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/nilah
+ name = "Nilah"
+ value = "nilah"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/olaf_lol
+ name = "Olaf_lol"
+ value = "olaf_lol"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/quinn
+ name = "Quinn"
+ value = "quinn"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lissandra
+ name = "Lissandra"
+ value = "lissandra"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hecarim
+ name = "Hecarim"
+ value = "hecarim"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/vi
+ name = "Vi"
+ value = "vi"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zyra
+ name = "Zyra"
+ value = "zyra"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zac
+ name = "Zac"
+ value = "zac"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/moira
+ name = "Moira"
+ value = "moira"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ashe
+ name = "Ashe"
+ value = "ashe"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/brigitte
+ name = "Brigitte"
+ value = "brigitte"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/mercy
+ name = "Mercy"
+ value = "mercy"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lucio_ov
+ name = "Lucio_ov"
+ value = "lucio_ov"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dva_ov
+ name = "Dva_ov"
+ value = "dva_ov"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/symmetra
+ name = "Symmetra"
+ value = "symmetra"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zarya
+ name = "Zarya"
+ value = "zarya"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cassidy
+ name = "Cassidy"
+ value = "cassidy"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/baptiste
+ name = "Baptiste"
+ value = "baptiste"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/junker_queen
+ name = "Junker_queen"
+ value = "junker_queen"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/doomfist
+ name = "Doomfist"
+ value = "doomfist"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/pharah
+ name = "Pharah"
+ value = "pharah"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sombra
+ name = "Sombra"
+ value = "sombra"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ana
+ name = "Ana"
+ value = "ana"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/widowmaker
+ name = "Widowmaker"
+ value = "widowmaker"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/harbor
+ name = "Harbor"
+ value = "harbor"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sage
+ name = "Sage"
+ value = "sage"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/brimstone
+ name = "Brimstone"
+ value = "brimstone"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sova
+ name = "Sova"
+ value = "sova"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_shrill
+ name = "F_shrill"
+ value = "f_shrill"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_haughty
+ name = "M_haughty"
+ value = "m_haughty"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_soldier
+ name = "M_soldier"
+ value = "m_soldier"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sven
+ name = "Sven"
+ value = "sven"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_sultry
+ name = "F_sultry"
+ value = "f_sultry"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/eorlund
+ name = "Eorlund"
+ value = "eorlund"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_commander
+ name = "M_commander"
+ value = "m_commander"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_nord
+ name = "F_nord"
+ value = "f_nord"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lydia
+ name = "Lydia"
+ value = "lydia"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/motierre
+ name = "Motierre"
+ value = "motierre"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_haughty
+ name = "F_haughty"
+ value = "f_haughty"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tullius
+ name = "Tullius"
+ value = "tullius"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/festus
+ name = "Festus"
+ value = "festus"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_nord
+ name = "M_nord"
+ value = "m_nord"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/olava
+ name = "Olava"
+ value = "olava"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_commander
+ name = "F_commander"
+ value = "f_commander"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/hadvar
+ name = "Hadvar"
+ value = "hadvar"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_argo
+ name = "F_argo"
+ value = "f_argo"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/arngeir
+ name = "Arngeir"
+ value = "arngeir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/nazeem
+ name = "Nazeem"
+ value = "nazeem"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/falion
+ name = "Falion"
+ value = "falion"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_coward
+ name = "F_coward"
+ value = "f_coward"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_guard
+ name = "M_guard"
+ value = "m_guard"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_commoner
+ name = "M_commoner"
+ value = "m_commoner"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/elisif
+ name = "Elisif"
+ value = "elisif"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/paarthurnax
+ name = "Paarthurnax"
+ value = "paarthurnax"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/grelka
+ name = "Grelka"
+ value = "grelka"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_commoner
+ name = "F_commoner"
+ value = "f_commoner"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ebony
+ name = "Ebony"
+ value = "ebony"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ulfric
+ name = "Ulfric"
+ value = "ulfric"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/farengar
+ name = "Farengar"
+ value = "farengar"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/astrid
+ name = "Astrid"
+ value = "astrid"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/brynjolf
+ name = "Brynjolf"
+ value = "brynjolf"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/maven
+ name = "Maven"
+ value = "maven"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_child
+ name = "F_child"
+ value = "f_child"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_orc
+ name = "F_orc"
+ value = "f_orc"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/delphine
+ name = "Delphine"
+ value = "delphine"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/f_darkelf
+ name = "F_darkelf"
+ value = "f_darkelf"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/grelod
+ name = "Grelod"
+ value = "grelod"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tolfdir
+ name = "Tolfdir"
+ value = "tolfdir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_bandit
+ name = "M_bandit"
+ value = "m_bandit"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/m_forsworn
+ name = "M_forsworn"
+ value = "m_forsworn"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/karliah
+ name = "Karliah"
+ value = "karliah"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/felldir
+ name = "Felldir"
+ value = "felldir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ancano
+ name = "Ancano"
+ value = "ancano"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/mercer
+ name = "Mercer"
+ value = "mercer"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/vex
+ name = "Vex"
+ value = "vex"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/mirabelle
+ name = "Mirabelle"
+ value = "mirabelle"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/aventus
+ name = "Aventus"
+ value = "aventus"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tsun
+ name = "Tsun"
+ value = "tsun"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/elenwen
+ name = "Elenwen"
+ value = "elenwen"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/gormlaith
+ name = "Gormlaith"
+ value = "gormlaith"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dragon
+ name = "Dragon"
+ value = "dragon"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/overwatch
+ name = "Overwatch"
+ value = "overwatch"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zak
+ name = "Zak"
+ value = "zak"
+ category = TTS_CATEGORY_EVILISLANDS
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/merc2
+ name = "Merc2"
+ value = "merc2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/forest1
+ name = "Forest1"
+ value = "forest1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/bandit3
+ name = "Bandit3"
+ value = "bandit3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/forest2
+ name = "Forest2"
+ value = "forest2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/merc1
+ name = "Merc1"
+ value = "merc1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/bandit2
+ name = "Bandit2"
+ value = "bandit2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/forest3
+ name = "Forest3"
+ value = "forest3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tribal3
+ name = "Tribal3"
+ value = "tribal3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/slave2
+ name = "Slave2"
+ value = "slave2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/miller
+ name = "Miller"
+ value = "miller"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/krest
+ name = "Krest"
+ value = "krest"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tribal1
+ name = "Tribal1"
+ value = "tribal1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/abathur
+ name = "Abathur"
+ value = "abathur"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/erik
+ name = "Erik"
+ value = "erik"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/varian
+ name = "Varian"
+ value = "varian"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/anduin
+ name = "Anduin"
+ value = "anduin"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/deckard
+ name = "Deckard"
+ value = "deckard"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/malfurion_h
+ name = "Malfurion_h"
+ value = "malfurion_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/demonhunter
+ name = "Demonhunter"
+ value = "demonhunter"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/demon
+ name = "Demon"
+ value = "demon"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kerrigan_h
+ name = "Kerrigan_h"
+ value = "kerrigan_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ladyofthorns
+ name = "Ladyofthorns"
+ value = "ladyofthorns"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/barbarian
+ name = "Barbarian"
+ value = "barbarian"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/crusader
+ name = "Crusader"
+ value = "crusader"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/whitemane
+ name = "Whitemane"
+ value = "whitemane"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/nexushunter
+ name = "Nexushunter"
+ value = "nexushunter"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/greymane_h
+ name = "Greymane_h"
+ value = "greymane_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/gardensdayannouncer
+ name = "Gardensdayannouncer"
+ value = "gardensdayannouncer"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/drekthar
+ name = "Drekthar"
+ value = "drekthar"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/squeamlish
+ name = "Squeamlish"
+ value = "squeamlish"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dagg
+ name = "Dagg"
+ value = "dagg"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/brukan
+ name = "Brukan"
+ value = "brukan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/bolan
+ name = "Bolan"
+ value = "bolan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/goya
+ name = "Goya"
+ value = "goya"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/stargazer
+ name = "Stargazer"
+ value = "stargazer"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/eudora
+ name = "Eudora"
+ value = "eudora"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/mozaki
+ name = "Mozaki"
+ value = "mozaki"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/katrana
+ name = "Katrana"
+ value = "katrana"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/valeera_hs
+ name = "Valeera_hs"
+ value = "valeera_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/malacrass
+ name = "Malacrass"
+ value = "malacrass"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/elise
+ name = "Elise"
+ value = "elise"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/flark
+ name = "Flark"
+ value = "flark"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/rhogi
+ name = "Rhogi"
+ value = "rhogi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/gallywix
+ name = "Gallywix"
+ value = "gallywix"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/talanji
+ name = "Talanji"
+ value = "talanji"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dr_sezavo
+ name = "Dr_sezavo"
+ value = "dr_sezavo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tierra
+ name = "Tierra"
+ value = "tierra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zenda
+ name = "Zenda"
+ value = "zenda"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/baechao
+ name = "Baechao"
+ value = "baechao"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lilian
+ name = "Lilian"
+ value = "lilian"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/aranna
+ name = "Aranna"
+ value = "aranna"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/oshi
+ name = "Oshi"
+ value = "oshi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/norroa
+ name = "Norroa"
+ value = "norroa"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/turalyon
+ name = "Turalyon"
+ value = "turalyon"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/aki
+ name = "Aki"
+ value = "aki"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lunara
+ name = "Lunara"
+ value = "lunara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/bob
+ name = "Bob"
+ value = "bob"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/illucia
+ name = "Illucia"
+ value = "illucia"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/yrel_hs
+ name = "Yrel_hs"
+ value = "yrel_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/fireheart
+ name = "Fireheart"
+ value = "fireheart"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lanathel
+ name = "Lanathel"
+ value = "lanathel"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tyrande_hs
+ name = "Tyrande_hs"
+ value = "tyrande_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/draemus
+ name = "Draemus"
+ value = "draemus"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/rasil
+ name = "Rasil"
+ value = "rasil"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kalec
+ name = "Kalec"
+ value = "kalec"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/karastamper
+ name = "Karastamper"
+ value = "karastamper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/george
+ name = "George"
+ value = "george"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/pollark
+ name = "Pollark"
+ value = "pollark"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/stelina
+ name = "Stelina"
+ value = "stelina"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kasa
+ name = "Kasa"
+ value = "kasa"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/whirt
+ name = "Whirt"
+ value = "whirt"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/anarii
+ name = "Anarii"
+ value = "anarii"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ilza
+ name = "Ilza"
+ value = "ilza"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/avozu
+ name = "Avozu"
+ value = "avozu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/jeklik
+ name = "Jeklik"
+ value = "jeklik"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/zibb
+ name = "Zibb"
+ value = "zibb"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/thrud
+ name = "Thrud"
+ value = "thrud"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/isiset
+ name = "Isiset"
+ value = "isiset"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ required_donator_level = 0
+
+/datum/tts_seed/silero/akazamzarak
+ name = "Akazamzarak"
+ value = "akazamzarak"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/arha
+ name = "Arha"
+ value = "arha"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+
+/datum/tts_seed/silero/aidar
+ name = "Aidar"
+ value = "aidar"
+ category = TTS_CATEGORY_OTHER
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/baya
+ name = "Baya"
+ value = "baya"
+ category = TTS_CATEGORY_OTHER
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/kseniya
+ name = "Kseniya"
+ value = "kseniya"
+ category = TTS_CATEGORY_OTHER
+ gender = TTS_GENDER_FEMALE
+
+/datum/tts_seed/silero/eugene
+ name = "Eugene"
+ value = "eugene"
+ category = TTS_CATEGORY_OTHER
+ gender = TTS_GENDER_MALE
+
+/datum/tts_seed/silero/senna
+ name = "Senna"
+ value = "senna"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/nunu
+ name = "Nunu"
+ value = "nunu"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ryze
+ name = "Ryze"
+ value = "ryze"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/yone
+ name = "Yone"
+ value = "yone"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/sett
+ name = "Sett"
+ value = "sett"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/camille
+ name = "Camille"
+ value = "camille"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lee_sin
+ name = "Lee_sin"
+ value = "lee_sin"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kayle
+ name = "Kayle"
+ value = "kayle"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/azir
+ name = "Azir"
+ value = "azir"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/tryndamere
+ name = "Tryndamere"
+ value = "tryndamere"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/nami
+ name = "Nami"
+ value = "nami"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/delvin
+ name = "Delvin"
+ value = "delvin"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cicero
+ name = "Cicero"
+ value = "cicero"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/linzi
+ name = "Linzi"
+ value = "linzi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cache
+ name = "Cache"
+ value = "cache"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/cravitz
+ name = "Cravitz"
+ value = "cravitz"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/lady_vashj
+ name = "Lady_vashj"
+ value = "lady_vashj"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/dendrologist
+ name = "Dendrologist"
+ value = "dendrologist"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/jythiros
+ name = "Jythiros"
+ value = "jythiros"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/draan
+ name = "Draan"
+ value = "draan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/rikkar
+ name = "Rikkar"
+ value = "rikkar"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/splintergraft
+ name = "Splintergraft"
+ value = "splintergraft"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/malchezaar
+ name = "Malchezaar"
+ value = "malchezaar"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/taskmaster
+ name = "Taskmaster"
+ value = "taskmaster"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/oxana
+ name = "Oxana"
+ value = "oxana"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/inara
+ name = "Inara"
+ value = "inara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ivan
+ name = "Ivan"
+ value = "ivan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/kazamon
+ name = "Kazamon"
+ value = "kazamon"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/albin
+ name = "Albin"
+ value = "albin"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/ammunae
+ name = "Ammunae"
+ value = "ammunae"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/illidara
+ name = "Illidara"
+ value = "illidara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
+
+/datum/tts_seed/silero/nici
+ name = "Nici"
+ value = "nici"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ required_donator_level = 0
diff --git a/modular_ss220/text_to_speech/code/sound.dm b/modular_ss220/text_to_speech/code/sound.dm
new file mode 100644
index 000000000000..b550cdc596a9
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/sound.dm
@@ -0,0 +1,102 @@
+#define SHELLEO_ERRORLEVEL 1
+#define SHELLEO_STDOUT 2
+#define SHELLEO_STDERR 3
+
+#define SHELLEO_NAME "data/shelleo."
+#define SHELLEO_ERR ".err"
+#define SHELLEO_OUT ".out"
+
+/world/proc/shelleo(command)
+ var/static/list/shelleo_ids = list()
+ var/stdout = ""
+ var/stderr = ""
+ var/errorcode = 1
+ var/shelleo_id
+ var/out_file = ""
+ var/err_file = ""
+ var/static/list/interpreters = list("[MS_WINDOWS]" = "cmd /c", "[UNIX]" = "sh -c")
+ var/interpreter = interpreters["[world.system_type]"]
+ if(interpreter)
+ for(var/seo_id in shelleo_ids)
+ if(!shelleo_ids[seo_id])
+ shelleo_ids[seo_id] = TRUE
+ shelleo_id = "[seo_id]"
+ break
+ if(!shelleo_id)
+ shelleo_id = "[shelleo_ids.len + 1]"
+ shelleo_ids += shelleo_id
+ shelleo_ids[shelleo_id] = TRUE
+ out_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_OUT]"
+ err_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_ERR]"
+ if(world.system_type == UNIX)
+ errorcode = shell("[interpreter] \"[replacetext(command, "\"", "\\\"")]\" > [out_file] 2> [err_file]")
+ else
+ errorcode = shell("[interpreter] \"[command]\" > [out_file] 2> [err_file]")
+ if(fexists(out_file))
+ stdout = file2text(out_file)
+ fdel(out_file)
+ if(fexists(err_file))
+ stderr = file2text(err_file)
+ fdel(err_file)
+ shelleo_ids[shelleo_id] = FALSE
+ else
+ CRASH("Operating System: [world.system_type] not supported") // If you encounter this error, you are encouraged to update this proc with support for the new operating system
+ . = list(errorcode, stdout, stderr)
+
+/proc/shell_url_scrub(url)
+ var/static/regex/bad_chars_regex = regex("\[^#%&./:=?\\w]*", "g")
+ var/scrubbed_url = ""
+ var/bad_match = ""
+ var/last_good = 1
+ var/bad_chars = 1
+ do
+ bad_chars = bad_chars_regex.Find(url)
+ scrubbed_url += copytext(url, last_good, bad_chars)
+ if(bad_chars)
+ bad_match = url_encode(bad_chars_regex.match)
+ scrubbed_url += bad_match
+ last_good = bad_chars + length(bad_chars_regex.match)
+ while(bad_chars)
+ . = scrubbed_url
+
+
+
+/proc/apply_sound_effect(effect, filename_input, filename_output)
+ if(!effect)
+ CRASH("Invalid sound effect chosen.")
+
+ var/taskset
+ if(GLOB.configuration.tts.ffmpeg_cpuaffinity)
+ taskset = "taskset -ac [GLOB.configuration.tts.ffmpeg_cpuaffinity]"
+
+ var/list/output
+ switch(effect)
+ if(SOUND_EFFECT_RADIO)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" [filename_output]"})
+ if(SOUND_EFFECT_ROBOT)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5" [filename_output]"})
+ if(SOUND_EFFECT_RADIO_ROBOT)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5, highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" [filename_output]"})
+ if(SOUND_EFFECT_MEGAPHONE)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" [filename_output]"})
+ if(SOUND_EFFECT_MEGAPHONE_ROBOT)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" [filename_output]"})
+ else
+ CRASH("Invalid sound effect chosen.")
+ var/errorlevel = output[SHELLEO_ERRORLEVEL]
+ var/stdout = output[SHELLEO_STDOUT]
+ var/stderr = output[SHELLEO_STDERR]
+ if(errorlevel)
+ error("Error: apply_sound_effect([effect], [filename_input], [filename_output]) - See debug logs.")
+ log_debug("apply_sound_effect([effect], [filename_input], [filename_output]) STDOUT: [stdout]")
+ log_debug("apply_sound_effect([effect], [filename_input], [filename_output]) STDERR: [stderr]")
+ return FALSE
+ return TRUE
+
+#undef SHELLEO_ERRORLEVEL
+#undef SHELLEO_STDOUT
+#undef SHELLEO_STDERR
+
+#undef SHELLEO_NAME
+#undef SHELLEO_ERR
+#undef SHELLEO_OUT
diff --git a/modular_ss220/text_to_speech/code/sound/radio_chatter.ogg b/modular_ss220/text_to_speech/code/sound/radio_chatter.ogg
new file mode 100644
index 000000000000..6e5b3ecfbfe2
Binary files /dev/null and b/modular_ss220/text_to_speech/code/sound/radio_chatter.ogg differ
diff --git a/modular_ss220/text_to_speech/code/tts_preferences.dm b/modular_ss220/text_to_speech/code/tts_preferences.dm
new file mode 100644
index 000000000000..b5533eebcf04
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/tts_preferences.dm
@@ -0,0 +1,107 @@
+/datum/preferences
+ var/static/list/explorer_users = list()
+
+/datum/preferences/New(client/C, datum/db_query/Q)
+ . = ..()
+ volume_mixer |= (list(
+ "1013" = 20, // CHANNEL_TTS_RADIO
+ "1012" = 50, // CHANNEL_TTS_LOCAL
+ ))
+
+/datum/character_save
+ var/tts_seed
+
+/datum/character_save/copy_to(mob/living/carbon/human/character)
+ . = ..()
+ character.tts_seed = tts_seed
+ character.dna.tts_seed_dna = tts_seed
+
+/datum/ui_module/tts_seeds_explorer
+ name = "Эксплорер TTS голосов"
+ var/phrases = TTS_PHRASES
+
+/datum/ui_module/tts_seeds_explorer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "TTSSeedsExplorer", name, 550, 800, master_ui, state)
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/datum/ui_module/tts_seeds_explorer/ui_data(mob/user)
+ var/list/data = list()
+
+ data["selected_seed"] = user.client.prefs.active_character.tts_seed
+
+ data["donator_level"] = user.client.donator_level
+
+ return data
+
+/datum/ui_module/tts_seeds_explorer/ui_static_data(mob/user)
+ var/list/data = list()
+
+ var/list/providers = list()
+ for(var/_provider in SStts220.tts_providers)
+ var/datum/tts_provider/provider = SStts220.tts_providers[_provider]
+ providers += list(list(
+ "name" = provider.name,
+ "is_enabled" = provider.is_enabled,
+ ))
+ data["providers"] = providers
+
+ var/list/seeds = list()
+ for(var/_seed in SStts220.tts_seeds)
+ var/datum/tts_seed/seed = SStts220.tts_seeds[_seed]
+ seeds += list(list(
+ "name" = seed.name,
+ "value" = seed.value,
+ "category" = seed.category,
+ "gender" = seed.gender,
+ "provider" = initial(seed.provider.name),
+ "required_donator_level" = seed.required_donator_level,
+ ))
+ data["seeds"] = seeds
+
+ data["phrases"] = phrases
+
+ return data
+
+/datum/ui_module/tts_seeds_explorer/ui_act(action, list/params)
+ if(..())
+ return
+ . = TRUE
+
+ switch(action)
+ if("listen")
+ var/phrase = params["phrase"]
+ var/seed_name = params["seed"]
+
+ if(!(phrase in phrases))
+ return
+ if(!(seed_name in SStts220.tts_seeds))
+ return
+
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), null, usr, phrase, seed_name, FALSE)
+ if("select")
+ var/seed_name = params["seed"]
+
+ if(!(seed_name in SStts220.tts_seeds))
+ return
+ var/datum/tts_seed/seed = SStts220.tts_seeds[seed_name]
+ if(usr.client.donator_level < seed.required_donator_level)
+ return
+
+ usr.client.prefs.active_character.tts_seed = seed_name
+ else
+ return FALSE
+
+/mob/new_player/proc/check_tts_seed_ready()
+ if(GLOB.configuration.tts.tts_enabled)
+ if(!client.prefs.active_character.tts_seed)
+ to_chat(usr, span_danger("Вам необходимо настроить голос персонажа! Не забудьте сохранить настройки."))
+ client.prefs.ShowChoices(src)
+ return FALSE
+ var/datum/tts_seed/seed = SStts220.tts_seeds[client.prefs.active_character.tts_seed]
+ if(client.donator_level < seed.required_donator_level)
+ to_chat(usr, span_danger("Выбранный голос персонажа более недоступен на текущем уровне подписки!"))
+ client.prefs.ShowChoices(src)
+ return FALSE
diff --git a/modular_ss220/text_to_speech/code/tts_provider.dm b/modular_ss220/text_to_speech/code/tts_provider.dm
new file mode 100644
index 000000000000..bd47e3d58567
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/tts_provider.dm
@@ -0,0 +1,32 @@
+/datum/tts_provider
+ var/name = "STUB"
+ var/is_enabled = TRUE
+ var/api_url
+
+ var/is_throttled = FALSE
+ var/throttled_until = 0
+
+ var/timed_out_requests = 0
+ var/failed_requests = 0
+ var/failed_requests_limit = 10
+
+/datum/tts_provider/proc/request(text, datum/tts_seed/seed, datum/callback/proc_callback)
+ return TRUE
+
+/datum/tts_provider/proc/process_response(datum/http_response/response)
+ return null
+
+/datum/tts_provider/proc/throttle_check()
+ if(is_throttled && throttled_until < world.time)
+ return TRUE
+ is_throttled = FALSE
+ return FALSE
+
+/datum/tts_provider/proc/pitch_whisper(text)
+ return text
+
+/datum/tts_provider/proc/rate_faster(text)
+ return text
+
+/datum/tts_provider/proc/rate_medium(text)
+ return text
diff --git a/modular_ss220/text_to_speech/code/tts_seed.dm b/modular_ss220/text_to_speech/code/tts_seed.dm
new file mode 100644
index 000000000000..11f20282d3b5
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/tts_seed.dm
@@ -0,0 +1,60 @@
+/atom
+ var/tts_seed
+
+// SS220 TODO: usage of tts in dna
+/datum/dna
+ var/tts_seed_dna
+
+/datum/dna/Clone()
+ . = ..()
+ var/datum/dna/new_dna = .
+ new_dna.tts_seed_dna = tts_seed_dna
+ return new_dna
+
+/mob/living/carbon/human/Initialize(mapload, datum/species/new_species)
+ . = ..()
+ if(dna)
+ dna.tts_seed_dna = tts_seed
+
+/atom/proc/select_voice(mob/user, silent_target = FALSE, override = FALSE)
+ if(!ismob(src) && !user)
+ return null
+ var/tts_test_str = "Так звучит мой голос."
+
+ var/tts_seeds
+ if(user && (check_rights(R_ADMIN, 0, user) || override))
+ tts_seeds = SStts220.tts_seeds_names
+ else
+ tts_seeds = SStts220.get_available_seeds(src)
+
+ var/new_tts_seed = input(user || src, "Choose your preferred voice:", "Character Preference", tts_seed) as null|anything in tts_seeds
+ if(!new_tts_seed)
+ return null
+ if(!silent_target && ismob(src) && src != user)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), null, src, tts_test_str, new_tts_seed, FALSE)
+ if(user)
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), null, user, tts_test_str, new_tts_seed, FALSE)
+ return new_tts_seed
+
+/atom/proc/change_voice(mob/user, override = FALSE)
+ set waitfor = FALSE
+ var/new_tts_seed = select_voice(user, override = override)
+ if(!new_tts_seed)
+ return null
+ return update_tts_seed(new_tts_seed)
+
+/atom/proc/update_tts_seed(new_tts_seed)
+ tts_seed = new_tts_seed
+ return new_tts_seed
+
+/datum/tts_seed
+ var/name = "STUB"
+ var/value = "STUB"
+ var/category = TTS_CATEGORY_OTHER
+ var/gender = TTS_GENDER_ANY
+ var/datum/tts_provider/provider = /datum/tts_provider
+ var/required_donator_level = 0
+
+/datum/tts_seed/vv_edit_var(var_name, var_value)
+ return FALSE
+
diff --git a/modular_ss220/text_to_speech/code/tts_subsystem.dm b/modular_ss220/text_to_speech/code/tts_subsystem.dm
new file mode 100644
index 000000000000..6a84ffbb1bf7
--- /dev/null
+++ b/modular_ss220/text_to_speech/code/tts_subsystem.dm
@@ -0,0 +1,570 @@
+SUBSYSTEM_DEF(tts220)
+ name = "Text-to-Speech 220"
+ init_order = INIT_ORDER_DEFAULT
+ wait = 1 SECONDS
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+
+ var/tts_wanted = 0
+ var/tts_request_failed = 0
+ var/tts_request_succeeded = 0
+ var/tts_reused = 0
+ var/list/tts_errors = list()
+ var/tts_error_raw = ""
+
+ // Simple Moving Average RPS
+ var/list/tts_rps_list = list()
+ var/tts_sma_rps = 0
+
+ // Requests per Second (RPS), only real API requests
+ var/tts_rps = 0
+ var/tts_rps_counter = 0
+
+ // Total Requests per Second (TRPS), all TTS request, even reused
+ var/tts_trps = 0
+ var/tts_trps_counter = 0
+
+ // Reused Requests per Second (RRPS), only reused requests
+ var/tts_rrps = 0
+ var/tts_rrps_counter = 0
+
+ var/is_enabled = TRUE
+
+ var/list/datum/tts_seed/tts_seeds = list()
+ var/list/tts_seeds_names = list()
+ var/list/tts_seeds_names_by_donator_levels = list()
+ var/list/datum/tts_provider/tts_providers = list()
+
+ var/list/tts_local_channels_by_owner = list()
+
+ var/list/tts_requests_queue = list()
+ var/tts_requests_queue_limit = 100
+ var/tts_rps_limit = 5
+
+ var/list/tts_queue = list()
+ var/list/tts_effects_queue = list()
+
+ var/sanitized_messages_caching = TRUE
+ var/list/sanitized_messages_cache = list()
+ var/sanitized_messages_cache_hit = 0
+ var/sanitized_messages_cache_miss = 0
+
+ var/debug_mode_enabled = FALSE
+
+ var/static/list/tts_job_replacements = list(
+ "nanotrasen navy field officer" = "Полевой офицер флота Нанотрэйзен",
+ "nanotrasen navy officer" = "Офицер флота nanotrasen",
+ "supreme commander" = "Верховный главнокомандующий",
+ "solar federation general" = "Генерал Солнечной Федерации",
+ "special operations officer" = "Офицер специальных операций",
+ "civilian" = "Гражданский",
+ "tourist" = "Турист",
+ "businessman" = "Бизнэсмэн",
+ "trader" = "Торговец",
+ "assistant" = "Ассистент",
+ "chief engineer" = "Главный Инженер",
+ "station engineer" = "Станционный инженер",
+ "trainee engineer" = "Инженер-стажер",
+ "Engineer Assistant" = "Инженерный Ассистент",
+ "Technical Assistant" = "Технический Ассистент",
+ "Engineer Student" = "Инженер-практикант",
+ "Technical Student" = "Техник-практикант",
+ "Technical Trainee" = "Техник-стажер",
+ "maintenance technician" = "Техник по обслуживанию",
+ "engine technician" = "Техник по двигателям",
+ "electrician" = "Электрик",
+ "life support specialist" = "Специалист по жизнеобеспечению",
+ "atmospheric technician" = "Атмосферный техник",
+ "mechanic" = "Механик",
+ "chief medical officer" = "Главный врач",
+ "medical doctor" = "Врач",
+ "Intern" = "Интерн",
+ "Student Medical Doctor" = "Врач-практикант",
+ "Medical Assistant" = "Ассистирующий врач",
+ "surgeon" = "Хирург",
+ "nurse" = "Медсестра",
+ "coroner" = "К+оронэр",
+ "chemist" = "Химик",
+ "pharmacist" = "Фармацевт",
+ "pharmacologist" = "Фармаколог",
+ "geneticist" = "Генетик",
+ "virologist" = "Вирусолог",
+ "pathologist" = "Патологоанатом",
+ "microbiologist" = "Микробиолог",
+ "psychiatrist" = "Психиатр",
+ "psychologist" = "Психолог",
+ "therapist" = "Терапевт",
+ "paramedic" = "Парамедик",
+ "research director" = "Директор исследований",
+ "scientist" = "Учёный",
+ "student scientist" = "Учёный-практикант",
+ "Scientist Assistant" = "Научный Ассистент",
+ "Scientist Pregraduate" = "Учёный-бакалавр",
+ "Scientist Graduate" = "Научный выпускник",
+ "Scientist Postgraduate" = "Учёный-аспирант",
+ "anomalist" = "Аномалист",
+ "plasma researcher" = "Исследователь плазмы",
+ "xenobiologist" = "Ксенобиолог",
+ "chemical researcher" = "Химик-исследователь",
+ "roboticist" = "Робототехник",
+ "student robotist" = "Студент-робототехник",
+ "biomechanical engineer" = "Биомеханический инженер",
+ "mechatronic engineer" = "Инженер мехатроники",
+ "head of security" = "Глава службы безопасности",
+ "warden" = "Смотритель",
+ "detective" = "Детектив",
+ "forensic technician" = "Криминалист",
+ "security officer" = "Офицер службы безопасности",
+ "security cadet" = "Кадет службы безопасности",
+ "Security Assistant" = "Ассистент службы безопасности",
+ "Security Graduate" = "Выпускник кадетской академии",
+ "brig physician" = "Врач брига",
+ "security pod pilot" = "Пилот пода службы безопасности",
+ "ai" = "И И",
+ "cyborg" = "Киборг",
+ "robot" = "Робот",
+ "captain" = "Капитан",
+ "head of personnel" = "Глава персонала",
+ "nanotrasen representative" = "Представитель Нанотрэйзен",
+ "blueshield" = "Блюшилд",
+ "magistrate" = "Магистрат",
+ "internal affairs agent" = "Агент внутренних дел",
+ "human resources agent" = "Агент по персоналу",
+ "bartender" = "Бармэн",
+ "chef" = "Повар",
+ "cook" = "Кук",
+ "culinary artist" = "Кулинар",
+ "butcher" = "Мясник",
+ "botanist" = "Ботаник",
+ "hydroponicist" = "Гидропонист",
+ "botanical researcher" = "Ботаник-исследователь",
+ "quartermaster" = "Квартирмейстер",
+ "cargo technician" = "Карго техник",
+ "shaft miner" = "Шахтёр",
+ "spelunker" = "Спелеолог",
+ "clown" = "Клоун",
+ "mime" = "Мим",
+ "janitor" = "Уборщик",
+ "custodial technician" = "Техник по уходу за помещениями",
+ "librarian" = "Библиотекарь",
+ "journalist" = "Журналист",
+ "barber" = "Парикмахер",
+ "hair stylist" = "Стилист",
+ "beautician" = "Косметолог",
+ "explorer" = "Исследователь",
+ "chaplain" = "Священник",
+ "syndicate officer" = "Офицер синдиката",
+ "visitor" = "посетитель",
+ )
+
+/datum/controller/subsystem/tts220/stat_entry(msg)
+ msg += "tRPS:[tts_trps] "
+ msg += "rRPS:[tts_rrps] "
+ msg += "RPS:[tts_rps] "
+ msg += "smaRPS:[tts_sma_rps] | "
+ msg += "W:[tts_wanted] "
+ msg += "F:[tts_request_failed] "
+ msg += "S:[tts_request_succeeded] "
+ msg += "R:[tts_reused] "
+ return ..()
+
+/datum/controller/subsystem/tts220/PreInit()
+ . = ..()
+ for(var/path in subtypesof(/datum/tts_provider))
+ var/datum/tts_provider/provider = new path
+ tts_providers[provider.name] += provider
+ for(var/path in subtypesof(/datum/tts_seed))
+ var/datum/tts_seed/seed = new path
+ if(seed.value == "STUB")
+ continue
+ seed.provider = tts_providers[initial(seed.provider.name)]
+ tts_seeds[seed.name] = seed
+ tts_seeds_names += seed.name
+ tts_seeds_names_by_donator_levels["[seed.required_donator_level]"] += list(seed.name)
+ tts_seeds_names = sortTim(tts_seeds_names, /proc/cmp_text_asc)
+
+/datum/controller/subsystem/tts220/Initialize(start_timeofday)
+ is_enabled = GLOB.configuration.tts.tts_enabled
+ if(!is_enabled)
+ flags |= SS_NO_FIRE
+
+/datum/controller/subsystem/tts220/fire()
+ tts_rps = tts_rps_counter
+ tts_rps_counter = 0
+ tts_trps = tts_trps_counter
+ tts_trps_counter = 0
+ tts_rrps = tts_rrps_counter
+ tts_rrps_counter = 0
+
+ tts_rps_list += tts_rps
+ if(tts_rps_list.len > 15)
+ tts_rps_list.Cut(1,2)
+
+ var/rps_sum = 0
+ for(var/rps in tts_rps_list)
+ rps_sum += rps
+ tts_sma_rps = round(rps_sum / tts_rps_list.len, 0.1)
+
+ var/free_rps = clamp(tts_rps_limit - tts_rps, 0, tts_rps_limit)
+ var/requests = tts_requests_queue.Copy(1, clamp(LAZYLEN(tts_requests_queue), 0, free_rps) + 1)
+ for(var/request in requests)
+ var/text = request[1]
+ var/datum/tts_seed/seed = request[2]
+ var/datum/callback/proc_callback = request[3]
+ var/datum/tts_provider/provider = seed.provider
+ provider.request(text, seed, proc_callback)
+ tts_rps_counter++
+ tts_requests_queue.Cut(1, clamp(LAZYLEN(tts_requests_queue), 0, free_rps) + 1)
+
+ if(sanitized_messages_caching)
+ sanitized_messages_cache.Cut()
+ if(debug_mode_enabled)
+ world.log << "sanitized_messages_cache: HIT=[sanitized_messages_cache_hit] / MISS=[sanitized_messages_cache_miss]"
+ sanitized_messages_cache_hit = 0
+ sanitized_messages_cache_miss = 0
+
+/datum/controller/subsystem/tts220/Recover()
+ is_enabled = SStts220.is_enabled
+ tts_wanted = SStts220.tts_wanted
+ tts_request_failed = SStts220.tts_request_failed
+ tts_request_succeeded = SStts220.tts_request_succeeded
+ tts_reused = SStts220.tts_reused
+
+/datum/controller/subsystem/tts220/proc/queue_request(text, datum/tts_seed/seed, datum/callback/proc_callback)
+ if(LAZYLEN(tts_requests_queue) > tts_requests_queue_limit)
+ is_enabled = FALSE
+ to_chat(world, span_announcement("SERVER: очередь запросов превысила лимит, подсистема SStts220 принудительно отключена!"))
+ return FALSE
+
+ if(tts_rps_counter < tts_rps_limit)
+ var/datum/tts_provider/provider = seed.provider
+ provider.request(text, seed, proc_callback)
+ tts_rps_counter++
+ return TRUE
+
+ tts_requests_queue += list(list(text, seed, proc_callback))
+ return TRUE
+
+/datum/controller/subsystem/tts220/proc/get_tts(atom/speaker, mob/listener, message, seed_name, is_local = TRUE, effect = SOUND_EFFECT_NONE, traits = TTS_TRAIT_RATE_FASTER, preSFX = null, postSFX = null)
+ if(!is_enabled)
+ return
+ if(!message)
+ return
+ if(isnull(listener) || !listener.client)
+ return
+ if(isnull(seed_name) || !(seed_name in tts_seeds))
+ return
+ var/datum/tts_seed/seed = tts_seeds[seed_name]
+
+ tts_wanted++
+ tts_trps_counter++
+
+ var/datum/tts_provider/provider = seed.provider
+ if(!provider.is_enabled)
+ return
+ if(provider.throttle_check())
+ return
+
+ var/dirty_text = message
+ var/text = sanitize_tts_input(dirty_text)
+
+ if(!text || length_char(text) > MAX_MESSAGE_LEN)
+ return
+
+ if(traits & TTS_TRAIT_RATE_FASTER)
+ text = provider.rate_faster(text)
+
+ if(traits & TTS_TRAIT_RATE_MEDIUM)
+ text = provider.rate_medium(text)
+
+ if(traits & TTS_TRAIT_PITCH_WHISPER)
+ text = provider.pitch_whisper(text)
+
+ var/hash = rustgss220_hash_string(RUSTG_HASH_MD5, text)
+ var/filename = "sound/tts_cache/[seed.name]/[hash]"
+
+ var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX)
+
+ if(fexists("[filename].ogg"))
+ tts_reused++
+ tts_rrps_counter++
+ play_tts(speaker, listener, filename, is_local, effect, preSFX, postSFX)
+ return
+
+ if(LAZYLEN(tts_queue[filename]))
+ tts_reused++
+ tts_rrps_counter++
+ LAZYADD(tts_queue[filename], play_tts_cb)
+ return
+
+ var/datum/callback/cb = CALLBACK(src, PROC_REF(get_tts_callback), speaker, listener, filename, seed, is_local, effect, preSFX, postSFX)
+ queue_request(text, seed, cb)
+ LAZYADD(tts_queue[filename], play_tts_cb)
+
+/datum/controller/subsystem/tts220/proc/get_tts_callback(atom/speaker, mob/listener, filename, datum/tts_seed/seed, is_local, effect, preSFX, postSFX, datum/http_response/response)
+ var/datum/tts_provider/provider = seed.provider
+
+ // Bail if it errored
+ if(response.errored)
+ provider.timed_out_requests++
+ message_admins("Error connecting to [provider.name] TTS API. Please inform a maintainer or server host.")
+ return
+
+ if(response.status_code != 200)
+ provider.failed_requests++
+ if(provider.failed_requests >= provider.failed_requests_limit)
+ provider.is_enabled = FALSE
+ message_admins("Error performing [provider.name] TTS API request (Code: [response.status_code])")
+ tts_request_failed++
+ if(response.status_code)
+ if(tts_errors["[response.status_code]"])
+ tts_errors["[response.status_code]"]++
+ else
+ tts_errors += "[response.status_code]"
+ tts_errors["[response.status_code]"] = 1
+ tts_error_raw = response.error
+ return
+
+ tts_request_succeeded++
+
+ var/voice = provider.process_response(response)
+ if(!voice)
+ return
+
+ rustgss220_file_write_b64decode(voice, "[filename].ogg")
+
+ if (!GLOB.configuration.tts.tts_cache_enabled)
+ addtimer(CALLBACK(src, PROC_REF(cleanup_tts_file), "[filename].ogg"), 30 SECONDS)
+
+ for(var/datum/callback/cb in tts_queue[filename])
+ cb.InvokeAsync()
+ tts_queue[filename] -= cb
+
+ tts_queue -= filename
+
+/datum/controller/subsystem/tts220/proc/play_tts(atom/speaker, mob/listener, filename, is_local = TRUE, effect = SOUND_EFFECT_NONE, preSFX = null, postSFX = null)
+ if(isnull(listener) || !listener.client)
+ return
+
+ var/voice
+ switch(effect)
+ if(SOUND_EFFECT_NONE)
+ voice = "[filename].ogg"
+ if(SOUND_EFFECT_RADIO)
+ voice = "[filename]_radio.ogg"
+ if(SOUND_EFFECT_ROBOT)
+ voice = "[filename]_robot.ogg"
+ if(SOUND_EFFECT_RADIO_ROBOT)
+ voice = "[filename]_radio_robot.ogg"
+ if(SOUND_EFFECT_MEGAPHONE)
+ voice = "[filename]_megaphone.ogg"
+ if(SOUND_EFFECT_MEGAPHONE_ROBOT)
+ voice = "[filename]_megaphone_robot.ogg"
+ else
+ CRASH("Invalid sound effect chosen.")
+ if(effect != SOUND_EFFECT_NONE)
+ if(!fexists(voice))
+ var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX)
+ if(LAZYLEN(tts_effects_queue[voice]))
+ LAZYADD(tts_effects_queue[voice], play_tts_cb)
+ return
+ LAZYADD(tts_effects_queue[voice], play_tts_cb)
+ apply_sound_effect(effect, "[filename].ogg", voice)
+ for(var/datum/callback/cb in tts_effects_queue[voice])
+ tts_effects_queue[voice] -= cb
+ if(cb == play_tts_cb)
+ continue
+ cb.InvokeAsync()
+ tts_effects_queue -= voice
+
+ var/turf/turf_source = get_turf(speaker)
+
+ var/volume = 100
+ var/channel = CHANNEL_TTS_RADIO
+ if(is_local)
+ volume *= listener.client.prefs.get_channel_volume(CHANNEL_TTS_LOCAL)
+ channel = get_local_channel_by_owner(speaker)
+ else
+ volume *= listener.client.prefs.get_channel_volume(CHANNEL_TTS_RADIO)
+ channel = CHANNEL_TTS_RADIO
+
+ var/sound/output = sound(voice)
+ output.status = SOUND_STREAM
+
+ if(isnull(speaker))
+ output.wait = TRUE
+ output.channel = channel
+ output.volume = volume * listener.client.prefs.get_channel_volume(channel)
+ output.environment = -1
+
+ if(output.volume <= 0)
+ return
+
+ if(preSFX)
+ play_sfx(listener, preSFX, output.channel, output.volume, output.environment)
+
+ SEND_SOUND(listener, output)
+ return
+
+ if(preSFX)
+ play_sfx(listener, preSFX, output.channel, output.volume, output.environment)
+
+ listener.playsound_local(turf_source, output, volume, S = output, channel = channel, wait = TRUE)
+
+ if(!output || output.volume <= 0)
+ return
+
+ if(postSFX)
+ play_sfx(listener, postSFX, output.channel, output.volume, output.environment)
+
+/datum/controller/subsystem/tts220/proc/play_sfx(mob/listener, sfx, channel, volume, environment)
+ var/sound/output = sound(sfx)
+ output.status = SOUND_STREAM
+ output.wait = TRUE
+ output.channel = channel
+ output.volume = volume
+ output.environment = environment
+ SEND_SOUND(listener, output)
+
+/datum/controller/subsystem/tts220/proc/get_local_channel_by_owner(owner)
+ var/channel = tts_local_channels_by_owner[owner]
+ if(isnull(channel))
+ channel = SSsounds.reserve_sound_channel_datumless()
+ tts_local_channels_by_owner[owner] = channel
+ return channel
+
+/datum/controller/subsystem/tts220/proc/cleanup_tts_file(filename)
+ fdel(filename)
+
+/datum/controller/subsystem/tts220/proc/get_available_seeds(owner)
+ var/list/_tts_seeds_names = list()
+ _tts_seeds_names |= tts_seeds_names
+
+ if(!ismob(owner))
+ return _tts_seeds_names
+
+ var/mob/M = owner
+
+ if(!M.client)
+ return _tts_seeds_names
+
+ for(var/donator_level in 0 to DONATOR_LEVEL_MAX)
+ if(M.client.donator_level < donator_level)
+ _tts_seeds_names -= tts_seeds_names_by_donator_levels["[donator_level]"]
+ return _tts_seeds_names
+
+/datum/controller/subsystem/tts220/proc/get_random_seed(owner)
+ return pick(get_available_seeds(owner))
+
+/datum/controller/subsystem/tts220/proc/sanitize_tts_input(message)
+ var/hash
+ if(sanitized_messages_caching)
+ hash = rustgss220_hash_string(RUSTG_HASH_MD5, message)
+ if(sanitized_messages_cache[hash])
+ sanitized_messages_cache_hit++
+ return sanitized_messages_cache[hash]
+ sanitized_messages_cache_miss++
+ . = message
+ . = trim(.)
+
+ var/static/regex/punctuation_check = new(@"[.,?!]\Z")
+ if(!punctuation_check.Find(.))
+ . += "."
+
+ var/static/regex/html_tags = new(@"<[^>]*>", "g")
+ . = html_tags.Replace(., "")
+ . = html_decode(.)
+
+ var/static/regex/forbidden_symbols = new(@"[^a-zA-Z0-9а-яА-ЯёЁ,!?+./ \r\n\t:—()-]", "g")
+ . = forbidden_symbols.Replace(., "")
+
+ var/static/regex/words = new(@"(?
+
+
+
+
+
+
+ "}
diff --git a/modular_ss220/title_screen/code/dead.dm b/modular_ss220/title_screen/code/dead.dm
new file mode 100644
index 000000000000..ab33d670ae81
--- /dev/null
+++ b/modular_ss220/title_screen/code/dead.dm
@@ -0,0 +1,3 @@
+/mob/dead/Login()
+ . = ..()
+ hide_title_screen()
diff --git a/modular_ss220/title_screen/code/living.dm b/modular_ss220/title_screen/code/living.dm
new file mode 100644
index 000000000000..9b96cbd348d0
--- /dev/null
+++ b/modular_ss220/title_screen/code/living.dm
@@ -0,0 +1,3 @@
+/mob/living/Login()
+ . = ..()
+ hide_title_screen()
diff --git a/modular_ss220/title_screen/code/mob.dm b/modular_ss220/title_screen/code/mob.dm
new file mode 100644
index 000000000000..17f02d8b2b9f
--- /dev/null
+++ b/modular_ss220/title_screen/code/mob.dm
@@ -0,0 +1,35 @@
+/**
+ * Shows the titlescreen to a new player.
+ */
+/mob/proc/show_title_screen()
+ if(!client)
+ return
+ winset(src, "title_browser", "is-disabled=true;is-visible=true")
+ winset(src, "status_bar", "is-visible=false")
+
+ var/datum/asset/assets = get_asset_datum(/datum/asset/simple/lobby) //Sending pictures to the client
+ assets.send(src)
+
+ update_title_screen()
+
+/**
+ * Hard updates the title screen HTML, it causes visual glitches if used.
+ */
+/mob/proc/update_title_screen()
+ var/dat = get_title_html()
+
+ src << browse(SStitle.current_title_screen, "file=loading_screen.gif;display=0")
+ src << browse(dat, "window=title_browser")
+
+/datum/asset/simple/lobby
+ assets = list(
+ "FixedsysExcelsior3.01Regular.ttf" = 'modular_ss220/title_screen/html/browser/FixedsysExcelsior3.01Regular.ttf',
+ )
+
+/**
+ * Removes the titlescreen entirely from a mob.
+ */
+/mob/proc/hide_title_screen()
+ if(client?.mob)
+ winset(client, "title_browser", "is-disabled=true;is-visible=false")
+ winset(client, "status_bar", "is-visible=true")
diff --git a/modular_ss220/title_screen/code/new_player.dm b/modular_ss220/title_screen/code/new_player.dm
new file mode 100644
index 000000000000..1aa09cd80e56
--- /dev/null
+++ b/modular_ss220/title_screen/code/new_player.dm
@@ -0,0 +1,13 @@
+GLOBAL_LIST_EMPTY(new_player_list)
+
+/mob/new_player/Initialize(mapload)
+ GLOB.new_player_list += src
+ . = ..()
+
+/mob/new_player/Destroy()
+ GLOB.new_player_list -= src
+ . = ..()
+
+/mob/new_player/Login()
+ . = ..()
+ show_title_screen()
diff --git a/modular_ss220/title_screen/code/title_screen_controls.dm b/modular_ss220/title_screen/code/title_screen_controls.dm
new file mode 100644
index 000000000000..f2be6e8d47d8
--- /dev/null
+++ b/modular_ss220/title_screen/code/title_screen_controls.dm
@@ -0,0 +1,80 @@
+/**
+ * Enables an admin to upload a new titlescreen image.
+ */
+/client/proc/admin_change_title_screen()
+ set category = "Event"
+ set name = "Title Screen: Change"
+
+ if(!check_rights(R_EVENT))
+ return
+
+ log_admin("[key_name(usr)] is changing the title screen.")
+ message_admins("[key_name_admin(usr)] is changing the title screen.")
+
+ switch(alert(usr, "Please select a new title screen.", "Title Screen", "Change", "Reset", "Cancel"))
+ if("Change")
+ var/file = input(usr) as icon|null
+ if(!file)
+ return
+ SStitle.change_title_screen(file)
+ if("Reset")
+ SStitle.change_title_screen()
+ if("Cancel")
+ return
+
+/**
+ * Sets a titlescreen notice, a big red text on the main screen.
+ */
+/client/proc/change_title_screen_notice()
+ set category = "Event"
+ set name = "Title Screen: Set Notice"
+
+ if(!check_rights(R_EVENT))
+ return
+
+ log_admin("[key_name(usr)] is setting the title screen notice.")
+ message_admins("[key_name_admin(usr)] is setting the title screen notice.")
+
+ var/new_notice = input(usr, "Please input a notice to be displayed on the title screen:", "Titlescreen Notice") as text|null
+ SStitle.set_notice(new_notice)
+ if(!new_notice)
+ return
+ for(var/mob/new_player/new_player in GLOB.new_player_list)
+ to_chat(new_player, span_boldannounce("TITLE NOTICE UPDATED: [new_notice]"))
+ SEND_SOUND(new_player, sound('sound/items/bikehorn.ogg'))
+
+/**
+ * Reloads the titlescreen if it is bugged for someone.
+ */
+/client/verb/fix_title_screen()
+ set name = "Fix Lobby Screen"
+ set desc = "Lobbyscreen broke? Press this."
+ set category = "OOC"
+
+ if(istype(mob, /mob/new_player))
+ mob.show_title_screen()
+ else
+ mob.hide_title_screen()
+
+/**
+ * An admin debug command that enables you to change the HTML on the go.
+ */
+/client/proc/change_title_screen_html()
+ set category = "Event"
+ set name = "Title Screen: Set HTML"
+
+ if(!check_rights(R_DEBUG))
+ return
+
+ log_admin("[key_name(usr)] is setting the title screen HTML.")
+ message_admins("[key_name_admin(usr)] is setting the title screen HTML.")
+
+ var/new_html = input(usr, "Please enter your desired HTML(WARNING: YOU WILL BREAK SHIT)", "DANGER: TITLE HTML EDIT") as message|null
+
+ if(!new_html)
+ return
+
+ SStitle.title_html = new_html
+ SStitle.show_title_screen()
+
+ message_admins("[key_name_admin(usr)] has changed the title screen HTML.")
diff --git a/modular_ss220/title_screen/code/title_screen_html.dm b/modular_ss220/title_screen/code/title_screen_html.dm
new file mode 100644
index 000000000000..bde98fd53895
--- /dev/null
+++ b/modular_ss220/title_screen/code/title_screen_html.dm
@@ -0,0 +1,16 @@
+// FOR MOR INFO ON HTML CUSTOMISATION, SEE: https://github.com/Skyrat-SS13/Skyrat-tg/pull/4783
+
+/mob/proc/get_title_html()
+ var/dat = SStitle.title_html
+ dat += {""}
+
+ if(SStitle.current_notice)
+ dat += {"
+
+
+
+ "}
+
+ dat += " |
|