diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 339d48c9fe39..c66b16031c63 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -167,20 +167,6 @@ Certain PRs, such as those which directly change number values (i.e. health, rec
* We understand that having something you have worked on for quite some time being denied can be frustrating. Therefore, it is recommended that you check with a maintainer before beginning to code your PR if you have any doubts that it will be accepted. This will save everyone's time and energy.
-## Good Boy Points
-
-Each GitHub account has a score known as Good Boy Points, or GBP. This is a system we use to ensure that the codebase stays maintained and that contributors fix bugs as well as add features.
-
-The GBP gain or loss for a PR depends on the type of changes the PR makes, represented by the tags assigned to the PR by the CM-SS13 github bot or maintainers. Generally speaking, fixing bugs, updating sprites, or improving maps increases your GBP score, while adding mechanics, or rebalancing things will cost you GBP.
-
-The GBP change of a PR is the sum of greatest positive and lowest negative values it has. For example, a PR that has tags worth +10, +4, -1, -7, will net 3 GBP (10 - 7).
-
-Negative GBP increases the likelihood of a maintainer closing your PR. With that chance being higher the lower your GBP is. Be sure to use the proper tags in the changelog to prevent unnecessary GBP loss. Maintainers reserve the right to change tags as they deem appropriate.
-
-There is no benefit to having a higher positive GBP score, since GBP only comes into consideration when it is negative.
-
-You can see each tag and their GBP values [Here](https://github.com/cmss13-devs/cmss13/blob/master/.github/gbp.toml).
-
## Porting features/sprites/sounds/tools from other codebases
If you are porting features/tools from other codebases, you must give them credit where it's due. Typically, crediting them in your pull request and the changelog is the recommended way of doing it. Take note of what license they use though, porting stuff from AGPLv3 and GPLv3 codebases are allowed.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 8f8ccbe07f60..cb1790053744 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -26,8 +26,8 @@ Put screenshots and videos here with an empty line between the screenshots and t
# Changelog
-
-
+
+
:cl:
add: Added something
diff --git a/.github/alternate_byond_versions.txt b/.github/alternate_byond_versions.txt
index f25861c46714..005803964cca 100644
--- a/.github/alternate_byond_versions.txt
+++ b/.github/alternate_byond_versions.txt
@@ -6,4 +6,4 @@
# Example:
# 500.1337: runtimestation
-515.1603: lv624
+515.1610: lv624
diff --git a/.github/gbp.toml b/.github/gbp.toml
deleted file mode 100644
index 85dd702803e2..000000000000
--- a/.github/gbp.toml
+++ /dev/null
@@ -1,23 +0,0 @@
-no_balance_label = "GBP: No Update"
-reset_label = "GBP: Reset"
-
-[points]
-"Accessibility" = 3
-"Admin" = 2
-"Atomic" = 2
-"Balance" = -5
-"Code Improvement" = 2
-"Fix" = 3
-"Grammar and Formatting" = 1
-"Hard Deletes" = 12
-"Logging" = 1
-"Feature" = -5
-"Performance" = 12
-"Priority: CRITICAL" = 20
-"Priority: High" = 15
-"Quality of Life" = 1
-"Refactor" = 6
-"Roadmap" = 15
-"Sound" = 3
-"Sprites" = 3
-"UI" = 3
diff --git a/.github/workflows/autowiki.yml b/.github/workflows/autowiki.yml
new file mode 100644
index 000000000000..82d0ac76f32f
--- /dev/null
+++ b/.github/workflows/autowiki.yml
@@ -0,0 +1,52 @@
+name: Autowiki
+on:
+ schedule:
+ - cron: "5 4 * * *"
+ workflow_dispatch:
+permissions:
+ contents: read
+
+jobs:
+ autowiki:
+ runs-on: ubuntu-20.04
+ steps:
+ - name: "Check for AUTOWIKI_USERNAME"
+ id: secrets_set
+ env:
+ ENABLER_SECRET: ${{ secrets.AUTOWIKI_USERNAME }}
+ run: |
+ unset SECRET_EXISTS
+ if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
+ echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
+ - name: Checkout
+ if: steps.secrets_set.outputs.SECRETS_ENABLED
+ uses: actions/checkout@v3
+ - name: Restore BYOND cache
+ if: steps.secrets_set.outputs.SECRETS_ENABLED
+ uses: actions/cache@v3
+ with:
+ path: ~/BYOND
+ key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }}
+ - name: Install rust-g
+ if: steps.secrets_set.outputs.SECRETS_ENABLED
+ run: |
+ sudo dpkg --add-architecture i386
+ sudo apt update || true
+ sudo apt install -o APT::Immediate-Configure=false libssl1.1:i386
+ bash tools/ci/install_rust_g.sh
+ - name: Compile and generate Autowiki files
+ if: steps.secrets_set.outputs.SECRETS_ENABLED
+ run: |
+ bash tools/ci/install_byond.sh
+ source $HOME/BYOND/byond/bin/byondsetup
+ tools/build/build --ci autowiki
+ - name: Run Autowiki
+ if: steps.secrets_set.outputs.SECRETS_ENABLED
+ env:
+ USERNAME: ${{ secrets.AUTOWIKI_USERNAME }}
+ PASSWORD: ${{ secrets.AUTOWIKI_PASSWORD }}
+ run: |
+ cd tools/autowiki
+ npm install
+ cd ../..
+ node tools/autowiki/autowiki.js data/autowiki_edits.txt data/autowiki_files/
diff --git a/.github/workflows/gbp.yml b/.github/workflows/gbp.yml
deleted file mode 100644
index 9c92e5f379dc..000000000000
--- a/.github/workflows/gbp.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: GBP
-on:
- pull_request_target:
- types: [closed, opened]
-jobs:
- gbp:
- runs-on: ubuntu-latest
- steps:
- - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
- id: value_holder
- env:
- ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
- run: |
- unset SECRET_EXISTS
- if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
- echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS"
- - name: Checkout
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- uses: actions/checkout@v3
- - name: Setup git
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- run: |
- git config --global user.name "gbp-action"
- git config --global user.email "<>"
- - name: Checkout alternate branch
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- uses: actions/checkout@v3
- with:
- ref: "gbp-balances" # The branch name
- path: gbp-balances
- # This is to ensure we keep the gbp.toml from master
- # without having to update our separate branch.
- - name: Copy configuration
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
- - name: GBP action
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- uses: tgstation/gbp-action@master
- with:
- branch: "gbp-balances"
- directory: ./gbp-balances
- token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/gbp_collect.yml b/.github/workflows/gbp_collect.yml
deleted file mode 100644
index dc2af17a12de..000000000000
--- a/.github/workflows/gbp_collect.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: GBP Collection
-# Every hour at the :20 minute mark. GitHub tells us to pick odd hours, instead of just using the start.
-on:
- schedule:
- - cron: "20 * * * *"
- workflow_dispatch:
-jobs:
- gbp_collection:
- runs-on: ubuntu-latest
- steps:
- - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
- id: value_holder
- env:
- ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
- run: |
- unset SECRET_EXISTS
- if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
- echo "::set-output name=ACTIONS_ENABLED::$SECRET_EXISTS"
- - name: Checkout
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- uses: actions/checkout@v3
- - name: Setup git
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- run: |
- git config --global user.name "github-actions[bot]"
- git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- - name: Checkout alternate branch
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- uses: actions/checkout@v3
- with:
- ref: "gbp-balances" # The branch name
- path: gbp-balances
- # This is to ensure we keep the gbp.toml from master
- # without having to update our separate branch.
- - name: Copy configuration
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
- - name: GBP action
- if: steps.value_holder.outputs.ACTIONS_ENABLED
- uses: tgstation/gbp-action@master
- with:
- collect: "true"
- directory: ./gbp-balances
- token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/code/__DEFINES/__game.dm b/code/__DEFINES/__game.dm
index 3116d7f19555..113b78dbada1 100644
--- a/code/__DEFINES/__game.dm
+++ b/code/__DEFINES/__game.dm
@@ -39,6 +39,7 @@ block( \
#define MAP_RUNTIME "USS Runtime"
#define MAP_LV522_CHANCES_CLAIM "LV-522 Chance's Claim" // Highpop Only
#define MAP_NEW_VARADERO "New Varadero"//ice colony underground but as its own map
+#define MAP_CHINOOK "Chinook 91 GSO" //admin level
#define GAMEMODE_WHISKEY_OUTPOST "Whiskey Outpost"
#define GAMEMODE_HIVE_WARS "Hive Wars"
diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm
index dcc8e4bf6c4b..044271be9b5a 100644
--- a/code/__DEFINES/access.dm
+++ b/code/__DEFINES/access.dm
@@ -42,6 +42,7 @@ most of them are tied into map-placed objects. This should be reworked in the fu
#define ACCESS_MARINE_OT 35
#define ACCESS_MARINE_SYNTH 36
+#define ACCESS_MARINE_ASO 37
// AI Core Accesses
/// Used in temporary passes
diff --git a/code/__DEFINES/wj_emotes.dm b/code/__DEFINES/emote_panels.dm
similarity index 70%
rename from code/__DEFINES/wj_emotes.dm
rename to code/__DEFINES/emote_panels.dm
index f315c6eb2ba5..59959818da74 100644
--- a/code/__DEFINES/wj_emotes.dm
+++ b/code/__DEFINES/emote_panels.dm
@@ -6,3 +6,7 @@
#define JOE_EMOTE_CATEGORY_WARNING "Warning"
#define JOE_EMOTE_CATEGORY_QUESTION "Question"
#define JOE_EMOTE_CATEGORY_NOTICE "Notice"
+
+#define YAUTJA_EMOTE_CATEGORY_FAKESOUND "Fake Sound"
+#define YAUTJA_EMOTE_CATEGORY_VOICE "Voice Synthesizer"
+#define YAUTJA_EMOTE_CATEGORY_SPECIES "Yautja"
diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm
index cede518e8db0..eac5121f173e 100644
--- a/code/__DEFINES/job.dm
+++ b/code/__DEFINES/job.dm
@@ -72,10 +72,13 @@ var/global/list/job_squad_roles = JOB_SQUAD_ROLES_LIST
#define JOB_COMMAND_ROLES_LIST list(JOB_CO, JOB_XO, JOB_SO)
var/global/list/job_command_roles = JOB_COMMAND_ROLES_LIST
+#define JOB_AUXILIARY_OFFICER "Auxiliary Support Officer"
#define JOB_PILOT "Pilot Officer"
#define JOB_DROPSHIP_CREW_CHIEF "Dropship Crew Chief"
#define JOB_CREWMAN "Vehicle Crewman"
#define JOB_INTEL "Intelligence Officer"
+#define JOB_AUXILIARY_ROLES /datum/timelock/auxiliary
+#define JOB_AUXILIARY_ROLES_LIST list(JOB_PILOT, JOB_DROPSHIP_CREW_CHIEF, JOB_CREWMAN, JOB_INTEL)
#define JOB_POLICE "Military Police"
#define JOB_WARDEN "Military Warden"
@@ -91,7 +94,7 @@ var/global/list/job_command_roles = JOB_COMMAND_ROLES_LIST
#define JOB_ENGINEER_ROLES /datum/timelock/engineer
#define JOB_ENGINEER_ROLES_LIST list(JOB_SQUAD_ENGI, JOB_MAINT_TECH, JOB_ORDNANCE_TECH, JOB_CHIEF_ENGINEER)
-#define JOB_CHIEF_REQUISITION "Requisitions Officer"
+#define JOB_CHIEF_REQUISITION "Quartermaster"
#define JOB_CARGO_TECH "Cargo Technician"
#define JOB_REQUISITION_ROLES /datum/timelock/requisition
#define JOB_REQUISITION_ROLES_LIST list(JOB_CHIEF_REQUISITION, JOB_CARGO_TECH)
@@ -143,7 +146,7 @@ var/global/list/job_command_roles = JOB_COMMAND_ROLES_LIST
#define JOB_WO_CHIEF_ENGINEER "Bunker Crew Master"
#define JOB_WO_ORDNANCE_TECH "Bunker Crew"
-#define JOB_WO_CHIEF_REQUISITION "Quartermaster"
+#define JOB_WO_CHIEF_REQUISITION "Bunker Quartermaster"
#define JOB_WO_REQUISITION "Bunker Crew Logistics"
#define JOB_WO_CMO "Head Surgeon"
@@ -230,6 +233,17 @@ var/global/list/job_command_roles = JOB_COMMAND_ROLES_LIST
#define CMB_GRUNT_LIST list(JOB_CMB, JOB_CMB_TL)
+//-------- FORECON --------//
+
+#define JOB_FORECON_CO "Reconnaissance Commander"
+#define JOB_FORECON_SL "Reconnaissance Squad Leader"
+#define JOB_FORECON_SYN "Reconnaissance Synthetic"
+#define JOB_FORECON_SNIPER "Reconnaissance Sniper"
+#define JOB_FORECON_MARKSMAN "Reconnaissance Marksman"
+#define JOB_FORECON_SUPPORT "Reconnaissance Support Technician"
+#define JOB_FORECON_RIFLEMAN "Reconnaissance Rifleman"
+#define JOB_FORECON_SMARTGUNNER "Reconnaissance Smartgunner"
+
//-------- UPP --------//
#define JOB_UPP "UPP Private"
#define JOB_UPP_CONSCRIPT "UPP Conscript"
@@ -333,11 +347,12 @@ var/global/list/job_command_roles = JOB_COMMAND_ROLES_LIST
#define JOB_PLAYTIME_TIER_4 (175 HOURS)
#define XENO_NO_AGE -1
-#define XENO_NORMAL 0
-#define XENO_MATURE 1
-#define XENO_ELDER 2
-#define XENO_ANCIENT 3
-#define XENO_PRIME 4
+#define XENO_YOUNG 0
+#define XENO_NORMAL 1
+#define XENO_MATURE 2
+#define XENO_ELDER 3
+#define XENO_ANCIENT 4
+#define XENO_PRIME 5
/// For monthly time tracking
#define JOB_OBSERVER "Observer"
diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm
index c3c4923e9638..6b438b030879 100644
--- a/code/__DEFINES/language.dm
+++ b/code/__DEFINES/language.dm
@@ -21,6 +21,8 @@
#define ALL_SYNTH_LANGUAGES list(LANGUAGE_ENGLISH, LANGUAGE_JAPANESE, LANGUAGE_CHINESE, LANGUAGE_RUSSIAN, LANGUAGE_GERMAN, LANGUAGE_SPANISH, LANGUAGE_YAUTJA, LANGUAGE_XENOMORPH)
+#define ALL_SYNTH_LANGUAGES_UPP list(LANGUAGE_RUSSIAN, LANGUAGE_ENGLISH, LANGUAGE_JAPANESE, LANGUAGE_CHINESE, LANGUAGE_GERMAN, LANGUAGE_SPANISH, LANGUAGE_YAUTJA, LANGUAGE_XENOMORPH)
+
//Chinese language sound bitflags
//initial flags
diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm
index ac050e467a71..5a4ba7676233 100644
--- a/code/__DEFINES/lighting.dm
+++ b/code/__DEFINES/lighting.dm
@@ -1,3 +1,5 @@
#define LIGHTING_PLANE_ALPHA_VISIBLE 255
+///The dim natural vision of Yautja
+#define LIGHTING_PLANE_ALPHA_YAUTJA 235
#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 127
#define LIGHTING_PLANE_ALPHA_INVISIBLE 0
diff --git a/code/__DEFINES/minimap.dm b/code/__DEFINES/minimap.dm
index 9069ed323357..c9f21484f622 100644
--- a/code/__DEFINES/minimap.dm
+++ b/code/__DEFINES/minimap.dm
@@ -4,7 +4,8 @@
#define MINIMAP_FLAG_PMC (1<<2)
#define MINIMAP_FLAG_UPP (1<<3)
#define MINIMAP_FLAG_CLF (1<<4)
-#define MINIMAP_FLAG_ALL (1<<5) - 1
+#define MINIMAP_FLAG_YAUTJA (1<<5)
+#define MINIMAP_FLAG_ALL (1<<6) - 1
///Converts the overworld x and y to minimap x and y values
#define MINIMAP_SCALE 2
@@ -59,6 +60,7 @@ GLOBAL_LIST_INIT(all_minimap_flags, bitfield2list(MINIMAP_FLAG_ALL))
#define MINIMAP_ICON_COLOR_COMMANDER "#c6fcfc"
#define MINIMAP_ICON_COLOR_HEAD "#F0C542"
+#define MINIMAP_ICON_COLOR_SILVER "#c0c0c0"
#define MINIMAP_ICON_COLOR_BRONZE "#eb9545"
#define MINIMAP_ICON_COLOR_DOCTOR "#b83737"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 673bb4fc6d81..c0886ab871f9 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -131,8 +131,9 @@
#define XENO_HIVE_MUTATED "xeno_hive_mutated"
#define XENO_HIVE_FORSAKEN "xeno_hive_forsaken"
#define XENO_HIVE_YAUTJA "xeno_hive_yautja"
+#define XENO_HIVE_RENEGADE "xeno_hive_renegade"
-#define ALL_XENO_HIVES list(XENO_HIVE_NORMAL, XENO_HIVE_CORRUPTED, XENO_HIVE_ALPHA, XENO_HIVE_BRAVO, XENO_HIVE_CHARLIE, XENO_HIVE_DELTA, XENO_HIVE_FERAL, XENO_HIVE_TAMED, XENO_HIVE_MUTATED, XENO_HIVE_FORSAKEN, XENO_HIVE_YAUTJA)
+#define ALL_XENO_HIVES list(XENO_HIVE_NORMAL, XENO_HIVE_CORRUPTED, XENO_HIVE_ALPHA, XENO_HIVE_BRAVO, XENO_HIVE_CHARLIE, XENO_HIVE_DELTA, XENO_HIVE_FERAL, XENO_HIVE_TAMED, XENO_HIVE_MUTATED, XENO_HIVE_FORSAKEN, XENO_HIVE_YAUTJA, XENO_HIVE_RENEGADE)
//=================================================
diff --git a/code/__DEFINES/mode.dm b/code/__DEFINES/mode.dm
index aa22c70d4213..bb31f4d84b1e 100644
--- a/code/__DEFINES/mode.dm
+++ b/code/__DEFINES/mode.dm
@@ -70,11 +70,12 @@
#define MODE_LZ_PROTECTION (1<<7) /// Prevents the LZ from being mortared
#define MODE_SHIPSIDE_SD (1<<8) /// Toggles whether Predators can big SD when not on the groundmap
#define MODE_HARDCORE_PERMA (1<<9) /// Toggles Hardcore for all marines, meaning they instantly perma upon death
+#define MODE_DISPOSABLE_MOBS (1<<10) // Toggles if mobs fit in disposals or not. Off by default.
#define ROUNDSTATUS_FOG_DOWN 1
#define ROUNDSTATUS_PODDOORS_OPEN 2
-#define LATEJOIN_MARINES_PER_LATEJOIN_LARVA 3
+#define LATEJOIN_MARINES_PER_LATEJOIN_LARVA 2.5
//=================================================
#define SHOW_ITEM_ANIMATIONS_NONE 0 //Do not show any item pickup animations
@@ -107,12 +108,12 @@
//=================================================
//Role defines, specifically lists of roles for job bans, crew manifests and the like.
-var/global/list/ROLES_COMMAND = list(JOB_CO, JOB_XO, JOB_SO, JOB_INTEL, JOB_PILOT, JOB_DROPSHIP_CREW_CHIEF, JOB_CREWMAN, JOB_POLICE, JOB_CORPORATE_LIAISON, JOB_COMBAT_REPORTER, JOB_CHIEF_REQUISITION, JOB_CHIEF_ENGINEER, JOB_CMO, JOB_CHIEF_POLICE, JOB_SEA, JOB_SYNTH, JOB_WARDEN)
+var/global/list/ROLES_COMMAND = list(JOB_CO, JOB_XO, JOB_SO, JOB_AUXILIARY_OFFICER, JOB_INTEL, JOB_PILOT, JOB_DROPSHIP_CREW_CHIEF, JOB_CREWMAN, JOB_POLICE, JOB_CORPORATE_LIAISON, JOB_COMBAT_REPORTER, JOB_CHIEF_REQUISITION, JOB_CHIEF_ENGINEER, JOB_CMO, JOB_CHIEF_POLICE, JOB_SEA, JOB_SYNTH, JOB_WARDEN)
//Marine roles
-#define ROLES_OFFICERS list(JOB_CO, JOB_XO, JOB_SO, JOB_INTEL, JOB_PILOT, JOB_DROPSHIP_CREW_CHIEF, JOB_SEA, JOB_CORPORATE_LIAISON, JOB_COMBAT_REPORTER, JOB_SYNTH, JOB_CHIEF_POLICE, JOB_WARDEN, JOB_POLICE)
+#define ROLES_OFFICERS list(JOB_CO, JOB_XO, JOB_SO, JOB_AUXILIARY_OFFICER, JOB_INTEL, JOB_PILOT, JOB_DROPSHIP_CREW_CHIEF, JOB_SEA, JOB_CORPORATE_LIAISON, JOB_COMBAT_REPORTER, JOB_SYNTH, JOB_CHIEF_POLICE, JOB_WARDEN, JOB_POLICE)
var/global/list/ROLES_CIC = list(JOB_CO, JOB_XO, JOB_SO, JOB_WO_CO, JOB_WO_XO)
-var/global/list/ROLES_AUXIL_SUPPORT = list(JOB_INTEL, JOB_PILOT, JOB_DROPSHIP_CREW_CHIEF, JOB_WO_CHIEF_POLICE, JOB_WO_SO, JOB_WO_CREWMAN, JOB_WO_POLICE, JOB_WO_PILOT)
+var/global/list/ROLES_AUXIL_SUPPORT = list(JOB_AUXILIARY_OFFICER, JOB_INTEL, JOB_PILOT, JOB_DROPSHIP_CREW_CHIEF, JOB_WO_CHIEF_POLICE, JOB_WO_SO, JOB_WO_CREWMAN, JOB_WO_POLICE, JOB_WO_PILOT)
var/global/list/ROLES_MISC = list(JOB_SYNTH, JOB_WORKING_JOE, JOB_SEA, JOB_CORPORATE_LIAISON, JOB_COMBAT_REPORTER, JOB_MESS_SERGEANT, JOB_WO_CORPORATE_LIAISON, JOB_WO_SYNTH)
var/global/list/ROLES_POLICE = list(JOB_CHIEF_POLICE, JOB_WARDEN, JOB_POLICE)
var/global/list/ROLES_ENGINEERING = list(JOB_CHIEF_ENGINEER, JOB_ORDNANCE_TECH, JOB_MAINT_TECH, JOB_WO_CHIEF_ENGINEER, JOB_WO_ORDNANCE_TECH)
@@ -184,7 +185,7 @@ var/global/list/whitelist_hierarchy = list(WHITELIST_NORMAL, WHITELIST_COUNCIL,
#define WHITELIST_EVERYTHING (WHITELISTS_GENERAL|WHITELISTS_COUNCIL|WHITELISTS_LEADER)
-#define isCouncil(A) (RoleAuthority.roles_whitelist[A.ckey] & (WHITELIST_YAUTJA_COUNCIL | WHITELIST_SYNTHETIC_COUNCIL | WHITELIST_COMMANDER_COUNCIL))
+#define isCouncil(A) (RoleAuthority.roles_whitelist[A.ckey] & WHITELIST_YAUTJA_COUNCIL) || (RoleAuthority.roles_whitelist[A.ckey] & WHITELIST_SYNTHETIC_COUNCIL) || (RoleAuthority.roles_whitelist[A.ckey] & WHITELIST_COMMANDER_COUNCIL)
//=================================================
diff --git a/code/__DEFINES/skills.dm b/code/__DEFINES/skills.dm
index 8e1bde82c839..fdd1a8f083ad 100644
--- a/code/__DEFINES/skills.dm
+++ b/code/__DEFINES/skills.dm
@@ -39,14 +39,24 @@
//spec_weapons skill
//hidden. who can and can't use specialist weapons
#define SKILL_SPEC_DEFAULT 0
-#define SKILL_SPEC_ROCKET 1 //can use the demolitionist specialist gear
-#define SKILL_SPEC_SCOUT 2
-#define SKILL_SPEC_SNIPER 3
-#define SKILL_SPEC_GRENADIER 4
-#define SKILL_SPEC_PYRO 5
-#define SKILL_SPEC_SMARTGUN 6 //for smartgunners
-#define SKILL_SPEC_UPP 7 //for upp
-#define SKILL_SPEC_ALL 8 //can use all specialist gear
+/// Is trained to use specialist gear, but hasn't picked a kit.
+#define SKILL_SPEC_TRAINED 1
+/// Can use RPG
+#define SKILL_SPEC_ROCKET 2
+/// Can use thermal cloaks and custom M4RA rifle
+#define SKILL_SPEC_SCOUT 3
+/// Can use sniper rifles and camo suits
+#define SKILL_SPEC_SNIPER 4
+/// Can use the rotary grenade launcher and heavy armor
+#define SKILL_SPEC_GRENADIER 5
+/// Can use heavy flamers
+#define SKILL_SPEC_PYRO 6
+/// Can use smartguns
+#define SKILL_SPEC_SMARTGUN 7
+/// UPP special training
+#define SKILL_SPEC_UPP 8
+/// Can use ALL specialist weapons
+#define SKILL_SPEC_ALL 9
//construction skill
#define SKILL_CONSTRUCTION_DEFAULT 0
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 288604434e34..1aaf3714182e 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -127,6 +127,8 @@
#define TRAIT_INTENT_EYES "t_intent_eyes"
/// Masked synthetic biology. Basic medHUDs will percieve the mob as human. (Infiltrator Synths)
#define TRAIT_INFILTRATOR_SYNTH "t_infiltrator_synth"
+/// Makes it impossible to strip the inventory of this mob.
+#define TRAIT_UNSTRIPPABLE "t_unstrippable"
// HIVE TRAITS
/// If the Hive is a Xenonid Hive
@@ -251,6 +253,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FOREIGN_BIO" = TRAIT_FOREIGN_BIO,
"TRAIT_INTENT_EYES" = TRAIT_INTENT_EYES,
"TRAIT_INFILTRATOR_SYNTH" = TRAIT_INFILTRATOR_SYNTH,
+ "TRAIT_UNSTRIPPABLE" = TRAIT_UNSTRIPPABLE,
"TRAIT_NESTED" = TRAIT_NESTED,
"TRAIT_CRAWLER" = TRAIT_CRAWLER,
"TRAIT_SIMPLE_DESC" = TRAIT_SIMPLE_DESC,
diff --git a/code/__DEFINES/typecheck/xenos.dm b/code/__DEFINES/typecheck/xenos.dm
index 6d81c853f6ec..ea981a9e02bf 100644
--- a/code/__DEFINES/typecheck/xenos.dm
+++ b/code/__DEFINES/typecheck/xenos.dm
@@ -39,6 +39,10 @@
if(!hive)
return FALSE
+ if(hivenumber == XENO_HIVE_RENEGADE)
+ var/datum/hive_status/corrupted/renegade/renegade_hive = hive
+ return renegade_hive.iff_protection_check(src, attempt_harm_mob)
+
return hive.is_ally(attempt_harm_mob)
// need this to set the data for walls/eggs/huggers when they are initialized
diff --git a/code/__DEFINES/urls.dm b/code/__DEFINES/urls.dm
index e12ec1079589..4d9268220f2b 100644
--- a/code/__DEFINES/urls.dm
+++ b/code/__DEFINES/urls.dm
@@ -34,7 +34,8 @@
#define URL_WIKI_CMP_GUIDE "https://cm-ss13.com/wiki/Chief_MP" // MP Roles //
#define URL_WIKI_MW_GUIDE "https://cm-ss13.com/wiki/Warden"
#define URL_WIKI_MP_GUIDE "https://cm-ss13.com/wiki/Military_Police"
-#define URL_WIKI_PO_GUIDE "https://cm-ss13.com/wiki/Pilot_Officer" // Auxiliary Support
+#define URL_WIKI_ASO_GUIDE "https://cm-ss13.com/wiki/Auxiliary_Support_Officer" // Auxiliary Support
+#define URL_WIKI_PO_GUIDE "https://cm-ss13.com/wiki/Pilot_Officer"
#define URL_WIKI_DCC_GUIDE "https://cm-ss13.com/wiki/Dropship_Crew_Chief"
#define URL_WIKI_IO_GUIDE "https://cm-ss13.com/wiki/Intelligence_Officer"
#define URL_WIKI_SYN_GUIDE "https://cm-ss13.com/wiki/Synthetic"
diff --git a/code/__DEFINES/weapon_stats.dm b/code/__DEFINES/weapon_stats.dm
index bef8413e9615..590223426a66 100644
--- a/code/__DEFINES/weapon_stats.dm
+++ b/code/__DEFINES/weapon_stats.dm
@@ -136,19 +136,21 @@ As such, don't expect any values assigned to common firearms to even consider ho
//How many ticks you have to wait between firing. Burst delay uses the same variable!
*/
-#define FIRE_DELAY_TIER_1 10
-#define FIRE_DELAY_TIER_2 9
-#define FIRE_DELAY_TIER_3 8
-#define FIRE_DELAY_TIER_4 7
-#define FIRE_DELAY_TIER_5 6
-#define FIRE_DELAY_TIER_6 5
-#define FIRE_DELAY_TIER_7 4
-#define FIRE_DELAY_TIER_8 3
-#define FIRE_DELAY_TIER_9 2
-#define FIRE_DELAY_TIER_LMG 1.5
-#define FIRE_DELAY_TIER_SG 1.5
-#define FIRE_DELAY_TIER_SMG 1.3
-#define FIRE_DELAY_TIER_10 1
+#define FIRE_DELAY_TIER_1 12
+#define FIRE_DELAY_TIER_2 10
+#define FIRE_DELAY_TIER_3 9
+#define FIRE_DELAY_TIER_4 8
+#define FIRE_DELAY_TIER_5 7
+#define FIRE_DELAY_TIER_6 6
+#define FIRE_DELAY_TIER_7 5
+#define FIRE_DELAY_TIER_8 4
+#define FIRE_DELAY_TIER_9 3.5
+#define FIRE_DELAY_TIER_10 3
+#define FIRE_DELAY_TIER_11 2.5
+#define FIRE_DELAY_TIER_LMG 2
+#define FIRE_DELAY_TIER_SG 2
+#define FIRE_DELAY_TIER_SMG 1.5
+#define FIRE_DELAY_TIER_12 1
/*
////RANGE RELATED////
diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm
index 82237cd793b2..0f822385ad13 100644
--- a/code/__DEFINES/xeno.dm
+++ b/code/__DEFINES/xeno.dm
@@ -8,6 +8,9 @@
#define TUNNEL_ENTER_BIG_XENO_DELAY 120
#define TUNNEL_ENTER_LARVA_DELAY 10
+/// The duration it takes a player controlled facehugger to leap or hug adjacently
+#define FACEHUGGER_WINDUP_DURATION 1 SECONDS
+
// Defines for action types and click delays used by xenomorph/unarmedattack() and attack_alien().
/// Full attack delay.
@@ -165,6 +168,12 @@
/// The amount of time after round start before buried larva spawns are disallowed
#define XENO_BURIED_LARVA_TIME_LIMIT (30 MINUTES)
+/// The time when xenos can start taking over comm towers
+#define XENO_COMM_ACQUISITION_TIME (90 MINUTES)
+
+/// The time it takes for a pylon to give one larva while activated
+#define XENO_PYLON_ACTIVATION_COOLDOWN (5 MINUTES)
+
/// The time against away_timer when an AFK xeno larva can be replaced
#define XENO_LEAVE_TIMER_LARVA 80 //80 seconds
/// The time against away_timer when an AFK xeno (not larva) can be replaced
diff --git a/code/__HELPERS/logging.dm b/code/__HELPERS/logging.dm
index 32da4c953188..5ecbff108725 100644
--- a/code/__HELPERS/logging.dm
+++ b/code/__HELPERS/logging.dm
@@ -108,8 +108,10 @@
if (CONFIG_GET(flag/log_interact))
WRITE_LOG(GLOB.world_game_log, "INTERACT: [msg]")
LOG_REDIS("interact", "\[[time]\] [msg]")
- origin.attack_log += "\[[time]\] [msg] "
- target.attack_log += "\[[time]\] [msg] "
+ if(origin)
+ origin.attack_log += "\[[time]\] [msg] "
+ if(target)
+ target.attack_log += "\[[time]\] [msg] "
GLOB.STUI.attack.Add("\[[time]]INTERACT: [msg]")
GLOB.STUI.processing |= STUI_LOG_ATTACK
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index ef539b8459c7..4936609d892e 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -415,6 +415,7 @@ DEFINE_BITFIELD(toggleable_flags, list(
"MODE_NO_COMBAT_CAS" = MODE_NO_COMBAT_CAS,
"MODE_LZ_PROTECTION" = MODE_LZ_PROTECTION,
"MODE_SHIPSIDE_SD" = MODE_SHIPSIDE_SD,
+ "MODE_DISPOSABLE_MOBS" = MODE_DISPOSABLE_MOBS,
))
DEFINE_BITFIELD(state, list(
diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm
index 7e65cfecd8b0..36058a44fc37 100644
--- a/code/_globalvars/global_lists.dm
+++ b/code/_globalvars/global_lists.dm
@@ -177,7 +177,8 @@ GLOBAL_LIST_INIT_TYPED(hive_datum, /datum/hive_status, list(
XENO_HIVE_TAMED = new /datum/hive_status/corrupted/tamed(),
XENO_HIVE_MUTATED = new /datum/hive_status/mutated(),
XENO_HIVE_FORSAKEN = new /datum/hive_status/forsaken(),
- XENO_HIVE_YAUTJA = new /datum/hive_status/yautja()
+ XENO_HIVE_YAUTJA = new /datum/hive_status/yautja(),
+ XENO_HIVE_RENEGADE = new /datum/hive_status/corrupted/renegade(),
))
GLOBAL_LIST_INIT(xeno_evolve_times, setup_xeno_evolve_times())
diff --git a/code/_globalvars/lists/mapping_globals.dm b/code/_globalvars/lists/mapping_globals.dm
index cf5b2ad435b8..47cc22dae5e1 100644
--- a/code/_globalvars/lists/mapping_globals.dm
+++ b/code/_globalvars/lists/mapping_globals.dm
@@ -27,6 +27,7 @@ GLOBAL_LIST_EMPTY(latewhiskey)
GLOBAL_LIST_EMPTY(latejoin)
GLOBAL_LIST_EMPTY(latejoin_by_squad)
+GLOBAL_LIST_EMPTY(latejoin_by_job)
GLOBAL_LIST_EMPTY(zombie_landmarks)
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 5acfe74f2965..b67953b1edbe 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -53,6 +53,13 @@
ManualFollow(target)
return FALSE
+ if(xeno.hive)
+ for(var/mob_name in xeno.hive.banished_ckeys)
+ if(xeno.hive.banished_ckeys[mob_name] == ckey)
+ to_chat(src, SPAN_WARNING("You are banished from the [xeno.hive], you may not rejoin unless the Queen re-admits you or dies."))
+ ManualFollow(target)
+ return FALSE
+
if(alert(src, "Are you sure you want to transfer yourself into [xeno]?", "Confirm Transfer", "Yes", "No") != "Yes")
return FALSE
if(((!islarva(xeno) && xeno.away_timer < XENO_LEAVE_TIMER) || (islarva(xeno) && xeno.away_timer < XENO_LEAVE_TIMER_LARVA)) || xeno.stat == DEAD) // Do it again, just in case
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 7988ff6d1a95..976256cb6c97 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -621,3 +621,5 @@ This maintains a list of ip addresses that are able to bypass topic filtering.
/datum/config_entry/string/instance_name
config_entry_value = "game"
protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED
+
+/datum/config_entry/flag/guest_ban
diff --git a/code/datums/components/weed_food.dm b/code/datums/components/weed_food.dm
index 0c578b661517..ce6c17e0af95 100644
--- a/code/datums/components/weed_food.dm
+++ b/code/datums/components/weed_food.dm
@@ -193,6 +193,8 @@
return FALSE
if(!parent_turf?.weeds)
return FALSE
+ if(SEND_SIGNAL(parent_mob, COMSIG_ATTEMPT_MOB_PULL) & COMPONENT_CANCEL_MOB_PULL)
+ return FALSE
if(unmerged_time == world.time)
return merge_with_weeds() // Weeds upgraded, re-merge now re-using the apperance
@@ -245,6 +247,9 @@
UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
parent_buckle = null
+ if(SEND_SIGNAL(parent_mob, COMSIG_ATTEMPT_MOB_PULL) & COMPONENT_CANCEL_MOB_PULL)
+ return FALSE
+
absorbing_weeds = parent_turf?.weeds
if(!absorbing_weeds)
return FALSE
diff --git a/code/datums/diseases/black_goo.dm b/code/datums/diseases/black_goo.dm
index 97aec074d2d3..38a26f3648c7 100644
--- a/code/datums/diseases/black_goo.dm
+++ b/code/datums/diseases/black_goo.dm
@@ -124,7 +124,7 @@
icon = 'icons/mob/humans/species/r_zombie.dmi'
icon_state = "claw_l"
flags_item = NODROP|DELONDROP|ITEM_ABSTRACT
- force = 40
+ force = MELEE_FORCE_TIER_6 //slightly higher than normal
w_class = SIZE_MASSIVE
sharp = 1
attack_verb = list("slashed", "torn", "scraped", "gashed", "ripped")
@@ -135,8 +135,9 @@
return FALSE
. = ..()
- if(.)
- playsound(loc, 'sound/weapons/bladeslice.ogg', 25, 1, 5)
+ if(!.)
+ return FALSE
+ playsound(loc, 'sound/weapons/bladeslice.ogg', 25, 1, 5)
if(ishuman_strict(target))
var/mob/living/carbon/human/human = target
@@ -149,10 +150,7 @@
target.AddDisease(new /datum/disease/black_goo)
to_chat(user, SPAN_XENOWARNING("You sense your target is now infected."))
- if(issynth(target))
- target.apply_effect(2, SLOW)
- else
- target.apply_effect(2, SUPERSLOW)
+ target.apply_effect(2, SLOW)
/obj/item/weapon/zombie_claws/afterattack(obj/O as obj, mob/user as mob, proximity)
if(get_dist(src, O) > 1)
diff --git a/code/datums/emergency_calls/cryo_marines.dm b/code/datums/emergency_calls/cryo_marines.dm
index eb914e198b37..e7dcba08ed61 100644
--- a/code/datums/emergency_calls/cryo_marines.dm
+++ b/code/datums/emergency_calls/cryo_marines.dm
@@ -12,15 +12,17 @@
name_of_spawn = /obj/effect/landmark/ert_spawns/distress_cryo
shuttle_id = ""
var/leaders = 0
+ spawn_max_amount = TRUE
/datum/emergency_call/cryo_squad/spawn_candidates(announce, override_spawn_loc, announce_dispatch_message)
var/datum/squad/marine/cryo/cryo_squad = RoleAuthority.squads_by_type[/datum/squad/marine/cryo]
leaders = cryo_squad.num_leaders
. = ..()
- if(length(members))
- shipwide_ai_announcement("Successfully deployed [length(members)] Foxtrot marines.")
+ shipwide_ai_announcement("Successfully deployed [mob_max] Foxtrot marines, of which [length(members)] are ready for duty.")
+ if(mob_max > length(members))
+ announce_dchat("Some cryomarines were not taken, use the Join As Freed Mob verb to take one of them.")
-/datum/emergency_call/cryo_squad/create_member(datum/mind/M, turf/override_spawn_loc)
+/datum/emergency_call/cryo_squad/create_member(datum/mind/mind, turf/override_spawn_loc)
set waitfor = 0
if(SSmapping.configs[GROUND_MAP].map_name == MAP_WHISKEY_OUTPOST)
name_of_spawn = /obj/effect/landmark/ert_spawns/distress_wo
@@ -28,44 +30,61 @@
if(!istype(spawn_loc)) return //Didn't find a useable spawn point.
- var/mob/living/carbon/human/H = new(spawn_loc)
- M.transfer_to(H, TRUE)
+ var/mob/living/carbon/human/human = new(spawn_loc)
+
+ if(mind)
+ mind.transfer_to(human, TRUE)
+ else
+ human.create_hud()
+
+ if(!mind)
+ for(var/obj/structure/machinery/cryopod/pod in view(7,human))
+ if(pod && !pod.occupant)
+ pod.go_in_cryopod(human, silent = TRUE)
+ break
sleep(5)
var/datum/squad/marine/cryo/cryo_squad = RoleAuthority.squads_by_type[/datum/squad/marine/cryo]
- if(leaders < cryo_squad.max_leaders && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(H.client, JOB_SQUAD_LEADER, time_required_for_job))
- leader = H
+ if(leaders < cryo_squad.max_leaders && (!mind || (HAS_FLAG(human.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(human.client, JOB_SQUAD_LEADER, time_required_for_job))))
+ leader = human
leaders++
- arm_equipment(H, /datum/equipment_preset/uscm/leader/cryo, TRUE, TRUE)
- to_chat(H, SPAN_ROLE_HEADER("You are a Squad Leader in the USCM"))
- to_chat(H, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
- to_chat(H, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
- else if (heavies < max_heavies && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_HEAVY) && check_timelock(H.client, JOB_SQUAD_SPECIALIST, time_required_for_job))
+ human.client?.prefs.copy_all_to(human, JOB_SQUAD_LEADER, TRUE, TRUE)
+ arm_equipment(human, /datum/equipment_preset/uscm/leader/cryo, mind == null, TRUE)
+ to_chat(human, SPAN_ROLE_HEADER("You are a Squad Leader in the USCM"))
+ to_chat(human, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
+ to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
+ else if (heavies < max_heavies && (!mind || (HAS_FLAG(human.client.prefs.toggles_ert, PLAY_HEAVY) && check_timelock(human.client, JOB_SQUAD_SPECIALIST, time_required_for_job))))
heavies++
- arm_equipment(H, /datum/equipment_preset/uscm/spec/cryo, TRUE, TRUE)
- to_chat(H, SPAN_ROLE_HEADER("You are a Weapons Specialist in the USCM"))
- to_chat(H, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
- to_chat(H, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
- else if (medics < max_medics && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(H.client, JOB_SQUAD_MEDIC, time_required_for_job))
+ human.client?.prefs.copy_all_to(human, JOB_SQUAD_SPECIALIST, TRUE, TRUE)
+ arm_equipment(human, /datum/equipment_preset/uscm/spec/cryo, mind == null, TRUE)
+ to_chat(human, SPAN_ROLE_HEADER("You are a Weapons Specialist in the USCM"))
+ to_chat(human, SPAN_ROLE_BODY("Your squad is here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
+ to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
+ else if (medics < max_medics && (!mind || (HAS_FLAG(human.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(human.client, JOB_SQUAD_MEDIC, time_required_for_job))))
medics++
- arm_equipment(H, /datum/equipment_preset/uscm/medic/cryo, TRUE, TRUE)
- to_chat(H, SPAN_ROLE_HEADER("You are a Hospital Corpsman in the USCM"))
- to_chat(H, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
- to_chat(H, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
- else if (engineers < max_engineers && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_ENGINEER) && check_timelock(H.client, JOB_SQUAD_ENGI, time_required_for_job))
+ human.client?.prefs.copy_all_to(human, JOB_SQUAD_MEDIC, TRUE, TRUE)
+ arm_equipment(human, /datum/equipment_preset/uscm/medic/cryo, mind == null, TRUE)
+ to_chat(human, SPAN_ROLE_HEADER("You are a Hospital Corpsman in the USCM"))
+ to_chat(human, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
+ to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
+ else if (engineers < max_engineers && (!mind || (HAS_FLAG(human.client.prefs.toggles_ert, PLAY_ENGINEER) && check_timelock(human.client, JOB_SQUAD_ENGI, time_required_for_job))))
engineers++
- arm_equipment(H, /datum/equipment_preset/uscm/engineer/cryo, TRUE, TRUE)
- to_chat(H, SPAN_ROLE_HEADER("You are an Engineer in the USCM"))
- to_chat(H, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
- to_chat(H, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
+ human.client?.prefs.copy_all_to(human, JOB_SQUAD_ENGI, TRUE, TRUE)
+ arm_equipment(human, /datum/equipment_preset/uscm/engineer/cryo, mind == null, TRUE)
+ to_chat(human, SPAN_ROLE_HEADER("You are an Engineer in the USCM"))
+ to_chat(human, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
+ to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
else
- arm_equipment(H, /datum/equipment_preset/uscm/pfc/cryo, TRUE, TRUE)
- to_chat(H, SPAN_ROLE_HEADER("You are a Rifleman in the USCM"))
- to_chat(H, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
- to_chat(H, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
+ human.client?.prefs.copy_all_to(human, JOB_SQUAD_MARINE, TRUE, TRUE)
+ arm_equipment(human, /datum/equipment_preset/uscm/pfc/cryo, mind == null, TRUE)
+ to_chat(human, SPAN_ROLE_HEADER("You are a Rifleman in the USCM"))
+ to_chat(human, SPAN_ROLE_BODY("You are here to assist in the defence of the [SSmapping.configs[GROUND_MAP].map_name]. Listen to the chain of command."))
+ to_chat(human, SPAN_BOLDWARNING("If you wish to cryo or ghost upon spawning in, you must ahelp and inform staff so you can be replaced."))
sleep(10)
- to_chat(H, SPAN_BOLD("Objectives: [objectives]"))
+ if(!mind)
+ human.free_for_ghosts()
+ to_chat(human, SPAN_BOLD("Objectives: [objectives]"))
/datum/emergency_call/cryo_squad/platoon
name = "Marine Cryo Reinforcements (Platoon)"
diff --git a/code/datums/emergency_calls/emergency_call.dm b/code/datums/emergency_calls/emergency_call.dm
index 2a305dcc4342..3d43917b2bc7 100644
--- a/code/datums/emergency_calls/emergency_call.dm
+++ b/code/datums/emergency_calls/emergency_call.dm
@@ -9,6 +9,11 @@
var/list/datum/emergency_call/all_calls = list() //initialized at round start and stores the datums.
var/datum/emergency_call/picked_calls[] = list() //Which distress calls are currently active
+/datum/game_mode/proc/ares_online()
+ var/name = "ARES Online"
+ var/input = "ARES. Online. Good morning, marines."
+ shipwide_ai_announcement(input, name, 'sound/AI/ares_online.ogg')
+
//The distress call parent. Cannot be called itself due to "name" being a filtered target.
/datum/emergency_call
var/name = "name"
diff --git a/code/datums/entities/player.dm b/code/datums/entities/player.dm
index 9b8f95938de6..ed97c4eafaae 100644
--- a/code/datums/entities/player.dm
+++ b/code/datums/entities/player.dm
@@ -105,6 +105,7 @@ BSQL_PROTECT_DATUM(/datum/entity/player)
note.player_id = id
note.text = note_text
note.date = "[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]"
+ note.round_id = GLOB.round_id
note.is_confidential = is_confidential
note.note_category = note_category
note.is_ban = is_ban
diff --git a/code/datums/entities/player_note.dm b/code/datums/entities/player_note.dm
index f6662a153113..420bb5f0a470 100644
--- a/code/datums/entities/player_note.dm
+++ b/code/datums/entities/player_note.dm
@@ -1,8 +1,11 @@
+#define NOTE_ROUND_ID(note_entity) note_entity.round_id ? "(ID: [note_entity.round_id])" : ""
+
/datum/entity/player_note
var/player_id
var/admin_id
var/text
var/date
+ var/round_id
var/is_ban = FALSE
var/ban_time
var/is_confidential = FALSE
@@ -19,15 +22,16 @@ BSQL_PROTECT_DATUM(/datum/entity/player_note)
entity_type = /datum/entity/player_note
table_name = "player_notes"
field_types = list(
- "player_id"=DB_FIELDTYPE_BIGINT,
- "admin_id"=DB_FIELDTYPE_BIGINT,
- "text"=DB_FIELDTYPE_STRING_MAX,
- "date"=DB_FIELDTYPE_STRING_LARGE,
- "is_ban"=DB_FIELDTYPE_INT,
- "ban_time"=DB_FIELDTYPE_BIGINT,
- "is_confidential"=DB_FIELDTYPE_INT,
- "admin_rank"=DB_FIELDTYPE_STRING_MEDIUM,
- "note_category" =DB_FIELDTYPE_INT,
+ "player_id" = DB_FIELDTYPE_BIGINT,
+ "admin_id" = DB_FIELDTYPE_BIGINT,
+ "text" = DB_FIELDTYPE_STRING_MAX,
+ "date" = DB_FIELDTYPE_STRING_LARGE,
+ "round_id" = DB_FIELDTYPE_BIGINT,
+ "is_ban" = DB_FIELDTYPE_INT,
+ "ban_time" = DB_FIELDTYPE_BIGINT,
+ "is_confidential" = DB_FIELDTYPE_INT,
+ "admin_rank" = DB_FIELDTYPE_STRING_MEDIUM,
+ "note_category" = DB_FIELDTYPE_INT,
)
/datum/entity_meta/player_note/on_read(datum/entity/player_note/note)
@@ -64,6 +68,7 @@ BSQL_PROTECT_DATUM(/datum/entity/player_note)
var/is_ban
var/admin_ckey
var/date
+ var/round_id
var/ban_time
var/is_confidential
var/admin_rank
@@ -79,6 +84,7 @@ BSQL_PROTECT_DATUM(/datum/entity/player_note)
"is_ban",
"admin_ckey" = "admin.ckey",
"date",
+ "round_id",
"ban_time",
"is_confidential",
"admin_rank",
@@ -88,4 +94,4 @@ BSQL_PROTECT_DATUM(/datum/entity/player_note)
/// Returns all notes associated with a CKEY, structured as a list of strings.
/proc/get_all_notes(player_ckey)
for(var/datum/view_record/note_view/note in DB_VIEW(/datum/view_record/note_view, DB_COMP("player_ckey", DB_EQUALS, player_ckey)))
- LAZYADDASSOC(., "[note.note_category]", "\"[note.text]\", by [note.admin_ckey] ([note.admin_rank]) on [note.date]")
+ LAZYADDASSOC(., "[note.note_category]", "\"[note.text]\", by [note.admin_ckey] ([note.admin_rank]) on [note.date] ([note.round_id])")
diff --git a/code/datums/factions/uscm.dm b/code/datums/factions/uscm.dm
index 4d13e7d285ca..cf77142ce5d6 100644
--- a/code/datums/factions/uscm.dm
+++ b/code/datums/factions/uscm.dm
@@ -77,6 +77,9 @@
if(JOB_SO)
marine_rk = "so"
border_rk = "command"
+ if(JOB_AUXILIARY_OFFICER)
+ marine_rk = "aso"
+ border_rk = "command"
if(JOB_GENERAL, JOB_COLONEL, JOB_ACMC, JOB_CMC)
marine_rk = "general"
border_rk = "command"
diff --git a/code/datums/map_config.dm b/code/datums/map_config.dm
index c37bf802f12b..1f3c265ead76 100644
--- a/code/datums/map_config.dm
+++ b/code/datums/map_config.dm
@@ -31,6 +31,7 @@
var/shuttles = list()
var/announce_text = ""
+ var/infection_announce_text = ""
var/squads_max_num = 4
@@ -339,6 +340,9 @@
if(json["announce_text"])
announce_text = json["announce_text"]
+ if(json["infection_announce_text"])
+ infection_announce_text = json["infection_announce_text"]
+
if(json["weather_holder"])
weather_holder = text2path(json["weather_holder"])
if(!weather_holder)
diff --git a/code/datums/medal_awards.dm b/code/datums/medal_awards.dm
index a7db6f7bd6e3..54af48fd3345 100644
--- a/code/datums/medal_awards.dm
+++ b/code/datums/medal_awards.dm
@@ -20,9 +20,10 @@ GLOBAL_LIST_EMPTY(jelly_awards)
var/recipient_rank
var/recipient_ckey
var/mob/recipient_mob
- var/list/giver_name // Actually key for xenos
- var/list/giver_rank // Actually name for xenos
+ var/list/giver_name // Designation for xenos
+ var/list/giver_rank // "Name" for xenos
var/list/giver_mob
+ var/list/giver_ckey
/datum/recipient_awards/New()
medal_names = list()
@@ -32,6 +33,7 @@ GLOBAL_LIST_EMPTY(jelly_awards)
giver_name = list()
giver_rank = list()
giver_mob = list()
+ giver_ckey = list()
/proc/give_medal_award(medal_location, as_admin = FALSE)
@@ -116,12 +118,13 @@ GLOBAL_LIST_EMPTY(jelly_awards)
recipient_award.medal_names += medal_type
recipient_award.medal_citations += citation
recipient_award.posthumous += posthumous
+ recipient_award.giver_ckey += usr.ckey
if(!as_admin)
recipient_award.giver_rank += recipient_ranks[usr.real_name] // Currently not used in marine award message
recipient_award.giver_name += usr.real_name // Currently not used in marine award message
else
- recipient_award.giver_rank += "([usr.ckey])" // Just because it'll be displayed in the panel
+ recipient_award.giver_rank += null
recipient_award.giver_name += null
// Create an actual medal item
@@ -258,15 +261,21 @@ GLOBAL_LIST_EMPTY(jelly_awards)
recipient_award.medal_names += medal_type
recipient_award.medal_citations += citation
recipient_award.posthumous += posthumous
+ recipient_award.giver_ckey += usr.ckey
+
if(!admin_attribution)
recipient_award.giver_rank += usr.name
- recipient_award.giver_name += usr.key
+ var/mob/living/carbon/xenomorph/giving_xeno = usr
+ if(istype(giving_xeno))
+ recipient_award.giver_name += giving_xeno.full_designation
+ else
+ recipient_award.giver_name += null
else if(admin_attribution == "none")
recipient_award.giver_rank += null
recipient_award.giver_name += null
else
recipient_award.giver_rank += admin_attribution
- recipient_award.giver_name += null // If not null, rescinding it will take stats away from a mob with this key
+ recipient_award.giver_name += null
recipient_award.medal_items += null // TODO: Xeno award item?
@@ -337,6 +346,7 @@ GLOBAL_LIST_EMPTY(jelly_awards)
recipient_award.giver_name.Cut(index, index + 1)
recipient_award.giver_rank.Cut(index, index + 1)
recipient_award.giver_mob.Cut(index, index + 1)
+ recipient_award.giver_ckey.Cut(index, index + 1)
recipient_award.medal_items.Cut(index, index + 1)
// Remove giver's stat
diff --git a/code/datums/mob_hud.dm b/code/datums/mob_hud.dm
index 815507db0633..0f77f5039b48 100644
--- a/code/datums/mob_hud.dm
+++ b/code/datums/mob_hud.dm
@@ -454,9 +454,9 @@ var/list/datum/mob_hud/huds = list(
holder2_set = 1
return
- holder.icon_state = "huddead"
+ holder.icon_state = HAS_TRAIT(src, TRAIT_HARDCORE) || MODE_HAS_TOGGLEABLE_FLAG(MODE_HARDCORE_PERMA) ? "hudhcdead" : "huddead"
if(!holder2_set)
- holder2.icon_state = "huddead"
+ holder2.icon_state = holder.icon_state
holder3.icon_state = "huddead"
holder2_set = 1
diff --git a/code/datums/redis/callbacks/asay.dm b/code/datums/redis/callbacks/asay.dm
index 2ccbca08fb6c..9c60a394a4ac 100644
--- a/code/datums/redis/callbacks/asay.dm
+++ b/code/datums/redis/callbacks/asay.dm
@@ -7,10 +7,10 @@
if(data["source"] == SSredis.instance_name)
return
- var/msg = SPAN_ADMINSAY("[data["rank"]]:[data["author"]]@[data["source"]]: [strip_html(data["message"])]")
+ var/msg = SPAN_MOD("[data["rank"]]:[data["author"]]@[data["source"]]: [strip_html(data["message"])]")
for(var/client/client in GLOB.admins)
- if(!(R_ADMIN & client.admin_holder.rights))
+ if(!(R_ADMIN & client.admin_holder.rights) && !(R_MOD & client.admin_holder.rights))
continue
to_chat(client, msg)
diff --git a/code/datums/skills.dm b/code/datums/skills.dm
index f37ea3a5a64e..9bc53007173d 100644
--- a/code/datums/skills.dm
+++ b/code/datums/skills.dm
@@ -530,15 +530,15 @@ MILITARY SURVIVORS
SKILL_JTAC = SKILL_JTAC_TRAINED,
)
-/datum/skills/military/survivor/forecon_grenadier
- name = "Reconnaissance Grenadier"
+/datum/skills/military/survivor/forecon_sniper
+ name = "Reconnaissance Sniper"
skills = list(
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_DEFAULT,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
- SKILL_SPEC_WEAPONS = SKILL_SPEC_GRENADIER,
+ SKILL_SPEC_WEAPONS = SKILL_SPEC_SNIPER,
SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
SKILL_ENDURANCE = SKILL_ENDURANCE_SURVIVOR,
SKILL_LEADERSHIP = SKILL_LEAD_NOVICE,
@@ -597,6 +597,7 @@ COMMAND STAFF
SKILL_SURGERY = SKILL_SURGERY_NOVICE,
SKILL_POLICE = SKILL_POLICE_SKILLED,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_SPEC_WEAPONS = SKILL_SPEC_SMARTGUN,
SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
@@ -616,6 +617,7 @@ COMMAND STAFF
SKILL_MEDICAL = SKILL_MEDICAL_DOCTOR,
SKILL_SURGERY = SKILL_SURGERY_NOVICE,
SKILL_POLICE = SKILL_POLICE_FLASH,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
SKILL_FIREMAN = SKILL_FIREMAN_SKILLED,
SKILL_CQC = SKILL_CQC_SKILLED,
SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
@@ -699,6 +701,24 @@ COMMAND STAFF
SKILL_INTEL = SKILL_INTEL_TRAINED,
)
+/datum/skills/auxiliary_officer
+ name = "Auxiliary Support Officer"
+ skills = list(
+ SKILL_PILOT = SKILL_PILOT_EXPERT,
+ SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
+ SKILL_LEADERSHIP = SKILL_LEAD_MASTER,
+ SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
+ SKILL_SURGERY = SKILL_SURGERY_NOVICE,
+ SKILL_JTAC = SKILL_JTAC_EXPERT,
+ SKILL_INTEL = SKILL_INTEL_EXPERT,
+ SKILL_VEHICLE = SKILL_VEHICLE_SMALL,
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_POLICE = SKILL_POLICE_FLASH,
+ SKILL_NAVIGATIONS = SKILL_NAVIGATIONS_TRAINED,
+ SKILL_FIREMAN = SKILL_FIREMAN_TRAINED,
+ )
+
/datum/skills/CE
name = "Chief Engineer"
skills = list(
@@ -989,7 +1009,7 @@ United States Colonial Marines
SKILL_CQC = SKILL_CQC_TRAINED,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
SKILL_ENGINEER = SKILL_ENGINEER_TRAINED, //to use c4 in demo set.
- SKILL_SPEC_WEAPONS = SKILL_SPEC_ALL,
+ SKILL_SPEC_WEAPONS = SKILL_SPEC_TRAINED,
SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
SKILL_JTAC = SKILL_JTAC_BEGINNER
@@ -1594,6 +1614,82 @@ COLONIAL MARSHALS
SKILL_INTEL = SKILL_INTEL_EXPERT,
SKILL_DOMESTIC = SKILL_DOMESTIC_MASTER
)
+
+/datum/skills/military/survivor/upp_private
+ name = "UPP Private"
+ skills = list(
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
+ SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
+ SKILL_JTAC = SKILL_JTAC_TRAINED,
+ )
+
+/datum/skills/military/survivor/upp_sapper
+ name = "UPP Sapper"
+ skills = list(
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
+ SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
+ SKILL_JTAC = SKILL_JTAC_TRAINED,
+ )
+
+/datum/skills/military/survivor/upp_medic
+ name = "UPP Medic"
+ skills = list(
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_MEDICAL = SKILL_MEDICAL_DOCTOR,
+ SKILL_SURGERY = SKILL_SURGERY_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
+ SKILL_JTAC = SKILL_JTAC_TRAINED,
+ )
+
+/datum/skills/military/survivor/upp_spec
+ name = "UPP Specialist"
+ skills = list(
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_TRAINED,
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_MEDICAL = SKILL_MEDICAL_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
+ SKILL_JTAC = SKILL_JTAC_TRAINED,
+ SKILL_SPEC_WEAPONS = SKILL_SPEC_UPP,
+ SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
+ SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
+ )
+
+/datum/skills/military/survivor/upp_sl
+ name = "UPP Squad Leader"
+ skills = list(
+ SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
+ SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ SKILL_FIREARMS = SKILL_FIREARMS_TRAINED,
+ SKILL_CQC = SKILL_CQC_TRAINED,
+ SKILL_MELEE_WEAPONS = SKILL_MELEE_TRAINED,
+ SKILL_LEADERSHIP = SKILL_LEAD_EXPERT,
+ SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
+ SKILL_VEHICLE = SKILL_VEHICLE_DEFAULT,
+ SKILL_JTAC = SKILL_JTAC_EXPERT,
+ )
+
/*
---------------------
SPEC-OPS
diff --git a/code/datums/statistics/entities/death_stats.dm b/code/datums/statistics/entities/death_stats.dm
index 7e26e92ae135..4a01e4e9d72b 100644
--- a/code/datums/statistics/entities/death_stats.dm
+++ b/code/datums/statistics/entities/death_stats.dm
@@ -65,13 +65,25 @@
)
/mob/proc/track_mob_death(datum/cause_data/cause_data, turf/death_loc)
- if(!mind || statistic_exempt)
- return
-
if(cause_data && !istype(cause_data))
stack_trace("track_mob_death called with string cause ([cause_data]) instead of datum")
cause_data = create_cause_data(cause_data)
+ var/log_message = "\[[time_stamp()]\] [key_name(src)] died to "
+ if(cause_data)
+ log_message += "[cause_data.cause_name]"
+ else
+ log_message += "unknown causes"
+ var/mob/cause_mob = cause_data?.resolve_mob()
+ if(cause_mob)
+ log_message += " from [key_name(cause_data.resolve_mob())]"
+ cause_mob.attack_log += "\[[time_stamp()]\] [key_name(cause_mob)] killed [key_name(src)] with [cause_data.cause_name]."
+
+ attack_log += "[log_message]."
+
+ if(!mind || statistic_exempt)
+ return
+
var/datum/entity/statistic/death/new_death = DB_ENTITY(/datum/entity/statistic/death)
var/datum/entity/player/player_entity = get_player_from_key(mind.ckey)
if(player_entity)
@@ -96,7 +108,6 @@
new_death.cause_role_name = cause_data?.role
new_death.cause_faction_name = cause_data?.faction
- var/mob/cause_mob = cause_data?.resolve_mob()
if(cause_mob)
cause_mob.life_kills_total += life_value
diff --git a/code/datums/statistics/random_facts/random_fact.dm b/code/datums/statistics/random_facts/random_fact.dm
index 2a83c7b2c1ad..76c6e82f776d 100644
--- a/code/datums/statistics/random_facts/random_fact.dm
+++ b/code/datums/statistics/random_facts/random_fact.dm
@@ -41,6 +41,8 @@
list_to_check += GLOB.living_xeno_list
for(var/mob/checked_mob as anything in list_to_check)
+ if(!checked_mob?.persistent_ckey)
+ continue // We don't care about NPCs
if(living_stat_gotten < life_grab_stat(checked_mob))
mob_to_report = checked_mob
living_stat_gotten = life_grab_stat(checked_mob)
diff --git a/code/datums/supply_packs/attachments.dm b/code/datums/supply_packs/attachments.dm
index 2a812e94cfc1..b685e5c3c37f 100644
--- a/code/datums/supply_packs/attachments.dm
+++ b/code/datums/supply_packs/attachments.dm
@@ -84,17 +84,6 @@
containername = "extended barrel attachment crate"
group = "Attachments"
-/datum/supply_packs/muzzle_heavy
- name = "barrel charger attachment crate (x2)"
- contains = list(
- /obj/item/attachable/heavy_barrel,
- /obj/item/attachable/heavy_barrel,
- )
- cost = 30
- containertype = /obj/structure/closet/crate
- containername = "heavy barrel attachment crate"
- group = "Attachments"
-
/datum/supply_packs/muzzle_compensator
name = "compensator attachment crate (x6)"
contains = list(
diff --git a/code/datums/supply_packs/clothing.dm b/code/datums/supply_packs/clothing.dm
index 0e7604ead7c7..1c7241bb253b 100644
--- a/code/datums/supply_packs/clothing.dm
+++ b/code/datums/supply_packs/clothing.dm
@@ -110,7 +110,7 @@
/datum/supply_packs/officer_outfits//lmao this shit is so hideously out of date
contains = list(
- /obj/item/clothing/under/rank/ro_suit,
+ /obj/item/clothing/under/rank/qm_suit,
/obj/item/clothing/under/marine/officer/bridge,
/obj/item/clothing/under/marine/officer/bridge,
/obj/item/clothing/under/marine/officer/exec,
diff --git a/code/game/area/LV522_Chances_Claim.dm b/code/game/area/LV522_Chances_Claim.dm
index 8a424148d3b5..3d15d49c3df7 100644
--- a/code/game/area/LV522_Chances_Claim.dm
+++ b/code/game/area/LV522_Chances_Claim.dm
@@ -456,3 +456,18 @@
name = "Atmospheric Processor - Filtration System"
icon_state = "mechbay"
ceiling = CEILING_UNDERGROUND_METAL_BLOCK_CAS
+
+/area/lv522/atmos/way_in_command_centre
+ name = "Atmospheric Processor - North Corpo Reactor Entrance"
+ icon_state = "blue"
+ ceiling = CEILING_UNDERGROUND_METAL_BLOCK_CAS
+
+/area/lv522/atmos/sewer
+ name = "Atmospheric Processor - Sewer"
+ icon_state = "red"
+ ceiling = CEILING_UNDERGROUND_METAL_BLOCK_CAS
+
+/area/lv522/atmos/reactor_garage
+ name = "Atmospheric Processor - Garage"
+ icon_state = "green"
+ ceiling = CEILING_UNDERGROUND_METAL_BLOCK_CAS
diff --git a/code/game/area/Sulaco.dm b/code/game/area/Sulaco.dm
index ffc087ad9851..851025e1b63a 100644
--- a/code/game/area/Sulaco.dm
+++ b/code/game/area/Sulaco.dm
@@ -13,6 +13,7 @@
is_resin_allowed = FALSE
flags_area = AREA_NOTUNNEL
is_landing_zone = TRUE
+ ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop1/Enter(atom/movable/O, atom/oldloc)
if(istype(O, /obj/structure/barricade))
@@ -23,36 +24,30 @@
name = "\improper Dropship Alamo"
icon_state = "shuttlered"
base_muffle = MUFFLE_HIGH
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop1/LV624
name = "\improper Dropship Alamo"
ambience_exterior = AMBIENCE_LV624
icon_state = "shuttle"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop1/prison
name = "\improper Dropship Alamo"
ambience_exterior = AMBIENCE_PRISON
icon_state = "shuttle"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop1/BigRed
name = "\improper Dropship Alamo"
ambience_exterior = AMBIENCE_BIGRED
icon_state = "shuttle"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop1/ice_colony
name = "\improper Dropship Alamo"
icon_state = "shuttle"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop1/DesertDam
name = "\improper Dropship Alamo"
ambience_exterior = AMBIENCE_TRIJENT
icon_state = "shuttle"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop1/transit
ambience_exterior = 'sound/ambience/dropship_ambience_loop.ogg'
@@ -75,41 +70,36 @@
is_resin_allowed = FALSE
flags_area = AREA_NOTUNNEL
is_landing_zone = TRUE
+ ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop2/sulaco
name = "\improper Dropship Normandy"
icon_state = "shuttle"
base_muffle = MUFFLE_HIGH
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop2/LV624
name = "\improper Dropship Normandy"
ambience_exterior = AMBIENCE_LV624
icon_state = "shuttle2"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop2/prison
name = "\improper Dropship Normandy"
ambience_exterior = AMBIENCE_PRISON
icon_state = "shuttle2"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop2/BigRed
name = "\improper Dropship Normandy"
ambience_exterior = AMBIENCE_BIGRED
icon_state = "shuttle2"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop2/ice_colony
name = "\improper Dropship Normandy"
icon_state = "shuttle2"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop2/DesertDam
name = "\improper Dropship Normandy"
ambience_exterior = AMBIENCE_TRIJENT
icon_state = "shuttle2"
- ceiling = CEILING_REINFORCED_METAL
/area/shuttle/drop2/transit
ambience_exterior = 'sound/ambience/dropship_ambience_loop.ogg'
diff --git a/code/game/area/almayer.dm b/code/game/area/almayer.dm
index d19cbd3a6dec..6ced81a22b15 100644
--- a/code/game/area/almayer.dm
+++ b/code/game/area/almayer.dm
@@ -317,6 +317,11 @@
icon_state = "livingspace"
fake_zlevel = 2
+/area/almayer/living/auxiliary_officer_office
+ name = "\improper Auxiliary Support Officer office"
+ icon_state = "livingspace"
+ fake_zlevel = 2
+
/area/almayer/squads/tankdeliveries
name = "\improper Vehicle ASRS"
icon_state = "req"
diff --git a/code/game/area/strata.dm b/code/game/area/strata.dm
index 91bdff277c3d..1cf0eac58d1c 100644
--- a/code/game/area/strata.dm
+++ b/code/game/area/strata.dm
@@ -50,6 +50,10 @@ EXTERIOR is FUCKING FREEZING, and refers to areas out in the open and or exposed
temperature = T20C //Nice and room temp
ceiling = CEILING_METAL
+/area/strata/ag/interior/mountain
+ name = "Outside mountain"
+ icon_state = "ag_e"
+
/area/strata/ag/interior/restricted
is_resin_allowed = FALSE
flags_area = AREA_NOTUNNEL
diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm
index a7e8ab612bb4..becee89adb6d 100644
--- a/code/game/gamemodes/cm_initialize.dm
+++ b/code/game/gamemodes/cm_initialize.dm
@@ -576,6 +576,11 @@ Additional game mode variables.
to_chat(xeno_candidate, SPAN_WARNING("The selected hive does not have a hive core to spawn from!"))
return
+ for(var/mob_name in hive.banished_ckeys)
+ if(hive.banished_ckeys[mob_name] == xeno_candidate.ckey)
+ to_chat(xeno_candidate, SPAN_WARNING("You are banished from the [hive], you may not rejoin unless the Queen re-admits you or dies."))
+ return
+
hive.hive_location.spawn_lesser_drone(xeno_candidate)
return TRUE
@@ -964,7 +969,7 @@ Additional game mode variables.
log_debug("Null client attempted to transform_joe")
return
- var/turf/spawn_point = get_turf(pick(GLOB.latejoin))
+ var/turf/spawn_point = get_turf(pick(GLOB.latejoin_by_job[JOB_WORKING_JOE]))
var/mob/living/carbon/human/synthetic/new_joe = new(spawn_point)
joe_candidate.mind.transfer_to(new_joe, TRUE)
var/datum/job/joe_job = RoleAuthority.roles_by_name[JOB_WORKING_JOE]
diff --git a/code/game/gamemodes/cm_process.dm b/code/game/gamemodes/cm_process.dm
index e4e0e32adff4..33377f7dc6fd 100644
--- a/code/game/gamemodes/cm_process.dm
+++ b/code/game/gamemodes/cm_process.dm
@@ -58,18 +58,18 @@ of predators), but can be added to include variant game modes (like humans vs. h
if(LAZYLEN(xenomorphs) || LAZYLEN(dead_queens))
var/dat = " "
dat += SPAN_ROUNDBODY(" The xenomorph Queen(s) were:")
- var/mob/M
+ var/mob/living/carbon/xenomorph/xeno_mob
for (var/msg in dead_queens)
dat += msg
- for(var/datum/mind/X in xenomorphs)
- if(!istype(X))
+ for(var/datum/mind/xeno_mind in xenomorphs)
+ if(!istype(xeno_mind))
continue
- M = X.current
- if(!M || !M.loc)
- M = X.original
- if(M && M.loc && isqueen(M) && M.stat != DEAD) // Dead queens handled separately
- dat += " [X.key] was [M] [SPAN_BOLDNOTICE("(SURVIVED)")]"
+ xeno_mob = xeno_mind.current
+ if(!xeno_mob || !xeno_mob.loc)
+ xeno_mob = xeno_mind.original
+ if(xeno_mob && xeno_mob.loc && isqueen(xeno_mob) && xeno_mob.stat != DEAD) // Dead queens handled separately
+ dat += " [xeno_mob.full_designation] was [xeno_mob] [SPAN_BOLDNOTICE("(SURVIVED)")]"
to_world("[dat]")
@@ -244,6 +244,34 @@ GLOBAL_VAR_INIT(next_admin_bioscan, 30 MINUTES)
return num_marines
+/datum/game_mode/proc/count_per_faction(list/z_levels = SSmapping.levels_by_any_trait(list(ZTRAIT_GROUND, ZTRAIT_RESERVED, ZTRAIT_MARINE_MAIN_SHIP)))
+ var/num_marines = 0
+ var/num_WY = 0
+ var/num_UPP = 0
+ var/num_CLF = 0
+ var/num_headcount = 0
+
+ for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list)
+ if(!(current_human.z && (current_human.z in z_levels) && !istype(current_human.loc, /turf/open/space)))
+ continue
+ if(current_human.faction in FACTION_LIST_WY || current_human.job == "Corporate Liaison") //The CL is assigned the USCM faction for gameplay purposes
+ num_WY++
+ num_headcount++
+ continue
+ if(current_human.faction == FACTION_UPP)
+ num_UPP++
+ num_headcount++
+ continue
+ if(current_human.faction == FACTION_CLF)
+ num_CLF++
+ num_headcount++
+ continue
+ if(current_human.faction == FACTION_MARINE)
+ num_marines++
+ num_headcount++
+ continue
+ num_headcount++
+ return list("marine_headcount" = num_marines,"WY_headcount" = num_WY,"UPP_headcount" = num_UPP,"CLF_headcount" = num_CLF,"total_headcount" = num_headcount)
/*
#undef QUEEN_DEATH_COUNTDOWN
diff --git a/code/game/gamemodes/colonialmarines/colonialmarines.dm b/code/game/gamemodes/colonialmarines/colonialmarines.dm
index 65dc2666070d..cf2b7819a596 100644
--- a/code/game/gamemodes/colonialmarines/colonialmarines.dm
+++ b/code/game/gamemodes/colonialmarines/colonialmarines.dm
@@ -118,8 +118,6 @@
if(SSmapping.configs[GROUND_MAP].environment_traits[ZTRAIT_BASIC_RT])
flags_round_type |= MODE_BASIC_RT
- round_time_lobby = world.time
-
addtimer(CALLBACK(src, PROC_REF(ares_online)), 5 SECONDS)
addtimer(CALLBACK(src, PROC_REF(map_announcement)), 20 SECONDS)
@@ -143,11 +141,6 @@
var/monkey_to_spawn = pick(monkey_types)
new monkey_to_spawn(T)
-/datum/game_mode/colonialmarines/proc/ares_online()
- var/name = "ARES Online"
- var/input = "ARES. Online. Good morning, marines."
- shipwide_ai_announcement(input, name, 'sound/AI/ares_online.ogg')
-
/datum/game_mode/colonialmarines/proc/map_announcement()
if(SSmapping.configs[GROUND_MAP].announce_text)
var/rendered_announce_text = replacetext(SSmapping.configs[GROUND_MAP].announce_text, "###SHIPNAME###", MAIN_SHIP_NAME)
@@ -354,6 +347,8 @@
//////////////////////////////////////////////////////////////////////
//Announces the end of the game with all relevant information stated//
//////////////////////////////////////////////////////////////////////
+#define MAJORITY 0.5 // What percent do we consider a 'majority?'
+
/datum/game_mode/colonialmarines/declare_completion()
announce_ending()
var/musical_track
@@ -372,7 +367,20 @@
round_statistics.current_map.total_marine_victories++
round_statistics.current_map.total_marine_majors++
if(MODE_INFESTATION_X_MINOR)
- musical_track = pick('sound/theme/neutral_melancholy1.ogg','sound/theme/neutral_melancholy2.ogg')
+ var/list/living_player_list = count_humans_and_xenos(EvacuationAuthority.get_affected_zlevels())
+ if(living_player_list[1] && !living_player_list[2]) // If Xeno Minor but Xenos are dead and Humans are alive, see which faction is the last standing
+ var/headcount = count_per_faction()
+ var/living = headcount["total_headcount"]
+ if ((headcount["WY_headcount"] / living) > MAJORITY)
+ musical_track = pick('sound/theme/lastmanstanding_wy.ogg')
+ else if ((headcount["UPP_headcount"] / living) > MAJORITY)
+ musical_track = pick('sound/theme/lastmanstanding_upp.ogg')
+ else if ((headcount["CLF_headcount"] / living) > MAJORITY)
+ musical_track = pick('sound/theme/lastmanstanding_clf.ogg')
+ else if ((headcount["marine_headcount"] / living) > MAJORITY)
+ musical_track = pick('sound/theme/neutral_melancholy2.ogg') //This is the theme song for Colonial Marines the game, fitting
+ else
+ musical_track = pick('sound/theme/neutral_melancholy1.ogg')
end_icon = "xeno_minor"
if(round_statistics && round_statistics.current_map)
round_statistics.current_map.total_xeno_victories++
@@ -581,3 +589,4 @@
#undef HIJACK_EXPLOSION_COUNT
#undef MARINE_MAJOR_ROUND_END_DELAY
+#undef MAJORITY
diff --git a/code/game/gamemodes/colonialmarines/whiskey_outpost.dm b/code/game/gamemodes/colonialmarines/whiskey_outpost.dm
index 64d8795be3d2..e172939c847f 100644
--- a/code/game/gamemodes/colonialmarines/whiskey_outpost.dm
+++ b/code/game/gamemodes/colonialmarines/whiskey_outpost.dm
@@ -664,8 +664,8 @@
/obj/item/ammo_magazine/rocket/wp)
if(2) //Smartgun supplies
spawnitems = list(
- /obj/item/cell/high,
- /obj/item/cell/high,
+ /obj/item/smartgun_battery,
+ /obj/item/smartgun_battery,
/obj/item/ammo_magazine/smartgun,
/obj/item/ammo_magazine/smartgun,
/obj/item/ammo_magazine/smartgun,
diff --git a/code/game/gamemodes/extended/infection.dm b/code/game/gamemodes/extended/infection.dm
index d42f71798a50..04e0545361aa 100644
--- a/code/game/gamemodes/extended/infection.dm
+++ b/code/game/gamemodes/extended/infection.dm
@@ -25,17 +25,25 @@
initialize_post_marine_gear_list()
for(var/mob/new_player/np in GLOB.new_player_list)
np.new_player_panel_proc()
- spawn(50)
- marine_announcement("We've lost contact with the Weyland-Yutani's research facility, [name]. The [MAIN_SHIP_NAME] has been dispatched to assist.", "[MAIN_SHIP_NAME]")
+
+ addtimer(CALLBACK(src, PROC_REF(ares_online)), 5 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(map_announcement)), 20 SECONDS)
return ..()
+/datum/game_mode/infection/proc/map_announcement()
+ if(SSmapping.configs[GROUND_MAP].infection_announce_text)
+ var/rendered_announce_text = replacetext(SSmapping.configs[GROUND_MAP].infection_announce_text, "###SHIPNAME###", MAIN_SHIP_NAME)
+ marine_announcement(rendered_announce_text, "[MAIN_SHIP_NAME]")
+ else if(SSmapping.configs[GROUND_MAP].announce_text) //if we missed a infection text for above, or just don't need a special one, we just use default announcement
+ var/rendered_announce_text = replacetext(SSmapping.configs[GROUND_MAP].announce_text, "###SHIPNAME###", MAIN_SHIP_NAME)
+ marine_announcement(rendered_announce_text, "[MAIN_SHIP_NAME]")
+
/datum/game_mode/infection/proc/initialize_post_survivor_list()
if(synth_survivor)
transform_survivor(synth_survivor, TRUE)
for(var/datum/mind/survivor in survivors)
if(transform_survivor(survivor) == 1)
survivors -= survivor
- tell_survivor_story()
/datum/game_mode/infection/can_start()
initialize_starting_survivor_list()
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 8017056c682d..5b007d275c32 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -103,17 +103,18 @@ var/global/cas_tracking_id_increment = 0 //this var used to assign unique tracki
for(var/mob/new_player/np in GLOB.new_player_list)
np.new_player_panel_proc()
+ round_time_lobby = world.time
log_game("Round started at [time2text(world.realtime)]")
if(SSticker.mode)
log_game("Game mode set to [SSticker.mode]")
log_game("Server IP: [world.internet_address]:[world.port]")
- return 1
+ return TRUE
///process()
///Called by the gameticker
/datum/game_mode/process()
- return 0
+ return FALSE
/datum/game_mode/proc/check_finished() //to be called by ticker
diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm
index 54f79ce32881..425a09afb591 100644
--- a/code/game/jobs/access.dm
+++ b/code/game/jobs/access.dm
@@ -156,6 +156,7 @@
ACCESS_MARINE_KITCHEN,
ACCESS_MARINE_SYNTH,
ACCESS_PRESS,
+ ACCESS_MARINE_ASO,
)
/proc/get_all_weyland_access()
diff --git a/code/game/jobs/job/civilians/other/mess_seargent.dm b/code/game/jobs/job/civilians/other/mess_seargent.dm
index 37e0b01ad711..615df24ecb47 100644
--- a/code/game/jobs/job/civilians/other/mess_seargent.dm
+++ b/code/game/jobs/job/civilians/other/mess_seargent.dm
@@ -4,7 +4,7 @@
spawn_positions = 1
selection_class = "job_ot"
flags_startup_parameters = ROLE_ADD_TO_DEFAULT
- supervisors = "the acting commanding officer"
+ supervisors = "the auxiliary support officer"
gear_preset = /datum/equipment_preset/uscm_ship/chef
entry_message_body = "Your job is to service the marines with excellent food, drinks and entertaining the shipside crew when needed. You have a lot of freedom and it is up to you, to decide what to do with it. Good luck!"
diff --git a/code/game/jobs/job/command/auxiliary/auxiliary_support_officer.dm b/code/game/jobs/job/command/auxiliary/auxiliary_support_officer.dm
new file mode 100644
index 000000000000..262ba271edbf
--- /dev/null
+++ b/code/game/jobs/job/command/auxiliary/auxiliary_support_officer.dm
@@ -0,0 +1,26 @@
+/datum/job/command/auxiliary_officer
+ title = JOB_AUXILIARY_OFFICER
+ total_positions = 1
+ spawn_positions = 1
+ allow_additional = TRUE
+ flags_startup_parameters = ROLE_ADD_TO_DEFAULT
+ gear_preset = /datum/equipment_preset/uscm_ship/auxiliary_officer
+ entry_message_body = "Your job is to oversee the hangar crew, the intel officers, the engineering department, and requisition department. You have many responsibilities and a few plates to keep spinning but your subordinates are mostly self-reliant. Assist where you can and make sure command personnel are confident the auxiliary departments are operating at peak efficiency."
+
+AddTimelock(/datum/job/command/auxiliary_officer, list(
+ JOB_SQUAD_ROLES = 5 HOURS,
+ JOB_REQUISITION_ROLES = 5 HOURS,
+ JOB_ENGINEER_ROLES = 5 HOURS,
+ JOB_AUXILIARY_ROLES = 5 HOURS,
+))
+
+/obj/effect/landmark/start/auxiliary_officer
+ name = JOB_AUXILIARY_OFFICER
+ job = /datum/job/command/auxiliary_officer
+
+/datum/timelock/auxiliary
+ name = "Auxiliary Roles"
+
+/datum/timelock/auxiliary/New(name, time_required, list/roles)
+ . = ..()
+ src.roles = JOB_AUXILIARY_ROLES_LIST
diff --git a/code/game/jobs/job/command/auxiliary/crew_chief.dm b/code/game/jobs/job/command/auxiliary/crew_chief.dm
index 3bf7f0bcd33e..5f846bf6581e 100644
--- a/code/game/jobs/job/command/auxiliary/crew_chief.dm
+++ b/code/game/jobs/job/command/auxiliary/crew_chief.dm
@@ -4,6 +4,7 @@
spawn_positions = 2
allow_additional = TRUE
scaled = TRUE
+ supervisors = "the pilot officers"
flags_startup_parameters = ROLE_ADD_TO_DEFAULT
gear_preset = /datum/equipment_preset/uscm_ship/dcc
entry_message_body = "Your job is to assist the pilot officer maintain the ship's dropship. You have authority only on the dropship, but you are expected to maintain order, as not to disrupt the pilot."
diff --git a/code/game/jobs/job/command/auxiliary/intel.dm b/code/game/jobs/job/command/auxiliary/intel.dm
index f30bb62e8d72..10b8381c417e 100644
--- a/code/game/jobs/job/command/auxiliary/intel.dm
+++ b/code/game/jobs/job/command/auxiliary/intel.dm
@@ -5,6 +5,7 @@
spawn_positions = 3
allow_additional = 1
scaled = 1
+ supervisors = "the auxiliary support officer"
flags_startup_parameters = ROLE_ADD_TO_DEFAULT
gear_preset = "USCM Intelligence Officer (IO) (Cryo)"
entry_message_body = "Your job is to assist the marines in collecting intelligence related to the current operation to better inform command of their opposition. You are in charge of gathering any data disks, folders, and notes you may find on the operational grounds and decrypt them to grant the USCM additional resources."
diff --git a/code/game/jobs/job/command/auxiliary/pilot.dm b/code/game/jobs/job/command/auxiliary/pilot.dm
index b7c9d15fa366..57495fe8be28 100644
--- a/code/game/jobs/job/command/auxiliary/pilot.dm
+++ b/code/game/jobs/job/command/auxiliary/pilot.dm
@@ -4,6 +4,7 @@
spawn_positions = 2
allow_additional = TRUE
scaled = TRUE
+ supervisors = "the auxiliary support officer"
flags_startup_parameters = ROLE_ADD_TO_DEFAULT
gear_preset = /datum/equipment_preset/uscm_ship/po
entry_message_body = "Your job is to fly, protect, and maintain the ship's dropship. While you are an officer, your authority is limited to the dropship, where you have authority over the enlisted personnel. If you are not piloting, there is an autopilot fallback for command, but don't leave the dropship without reason."
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index b860667486be..234902e11d22 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -288,6 +288,8 @@
join_turf = get_turf(pick(GLOB.spawns_by_job[type]))
else if(assigned_squad && GLOB.latejoin_by_squad[assigned_squad])
join_turf = get_turf(pick(GLOB.latejoin_by_squad[assigned_squad]))
+ else if(GLOB.latejoin_by_job[title])
+ join_turf = get_turf(pick(GLOB.latejoin_by_job[title]))
else
join_turf = get_turf(pick(GLOB.latejoin))
human.forceMove(join_turf)
diff --git a/code/game/jobs/job/logistics/cargo/chief_req.dm b/code/game/jobs/job/logistics/cargo/chief_req.dm
index ffdb04da3421..3b6fb7262a80 100644
--- a/code/game/jobs/job/logistics/cargo/chief_req.dm
+++ b/code/game/jobs/job/logistics/cargo/chief_req.dm
@@ -1,9 +1,8 @@
-//Requisitions Officer
/datum/job/logistics/requisition
title = JOB_CHIEF_REQUISITION
- selection_class = "job_ro"
+ selection_class = "job_qm"
flags_startup_parameters = ROLE_ADD_TO_DEFAULT
- gear_preset = /datum/equipment_preset/uscm_ship/ro
+ gear_preset = /datum/equipment_preset/uscm_ship/qm
entry_message_body = "Your job is to dispense supplies to the marines, including weapon attachments. Your cargo techs can help you out, but you have final say in your department. Make sure they're not goofing off. While you may request paperwork for supplies, do not go out of your way to screw with marines, unless you want to get deposed. A happy ship is a well-functioning ship."
AddTimelock(/datum/job/logistics/requisition, list(
diff --git a/code/game/jobs/job/logistics/logistics.dm b/code/game/jobs/job/logistics/logistics.dm
index 6c0a2d0447e1..ef4364354067 100644
--- a/code/game/jobs/job/logistics/logistics.dm
+++ b/code/game/jobs/job/logistics/logistics.dm
@@ -1,5 +1,5 @@
/datum/job/logistics
- supervisors = "the acting commanding officer"
+ supervisors = "the auxiliary support officer"
total_positions = 1
spawn_positions = 1
diff --git a/code/game/jobs/role_authority.dm b/code/game/jobs/role_authority.dm
index e7697d54f0de..d1934c597da7 100644
--- a/code/game/jobs/role_authority.dm
+++ b/code/game/jobs/role_authority.dm
@@ -553,6 +553,8 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou
var/turf/late_join_turf
if(GLOB.latejoin_by_squad[assigned_squad])
late_join_turf = get_turf(pick(GLOB.latejoin_by_squad[assigned_squad]))
+ else if(GLOB.latejoin_by_job[J.title])
+ late_join_turf = get_turf(pick(GLOB.latejoin_by_job[J.title]))
else
late_join_turf = get_turf(pick(GLOB.latejoin))
H.forceMove(late_join_turf)
diff --git a/code/game/machinery/ARES/ARES.dm b/code/game/machinery/ARES/ARES.dm
index f8a7351d123e..de4140ef1b81 100644
--- a/code/game/machinery/ARES/ARES.dm
+++ b/code/game/machinery/ARES/ARES.dm
@@ -137,7 +137,7 @@
/// The current deleted chat log of 1:1 conversations being read.
var/list/deleted_1to1 = list()
- /// Holds all (/datum/ares_record/announcement)s and (/datum/ares_record/security/security_alert)s
+ /// Holds all (/datum/ares_record/announcement)s
var/list/records_announcement = list()
/// Holds all (/datum/ares_record/bioscan)s
var/list/records_bioscan = list()
@@ -151,8 +151,8 @@
var/list/records_asrs = list()
/// Holds all (/datum/ares_record/security)s and (/datum/ares_record/antiair)s
var/list/records_security = list()
- /// Is nuke request usable or not? (Nuke request is not currently coded to work.)
- var/nuke_available = FALSE
+ /// Is nuke request usable or not?
+ var/nuke_available = TRUE
COOLDOWN_DECLARE(ares_distress_cooldown)
diff --git a/code/game/machinery/ARES/ARES_procs.dm b/code/game/machinery/ARES/ARES_procs.dm
index 8b5f77fa6d84..c78a0131b7f1 100644
--- a/code/game/machinery/ARES/ARES_procs.dm
+++ b/code/game/machinery/ARES/ARES_procs.dm
@@ -223,7 +223,7 @@ GLOBAL_LIST_INIT(maintenance_categories, list(
data["records_announcement"] = logged_announcements
var/list/logged_alerts = list()
- for(var/datum/ares_record/security/security_alert as anything in records_announcement)
+ for(var/datum/ares_record/security/security_alert as anything in records_security)
if(!istype(security_alert))
continue
var/list/current_alert = list()
@@ -457,6 +457,10 @@ GLOBAL_LIST_INIT(maintenance_categories, list(
new_title = "[record.title] at [record.time]"
new_details = record.details
records_announcement -= record
+ if(ARES_RECORD_SECURITY)
+ new_title = "[record.title] at [record.time]"
+ new_details = record.details
+ records_security -= record
if(ARES_RECORD_BIOSCAN)
new_title = "[record.title] at [record.time]"
new_details = record.details
@@ -576,15 +580,22 @@ GLOBAL_LIST_INIT(maintenance_categories, list(
playsound(src, 'sound/machines/buzz-two.ogg', 15, 1)
return FALSE
if(security_level == SEC_LEVEL_DELTA || SSticker.mode.is_in_endgame)
- to_chat(usr, SPAN_WARNING("The mission has failed catastrophically, what do you want a nuke for!"))
+ to_chat(usr, SPAN_WARNING("The mission has failed catastrophically, what do you want a nuke for?!"))
playsound(src, 'sound/machines/buzz-two.ogg', 15, 1)
return FALSE
-
+ var/reason = tgui_input_text(usr, "Please enter reason nuclear ordnance is required.", "Reason for Nuclear Ordnance")
+ if(!reason)
+ return FALSE
for(var/client/admin in GLOB.admins)
if((R_ADMIN|R_MOD) & admin.admin_holder.rights)
playsound_client(admin,'sound/effects/sos-morse-code.ogg',10)
- message_admins("[key_name(usr)] has requested use of Nuclear Ordnance (via ARES)! [CC_MARK(usr)] (APPROVE) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]")
- to_chat(usr, SPAN_NOTICE("A nuclear ordnance request has been sent to USCM High Command."))
+ message_admins("[key_name(usr)] has requested use of Nuclear Ordnance (via ARES)! Reason: [reason] [CC_MARK(usr)] (APPROVE) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]")
+ to_chat(usr, SPAN_NOTICE("A nuclear ordnance request has been sent to USCM High Command for the following reason: [reason]"))
+ if(ares_can_log())
+ link.log_ares_security("Nuclear Ordnance Request", "[last_login] has sent a request for nuclear ordnance for the following reason: [reason]")
+ if(ares_can_interface())
+ ai_silent_announcement("[last_login] has sent a request for nuclear ordnance to USCM High Command.", ".V")
+ ai_silent_announcement("Reason given: [reason].", ".V")
COOLDOWN_START(src, ares_nuclear_cooldown, COOLDOWN_COMM_DESTRUCT)
return TRUE
// ------ End ARES Interface UI ------ //
diff --git a/code/game/machinery/computer/almayer_control.dm b/code/game/machinery/computer/almayer_control.dm
index b2a931224464..7d63a2e8c3af 100644
--- a/code/game/machinery/computer/almayer_control.dm
+++ b/code/game/machinery/computer/almayer_control.dm
@@ -1,16 +1,3 @@
-#define STATE_DEFAULT 1
-#define STATE_EVACUATION 2
-#define STATE_EVACUATION_CANCEL 3
-#define STATE_DISTRESS 4
-#define STATE_DESTROY 5
-#define STATE_DEFCONLIST 6
-
-#define STATE_MESSAGELIST 7
-#define STATE_VIEWMESSAGE 8
-#define STATE_DELMESSAGE 9
-
-
-
#define COMMAND_SHIP_ANNOUNCE "Command Ship Announcement"
/obj/structure/machinery/computer/almayer_control
@@ -21,273 +8,149 @@
unslashable = TRUE
unacidable = TRUE
- var/state = STATE_DEFAULT
-
- var/is_announcement_active = TRUE
-
- var/cooldown_request = 0
- var/cooldown_destruct = 0
- var/cooldown_central = 0
+ /// requesting a distress beacon
+ COOLDOWN_DECLARE(cooldown_request)
+ /// requesting evac
+ COOLDOWN_DECLARE(cooldown_destruct)
+ /// messaging HC (admins)
+ COOLDOWN_DECLARE(cooldown_central)
+ /// making a ship announcement
+ COOLDOWN_DECLARE(cooldown_message)
var/list/messagetitle = list()
var/list/messagetext = list()
- var/currmsg = 0
- var/aicurrmsg = 0
/obj/structure/machinery/computer/almayer_control/attack_remote(mob/user as mob)
return attack_hand(user)
/obj/structure/machinery/computer/almayer_control/attack_hand(mob/user as mob)
- if(..() || !allowed(user) || inoperable())
+ if(..() || inoperable())
return
+ if(!allowed(user))
+ to_chat(usr, SPAN_WARNING("Access denied."))
+ return FALSE
+
if(!istype(loc.loc, /area/almayer/command/cic)) //Has to be in the CIC. Can also be a generic CIC area to communicate, if wanted.
to_chat(usr, SPAN_WARNING("Unable to establish a connection."))
return FALSE
- ui_interact(user)
-
-/obj/structure/machinery/computer/almayer_control/ui_interact(mob/user as mob)
- user.set_interaction(src)
+ tgui_interact(user)
- var/dat = "
Almayer Control Console"
+// tgui boilerplate \\
- if(EvacuationAuthority.evac_status == EVACUATION_STATUS_INITIATING)
- dat += "Evacuation in Progress\n \nETA: [EvacuationAuthority.get_status_panel_eta()] "
-
- switch(state)
- if(STATE_DEFAULT)
- dat += "Alert Level: [get_security_level()] "
- dat += " [is_announcement_active ? "Make a ship announcement" : "*Unavailable*"]"
- dat += GLOB.admins.len > 0 ? " Send a message to USCM" : " USCM communication offline"
- dat += " Award a medal"
- dat += " "
- dat += " "
-
-
- dat += " Message list"
- dat += " Send Distress Beacon"
- dat += " Activate Self-Destruct"
- switch(EvacuationAuthority.evac_status)
- if(EVACUATION_STATUS_STANDING_BY)
- dat += " Initiate emergency evacuation"
- if(EVACUATION_STATUS_INITIATING)
- dat += " Cancel emergency evacuation"
-
- if(STATE_EVACUATION)
- dat += "Are you sure you want to evacuate the [MAIN_SHIP_NAME]? Confirm"
-
- if(STATE_EVACUATION_CANCEL)
- dat += "Are you sure you want to cancel the evacuation of the [MAIN_SHIP_NAME]? Confirm"
-
- if(STATE_DISTRESS)
- dat += "Are you sure you want to trigger a distress signal? The signal can be picked up by anyone listening, friendly or not. Confirm"
-
- if(STATE_DESTROY)
- dat += "Are you sure you want to trigger the self-destruct? This would mean abandoning ship. Confirm"
-
- if(STATE_MESSAGELIST)
- dat += "Messages:"
- for(var/i = 1; i<=messagetitle.len; i++)
- dat += " [messagetitle[i]]"
-
- if(STATE_VIEWMESSAGE)
- if (currmsg)
- dat += "[messagetitle[currmsg]]
[messagetext[currmsg]]"
- dat += "
Delete"
- else
- state = STATE_MESSAGELIST
- attack_hand(user)
- return FALSE
+/obj/structure/machinery/computer/almayer_control/tgui_interact(mob/user, datum/tgui/ui, datum/ui_state/state)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "AlmayerControl", "[name]")
+ ui.open()
- if(STATE_DELMESSAGE)
- if (currmsg)
- dat += "Are you sure you want to delete this message? OK|Cancel"
- else
- state = STATE_MESSAGELIST
- attack_hand(user)
- return FALSE
+/obj/structure/machinery/computer/almayer_control/ui_status(mob/user, datum/ui_state/state)
+ . = ..()
+ if(!allowed(user))
+ return UI_CLOSE
+ if(!operable())
+ return UI_CLOSE
- dat += " [(state != STATE_DEFAULT) ? "Main Menu|" : ""]Close"
+/obj/structure/machinery/computer/almayer_control/ui_state(mob/user)
+ return GLOB.not_incapacitated_and_adjacent_strict_state
- show_browser(user, dat, name, "almayer_control")
- onclose(user, "almayer_control")
+// tgui data \\
-/obj/structure/machinery/computer/almayer_control/Topic(href, href_list)
- if(..())
- return FALSE
-
- usr.set_interaction(src)
- var/datum/ares_link/link = GLOB.ares_link
- switch(href_list["operation"])
- if("main")
- state = STATE_DEFAULT
-
- if("ship_announce")
- if(!is_announcement_active)
- to_chat(usr, SPAN_WARNING("Please allow at least [COOLDOWN_COMM_MESSAGE*0.1] second\s to pass between announcements."))
- return FALSE
- var/input = stripped_multiline_input(usr, "Please write a message to announce to the station crew.", "Priority Announcement", "")
- if(!input || !is_announcement_active || !(usr in view(1,src)))
- return FALSE
-
- is_announcement_active = FALSE
-
- var/signed = null
- if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
- var/obj/item/card/id/id = H.wear_id
- if(istype(id))
- var/paygrade = get_paygrades(id.paygrade, FALSE, H.gender)
- signed = "[paygrade] [id.registered_name]"
+/obj/structure/machinery/computer/almayer_control/ui_static_data(mob/user)
+ var/list/data = list()
- shipwide_ai_announcement(input, COMMAND_SHIP_ANNOUNCE, signature = signed)
- addtimer(CALLBACK(src, PROC_REF(reactivate_announcement), usr), COOLDOWN_COMM_MESSAGE)
- message_admins("[key_name(usr)] has made a shipwide annoucement.")
- log_announcement("[key_name(usr)] has announced the following to the ship: [input]")
+ data["cooldown_request"] = COOLDOWN_COMM_REQUEST
+ data["cooldown_destruct"] = COOLDOWN_COMM_DESTRUCT
+ data["cooldown_central"] = COOLDOWN_COMM_CENTRAL
+ data["cooldown_message"] = COOLDOWN_COMM_MESSAGE
+ data["distresstimelock"] = DISTRESS_TIME_LOCK
+ return data
- if("evacuation_start")
- if(state == STATE_EVACUATION)
- if(security_level < SEC_LEVEL_RED)
- to_chat(usr, SPAN_WARNING("The ship must be under red alert in order to enact evacuation procedures."))
- return FALSE
+/obj/structure/machinery/computer/almayer_control/ui_data(mob/user)
+ var/list/data = list()
+ var/list/messages = list()
- if(EvacuationAuthority.flags_scuttle & FLAGS_EVACUATION_DENY)
- to_chat(usr, SPAN_WARNING("The USCM has placed a lock on deploying the evacuation pods."))
- return FALSE
+ data["alert_level"] = security_level
- if(!EvacuationAuthority.initiate_evacuation())
- to_chat(usr, SPAN_WARNING("You are unable to initiate an evacuation procedure right now!"))
- return FALSE
+ data["time_request"] = cooldown_request
+ data["time_destruct"] = cooldown_destruct
+ data["time_central"] = cooldown_central
+ data["time_message"] = cooldown_message
- log_game("[key_name(usr)] has called for an emergency evacuation.")
- message_admins("[key_name_admin(usr)] has called for an emergency evacuation.")
- link.log_ares_security("Initiate Evacuation", "[usr] has called for an emergency evacuation.")
- return TRUE
+ data["worldtime"] = world.time
- state = STATE_EVACUATION
+ data["evac_status"] = EvacuationAuthority.evac_status
+ if(EvacuationAuthority.evac_status == EVACUATION_STATUS_INITIATING)
+ data["evac_eta"] = EvacuationAuthority.get_status_panel_eta()
- if("evacuation_cancel")
- if(state == STATE_EVACUATION_CANCEL)
- if(!EvacuationAuthority.cancel_evacuation())
- to_chat(usr, SPAN_WARNING("You are unable to cancel the evacuation right now!"))
- return FALSE
+ if(!messagetitle.len)
+ data["messages"] = null
+ else
+ for(var/i in 1 to length(messagetitle))
+ var/list/messagedata = list(list(
+ "title" = messagetitle[i],
+ "text" = messagetext[i],
+ "number" = i
+ ))
+ messages += messagedata
- spawn(35)//some time between AI announcements for evac cancel and SD cancel.
- if(EvacuationAuthority.evac_status == EVACUATION_STATUS_STANDING_BY)//nothing changed during the wait
- //if the self_destruct is active we try to cancel it (which includes lowering alert level to red)
- if(!EvacuationAuthority.cancel_self_destruct(1))
- //if SD wasn't active (likely canceled manually in the SD room), then we lower the alert level manually.
- set_security_level(SEC_LEVEL_RED, TRUE) //both SD and evac are inactive, lowering the security level.
+ data["messages"] = messages
- log_game("[key_name(usr)] has canceled the emergency evacuation.")
- message_admins("[key_name_admin(usr)] has canceled the emergency evacuation.")
- link.log_ares_security("Cancel Evacuation", "[usr] has cancelled the emergency evacuation.")
- return TRUE
+ return data
- state = STATE_EVACUATION_CANCEL
+// end tgui data \\
- if("distress")
- if(state == STATE_DISTRESS)
- if(world.time < DISTRESS_TIME_LOCK)
- to_chat(usr, SPAN_WARNING("The distress beacon cannot be launched this early in the operation. Please wait another [time_left_until(DISTRESS_TIME_LOCK, world.time, 1 MINUTES)] minutes before trying again."))
- return FALSE
+// tgui interact \\
- if(!SSticker.mode)
- return FALSE //Not a game mode?
+/obj/structure/machinery/computer/almayer_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
- if(SSticker.mode.force_end_at == 0)
- to_chat(usr, SPAN_WARNING("ARES has denied your request for operational security reasons."))
- return FALSE
+ switch(action)
+ if("award")
+ print_medal(usr, src)
+ . = TRUE
- if(world.time < cooldown_request + COOLDOWN_COMM_REQUEST)
- to_chat(usr, SPAN_WARNING("The distress beacon has recently broadcast a message. Please wait."))
- return FALSE
+ // evac stuff start \\
- if(security_level == SEC_LEVEL_DELTA)
- to_chat(usr, SPAN_WARNING("The ship is already undergoing self-destruct procedures!"))
- return FALSE
+ if("evacuation_start")
+ if(security_level < SEC_LEVEL_RED)
+ to_chat(usr, SPAN_WARNING("The ship must be under red alert in order to enact evacuation procedures."))
+ return FALSE
- for(var/client/C in GLOB.admins)
- if((R_ADMIN|R_MOD) & C.admin_holder.rights)
- C << 'sound/effects/sos-morse-code.ogg'
- message_admins("[key_name(usr)] has requested a Distress Beacon! [CC_MARK(usr)] (SEND) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]")
- to_chat(usr, SPAN_NOTICE("A distress beacon request has been sent to USCM Central Command."))
+ if(EvacuationAuthority.flags_scuttle & FLAGS_EVACUATION_DENY)
+ to_chat(usr, SPAN_WARNING("The USCM has placed a lock on deploying the evacuation pods."))
+ return FALSE
- cooldown_request = world.time
- return TRUE
+ if(!EvacuationAuthority.initiate_evacuation())
+ to_chat(usr, SPAN_WARNING("You are unable to initiate an evacuation procedure right now!"))
+ return FALSE
- state = STATE_DISTRESS
+ log_game("[key_name(usr)] has called for an emergency evacuation.")
+ message_admins("[key_name_admin(usr)] has called for an emergency evacuation.")
+ var/datum/ares_link/link = GLOB.ares_link
+ link.log_ares_security("Initiate Evacuation", "[usr] has called for an emergency evacuation.")
+ . = TRUE
- if("destroy")
- if(state == STATE_DESTROY)
- //Comment to test
- if(world.time < DISTRESS_TIME_LOCK)
- to_chat(usr, SPAN_WARNING("The self-destruct cannot be activated this early in the operation. Please wait another [time_left_until(DISTRESS_TIME_LOCK, world.time, 1 MINUTES)] minutes before trying again."))
- return FALSE
-
- if(!SSticker.mode)
- return FALSE //Not a game mode?
-
- if(SSticker.mode.force_end_at == 0)
- to_chat(usr, SPAN_WARNING("ARES has denied your request for operational security reasons."))
- return FALSE
-
- if(world.time < cooldown_destruct + COOLDOWN_COMM_DESTRUCT)
- to_chat(usr, SPAN_WARNING("A self-destruct request has already been sent to high command. Please wait."))
- return FALSE
-
- if(get_security_level() == "delta")
- to_chat(usr, SPAN_WARNING("The [MAIN_SHIP_NAME]'s self-destruct is already activated."))
- return FALSE
-
- for(var/client/C in GLOB.admins)
- if((R_ADMIN|R_MOD) & C.admin_holder.rights)
- C << 'sound/effects/sos-morse-code.ogg'
- message_admins("[key_name(usr)] has requested Self-Destruct! [CC_MARK(usr)] (GRANT) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]")
- to_chat(usr, SPAN_NOTICE("A self-destruct request has been sent to USCM Central Command."))
- cooldown_destruct = world.time
- return TRUE
-
- state = STATE_DESTROY
-
- if("messagelist")
- currmsg = 0
- state = STATE_MESSAGELIST
-
- if("viewmessage")
- state = STATE_VIEWMESSAGE
- if (!currmsg)
- if(href_list["message-num"]) currmsg = text2num(href_list["message-num"])
- else state = STATE_MESSAGELIST
+ if("evacuation_cancel")
+ if(!EvacuationAuthority.cancel_evacuation())
+ to_chat(usr, SPAN_WARNING("You are unable to cancel the evacuation right now!"))
+ return FALSE
- if("delmessage")
- state = (currmsg) ? STATE_DELMESSAGE : STATE_MESSAGELIST
-
- if("delmessage2")
- if(currmsg)
- var/title = messagetitle[currmsg]
- var/text = messagetext[currmsg]
- messagetitle.Remove(title)
- messagetext.Remove(text)
- if(currmsg == aicurrmsg) aicurrmsg = 0
- currmsg = 0
- state = STATE_MESSAGELIST
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/structure/machinery/computer/almayer_control, cancel_evac)), 4 SECONDS)
- if("messageUSCM")
- if(world.time < cooldown_central + COOLDOWN_COMM_CENTRAL)
- to_chat(usr, SPAN_WARNING("Arrays recycling. Please stand by."))
- return FALSE
- var/input = stripped_input(usr, "Please choose a message to transmit to USCM. Please be aware that this process is very expensive, and abuse will lead to termination. Transmission does not guarantee a response. There is a small delay before you may send another message. Be clear and concise.", "To abort, send an empty message.", "")
- if(!input || !(usr in view(1,src)) || world.time < cooldown_central + COOLDOWN_COMM_CENTRAL) return FALSE
+ log_game("[key_name(usr)] has canceled the emergency evacuation.")
+ message_admins("[key_name_admin(usr)] has canceled the emergency evacuation.")
+ var/datum/ares_link/link = GLOB.ares_link
+ link.log_ares_security("Cancel Evacuation", "[usr] has cancelled the emergency evacuation.")
+ . = TRUE
- high_command_announce(input, usr)
- to_chat(usr, SPAN_NOTICE("Message transmitted."))
- log_announcement("[key_name(usr)] has made an USCM announcement: [input]")
- cooldown_central = world.time
+ // evac stuff end \\
- if("changeseclevel")
+ if("change_sec_level")
var/list/alert_list = list(num2seclevel(SEC_LEVEL_GREEN), num2seclevel(SEC_LEVEL_BLUE))
switch(security_level)
if(SEC_LEVEL_GREEN)
@@ -302,27 +165,125 @@
return
set_security_level(seclevel2num(level_selected))
-
log_game("[key_name(usr)] has changed the security level to [get_security_level()].")
message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].")
+ var/datum/ares_link/link = GLOB.ares_link
+ link.log_ares_security("Security Level Update", "[usr] has changed the security level to [get_security_level()].")
+ . = TRUE
- if("award")
- print_medal(usr, src)
+ if("messageUSCM")
+ if(!COOLDOWN_FINISHED(src, cooldown_central))
+ to_chat(usr, SPAN_WARNING("Arrays are re-cycling. Please stand by."))
+ return FALSE
+ var/input = stripped_input(usr, "Please choose a message to transmit to USCM. Please be aware that this process is very expensive, and abuse will lead to termination. Transmission does not guarantee a response. There is a small delay before you may send another message. Be clear and concise.", "To abort, send an empty message.", "")
+ if(!input || !(usr in view(1,src)) || !COOLDOWN_FINISHED(src, cooldown_central))
+ return FALSE
+
+ high_command_announce(input, usr)
+ to_chat(usr, SPAN_NOTICE("Message transmitted."))
+ log_announcement("[key_name(usr)] has made an USCM announcement: [input]")
+ COOLDOWN_START(src, cooldown_central, COOLDOWN_COMM_CENTRAL)
+ . = TRUE
+
+ if("ship_announce")
+ if(!COOLDOWN_FINISHED(src, cooldown_message))
+ to_chat(usr, SPAN_WARNING("Please allow at least [COOLDOWN_TIMELEFT(src, cooldown_message)/10] second\s to pass between announcements."))
+ return FALSE
+ var/input = stripped_multiline_input(usr, "Please write a message to announce to the station crew.", "Priority Announcement", "")
+ if(!input || !COOLDOWN_FINISHED(src, cooldown_message) || !(usr in view(1,src)))
+ return FALSE
+
+ var/signed = null
+ if(ishuman(usr))
+ var/mob/living/carbon/human/human_user = usr
+ var/obj/item/card/id/id = human_user.wear_id
+ if(istype(id))
+ var/paygrade = get_paygrades(id.paygrade, FALSE, human_user.gender)
+ signed = "[paygrade] [id.registered_name]"
+
+ COOLDOWN_START(src, cooldown_message, COOLDOWN_COMM_MESSAGE)
+ shipwide_ai_announcement(input, COMMAND_SHIP_ANNOUNCE, signature = signed)
+ message_admins("[key_name(usr)] has made a shipwide annoucement.")
+ log_announcement("[key_name(usr)] has announced the following to the ship: [input]")
+ . = TRUE
+
+ if("distress")
+ if(world.time < DISTRESS_TIME_LOCK)
+ to_chat(usr, SPAN_WARNING("The distress beacon cannot be launched this early in the operation. Please wait another [time_left_until(DISTRESS_TIME_LOCK, world.time, 1 MINUTES)] minutes before trying again."))
+ return FALSE
+
+ if(!SSticker.mode)
+ return FALSE //Not a game mode?
+
+ if(SSticker.mode.force_end_at == 0)
+ to_chat(usr, SPAN_WARNING("ARES has denied your request for operational security reasons."))
+ return FALSE
+
+ if(!COOLDOWN_FINISHED(src, cooldown_request))
+ to_chat(usr, SPAN_WARNING("The distress beacon has recently broadcast a message. Please wait."))
+ return FALSE
- updateUsrDialog()
+ if(security_level == SEC_LEVEL_DELTA)
+ to_chat(usr, SPAN_WARNING("The ship is already undergoing self-destruct procedures!"))
+ return FALSE
+
+ for(var/client/admin_client as anything in GLOB.admins)
+ if((R_ADMIN|R_MOD) & admin_client.admin_holder.rights)
+ admin_client << 'sound/effects/sos-morse-code.ogg'
+ message_admins("[key_name(usr)] has requested a Distress Beacon! [CC_MARK(usr)] (SEND) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]")
+ to_chat(usr, SPAN_NOTICE("A distress beacon request has been sent to USCM Central Command."))
+
+ COOLDOWN_START(src, cooldown_request, COOLDOWN_COMM_REQUEST)
+ . = TRUE
+
+ // sd \\
+
+ if("destroy")
+ if(world.time < DISTRESS_TIME_LOCK)
+ to_chat(usr, SPAN_WARNING("The self-destruct cannot be activated this early in the operation. Please wait another [time_left_until(DISTRESS_TIME_LOCK, world.time, 1 MINUTES)] minutes before trying again."))
+ return FALSE
+
+ if(!SSticker.mode)
+ return FALSE //Not a game mode?
+
+ if(SSticker.mode.force_end_at == 0)
+ to_chat(usr, SPAN_WARNING("ARES has denied your request for operational security reasons."))
+ return FALSE
+
+ if(!COOLDOWN_FINISHED(src, cooldown_destruct))
+ to_chat(usr, SPAN_WARNING("A self-destruct request has already been sent to high command. Please wait."))
+ return FALSE
+
+ if(get_security_level() == "delta")
+ to_chat(usr, SPAN_WARNING("The [MAIN_SHIP_NAME]'s self-destruct is already activated."))
+ return FALSE
+
+ for(var/client/admin_client as anything in GLOB.admins)
+ if((R_ADMIN|R_MOD) & admin_client.admin_holder.rights)
+ admin_client << 'sound/effects/sos-morse-code.ogg'
+ message_admins("[key_name(usr)] has requested Self-Destruct! [CC_MARK(usr)] (GRANT) (DENY) [ADMIN_JMP_USER(usr)] [CC_REPLY(usr)]")
+ to_chat(usr, SPAN_NOTICE("A self-destruct request has been sent to USCM Central Command."))
+ COOLDOWN_START(src, cooldown_destruct, COOLDOWN_COMM_DESTRUCT)
+ . = TRUE
+
+ if("delmessage")
+ var/number_of_message = params["number"]
+ if(!number_of_message)
+ return FALSE
+ var/title = messagetitle[number_of_message]
+ var/text = messagetext[number_of_message]
+ messagetitle.Remove(title)
+ messagetext.Remove(text)
+ . = TRUE
-/obj/structure/machinery/computer/almayer_control/proc/reactivate_announcement(mob/user)
- is_announcement_active = TRUE
- updateUsrDialog()
+// end tgui interact \\
-#undef STATE_DEFAULT
-#undef STATE_EVACUATION
-#undef STATE_EVACUATION_CANCEL
-#undef STATE_DISTRESS
-#undef STATE_DESTROY
-#undef STATE_DEFCONLIST
+// end tgui \\
-#undef STATE_MESSAGELIST
-#undef STATE_VIEWMESSAGE
-#undef STATE_DELMESSAGE
+/obj/structure/machinery/computer/almayer_control/proc/cancel_evac()
+ if(EvacuationAuthority.evac_status == EVACUATION_STATUS_STANDING_BY)//nothing changed during the wait
+ //if the self_destruct is active we try to cancel it (which includes lowering alert level to red)
+ if(!EvacuationAuthority.cancel_self_destruct(1))
+ //if SD wasn't active (likely canceled manually in the SD room), then we lower the alert level manually.
+ set_security_level(SEC_LEVEL_RED, TRUE) //both SD and evac are inactive, lowering the security level.
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 9c0a227008c3..ed7335ea8778 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -193,7 +193,7 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li
//Lifted from Unity stasis.dm and refactored. ~Zuhayr
/obj/structure/machinery/cryopod/process()
- if(occupant)
+ if(occupant && !(WEAKREF(occupant) in GLOB.freed_mob_list)) //ignore freed mobs
//if occupant ghosted, time till despawn is severely shorter
if(!occupant.key && time_till_despawn == 10 MINUTES)
time_till_despawn -= 8 MINUTES
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index b538f55292c1..ef6c74a052cd 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -11,6 +11,8 @@
var/mode = 1 // 1 is injecting, 0 is taking blood.
var/obj/item/reagent_container/beaker = null
var/datum/beam/current_beam
+ //make it so that IV doesn't require power to function.
+ use_power = USE_POWER_NONE
/obj/structure/machinery/iv_drip/update_icon()
if(attached)
@@ -39,38 +41,31 @@
overlays += filling
/obj/structure/machinery/iv_drip/proc/update_beam()
- if(current_beam)
+ if(current_beam && !attached)
QDEL_NULL(current_beam)
- else if(!QDELETED(src) && attached)
+ else if(!current_beam && attached && !QDELETED(src))
current_beam = beam(attached, "iv_tube")
-/obj/structure/machinery/iv_drip/power_change()
- . = ..()
- if(stat & NOPOWER && attached)
- visible_message("\The [src] retracts its IV tube and shuts down.")
- attached.active_transfusions -= src
- attached = null
- update_beam()
- update_icon()
-
/obj/structure/machinery/iv_drip/Destroy()
attached?.active_transfusions -= src
+ attached = null
update_beam()
. = ..()
/obj/structure/machinery/iv_drip/MouseDrop(over_object, src_location, over_location)
..()
- if(inoperable())
- visible_message("\The [src] is not powered.")
- return
if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
- if(H.stat || get_dist(H, src) > 1 || H.blinded || H.lying)
+ var/mob/living/carbon/human/user = usr
+ if(user.stat || get_dist(user, src) > 1 || user.blinded || user.lying)
+ return
+
+ if(!skillcheck(user, SKILL_SURGERY, SKILL_SURGERY_NOVICE))
+ to_chat(user, SPAN_WARNING("You don't know how to [attached ? "disconnect" : "connect"] this!"))
return
if(attached)
- H.visible_message("[H] detaches \the [src] from \the [attached].", \
+ user.visible_message("[user] detaches \the [src] from \the [attached].", \
"You detach \the [src] from \the [attached].")
attached.active_transfusions -= src
attached = null
@@ -80,7 +75,7 @@
return
if(in_range(src, usr) && iscarbon(over_object) && get_dist(over_object, src) <= 1)
- H.visible_message("[H] attaches \the [src] to \the [over_object].", \
+ user.visible_message("[user] attaches \the [src] to \the [over_object].", \
"You attach \the [src] to \the [over_object].")
attached = over_object
attached.active_transfusions += src
@@ -88,27 +83,27 @@
update_icon()
start_processing()
-
-/obj/structure/machinery/iv_drip/attackby(obj/item/W, mob/living/user)
- if (istype(W, /obj/item/reagent_container))
+/obj/structure/machinery/iv_drip/attackby(obj/item/container, mob/living/user)
+ if (istype(container, /obj/item/reagent_container))
if(beaker)
to_chat(user, SPAN_WARNING("There is already a reagent container loaded!"))
return
- if((!istype(W, /obj/item/reagent_container/blood) && !istype(W, /obj/item/reagent_container/glass)) || istype(W, /obj/item/reagent_container/glass/bucket))
+ if((!istype(container, /obj/item/reagent_container/blood) && !istype(container, /obj/item/reagent_container/glass)) || istype(container, /obj/item/reagent_container/glass/bucket))
to_chat(user, SPAN_WARNING("That won't fit!"))
return
- if(user.drop_inv_item_to_loc(W, src))
- beaker = W
+ if(user.drop_inv_item_to_loc(container, src))
+ beaker = container
var/reagentnames = ""
- for(var/datum/reagent/R in beaker.reagents.reagent_list)
- reagentnames += ";[R.name]"
+
+ for(var/datum/reagent/chem in beaker.reagents.reagent_list)
+ reagentnames += ";[chem.name]"
log_admin("[key_name(user)] put a [beaker] into [src], containing [reagentnames] at ([src.loc.x],[src.loc.y],[src.loc.z]).")
- to_chat(user, "You attach \the [W] to \the [src].")
+ to_chat(user, "You attach \the [container] to \the [src].")
update_beam()
update_icon()
return
@@ -151,20 +146,20 @@
if(prob(5)) visible_message("\The [src] pings.")
return
- var/mob/living/carbon/T = attached
+ var/mob/living/carbon/patient = attached
- if(!istype(T))
+ if(!istype(patient))
return
- if(ishuman(T))
- var/mob/living/carbon/human/H = T
- if(H.species && H.species.flags & NO_BLOOD)
+ if(ishuman(patient))
+ var/mob/living/carbon/human/human_patient = patient
+ if(human_patient.species && human_patient.species.flags & NO_BLOOD)
return
// If the human is losing too much blood, beep.
- if(T.blood_volume < BLOOD_VOLUME_SAFE) if(prob(5))
+ if(patient.blood_volume < BLOOD_VOLUME_SAFE) if(prob(5))
visible_message("\The [src] beeps loudly.")
- T.take_blood(beaker,amount)
+ patient.take_blood(beaker,amount)
update_icon()
/obj/structure/machinery/iv_drip/attack_hand(mob/user as mob)
diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm
index a25293aebbd3..47d4bb20849b 100644
--- a/code/game/machinery/telecomms/presets.dm
+++ b/code/game/machinery/telecomms/presets.dm
@@ -50,7 +50,7 @@
. = ..()
if(z)
- SSminimaps.add_marker(src, z, MINIMAP_FLAG_USCM, "supply")
+ SSminimaps.add_marker(src, z, MINIMAP_FLAG_ALL, "supply")
// doesn't need power, instead uses health
/obj/structure/machinery/telecomms/relay/preset/tower/inoperable(additional_flags)
@@ -212,12 +212,27 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers)
freq_listening = list(COLONY_FREQ)
var/toggle_cooldown = 0
+ /// Tower has been taken over by xenos, is not usable
+ var/corrupted = FALSE
+
+ /// Held image for the current overlay on the tower from xeno corruption
+ var/image/corruption_image
+
+/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/Initialize()
+ . = ..()
+
+ RegisterSignal(src, COMSIG_ATOM_TURF_CHANGE, PROC_REF(register_with_turf))
+ register_with_turf()
+
/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/attack_hand(mob/user)
if(user.action_busy)
return
if(toggle_cooldown > world.time) //cooldown only to prevent spam toggling
to_chat(user, SPAN_WARNING("\The [src]'s processors are still cooling! Wait before trying to flip the switch again."))
return
+ if(corrupted)
+ to_chat(user, SPAN_WARNING("[src] is entangled in resin. Impossible to interact with."))
+ return
var/current_state = on
if(!do_after(user, 20, INTERRUPT_NO_NEEDHAND|BEHAVIOR_IMMOBILE, BUSY_ICON_FRIENDLY, src))
return
@@ -282,6 +297,84 @@ GLOBAL_LIST_EMPTY(all_static_telecomms_towers)
else
update_icon()
+/// Handles xenos corrupting the tower when weeds touch the turf it is located on
+/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/handle_xeno_acquisition(turf/weeded_turf)
+ SIGNAL_HANDLER
+
+ if(corrupted)
+ return
+
+ if(!weeded_turf.weeds)
+ return
+
+ if(weeded_turf.weeds.weed_strength < WEED_LEVEL_HIVE)
+ return
+
+ if(!weeded_turf.weeds.parent)
+ return
+
+ if(!istype(weeded_turf.weeds.parent, /obj/effect/alien/weeds/node/pylon/cluster))
+ return
+
+ if(SSticker.mode.is_in_endgame)
+ return
+
+ if(ROUND_TIME < XENO_COMM_ACQUISITION_TIME)
+ addtimer(CALLBACK(src, PROC_REF(handle_xeno_acquisition), weeded_turf), (XENO_COMM_ACQUISITION_TIME - ROUND_TIME))
+ return
+
+ var/obj/effect/alien/weeds/node/pylon/cluster/parent_node = weeded_turf.weeds.parent
+
+ var/obj/effect/alien/resin/special/cluster/cluster_parent = parent_node.resin_parent
+
+ var/list/held_children_weeds = parent_node.children
+ var/cluster_loc = cluster_parent.loc
+ var/linked_hive = cluster_parent.linked_hive
+
+ parent_node.children = list()
+
+ qdel(cluster_parent)
+
+ var/obj/effect/alien/resin/special/pylon/endgame/new_pylon = new(cluster_loc, linked_hive)
+ new_pylon.node.children = held_children_weeds
+
+ for(var/obj/effect/alien/weeds/weed in new_pylon.node.children)
+ weed.parent = new_pylon.node
+
+ RegisterSignal(new_pylon, COMSIG_PARENT_QDELETING, PROC_REF(uncorrupt))
+
+ corrupted = TRUE
+
+ corruption_image = image(icon, icon_state = "resin_growing")
+
+ flick_overlay(src, corruption_image, (2 SECONDS))
+ addtimer(CALLBACK(src, PROC_REF(switch_to_idle_corruption)), (2 SECONDS))
+
+ new_pylon.comms_relay_connection()
+
+/// Handles removing corruption effects from the comms relay
+/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/uncorrupt(datum/deleting_datum)
+ SIGNAL_HANDLER
+
+ corrupted = FALSE
+
+ overlays -= corruption_image
+
+/// Handles moving the overlay from growing to idle
+/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/switch_to_idle_corruption()
+ if(!corrupted)
+ return
+
+ corruption_image = image(icon, icon_state = "resin_idle")
+
+ overlays += corruption_image
+
+/// Handles re-registering signals on new turfs if changed
+/obj/structure/machinery/telecomms/relay/preset/tower/mapcomms/proc/register_with_turf()
+ SIGNAL_HANDLER
+
+ RegisterSignal(get_turf(src), COMSIG_WEEDNODE_GROWTH, PROC_REF(handle_xeno_acquisition))
+
/obj/structure/machinery/telecomms/relay/preset/telecomms
id = "Telecomms Relay"
autolinkers = list("relay")
diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm
index 57d0e49a58bc..bf7c4fffee65 100644
--- a/code/game/machinery/vending/cm_vending.dm
+++ b/code/game/machinery/vending/cm_vending.dm
@@ -472,7 +472,7 @@ GLOBAL_LIST_EMPTY(vending_products)
to_chat(user, SPAN_WARNING("Only specialists can take specialist sets."))
vend_fail()
return FALSE
- else if(!user.skills || user.skills.get_skill_level(SKILL_SPEC_WEAPONS) != SKILL_SPEC_ALL)
+ else if(!user.skills || user.skills.get_skill_level(SKILL_SPEC_WEAPONS) != SKILL_SPEC_TRAINED)
to_chat(user, SPAN_WARNING("You already have a specialization."))
vend_fail()
return FALSE
diff --git a/code/game/machinery/vending/vendor_types/crew/commanding_officer.dm b/code/game/machinery/vending/vendor_types/crew/commanding_officer.dm
index 3d6a48b45daa..fac4182ea5fe 100644
--- a/code/game/machinery/vending/vendor_types/crew/commanding_officer.dm
+++ b/code/game/machinery/vending/vendor_types/crew/commanding_officer.dm
@@ -42,7 +42,6 @@ GLOBAL_LIST_INIT(cm_vending_gear_commanding_officer, list(
list("Underbarrel Flamethrower", 15, /obj/item/attachable/attached_gun/flamer, null, VENDOR_ITEM_REGULAR),
list("BARREL ATTACHMENTS", 0, null, null, null),
- list("Barrel Charger", 25, /obj/item/attachable/heavy_barrel, null, VENDOR_ITEM_RECOMMENDED),
list("Suppressor", 15, /obj/item/attachable/suppressor, null, VENDOR_ITEM_REGULAR),
list("Extended Barrel", 15, /obj/item/attachable/extended_barrel, null, VENDOR_ITEM_REGULAR),
list("Recoil Compensator", 15, /obj/item/attachable/compensator, null, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/crew/mp.dm b/code/game/machinery/vending/vendor_types/crew/mp.dm
index cdf374249211..b63a02248168 100644
--- a/code/game/machinery/vending/vendor_types/crew/mp.dm
+++ b/code/game/machinery/vending/vendor_types/crew/mp.dm
@@ -1,38 +1,50 @@
//------------ MP CLOTHING VENDOR---------------
GLOBAL_LIST_INIT(cm_vending_clothing_military_police, list(
+ list("POLICE SET (MANDATORY)", 0, null, null, null),
+ list("Essential Police Set", 0, /obj/effect/essentials_set/police, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_MANDATORY),
+
list("STANDARD EQUIPMENT (TAKE ALL)", 0, null, null, null),
list("Gloves", 0, /obj/item/clothing/gloves/marine, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY),
list("Uniform", 0, /obj/item/clothing/under/marine/mp, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY),
list("Headset", 0, /obj/item/device/radio/headset/almayer/mmpo, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
- list("Satchel", 0, /obj/item/storage/backpack/satchel/sec, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
-
- list("PERSONAL SIDEARM (CHOOSE 1)", 0, null, null, null),
- list("M44 Revolver", 0, /obj/item/storage/belt/gun/m44/mp, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("M4A3 Pistol", 0, /obj/item/storage/belt/gun/m4a3/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("Mod 88 Pistol", 0, /obj/item/storage/belt/gun/m4a3/mod88, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
+ list("Marine Combat Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY),
list("ARMOR (TAKE ALL)", 0, null, null, null),
list("Military Police M2 Armor", 0, /obj/item/clothing/suit/storage/marine/MP, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY),
+ list("MP Beret", 0, /obj/item/clothing/head/beret/marine/mp, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
- list("COMBAT EQUIPMENT (TAKE ALL)", 0, null, null, null),
- list("Military Police M10 Helmet", 0, /obj/item/clothing/head/helmet/marine/MP, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
- list("Marine Combat Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY),
+ list("HANDGUN CASE (CHOOSE 1)", 0, null, null, null),
+ list("88 mod 4 Combat Pistol Case", 0, /obj/item/storage/box/guncase/mod88, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+ list("M44 Combat Revolver Case", 0, /obj/item/storage/box/guncase/m44, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+ list("M4A3 Service Pistol Case", 0, /obj/item/storage/box/guncase/m4a3, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
- list("EYEWEAR (TAKE ALL)", 0, null, null, null),
- list("Security HUD Glasses", 0, /obj/item/clothing/glasses/sunglasses/sechud, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_REGULAR),
+ list("BACKPACK (CHOOSE 1)", 0, null, null, null),
+ list("Military Police Satchel", 0, /obj/item/storage/backpack/satchel/sec, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
+
+ list("BELT (CHOOSE 1)", 0, null, null, null),
+ list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 G8-A General Utility Pouch", 0, /obj/item/storage/backpack/general_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("POUCHES (CHOOSE 2)", 0, null, null, null),
- list("First-Aid Pouch (Full)", 0, /obj/item/storage/pouch/firstaid/full, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
list("Medium General Pouch", 0, /obj/item/storage/pouch/general/medium, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Refillable Injectors)", 0, /obj/item/storage/pouch/firstaid/full, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("First-Aid Pouch (Splints, Gauze, Ointment)", 0, /obj/item/storage/pouch/firstaid/full/alternate, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("First-Aid Pouch (Pill Packets)", 0, /obj/item/storage/pouch/firstaid/full/pills, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("Pistol Magazine Pouch", 0, /obj/item/storage/pouch/magazine/pistol, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("Pistol Pouch", 0, /obj/item/storage/pouch/pistol, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+
+ list("MASK (CHOOSE 1)", 0, null, null, null),
+ list("Gas Mask", 0, /obj/item/clothing/mask/gas, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
+ list("Heat Absorbent Coif", 0, /obj/item/clothing/mask/rebreather/scarf, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
+ list("Rebreather", 0, /obj/item/clothing/mask/rebreather, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
list("ACCESSORIES (CHOOSE 1)", 0, null, null, null),
+ list("Brown Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest/brown_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_RECOMMENDED),
+ list("Black Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Shoulder Holster", 0, /obj/item/clothing/accessory/storage/holster, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Webbing", 0, /obj/item/clothing/accessory/storage/webbing, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
-
- list("HAT (CHOOSE 1)", 0, null, null, null),
- list("MP Beret", 0, /obj/item/clothing/head/beret/marine/mp, MARINE_CAN_BUY_MASK, VENDOR_ITEM_MANDATORY),
-
))
/obj/structure/machinery/cm_vending/clothing/military_police
@@ -47,38 +59,50 @@ GLOBAL_LIST_INIT(cm_vending_clothing_military_police, list(
//------------ Warden CLOTHING VENDOR---------------
GLOBAL_LIST_INIT(cm_vending_clothing_military_police_warden, list(
+ list("POLICE SET (MANDATORY)", 0, null, null, null),
+ list("Essential Police Set", 0, /obj/effect/essentials_set/police, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_MANDATORY),
list("STANDARD EQUIPMENT (TAKE ALL)", 0, null, null, null),
list("Gloves", 0, /obj/item/clothing/gloves/marine, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY),
list("Warden Uniform", 0, /obj/item/clothing/under/marine/warden, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY),
list("Headset", 0, /obj/item/device/radio/headset/almayer/cmpcom, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
- list("Satchel", 0, /obj/item/storage/backpack/satchel/sec, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
-
- list("PERSONAL SIDEARM (Take ALL)", 0, null, null, null),
- list("M4A3 Service Pistol ", 0, /obj/item/storage/belt/gun/m4a3/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("Mod 88 Pistol", 0, /obj/item/storage/belt/gun/m4a3/mod88, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("M44 Revolver", 0, /obj/item/storage/belt/gun/m44/mp, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
+ list("Marine Combat Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY),
list("ARMOR (TAKE ALL)", 0, null, null, null),
list("Military Warden M3 Armor", 0, /obj/item/clothing/suit/storage/marine/MP/warden, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY),
+ list("Warden Peaked Cap", 0, /obj/item/clothing/head/beret/marine/mp/warden, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
- list("COMBAT EQUIPMENT (TAKE ALL)", 0, null, null, null),
- list("Military Police M10 Helmet", 0, /obj/item/clothing/head/helmet/marine/MP, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
- list("Marine Combat Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY),
+ list("HANDGUN CASE (CHOOSE 1)", 0, null, null, null),
+ list("88 mod 4 Combat Pistol Case", 0, /obj/item/storage/box/guncase/mod88, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+ list("M44 Combat Revolver Case", 0, /obj/item/storage/box/guncase/m44, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+ list("M4A3 Service Pistol Case", 0, /obj/item/storage/box/guncase/m4a3, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+
+ list("BACKPACK (CHOOSE 1)", 0, null, null, null),
+ list("Military Police Satchel", 0, /obj/item/storage/backpack/satchel/sec, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
- list("EYEWEAR (TAKE ALL)", 0, null, null, null),
- list("Security HUD Glasses", 0, /obj/item/clothing/glasses/sunglasses/sechud, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_REGULAR),
+ list("BELT (CHOOSE 1)", 0, null, null, null),
+ list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 G8-A General Utility Pouch", 0, /obj/item/storage/backpack/general_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("POUCHES (CHOOSE 2)", 0, null, null, null),
- list("First-Aid Pouch (Full)", 0, /obj/item/storage/pouch/firstaid/full, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
list("Medium General Pouch", 0, /obj/item/storage/pouch/general/medium, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Refillable Injectors)", 0, /obj/item/storage/pouch/firstaid/full, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("First-Aid Pouch (Splints, Gauze, Ointment)", 0, /obj/item/storage/pouch/firstaid/full/alternate, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("First-Aid Pouch (Pill Packets)", 0, /obj/item/storage/pouch/firstaid/full/pills, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("Pistol Magazine Pouch", 0, /obj/item/storage/pouch/magazine/pistol, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("Pistol Pouch", 0, /obj/item/storage/pouch/pistol, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+
+ list("MASK (CHOOSE 1)", 0, null, null, null),
+ list("Gas Mask", 0, /obj/item/clothing/mask/gas, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
+ list("Heat Absorbent Coif", 0, /obj/item/clothing/mask/rebreather/scarf, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
+ list("Rebreather", 0, /obj/item/clothing/mask/rebreather, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
list("ACCESSORIES (CHOOSE 1)", 0, null, null, null),
+ list("Brown Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest/brown_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_RECOMMENDED),
+ list("Black Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Shoulder Holster", 0, /obj/item/clothing/accessory/storage/holster, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Webbing", 0, /obj/item/clothing/accessory/storage/webbing, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
-
- list("HAT (CHOOSE 1)", 0, null, null, null),
- list("Warden Peaked Cap", 0, /obj/item/clothing/head/beret/marine/mp/warden, MARINE_CAN_BUY_MASK, VENDOR_ITEM_MANDATORY),
))
/obj/structure/machinery/cm_vending/clothing/military_police_warden
@@ -89,3 +113,10 @@ GLOBAL_LIST_INIT(cm_vending_clothing_military_police_warden, list(
/obj/structure/machinery/cm_vending/clothing/military_police_warden/get_listed_products(mob/user)
return GLOB.cm_vending_clothing_military_police_warden
+
+/obj/effect/essentials_set/police
+ spawned_gear_list = list(
+ /obj/item/clothing/glasses/sunglasses/sechud,
+ /obj/item/storage/belt/security/MP/full,
+ /obj/item/clothing/head/helmet/marine/MP,
+ )
diff --git a/code/game/machinery/vending/vendor_types/crew/pilot_officer.dm b/code/game/machinery/vending/vendor_types/crew/pilot_officer.dm
index 231e02bb5118..8d86669a1f70 100644
--- a/code/game/machinery/vending/vendor_types/crew/pilot_officer.dm
+++ b/code/game/machinery/vending/vendor_types/crew/pilot_officer.dm
@@ -65,11 +65,11 @@ GLOBAL_LIST_INIT(cm_vending_clothing_pilot_officer, list(
list("M30 Tactical Helmet", 0, /obj/item/clothing/head/helmet/marine/pilot, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
list("Leather Satchel", 0, /obj/item/storage/backpack/satchel, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
list("MRE", 0, /obj/item/storage/box/MRE, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY),
-
+
list("ARMOR (CHOOSE 1)", 0, null, null, null),
list("M70 Flak Jacket", 0, /obj/item/clothing/suit/armor/vest/pilot, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY),
list("M3-VL Pattern Flak Vest", 0, /obj/item/clothing/suit/storage/marine/light/vest/dcc, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY),
-
+
list("PERSONAL SIDEARM (CHOOSE 1)", 0, null, null, null),
list("88 Mod 4 Combat Pistol", 0, /obj/item/weapon/gun/pistol/mod88, MARINE_CAN_BUY_ATTACHMENT, VENDOR_ITEM_REGULAR),
list("VP78 Pistol", 0, /obj/item/weapon/gun/pistol/vp78, MARINE_CAN_BUY_ATTACHMENT, VENDOR_ITEM_REGULAR),
@@ -79,7 +79,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_pilot_officer, list(
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Lifesaver Bag (Full)", 0, /obj/item/storage/belt/medical/lifesaver/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 Medical Storage Rig (Full)", 0, /obj/item/storage/belt/medical/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
@@ -153,8 +153,8 @@ GLOBAL_LIST_INIT(cm_vending_clothing_dropship_crew_chief, list(
list("Gloves", 0, /obj/item/clothing/gloves/yellow, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY),
list("Patrol Cap", 0, /obj/item/clothing/head/cmcap, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
list("Leather Satchel", 0, /obj/item/storage/backpack/satchel, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
- list("MRE", 0, /obj/item/storage/box/MRE, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY),
-
+ list("MRE", 0, /obj/item/storage/box/MRE, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY),
+
list("ARMOR (CHOOSE 1)", 0, null, null, null),
list("M70 Flak Jacket", 0, /obj/item/clothing/suit/armor/vest/pilot, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY),
list("M3-VL Pattern Flak Vest", 0, /obj/item/clothing/suit/storage/marine/light/vest/dcc, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY),
@@ -168,7 +168,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_dropship_crew_chief, list(
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Lifesaver Bag (Full)", 0, /obj/item/storage/belt/medical/lifesaver/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 Medical Storage Rig (Full)", 0, /obj/item/storage/belt/medical/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/crew/senior_officers.dm b/code/game/machinery/vending/vendor_types/crew/senior_officers.dm
index 24b60079692b..55053bd724bd 100644
--- a/code/game/machinery/vending/vendor_types/crew/senior_officers.dm
+++ b/code/game/machinery/vending/vendor_types/crew/senior_officers.dm
@@ -2,7 +2,7 @@
name = "\improper ColMarTech Senior Officer Equipment Rack"
desc = "An automated equipment vendor for Senior Officers."
req_access = list(ACCESS_MARINE_SENIOR)
- vendor_role = list(JOB_CHIEF_POLICE,JOB_CMO,JOB_XO,JOB_CHIEF_ENGINEER,JOB_CHIEF_REQUISITION)
+ vendor_role = list(JOB_CHIEF_POLICE, JOB_CMO, JOB_XO, JOB_CHIEF_ENGINEER, JOB_CHIEF_REQUISITION, JOB_AUXILIARY_OFFICER)
/obj/structure/machinery/cm_vending/clothing/senior_officer/get_listed_products(mob/user)
if(!user)
@@ -23,53 +23,62 @@
return GLOB.cm_vending_clothing_cmo
else if(user.job == JOB_CHIEF_POLICE)
return GLOB.cm_vending_clothing_military_police_chief
+ else if(user.job == JOB_AUXILIARY_OFFICER)
+ return GLOB.cm_vending_clothing_auxiliary_officer
return ..()
-
//------------ CHIEF MP ---------------
GLOBAL_LIST_INIT(cm_vending_clothing_military_police_chief, list(
+ list("POLICE SET (MANDATORY)", 0, null, null, null),
+ list("Essential Police Set", 0, /obj/effect/essentials_set/chiefmilitarypolice, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_MANDATORY),
list("STANDARD EQUIPMENT (TAKE ALL)", 0, null, null, null),
list("Gloves", 0, /obj/item/clothing/gloves/marine, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY),
list("CMP Uniform", 0, /obj/item/clothing/under/marine/officer/warrant, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY),
list("Headset", 0, /obj/item/device/radio/headset/almayer/cmpcom, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
- list("Satchel", 0, /obj/item/storage/backpack/satchel/sec, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
- list("Gear Belt", 0, /obj/item/storage/belt/security/MP/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_MANDATORY),
-
- list("PERSONAL SIDEARM (CHOOSE 1)", 0, null, null, null),
- list("M4A3 Service Pistol", 0, /obj/item/storage/belt/gun/m4a3/full, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_REGULAR),
- list("Mod 88 Pistol", 0, /obj/item/storage/belt/gun/m4a3/mod88, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_REGULAR),
- list("M44 Revolver", 0, /obj/item/storage/belt/gun/m44/mp, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_RECOMMENDED),
+ list("Marine Combat Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY),
list("ARMOR (TAKE ALL)", 0, null, null, null),
list("Military Police Chief M3 Armor", 0, /obj/item/clothing/suit/storage/marine/MP/WO, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_RECOMMENDED),
-
- list("COMBAT EQUIPMENT (TAKE ALL)", 0, null, null, null),
list("Chief MP M10 Helmet", 0, /obj/item/clothing/head/helmet/marine/MP/WO, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
- list("Marine Combat Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY),
+ list("CMP Beret", 0, /obj/item/clothing/head/beret/marine/mp/cmp, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
- list("EYEWEAR (TAKE ALL)", 0, null, null, null),
- list("Security HUD Glasses", 0, /obj/item/clothing/glasses/sunglasses/sechud, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_MANDATORY),
+ list("HANDGUN CASE (CHOOSE 1)", 0, null, null, null),
+ list("88 mod 4 Combat Pistol Case", 0, /obj/item/storage/box/guncase/mod88, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+ list("M44 Combat Revolver Case", 0, /obj/item/storage/box/guncase/m44, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+ list("M4A3 Service Pistol Case", 0, /obj/item/storage/box/guncase/m4a3, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_MANDATORY),
+
+ list("BACKPACK (CHOOSE 1)", 0, null, null, null),
+ list("Military Police Satchel", 0, /obj/item/storage/backpack/satchel/sec, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
+
+ list("BELT (CHOOSE 1)", 0, null, null, null),
+ list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 G8-A General Utility Pouch", 0, /obj/item/storage/backpack/general_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("POUCHES (CHOOSE 2)", 0, null, null, null),
- list("First-Aid Pouch (Refillable Injectors)", 0, /obj/item/storage/pouch/firstaid/full, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
- list("First-Aid Pouch (Splints, Gauze, Ointment)", 0, /obj/item/storage/pouch/firstaid/full/alternate, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("Medium General Pouch", 0, /obj/item/storage/pouch/general/medium, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Refillable Injectors)", 0, /obj/item/storage/pouch/firstaid/full, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("First-Aid Pouch (Splints, Gauze, Ointment)", 0, /obj/item/storage/pouch/firstaid/full/alternate, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
list("First-Aid Pouch (Pill Packets)", 0, /obj/item/storage/pouch/firstaid/full/pills, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("Pistol Magazine Pouch", 0, /obj/item/storage/pouch/magazine/pistol, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("Pistol Pouch", 0, /obj/item/storage/pouch/pistol, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
list("Large General Pouch", 0, /obj/item/storage/pouch/general/large, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("MASK (CHOOSE 1)", 0, null, null, null),
+ list("Gas Mask", 0, /obj/item/clothing/mask/gas, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
+ list("Heat Absorbent Coif", 0, /obj/item/clothing/mask/rebreather/scarf, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
+ list("Rebreather", 0, /obj/item/clothing/mask/rebreather, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
+
list("ACCESSORIES (CHOOSE 1)", 0, null, null, null),
- list("Shoulder Holster", 0, /obj/item/clothing/accessory/storage/holster, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
+ list("Brown Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest/brown_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_RECOMMENDED),
list("Black Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
- list("Brown Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest/brown_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
+ list("Shoulder Holster", 0, /obj/item/clothing/accessory/storage/holster, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
list("Webbing", 0, /obj/item/clothing/accessory/storage/webbing, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
-
- list("HAT (CHOOSE 1)", 0, null, null, null),
- list("CMP Beret", 0, /obj/item/clothing/head/beret/marine/mp/cmp, MARINE_CAN_BUY_MASK, VENDOR_ITEM_MANDATORY),
))
-
//------------ CHIEF ENGINEER ---------------
GLOBAL_LIST_INIT(cm_vending_clothing_chief_engineer, list(
@@ -161,11 +170,11 @@ GLOBAL_LIST_INIT(cm_vending_clothing_req_officer, list(
list("STANDARD EQUIPMENT (TAKE ALL)", 0, null, null, null),
list("Insulated Gloves", 0, /obj/item/clothing/gloves/yellow, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY),
- list("RO Uniform", 0, /obj/item/clothing/under/rank/ro_suit, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY),
- list("Headset", 0, /obj/item/device/radio/headset/almayer/ro, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
+ list("Quartermaster Uniform", 0, /obj/item/clothing/under/rank/qm_suit, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY),
+ list("Headset", 0, /obj/item/device/radio/headset/almayer/qm, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
list("Satchel", 0, /obj/item/storage/backpack/marine/satchel/tech, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
list("Req Cap", 0, /obj/item/clothing/head/cmcap/req, MARINE_CAN_BUY_MASK, VENDOR_ITEM_MANDATORY),
- list("RO Jacket", 0, /obj/item/clothing/suit/storage/RO, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY),
+ list("Quartermaster Jacket", 0, /obj/item/clothing/suit/storage/RO, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY),
list("PERSONAL SIDEARM (CHOOSE 1)", 0, null, null, null),
list("M4A3 Service Pistol", 0, /obj/item/storage/belt/gun/m4a3/full, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_RECOMMENDED),
@@ -201,9 +210,12 @@ GLOBAL_LIST_INIT(cm_vending_clothing_cmo, list(
list("STANDARD EQUIPMENT (TAKE ALL)", 0, null, null, null),
list("Gloves", 0, /obj/item/clothing/gloves/latex, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY),
list("Headset", 0, /obj/item/device/radio/headset/almayer/cmo, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
- list("Medical HUD Glasses", 0, /obj/item/clothing/glasses/hud/health, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_MANDATORY),
list("Labcoat", 0, /obj/item/clothing/suit/storage/labcoat, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY),
+ list("EYEWARE (CHOOSE 1)", 0, null, null, null),
+ list("Medical HUD Glasses", 0, /obj/item/clothing/glasses/hud/health, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_MANDATORY),
+ list("Reagent Scanner HUD Goggles", 0, /obj/item/clothing/glasses/science, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_REGULAR),
+
list("UNIFORM (CHOOSE 1)", 0, null, null, null),
list("Green Scrubs", 0, /obj/item/clothing/under/rank/medical/green, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_RECOMMENDED),
list("Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/blue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR),
@@ -308,3 +320,47 @@ GLOBAL_LIST_INIT(cm_vending_clothing_xo, list(
list("Patrol Cap", 0, /obj/item/clothing/head/cmcap, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
list("Officer Cap", 0, /obj/item/clothing/head/cmcap/ro, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
))
+
+
+
+//------------ AUXILIARY SUPPORT OFFICER ---------------
+GLOBAL_LIST_INIT(cm_vending_clothing_auxiliary_officer, list(
+
+ list("STANDARD EQUIPMENT (TAKE ALL)", 0, null, null, null),
+ list("Insulated Gloves", 0, /obj/item/clothing/gloves/yellow, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY),
+ list("Officer Uniform", 0, /obj/item/clothing/under/marine/officer/bridge, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY),
+ list("Headset", 0, /obj/item/device/radio/headset/almayer/qm, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
+ list("Satchel", 0, /obj/item/storage/backpack/marine/satchel/tech, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
+ list("Patrol Cap", 0, /obj/item/clothing/head/cmcap, MARINE_CAN_BUY_MASK, VENDOR_ITEM_MANDATORY),
+ list("Auxiliary Support Officer Jacket", 0, /obj/item/clothing/suit/storage/jacket/marine/service/aso, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY),
+
+ list("PERSONAL SIDEARM (CHOOSE 1)", 0, null, null, null),
+ list("M4A3 Service Pistol", 0, /obj/item/storage/belt/gun/m4a3/full, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_RECOMMENDED),
+ list("Mod 88 Pistol", 0, /obj/item/storage/belt/gun/m4a3/mod88, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_RECOMMENDED),
+ list("M44 Custom Revolver", 0, /obj/item/storage/belt/gun/m44/custom, MARINE_CAN_BUY_SECONDARY, VENDOR_ITEM_RECOMMENDED),
+
+ list("COMBAT EQUIPMENT (TAKE ALL)", 0, null, null, null),
+ list("Officer M3 Armor", 0, /obj/item/clothing/suit/storage/marine/MP/SO, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_MANDATORY),
+ list("Officer M10 Helmet", 0, /obj/item/clothing/head/helmet/marine/MP/SO, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY),
+ list("Marine Combat Boots", 0, /obj/item/clothing/shoes/marine/knife, MARINE_CAN_BUY_SHOES, VENDOR_ITEM_MANDATORY),
+
+ list("POUCHES (CHOOSE 2)", 0, null, null, null),
+ list("First-Aid Pouch (Refillable Injectors)", 0, /obj/item/storage/pouch/firstaid/full, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Splints, Gauze, Ointment)", 0, /obj/item/storage/pouch/firstaid/full/alternate, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Pill Packets)", 0, /obj/item/storage/pouch/firstaid/full/pills, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_RECOMMENDED),
+ list("Large General Pouch", 0, /obj/item/storage/pouch/general/large, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("Tools Pouch (Empty)", 0, /obj/item/storage/pouch/tools, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+ list("Construction Pouch", 0, /obj/item/storage/pouch/construction, MARINE_CAN_BUY_POUCH, VENDOR_ITEM_REGULAR),
+
+ list("ACCESSORIES (CHOOSE 1)", 0, null, null, null),
+ list("Black Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
+ list("Brown Webbing Vest", 0, /obj/item/clothing/accessory/storage/black_vest/brown_vest, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_RECOMMENDED),
+ list("Webbing", 0, /obj/item/clothing/accessory/storage/webbing, MARINE_CAN_BUY_ACCESSORY, VENDOR_ITEM_REGULAR),
+ ))
+
+/obj/effect/essentials_set/chiefmilitarypolice
+ spawned_gear_list = list(
+ /obj/item/clothing/glasses/sunglasses/sechud,
+ /obj/item/storage/belt/security/MP/full,
+ /obj/item/clothing/head/helmet/marine/MP/WO,
+ )
diff --git a/code/game/machinery/vending/vendor_types/crew/staff_officer.dm b/code/game/machinery/vending/vendor_types/crew/staff_officer.dm
index 103efeedde61..3edbee6bbc47 100644
--- a/code/game/machinery/vending/vendor_types/crew/staff_officer.dm
+++ b/code/game/machinery/vending/vendor_types/crew/staff_officer.dm
@@ -41,7 +41,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_staff_officer, list(
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 Lifesaver Bag (Full)", 0, /obj/item/storage/belt/medical/lifesaver/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Medical Storage Rig (Full)", 0, /obj/item/storage/belt/medical/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M40 Grenade Rig", 0, /obj/item/storage/belt/grenade, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/crew/synthetic.dm b/code/game/machinery/vending/vendor_types/crew/synthetic.dm
index dd11cea0f242..0496530be861 100644
--- a/code/game/machinery/vending/vendor_types/crew/synthetic.dm
+++ b/code/game/machinery/vending/vendor_types/crew/synthetic.dm
@@ -21,6 +21,7 @@
list("Industrial Blowtorch", 2, /obj/item/tool/weldingtool/largetank, null, VENDOR_ITEM_REGULAR),
list("High-Capacity Industrial Blowtorch", 4, /obj/item/tool/weldingtool/hugetank, null, VENDOR_ITEM_REGULAR),
list("Plastic Explosive", 3, /obj/item/explosive/plastic, null, VENDOR_ITEM_REGULAR),
+ list("Toolkit", 1, /obj/item/storage/firstaid/toolkit/empty, null, VENDOR_ITEM_REGULAR),
list("FIRSTAID KITS", 0, null, null, null),
list("Advanced Firstaid Kit", 12, /obj/item/storage/firstaid/adv, null, VENDOR_ITEM_REGULAR),
@@ -75,7 +76,8 @@
list("Motion Detector", 5, /obj/item/device/motiondetector, null, VENDOR_ITEM_REGULAR),
list("Space Cleaner", 2, /obj/item/reagent_container/spray/cleaner, null, VENDOR_ITEM_REGULAR),
list("Whistle", 5, /obj/item/device/whistle, null, VENDOR_ITEM_REGULAR),
- list("Machete Scabbard (Full)", 2, /obj/item/storage/large_holster/machete/full, null, VENDOR_ITEM_REGULAR)
+ list("Machete Scabbard (Full)", 2, /obj/item/storage/large_holster/machete/full, null, VENDOR_ITEM_REGULAR),
+ list("Stethoscope", 2, /obj/item/clothing/accessory/stethoscope, null, VENDOR_ITEM_REGULAR)
)
@@ -250,7 +252,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_synth_snowflake, list(
list("Windbreaker, Exploration", 12, /obj/item/clothing/suit/storage/windbreaker/windbreaker_covenant, null, VENDOR_ITEM_REGULAR),
list("Labcoat", 12, /obj/item/clothing/suit/storage/labcoat, null, VENDOR_ITEM_REGULAR),
list("Labcoat, Researcher", 12, /obj/item/clothing/suit/storage/labcoat/researcher, null, VENDOR_ITEM_REGULAR),
- list("RO Jacket", 12, /obj/item/clothing/suit/storage/RO, null, VENDOR_ITEM_REGULAR),
+ list("Quartermaster Jacket", 12, /obj/item/clothing/suit/storage/RO, null, VENDOR_ITEM_REGULAR),
list("USCM Poncho", 12, /obj/item/clothing/accessory/poncho, null, VENDOR_ITEM_REGULAR),
list("BACKPACK", 0, null, null, null),
@@ -263,6 +265,12 @@ GLOBAL_LIST_INIT(cm_vending_clothing_synth_snowflake, list(
list("USCM RTO Pack", 12, /obj/item/storage/backpack/marine/satchel/rto, null, VENDOR_ITEM_REGULAR),
list("USCM Welderpack", 12, /obj/item/storage/backpack/marine/engineerpack, null, VENDOR_ITEM_REGULAR),
list("USCM Weldersatchel", 12, /obj/item/storage/backpack/marine/engineerpack/satchel, null, VENDOR_ITEM_REGULAR),
+
+ list("OTHER", 0, null, null, null),
+ list("Red Armband", 6, /obj/item/clothing/accessory/armband, null, VENDOR_ITEM_REGULAR),
+ list("Yellow Armband", 6, /obj/item/clothing/accessory/armband/engine, null, VENDOR_ITEM_REGULAR),
+ list("Green Armband", 6, /obj/item/clothing/accessory/armband/medgreen, null, VENDOR_ITEM_REGULAR),
+
))
/obj/structure/machinery/cm_vending/clothing/synth/snowflake
diff --git a/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm b/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm
index 023bbe390bfc..fb9b662be1bc 100644
--- a/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm
+++ b/code/game/machinery/vending/vendor_types/crew/vehicle_crew.dm
@@ -319,7 +319,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_vehicle_crew, list(
list("M103 Vehicle-Ammo Rig", 0, /obj/item/storage/belt/tank, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/engineering.dm b/code/game/machinery/vending/vendor_types/engineering.dm
index 83f69ae09889..2fe6962e93bb 100644
--- a/code/game/machinery/vending/vendor_types/engineering.dm
+++ b/code/game/machinery/vending/vendor_types/engineering.dm
@@ -25,6 +25,7 @@
list("Utility Tool Belt", round(scale * 2), /obj/item/storage/belt/utility, VENDOR_ITEM_REGULAR),
list("Welding Goggles", round(scale * 2), /obj/item/clothing/glasses/welding, VENDOR_ITEM_REGULAR),
list("Welding Helmet", round(scale * 2), /obj/item/clothing/head/welding, VENDOR_ITEM_REGULAR),
+ list("Toolkit", round(scale * 4), /obj/item/storage/firstaid/toolkit/empty, VENDOR_ITEM_REGULAR),
list("SCANNERS", -1, null, null),
list("Atmos Scanner", round(scale * 2), /obj/item/device/analyzer, VENDOR_ITEM_REGULAR),
@@ -54,6 +55,8 @@
list("EQUIPMENT", -1, null, null),
list("Utility Tool Belt", round(scale * 4), /obj/item/storage/belt/utility, VENDOR_ITEM_REGULAR),
list("Cable Coil", round(scale * 4), /obj/item/stack/cable_coil/random, VENDOR_ITEM_REGULAR),
+ list("Welding Goggles", round(scale * 2), /obj/item/clothing/glasses/welding, VENDOR_ITEM_REGULAR),
+ list("Toolkit", round(scale * 12), /obj/item/storage/firstaid/toolkit/empty, VENDOR_ITEM_REGULAR),
list("TOOLS", -1, null, null),
list("Blowtorch", round(scale * 4), /obj/item/tool/weldingtool, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/intelligence_officer.dm b/code/game/machinery/vending/vendor_types/intelligence_officer.dm
index 829c542f6b26..a2afe897b495 100644
--- a/code/game/machinery/vending/vendor_types/intelligence_officer.dm
+++ b/code/game/machinery/vending/vendor_types/intelligence_officer.dm
@@ -73,7 +73,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_intelligence_officer, list(
list("G8-A General Utility Pouch", 0, /obj/item/storage/backpack/general_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Toolbelt Rig (Full)", 0, /obj/item/storage/belt/utility/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
diff --git a/code/game/machinery/vending/vendor_types/requisitions.dm b/code/game/machinery/vending/vendor_types/requisitions.dm
index f85657e887a8..b979fe4a03a9 100644
--- a/code/game/machinery/vending/vendor_types/requisitions.dm
+++ b/code/game/machinery/vending/vendor_types/requisitions.dm
@@ -82,7 +82,7 @@
list("M276 Ammo Load Rig", round(scale * 15), /obj/item/storage/belt/marine, VENDOR_ITEM_REGULAR),
list("M276 General Pistol Holster Rig", round(scale * 10), /obj/item/storage/belt/gun/m4a3, VENDOR_ITEM_REGULAR),
list("M276 Knife Rig", round(scale * 5), /obj/item/storage/belt/knifepouch, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", round(scale * 5), /obj/item/storage/large_holster/m39, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", round(scale * 5), /obj/item/storage/belt/gun/m39, VENDOR_ITEM_REGULAR),
list("M276 M40 Grenade Rig", round(scale * 2), /obj/item/storage/belt/grenade, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", round(scale * 5), /obj/item/storage/belt/gun/m44, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", round(scale * 2), /obj/item/storage/belt/gun/flaregun, VENDOR_ITEM_REGULAR),
@@ -342,7 +342,6 @@
/obj/structure/machinery/cm_vending/sorted/attachments/populate_product_list(scale)
listed_products = list(
list("BARREL", -1, null, null),
- list("Barrel Charger", round(scale * 2.5), /obj/item/attachable/heavy_barrel, VENDOR_ITEM_REGULAR),
list("Extended Barrel", round(scale * 6.5), /obj/item/attachable/extended_barrel, VENDOR_ITEM_REGULAR),
list("M5 Bayonet", round(scale * 10.5), /obj/item/attachable/bayonet, VENDOR_ITEM_REGULAR),
list("Recoil Compensator", round(scale * 6.5), /obj/item/attachable/compensator, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm
index 3d39479167b0..faff01f7f299 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_engineer.dm
@@ -24,7 +24,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_engi, list(
list("Laser Designator", 15, /obj/item/device/binoculars/range/designator, null, VENDOR_ITEM_REGULAR),
list("Sandbags x25", 10, /obj/item/stack/sandbags_empty/half, null, VENDOR_ITEM_RECOMMENDED),
list("Super-Capacity Power Cell", 10, /obj/item/cell/super, null, VENDOR_ITEM_REGULAR),
- list("Welding Goggles", 5, /obj/item/clothing/glasses/welding, null, VENDOR_ITEM_MANDATORY),
+ list("Welding Goggles", 5, /obj/item/clothing/glasses/welding, null, VENDOR_ITEM_REGULAR),
list("ES-11 Mobile Fuel Canister", 4, /obj/item/tool/weldpack/minitank, null, VENDOR_ITEM_REGULAR),
list("EXPLOSIVES", 0, null, null, null),
@@ -110,12 +110,13 @@ GLOBAL_LIST_INIT(cm_vending_clothing_engi, list(
list("Technician Satchel", 0, /obj/item/storage/backpack/marine/satchel/tech, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_REGULAR),
list("Technician Welderpack", 0, /obj/item/storage/backpack/marine/engineerpack, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
list("Technician Welder-Satchel", 0, /obj/item/storage/backpack/marine/engineerpack/satchel, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_REGULAR),
+ list("Technician Welder Chestrig", 0, /obj/item/storage/backpack/marine/engineerpack/welder_chestrig, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY),
list("BELT (CHOOSE 1)", 0, null, null, null),
list("G8-A General Utility Pouch", 0, /obj/item/storage/backpack/general_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm
index fdfdabd8335e..9757576e6d0b 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_leader.dm
@@ -127,7 +127,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_leader, list(
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Lifesaver Bag", 0, /obj/item/storage/belt/medical/lifesaver, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Medical Storage Rig", 0, /obj/item/storage/belt/medical, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm
index 532c8e58dd02..7c9682985298 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_medic.dm
@@ -130,7 +130,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_medic, list(
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Lifesaver Bag (Full)", 0, /obj/item/storage/belt/medical/lifesaver/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 Medical Storage Rig (Full)", 0, /obj/item/storage/belt/medical/full, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
index 9775c20cac33..d2e50aee9042 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
@@ -55,6 +55,7 @@
//------------SQUAD PREP UNIFORM VENDOR---------------
+
/obj/structure/machinery/cm_vending/sorted/uniform_supply/squad_prep
name = "\improper ColMarTech Surplus Uniform Vendor"
desc = "An automated supply rack hooked up to a small storage of standard marine uniforms."
@@ -68,45 +69,92 @@
/obj/structure/machinery/cm_vending/sorted/uniform_supply/squad_prep/populate_product_list(scale)
listed_products = list(
- list("UNIFORM & STORAGE", -1, null, null),
- list("Lightweight IMP Backpack", 10, /obj/item/storage/backpack/marine, VENDOR_ITEM_REGULAR),
- list("Marine Radio Headset", 10, /obj/item/device/radio/headset/almayer, VENDOR_ITEM_REGULAR),
- list("Marine Combat Gloves", 10, /obj/item/clothing/gloves/marine, VENDOR_ITEM_REGULAR),
- list("Marine Black Combat Gloves", 10, /obj/item/clothing/gloves/marine/black, VENDOR_ITEM_REGULAR),
- list("Marine Combat Boots", 20, /obj/item/clothing/shoes/marine, VENDOR_ITEM_REGULAR),
- list("Shotgun Scabbard", 5, /obj/item/storage/large_holster/m37, VENDOR_ITEM_REGULAR),
- list("USCM Satchel", 10, /obj/item/storage/backpack/marine/satchel, VENDOR_ITEM_REGULAR),
- list("USCM Technical Satchel", 10, /obj/item/storage/backpack/marine/satchel/tech, VENDOR_ITEM_REGULAR),
- list("USCM Uniform", 20, /obj/item/clothing/under/marine, VENDOR_ITEM_REGULAR),
+ list("STANDARD EQUIPMENT", -1, null, null, null),
+ list("Marine Combat Boots", round(scale * 15), /obj/item/clothing/shoes/marine, VENDOR_ITEM_REGULAR),
+ list("USCM Uniform", round(scale * 15), /obj/item/clothing/under/marine, VENDOR_ITEM_REGULAR),
+ list("Marine Combat Gloves", round(scale * 15), /obj/item/clothing/gloves/marine, VENDOR_ITEM_REGULAR),
+ list("Marine Black Combat Gloves", round(scale * 15), /obj/item/clothing/gloves/marine/black, VENDOR_ITEM_REGULAR),
+ list("Marine Radio Headset", round(scale * 15), /obj/item/device/radio/headset/almayer, VENDOR_ITEM_REGULAR),
+ list("M10 Pattern Marine Helmet", round(scale * 15), /obj/item/clothing/head/helmet/marine, VENDOR_ITEM_REGULAR),
- list("BELTS", -1, null, null),
- list("M276 Pattern Ammo Load Rig", 10, /obj/item/storage/belt/marine, VENDOR_ITEM_REGULAR),
- list("M276 Pattern M40 Grenade Rig", 8, /obj/item/storage/belt/grenade, VENDOR_ITEM_REGULAR),
- list("M276 Pattern Shotgun Shell Loading Rig", 10, /obj/item/storage/belt/shotgun, VENDOR_ITEM_REGULAR),
- list("M276 Pattern General Pistol Holster Rig", 10, /obj/item/storage/belt/gun/m4a3, VENDOR_ITEM_REGULAR),
- list("M276 Pattern M39 Holster Rig", 10, /obj/item/storage/large_holster/m39, VENDOR_ITEM_REGULAR),
- list("M276 Pattern M44 Holster Rig", 10, /obj/item/storage/belt/gun/m44, VENDOR_ITEM_REGULAR),
- list("M276 Pattern M82F Holster Rig", 5, /obj/item/storage/belt/gun/flaregun, VENDOR_ITEM_REGULAR),
+ list("WEBBINGS", -1, null, null),
+ list("Brown Webbing Vest", round(scale * 1.25), /obj/item/clothing/accessory/storage/black_vest/brown_vest, VENDOR_ITEM_REGULAR),
+ list("Black Webbing Vest", round(scale * 0.5), /obj/item/clothing/accessory/storage/black_vest, VENDOR_ITEM_REGULAR),
+ list("Webbing", round(scale * 2), /obj/item/clothing/accessory/storage/webbing, VENDOR_ITEM_REGULAR),
+ list("Drop Pouch", round(scale * 0.5), /obj/item/clothing/accessory/storage/droppouch, VENDOR_ITEM_REGULAR),
+ list("Shoulder Holster", round(scale * 0.5), /obj/item/clothing/accessory/storage/holster, VENDOR_ITEM_REGULAR),
list("ARMOR", -1, null, null),
- list("M10 Pattern Marine Helmet", 20, /obj/item/clothing/head/helmet/marine, VENDOR_ITEM_REGULAR),
- list("M3 Pattern Carrier Marine Armor", 20, /obj/item/clothing/suit/storage/marine/carrier, VENDOR_ITEM_REGULAR),
- list("M3 Pattern Padded Marine Armor", 20, /obj/item/clothing/suit/storage/marine/padded, VENDOR_ITEM_REGULAR),
- list("M3 Pattern Padless Marine Armor", 20, /obj/item/clothing/suit/storage/marine/padless, VENDOR_ITEM_REGULAR),
- list("M3 Pattern Ridged Marine Armor", 20, /obj/item/clothing/suit/storage/marine/padless_lines, VENDOR_ITEM_REGULAR),
- list("M3 Pattern Skull Marine Armor", 20, /obj/item/clothing/suit/storage/marine/skull, VENDOR_ITEM_REGULAR),
- list("M3 Pattern Smooth Marine Armor", 20, /obj/item/clothing/suit/storage/marine/smooth, VENDOR_ITEM_REGULAR),
- list("M3-EOD Pattern Heavy Armor", 10, /obj/item/clothing/suit/storage/marine/heavy, VENDOR_ITEM_REGULAR),
- list("M3-L Pattern Light Armor", 10, /obj/item/clothing/suit/storage/marine/light, VENDOR_ITEM_REGULAR),
+ list("M3 Pattern Carrier Marine Armor", round(scale * 15), /obj/item/clothing/suit/storage/marine/carrier, VENDOR_ITEM_REGULAR),
+ list("M3 Pattern Padded Marine Armor", round(scale * 15), /obj/item/clothing/suit/storage/marine/padded, VENDOR_ITEM_REGULAR),
+ list("M3 Pattern Padless Marine Armor", round(scale * 15), /obj/item/clothing/suit/storage/marine/padless, VENDOR_ITEM_REGULAR),
+ list("M3 Pattern Ridged Marine Armor", round(scale * 15), /obj/item/clothing/suit/storage/marine/padless_lines, VENDOR_ITEM_REGULAR),
+ list("M3 Pattern Skull Marine Armor", round(scale * 15), /obj/item/clothing/suit/storage/marine/skull, VENDOR_ITEM_REGULAR),
+ list("M3 Pattern Smooth Marine Armor", round(scale * 15), /obj/item/clothing/suit/storage/marine/smooth, VENDOR_ITEM_REGULAR),
+ list("M3-EOD Pattern Heavy Armor", round(scale * 10), /obj/item/clothing/suit/storage/marine/heavy, VENDOR_ITEM_REGULAR),
+ list("M3-L Pattern Light Armor", round(scale * 10), /obj/item/clothing/suit/storage/marine/light, VENDOR_ITEM_REGULAR),
+
+ list("BACKPACK", -1, null, null, null),
+ list("Lightweight IMP Backpack", round(scale * 15), /obj/item/storage/backpack/marine, VENDOR_ITEM_REGULAR),
+ list("Technician Backpack", round(scale * 15), /obj/item/storage/backpack/marine/tech, VENDOR_ITEM_REGULAR),
+ list("Medical Backpack", round(scale * 15), /obj/item/storage/backpack/marine/medic, VENDOR_ITEM_REGULAR),
+ list("USCM Satchel", round(scale * 15), /obj/item/storage/backpack/marine/satchel, VENDOR_ITEM_REGULAR),
+ list("USCM Technical Satchel", round(scale * 15), /obj/item/storage/backpack/marine/satchel/tech, VENDOR_ITEM_REGULAR),
+ list("USCM Technical Chestrig", round(scale * 15), /obj/item/storage/backpack/marine/engineerpack/welder_chestrig, VENDOR_ITEM_REGULAR),
+ list("Medical Satchel", round(scale * 15), /obj/item/storage/backpack/marine/satchel/medic, VENDOR_ITEM_REGULAR),
+ list("Shotgun Scabbard", round(scale * 5), /obj/item/storage/large_holster/m37, VENDOR_ITEM_REGULAR),
+
+ list("RESTRICTED BACKPACKS", -1, null, null),
+ list("USCM Technician Welderpack", round(scale * 1.25), /obj/item/storage/backpack/marine/engineerpack, VENDOR_ITEM_REGULAR),
+ list("Technician Welder-Satchel", round(scale * 2), /obj/item/storage/backpack/marine/engineerpack/satchel, VENDOR_ITEM_REGULAR),
+ list("Radio Telephone Backpack", round(scale * 0.5), /obj/item/storage/backpack/marine/satchel/rto, VENDOR_ITEM_REGULAR),
+
+ list("BELTS", -1, null, null),
+ list("M276 Pattern Ammo Load Rig", round(scale * 15), /obj/item/storage/belt/marine, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern M40 Grenade Rig", round(scale * 10), /obj/item/storage/belt/grenade, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern Shotgun Shell Loading Rig", round(scale * 15), /obj/item/storage/belt/shotgun, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern General Pistol Holster Rig", round(scale * 15), /obj/item/storage/belt/gun/m4a3, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern M39 Holster Rig", round(scale * 15), /obj/item/storage/large_holster/m39, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern M39 Holster Rig And Pouch", round(scale * 10), /obj/item/storage/belt/gun/m39, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern M44 Holster Rig", round(scale * 15), /obj/item/storage/belt/gun/m44, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern M82F Holster Rig", round(scale * 5), /obj/item/storage/belt/gun/flaregun, VENDOR_ITEM_REGULAR),
+ list("M276 Knife Rig (Full)", round(scale * 15), /obj/item/storage/belt/knifepouch, VENDOR_ITEM_REGULAR),
+ list("M276 G8-A General Utility Pouch", round(scale * 15), /obj/item/storage/backpack/general_belt, VENDOR_ITEM_REGULAR),
+
+ list("POUCHES", -1, null, null, null),
+ list("Bayonet Sheath (Full)",round(scale * 15), /obj/item/storage/pouch/bayonet, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Splints, Gauze, Ointment)", round(scale * 15), /obj/item/storage/pouch/firstaid/full/alternate, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Pill Packets)", round(scale * 15), /obj/item/storage/pouch/firstaid/full/pills, VENDOR_ITEM_REGULAR),
+ list("Flare Pouch (Full)", round(scale * 15), /obj/item/storage/pouch/flare/full, VENDOR_ITEM_REGULAR),
+ list("Small Document Pouch", round(scale * 15), /obj/item/storage/pouch/document/small, VENDOR_ITEM_REGULAR),
+ list("Magazine Pouch", round(scale * 15), /obj/item/storage/pouch/magazine, VENDOR_ITEM_REGULAR),
+ list("Shotgun Shell Pouch", round(scale * 15), /obj/item/storage/pouch/shotgun, VENDOR_ITEM_REGULAR),
+ list("Medium General Pouch", round(scale * 15), /obj/item/storage/pouch/general/medium, VENDOR_ITEM_REGULAR),
+ list("Pistol Magazine Pouch", round(scale * 15), /obj/item/storage/pouch/magazine/pistol, VENDOR_ITEM_REGULAR),
+ list("Pistol Pouch", round(scale * 15), /obj/item/storage/pouch/pistol, VENDOR_ITEM_REGULAR),
+
+ list("RESTRICTED POUCHES", -1, null, null, null),
+ list("Construction Pouch", round(scale * 1.25), /obj/item/storage/pouch/construction, VENDOR_ITEM_REGULAR),
+ list("Explosive Pouch", round(scale * 1.25), /obj/item/storage/pouch/explosive, VENDOR_ITEM_REGULAR),
+ list("First Responder Pouch (Empty)", round(scale * 2.5), /obj/item/storage/pouch/first_responder, VENDOR_ITEM_REGULAR),
+ list("Large Pistol Magazine Pouch", round(scale * 2), /obj/item/storage/pouch/magazine/pistol/large, VENDOR_ITEM_REGULAR),
+ list("Tools Pouch", round(scale * 1.25), /obj/item/storage/pouch/tools, VENDOR_ITEM_REGULAR),
+ list("Sling Pouch", round(scale * 1.25), /obj/item/storage/pouch/sling, VENDOR_ITEM_REGULAR),
+
+ list("MASK", -1, null, null, null),
+ list("Gas Mask", round(scale * 15), /obj/item/clothing/mask/gas, VENDOR_ITEM_REGULAR),
+ list("Heat Absorbent Coif", round(scale * 10), /obj/item/clothing/mask/rebreather/scarf, VENDOR_ITEM_REGULAR),
+ list("Rebreather", round(scale * 10), /obj/item/clothing/mask/rebreather, MARINE_CAN_BUY_MASK, VENDOR_ITEM_REGULAR),
list("MISCELLANEOUS", -1, null, null, null),
- list("Gas Mask", 20, /obj/item/clothing/mask/gas, VENDOR_ITEM_REGULAR),
- list("Heat Absorbent Coif", 10, /obj/item/clothing/mask/rebreather/scarf, VENDOR_ITEM_REGULAR),
- list("M5 Integrated Gas Mask", 10, /obj/item/prop/helmetgarb/helmet_gasmask, VENDOR_ITEM_REGULAR),
- list("M10 Helmet Netting", 10, /obj/item/prop/helmetgarb/netting, VENDOR_ITEM_REGULAR),
- list("M10 Helmet Rain Cover", 10, /obj/item/prop/helmetgarb/raincover, VENDOR_ITEM_REGULAR),
- list("Firearm Lubricant", 20, /obj/item/prop/helmetgarb/gunoil, VENDOR_ITEM_REGULAR),
- list("USCM Flair", 20, /obj/item/prop/helmetgarb/flair_uscm, VENDOR_ITEM_REGULAR),
+ list("Ballistic goggles", round(scale * 10), /obj/item/clothing/glasses/mgoggles, VENDOR_ITEM_REGULAR),
+ list("Prescription ballistic goggles", round(scale * 10), /obj/item/clothing/glasses/mgoggles/prescription, VENDOR_ITEM_REGULAR),
+ list("Marine RPG glasses", round(scale * 10), /obj/item/clothing/glasses/regular, VENDOR_ITEM_REGULAR),
+ list("M5 Integrated Gas Mask", round(scale * 10), /obj/item/prop/helmetgarb/helmet_gasmask, VENDOR_ITEM_REGULAR),
+ list("M10 Helmet Netting", round(scale * 10), /obj/item/prop/helmetgarb/netting, VENDOR_ITEM_REGULAR),
+ list("M10 Helmet Rain Cover", round(scale * 10), /obj/item/prop/helmetgarb/raincover, VENDOR_ITEM_REGULAR),
+ list("Firearm Lubricant", round(scale * 15), /obj/item/prop/helmetgarb/gunoil, VENDOR_ITEM_REGULAR),
+ list("USCM Flair", round(scale * 15), /obj/item/prop/helmetgarb/flair_uscm, VENDOR_ITEM_REGULAR),
)
//--------------SQUAD SPECIFIC VERSIONS--------------
@@ -171,7 +219,6 @@
/obj/structure/machinery/cm_vending/sorted/cargo_ammo/squad/populate_product_list(scale)
listed_products = list(
-
list("ARMOR-PIERCING AMMUNITION", -1, null, null),
list("M4RA AP Magazine (10x24mm)", round(scale * 3.5), /obj/item/ammo_magazine/rifle/m4ra/ap, VENDOR_ITEM_REGULAR),
list("M39 AP Magazine (10x20mm)", round(scale * 3), /obj/item/ammo_magazine/smg/m39/ap, VENDOR_ITEM_REGULAR),
@@ -193,15 +240,15 @@
list("M56D Drum Magazine", round(scale * 2), /obj/item/ammo_magazine/m56d, VENDOR_ITEM_REGULAR),
list("M2C Box Magazine", round(scale * 2), /obj/item/ammo_magazine/m2c, VENDOR_ITEM_REGULAR),
list("HIRR Baton Slugs", round(scale * 6), /obj/item/explosive/grenade/slug/baton, VENDOR_ITEM_REGULAR),
- list("M74 AGM-S Star Shell", round(scale * 2), /obj/item/explosive/grenade/high_explosive/airburst/starshell, VENDOR_ITEM_REGULAR),
+ list("M74 AGM-S Star Shell", round(scale * 4), /obj/item/explosive/grenade/high_explosive/airburst/starshell, VENDOR_ITEM_REGULAR),
list("M74 AGM-S Hornet Shell", round(scale * 4), /obj/item/explosive/grenade/high_explosive/airburst/hornet_shell, VENDOR_ITEM_REGULAR),
)
//--------------SQUAD ARMAMENTS VENDOR--------------
/obj/structure/machinery/cm_vending/sorted/cargo_guns/squad
- name = "\improper ColMarTech Automated Armaments Squad Vendor"
- desc = "An automated supply rack hooked up to a small storage of various firearms and explosives. Can be accessed by any Marine Rifleman."
+ name = "\improper ColMarTech Automated Utilities Squad Vendor"
+ desc = "An automated supply rack hooked up to a small storage of various utilities and tools. Can be accessed by any Marine Rifleman."
req_access = list(ACCESS_MARINE_ALPHA)
req_one_access = list(ACCESS_MARINE_LEADER, ACCESS_MARINE_SPECPREP, ACCESS_MARINE_RO)
hackable = TRUE
@@ -215,47 +262,30 @@
/obj/structure/machinery/cm_vending/sorted/cargo_guns/squad/populate_product_list(scale)
listed_products = list(
- list("WEBBINGS", -1, null, null),
- list("Brown Webbing Vest", round(scale * 2), /obj/item/clothing/accessory/storage/black_vest/brown_vest, VENDOR_ITEM_REGULAR),
- list("Black Webbing Vest", round(scale * 1), /obj/item/clothing/accessory/storage/black_vest, VENDOR_ITEM_REGULAR),
- list("Webbing", round(scale * 3), /obj/item/clothing/accessory/storage/webbing, VENDOR_ITEM_REGULAR),
- list("Drop Pouch", round(scale * 1), /obj/item/clothing/accessory/storage/droppouch, VENDOR_ITEM_REGULAR),
- list("Shoulder Holster", round(scale * 1), /obj/item/clothing/accessory/storage/holster, VENDOR_ITEM_REGULAR),
+ list("FOOD", -1, null, null),
+ list("MRE", round(scale * 5), /obj/item/storage/box/MRE, VENDOR_ITEM_REGULAR),
+ list("MRE Box", round(scale * 1), /obj/item/ammo_box/magazine/misc/mre, VENDOR_ITEM_REGULAR),
- list("BACKPACKS", -1, null, null),
- list("Lightweight IMP Backpack", round(scale * 15), /obj/item/storage/backpack/marine, VENDOR_ITEM_REGULAR),
- list("Shotgun Scabbard", round(scale * 5), /obj/item/storage/large_holster/m37, VENDOR_ITEM_REGULAR),
- list("USCM Technician Welderpack", round(scale * 2), /obj/item/storage/backpack/marine/engineerpack, VENDOR_ITEM_REGULAR),
- list("Technician Welder-Satchel", round(scale * 3), /obj/item/storage/backpack/marine/engineerpack/satchel, VENDOR_ITEM_REGULAR),
- list("Radio Telephone Backpack", round(scale * 1), /obj/item/storage/backpack/marine/satchel/rto, VENDOR_ITEM_REGULAR),
-
- list("BELTS", -1, null, null),
- list("G8-A General Utility Pouch", round(scale * 2), /obj/item/storage/backpack/general_belt, VENDOR_ITEM_REGULAR),
- list("M276 General Pistol Holster Rig", round(scale * 10), /obj/item/storage/belt/gun/m4a3, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", round(scale * 2), /obj/item/storage/large_holster/m39, VENDOR_ITEM_REGULAR),
- list("M276 M44 Holster Rig", round(scale * 5), /obj/item/storage/belt/gun/m44, VENDOR_ITEM_REGULAR),
- list("M276 M82F Holster Rig", round(scale * 2), /obj/item/storage/belt/gun/flaregun, VENDOR_ITEM_REGULAR),
- list("M276 M40 Grenade Rig", round(scale * 3), /obj/item/storage/belt/grenade, VENDOR_ITEM_REGULAR),
-
- list("POUCHES", -1, null, null),
- list("Construction Pouch", round(scale * 2), /obj/item/storage/pouch/construction, VENDOR_ITEM_REGULAR),
- list("Document Pouch", round(scale * 2), /obj/item/storage/pouch/document/small, VENDOR_ITEM_REGULAR),
- list("Explosive Pouch", round(scale * 2), /obj/item/storage/pouch/explosive, VENDOR_ITEM_REGULAR),
- list("First-Aid Pouch (Full)", round(scale * 5), /obj/item/storage/pouch/firstaid/full/alternate, VENDOR_ITEM_REGULAR),
- list("First Responder Pouch (Empty)", round(scale * 4), /obj/item/storage/pouch/first_responder, VENDOR_ITEM_REGULAR),
- list("Flare Pouch", round(scale * 5), /obj/item/storage/pouch/flare/full, VENDOR_ITEM_REGULAR),
- list("Large Pistol Magazine Pouch", round(scale * 3), /obj/item/storage/pouch/magazine/pistol/large, VENDOR_ITEM_REGULAR),
- list("Magazine Pouch", round(scale * 5), /obj/item/storage/pouch/magazine, VENDOR_ITEM_REGULAR),
- list("Medium General Pouch", round(scale * 2), /obj/item/storage/pouch/general/medium, VENDOR_ITEM_REGULAR),
- list("Shotgun Shell Pouch", round(scale *5), /obj/item/storage/pouch/shotgun, VENDOR_ITEM_REGULAR),
- list("Sidearm Pouch", round(scale * 15), /obj/item/storage/pouch/pistol, VENDOR_ITEM_REGULAR),
- list("Tools Pouch", round(scale * 2), /obj/item/storage/pouch/tools, VENDOR_ITEM_REGULAR),
- list("Sling Pouch", round(scale * 2), /obj/item/storage/pouch/sling, VENDOR_ITEM_REGULAR),
+ list("TOOLS", -1, null, null),
+ list("Entrenching Tool (ET)", round(scale * 2), /obj/item/tool/shovel/etool/folded, VENDOR_ITEM_REGULAR),
+ list("Screwdriver", round(scale * 5), /obj/item/tool/screwdriver, VENDOR_ITEM_REGULAR),
+ list("Wirecutters", round(scale * 5), /obj/item/tool/wirecutters, VENDOR_ITEM_REGULAR),
+ list("Crowbar", round(scale * 5), /obj/item/tool/crowbar, VENDOR_ITEM_REGULAR),
+ list("Wrench", round(scale * 5), /obj/item/tool/wrench, VENDOR_ITEM_REGULAR),
+ list("ME3 hand welder", round(scale * 2), /obj/item/tool/weldingtool/simple, VENDOR_ITEM_REGULAR),
- list("MISCELLANEOUS", -1, null, null),
+ list("FLARE AND LIGHT", -1, null, null),
list("Combat Flashlight", round(scale * 5), /obj/item/device/flashlight/combat, VENDOR_ITEM_REGULAR),
- list("Entrenching Tool (ET)", round(scale * 2), /obj/item/tool/shovel/etool/folded, VENDOR_ITEM_REGULAR),
+ list("Flashlight", round(scale * 5), /obj/item/device/flashlight/combat, VENDOR_ITEM_REGULAR),
+ list("Box of Flashlight", round(scale * 1), /obj/item/ammo_box/magazine/misc/flashlight, VENDOR_ITEM_REGULAR),
+ list("Box of Flares", round(scale * 1), /obj/item/ammo_box/magazine/misc/flares, VENDOR_ITEM_REGULAR),
+ list("M94 Marking Flare Pack", round(scale * 10), /obj/item/storage/box/m94, VENDOR_ITEM_REGULAR),
list("M89-S Signal Flare Pack", round(scale * 1), /obj/item/storage/box/m94/signal, VENDOR_ITEM_REGULAR),
+
+ list("MISCELLANEOUS", -1, null, null),
+ list("Toolkit", round(scale * 1), /obj/item/storage/firstaid/toolkit/empty, VENDOR_ITEM_REGULAR),
+ list("Map", round(scale * 5), /obj/item/map/current_map, VENDOR_ITEM_REGULAR),
+ list("Extinguisher", round(scale * 5), /obj/item/tool/extinguisher, VENDOR_ITEM_REGULAR),
list("Machete Scabbard (Full)", round(scale * 5), /obj/item/storage/large_holster/machete/full, VENDOR_ITEM_REGULAR),
list("Binoculars", round(scale * 1), /obj/item/device/binoculars, VENDOR_ITEM_REGULAR),
list("MB-6 Folding Barricades (x3)", round(scale * 2), /obj/item/stack/folding_barricade/three, VENDOR_ITEM_REGULAR),
@@ -280,7 +310,6 @@
/obj/structure/machinery/cm_vending/sorted/attachments/squad/populate_product_list(scale)
listed_products = list(
list("BARREL", -1, null, null),
- list("Barrel Charger", round(scale * 0.9), /obj/item/attachable/heavy_barrel, VENDOR_ITEM_REGULAR),
list("Extended Barrel", round(scale * 2.5), /obj/item/attachable/extended_barrel, VENDOR_ITEM_REGULAR),
list("Recoil Compensator", round(scale * 2.5), /obj/item/attachable/compensator, VENDOR_ITEM_REGULAR),
list("Suppressor", round(scale * 2.5), /obj/item/attachable/suppressor, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm
index 543288c71706..d92eaabf52c1 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_rifleman.dm
@@ -20,7 +20,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_marine, list(
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Knife Rig (Full)", 0, /obj/item/storage/belt/knifepouch, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm
index 6b763936fdc8..e0900c3fd3c8 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm
@@ -90,7 +90,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_specialist, list(
list("G8-A General Utility Pouch", 0, /obj/item/storage/backpack/general_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm
index 4311a3982c86..ceef80ab6952 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_tl.dm
@@ -84,7 +84,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_tl, list(
list("G8-A General Utility Pouch", 0, /obj/item/storage/backpack/general_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Ammo Load Rig", 0, /obj/item/storage/belt/marine, MARINE_CAN_BUY_BELT, VENDOR_ITEM_RECOMMENDED),
list("M276 General Pistol Holster Rig", 0, /obj/item/storage/belt/gun/m4a3, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 0, /obj/item/storage/large_holster/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 0, /obj/item/storage/belt/gun/m39, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 0, /obj/item/storage/belt/gun/m44, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 0, /obj/item/storage/belt/gun/flaregun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 0, /obj/item/storage/belt/shotgun, MARINE_CAN_BUY_BELT, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/wo_vendors.dm b/code/game/machinery/vending/vendor_types/wo_vendors.dm
index bdba638c577e..645640f9dc84 100644
--- a/code/game/machinery/vending/vendor_types/wo_vendors.dm
+++ b/code/game/machinery/vending/vendor_types/wo_vendors.dm
@@ -12,7 +12,7 @@
list("Lightweight IMP Backpack", 10, /obj/item/storage/backpack/marine, VENDOR_ITEM_REGULAR),
list("M276 Ammo Load Rig", 10, /obj/item/storage/belt/marine, VENDOR_ITEM_REGULAR),
list("M276 General Pistol Holster Rig", 10, /obj/item/storage/belt/gun/m4a3, VENDOR_ITEM_REGULAR),
- list("M276 M39 Holster Rig", 10, /obj/item/storage/large_holster/m39, VENDOR_ITEM_REGULAR),
+ list("M276 M39 Holster Rig", 10, /obj/item/storage/belt/gun/m39, VENDOR_ITEM_REGULAR),
list("M276 M44 Holster Rig", 10, /obj/item/storage/belt/gun/m44, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", 10, /obj/item/storage/belt/gun/flaregun, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", 10, /obj/item/storage/belt/shotgun, VENDOR_ITEM_REGULAR),
@@ -236,7 +236,7 @@
list("G8-A General Utility Pouch", round(scale * 3), /obj/item/storage/backpack/general_belt, VENDOR_ITEM_REGULAR),
list("M276 Pattern Ammo Load Rig", round(scale * 15), /obj/item/storage/belt/marine, VENDOR_ITEM_REGULAR),
list("M276 Pattern General Pistol Holster Rig", round(scale * 10), /obj/item/storage/belt/gun/m4a3, VENDOR_ITEM_REGULAR),
- list("M276 Pattern M39 Holster Rig", round(scale * 5), /obj/item/storage/large_holster/m39, VENDOR_ITEM_REGULAR),
+ list("M276 Pattern M39 Holster Rig", round(scale * 5), /obj/item/storage/belt/gun/m39, VENDOR_ITEM_REGULAR),
list("M276 Pattern M44 Holster Rig", round(scale * 5), /obj/item/storage/belt/gun/m44, VENDOR_ITEM_REGULAR),
list("M276 M82F Holster Rig", round(scale * 2), /obj/item/storage/belt/gun/flaregun, VENDOR_ITEM_REGULAR),
list("M276 Pattern Shotgun Shell Loading Rig", round(scale * 10), /obj/item/storage/belt/shotgun, VENDOR_ITEM_REGULAR),
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index 0399d0996411..43c3500813a4 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -169,7 +169,7 @@
/obj/effect/decal/cleanable/blackgoo/Crossed(mob/living/carbon/human/H)
if(!istype(H)) return
if(H.species.name == "Human")
- if(!H.shoes || prob(25))
+ if(!H.shoes && prob(50))
H.contract_disease(new /datum/disease/black_goo)
diff --git a/code/game/objects/effects/landmarks/corpsespawner.dm b/code/game/objects/effects/landmarks/corpsespawner.dm
index 8350e68d66a3..bcedcb85f47e 100644
--- a/code/game/objects/effects/landmarks/corpsespawner.dm
+++ b/code/game/objects/effects/landmarks/corpsespawner.dm
@@ -201,3 +201,9 @@
/obj/effect/landmark/corpsespawner/gladiator/burst
name = "Burst Gladiator"
equip_path = /datum/equipment_preset/corpse/gladiator/burst
+
+//FORECON
+
+/obj/effect/landmark/corpsespawner/forecon_spotter
+ name = "USCM Reconnaissance Spotter"
+ equip_path = /datum/equipment_preset/corpse/forecon_spotter
diff --git a/code/game/objects/effects/landmarks/landmarks.dm b/code/game/objects/effects/landmarks/landmarks.dm
index 1cbe10c497f6..5f4a374ba31c 100644
--- a/code/game/objects/effects/landmarks/landmarks.dm
+++ b/code/game/objects/effects/landmarks/landmarks.dm
@@ -378,6 +378,8 @@
name = "late join"
icon_state = "x2"
var/squad
+ /// What job should latejoin on this landmark
+ var/job
/obj/effect/landmark/late_join/alpha
name = "alpha late join"
@@ -396,16 +398,24 @@
squad = SQUAD_MARINE_4
+/obj/effect/landmark/late_join/working_joe
+ name = "working joe late join"
+ job = JOB_WORKING_JOE
+
/obj/effect/landmark/late_join/Initialize(mapload, ...)
. = ..()
if(squad)
LAZYADD(GLOB.latejoin_by_squad[squad], src)
+ else if(job)
+ LAZYADD(GLOB.latejoin_by_job[job], src)
else
GLOB.latejoin += src
/obj/effect/landmark/late_join/Destroy()
if(squad)
LAZYREMOVE(GLOB.latejoin_by_squad[squad], src)
+ else if(job)
+ LAZYREMOVE(GLOB.latejoin_by_job[job], src)
else
GLOB.latejoin -= src
return ..()
diff --git a/code/game/objects/effects/landmarks/survivor_spawner.dm b/code/game/objects/effects/landmarks/survivor_spawner.dm
index b7a2e11fb0aa..d19bbbe49516 100644
--- a/code/game/objects/effects/landmarks/survivor_spawner.dm
+++ b/code/game/objects/effects/landmarks/survivor_spawner.dm
@@ -117,7 +117,6 @@
spawn_priority = SPAWN_PRIORITY_VERY_HIGH
-
//Military Survivors//
/obj/effect/landmark/survivor_spawner/lv522_forecon_tech
@@ -132,10 +131,36 @@
equipment = /datum/equipment_preset/survivor/forecon/smartgunner
spawn_priority = SPAWN_PRIORITY_MEDIUM
-/obj/effect/landmark/survivor_spawner/lv522_forecon_grenadier
- equipment = /datum/equipment_preset/survivor/forecon/grenadier
+/obj/effect/landmark/survivor_spawner/lv522_forecon_sniper
+ equipment = /datum/equipment_preset/survivor/forecon/sniper
spawn_priority = SPAWN_PRIORITY_MEDIUM
/obj/effect/landmark/survivor_spawner/lv522_forecon_squad_leader
equipment = /datum/equipment_preset/survivor/forecon/squad_leader
spawn_priority = SPAWN_PRIORITY_HIGH
+
+/obj/effect/landmark/survivor_spawner/upp/soldier
+ equipment = /datum/equipment_preset/survivor/upp/soldier
+ synth_equipment = /datum/equipment_preset/synth/survivor/upp
+ spawn_priority = SPAWN_PRIORITY_MEDIUM
+
+/obj/effect/landmark/survivor_spawner/upp_sapper
+ equipment = /datum/equipment_preset/survivor/upp/sapper
+ synth_equipment = /datum/equipment_preset/synth/survivor/upp
+ spawn_priority = SPAWN_PRIORITY_MEDIUM
+
+/obj/effect/landmark/survivor_spawner/upp_medic
+ equipment = /datum/equipment_preset/survivor/upp/medic
+ synth_equipment = /datum/equipment_preset/synth/survivor/upp
+ spawn_priority = SPAWN_PRIORITY_HIGH
+
+/obj/effect/landmark/survivor_spawner/upp_specialist
+ equipment = /datum/equipment_preset/survivor/upp/specialist
+ synth_equipment = /datum/equipment_preset/synth/survivor/upp
+ spawn_priority = SPAWN_PRIORITY_HIGH
+
+/obj/effect/landmark/survivor_spawner/squad_leader
+ equipment = /datum/equipment_preset/survivor/upp/squad_leader
+ synth_equipment = /datum/equipment_preset/synth/survivor/upp
+ spawn_priority = SPAWN_PRIORITY_VERY_HIGH
+
diff --git a/code/game/objects/effects/spawners/wo_spawners/supplies.dm b/code/game/objects/effects/spawners/wo_spawners/supplies.dm
index f2ef887d076c..28f9936b972f 100644
--- a/code/game/objects/effects/spawners/wo_spawners/supplies.dm
+++ b/code/game/objects/effects/spawners/wo_spawners/supplies.dm
@@ -175,7 +175,7 @@
/obj/effect/landmark/wo_supplies/storage/belts/m39holster
icon_state = "m39_holster"
amount = list(1,5)
- stuff = list(/obj/item/storage/large_holster/m39)
+ stuff = list(/obj/item/storage/belt/gun/m39)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 2edfc3d6ea2b..dd5e99545d11 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1064,3 +1064,7 @@ cases. Override_icon_state should be a list.*/
animate(attack_image, alpha = 175, transform = copy_transform.Scale(0.75), pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3)
animate(time = 1)
animate(alpha = 0, time = 3, easing = CIRCULAR_EASING|EASE_OUT)
+
+///Called by /mob/living/carbon/swap_hand() when hands are swapped
+/obj/item/proc/hands_swapped(mob/living/carbon/swapper_of_hands)
+ return
diff --git a/code/game/objects/items/backpack_sprayers.dm b/code/game/objects/items/backpack_sprayers.dm
index 09f620c345d5..427a1dd597c7 100644
--- a/code/game/objects/items/backpack_sprayers.dm
+++ b/code/game/objects/items/backpack_sprayers.dm
@@ -8,6 +8,7 @@
w_class = SIZE_LARGE
flags_equip_slot = SLOT_BACK
flags_atom = OPENCONTAINER
+ possible_transfer_amounts = null//no point giving it possibility when mister can't it just confuse people
volume = 500
var/fill_reagent = "water"
var/spawn_empty = FALSE
@@ -83,8 +84,10 @@
/obj/item/reagent_container/glass/watertank/verb/toggle_mister_verb()
set name = "Toggle Mister"
set category = "Object"
+ set src in usr
toggle_mister(usr)
+
/obj/item/reagent_container/glass/watertank/MouseDrop(obj/over_object as obj)
if(!CAN_PICKUP(usr, src))
return ..()
@@ -132,7 +135,7 @@
item_state = "nozzle"
w_class = SIZE_LARGE
flags_equip_slot = null
- amount_per_transfer_from_this = 50
+ amount_per_transfer_from_this = 5
possible_transfer_amounts = null
spray_size = 5
volume = 500
diff --git a/code/game/objects/items/devices/cictablet.dm b/code/game/objects/items/devices/cictablet.dm
index 1a4aebe813cc..b2707a20aa90 100644
--- a/code/game/objects/items/devices/cictablet.dm
+++ b/code/game/objects/items/devices/cictablet.dm
@@ -53,6 +53,7 @@
data["faction"] = announcement_faction
data["cooldown_message"] = cooldown_between_messages
+ data["distresstimelock"] = DISTRESS_TIME_LOCK
return data
@@ -63,7 +64,6 @@
data["evac_status"] = EvacuationAuthority.evac_status
data["endtime"] = announcement_cooldown
data["distresstime"] = distress_cooldown
- data["distresstimelock"] = DISTRESS_TIME_LOCK
data["worldtime"] = world.time
return data
diff --git a/code/game/objects/items/devices/defibrillator.dm b/code/game/objects/items/devices/defibrillator.dm
index 0596ae9d14b8..30d0467a9b76 100644
--- a/code/game/objects/items/devices/defibrillator.dm
+++ b/code/game/objects/items/devices/defibrillator.dm
@@ -11,7 +11,10 @@
w_class = SIZE_MEDIUM
var/blocked_by_suit = TRUE
- var/heart_damage_to_deal = 5
+ /// Min damage defib deals to victims' heart
+ var/min_heart_damage_dealt = 3
+ /// Max damage defib deals to victims' heart
+ var/max_heart_damage_dealt = 5
var/ready = 0
var/damage_heal_threshold = 12 //This is the maximum non-oxy damage the defibrillator will heal to get a patient above -100, in all categories
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
@@ -191,8 +194,11 @@
shock_cooldown = world.time + 10 //1 second cooldown before you can shock again
var/datum/internal_organ/heart/heart = H.internal_organs_by_name["heart"]
+ /// Has the defib already caused the chance of heart damage, to not potentially double up later
+ var/heart_already_damaged = FALSE
if(heart && prob(25))
- heart.take_damage(heart_damage_to_deal, TRUE) //Allow the defibrillator to possibly worsen heart damage. Still rare enough to just be the "clone damage" of the defib
+ heart.take_damage(rand(min_heart_damage_dealt, max_heart_damage_dealt), TRUE) // Make death and revival leave lasting consequences
+ heart_already_damaged = TRUE
if(!H.is_revivable())
playsound(get_turf(src), 'sound/items/defib_failed.ogg', 25, 0)
@@ -230,6 +236,9 @@
user.track_life_saved(user.job)
user.life_revives_total++
H.handle_revive()
+ if(heart && !heart_already_damaged)
+ heart.take_damage(rand(min_heart_damage_dealt, max_heart_damage_dealt), TRUE) // Make death and revival leave lasting consequences
+
to_chat(H, SPAN_NOTICE("You suddenly feel a spark and your consciousness returns, dragging you back to the mortal plane."))
if(H.client?.prefs.toggles_flashing & FLASH_CORPSEREVIVE)
window_flash(H.client)
@@ -239,13 +248,14 @@
/obj/item/device/defibrillator/compact_adv
name = "advanced compact defibrillator"
- desc = "An advanced compact defibrillator that trades capacity for strong immediate power. Ignores armor and heals strongly and quickly, at the cost of very low charge."
+ desc = "An advanced compact defibrillator that trades capacity for strong immediate power. Ignores armor and heals strongly and quickly, at the cost of very low charge. It does not damage the heart."
icon = 'icons/obj/items/experimental_tools.dmi'
icon_state = "compact_defib"
item_state = "defib"
w_class = SIZE_MEDIUM
blocked_by_suit = FALSE
- heart_damage_to_deal = 0
+ min_heart_damage_dealt = 0
+ max_heart_damage_dealt = 0
damage_heal_threshold = 40
charge_cost = 198
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 07b56eedb7d2..9bcda4a82bbb 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -441,7 +441,7 @@
if(mapload)
return INITIALIZE_HINT_QDEL
. = ..()
- fuel = rand(5 SECONDS, 60 SECONDS)
+ fuel = rand(30 SECONDS, 60 SECONDS)
/obj/item/device/flashlight/flare/on/illumination/chemical
name = "chemical light"
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 6a01dfdba9da..c6da7d354054 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -28,6 +28,11 @@
var/message = tgui_input_text(user, "Shout a message?", "Megaphone", multiline = TRUE)
if(!message)
return
+ // we know user is a human now, so adjust user for this check
+ var/mob/living/carbon/human/humanoid = user
+ if(humanoid.speech_problem_flag)
+ var/list/new_message = humanoid.handle_speech_problems(message)
+ message = new_message[1]
message = capitalize(message)
log_admin("[key_name(user)] used a megaphone to say: >[message]<")
diff --git a/code/game/objects/items/devices/motion_detector.dm b/code/game/objects/items/devices/motion_detector.dm
index ade74531bc91..dd0c5d45eda4 100644
--- a/code/game/objects/items/devices/motion_detector.dm
+++ b/code/game/objects/items/devices/motion_detector.dm
@@ -229,11 +229,14 @@
if(human_user)
show_blip(human_user, M)
- for(var/mob/hologram/queen/Q in GLOB.hologram_list)
- if(Q.z != cur_turf.z || !(range_bounds.contains_atom(Q))) continue
+ for(var/mob/hologram/holo as anything in GLOB.hologram_list)
+ if(!holo.motion_sensed)
+ continue
+ if(holo.z != cur_turf.z || !(range_bounds.contains_atom(holo)))
+ continue
ping_count++
if(human_user)
- show_blip(human_user, Q, "queen_eye")
+ show_blip(human_user, holo, "queen_eye")
if(ping_count > 0)
playsound(loc, pick('sound/items/detector_ping_1.ogg', 'sound/items/detector_ping_2.ogg', 'sound/items/detector_ping_3.ogg', 'sound/items/detector_ping_4.ogg'), 60, 0, 7, 2)
diff --git a/code/game/objects/items/devices/portable_vendor.dm b/code/game/objects/items/devices/portable_vendor.dm
index 0b96b859c4ac..875087efbff0 100644
--- a/code/game/objects/items/devices/portable_vendor.dm
+++ b/code/game/objects/items/devices/portable_vendor.dm
@@ -270,4 +270,7 @@
list("MISC", 0, null, null, null),
list("Hollow Cane", 15, /obj/item/weapon/pole/fancy_cane/this_is_a_knife, "white", "A hollow cane that can store any commonplace sharp weaponry. Said weapon not included."),
+
+ list("AMMO", 0, null, null, null),
+ list("ES-4 stun magazine", 10, /obj/item/ammo_magazine/pistol/es4, "white", "Holds 19 rounds of specialized Conductive 9mm."),
)
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index d45b4e8b8212..d724e1cec10f 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -68,6 +68,9 @@
icon_state = "cap_key"
channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+/obj/item/device/encryptionkey/mcom/alt
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+
// MARINE ENGINEERING
/obj/item/device/encryptionkey/ce
@@ -85,7 +88,7 @@
/obj/item/device/encryptionkey/cmo
name = "Chief Medical Officer's Encryption Key"
icon_state = "cmo_key"
- channels = list(RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_COMMAND = TRUE)
+ channels = list(RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/med
name = "Medical Radio Encryption Key"
@@ -116,7 +119,7 @@
// MARINE REQUISTIONS
-/obj/item/device/encryptionkey/ro
+/obj/item/device/encryptionkey/qm
name = "Requisition Officer's Encryption Key"
icon_state = "ce_key"
channels = list(RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_ENGI = FALSE, RADIO_CHANNEL_MEDSCI = FALSE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE)
@@ -215,6 +218,11 @@
icon_state = "binary_key"
channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_INTEL = TRUE, RADIO_CHANNEL_JTAC = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE)
+/obj/item/device/encryptionkey/soc/forecon
+ name = "\improper SOF Radio Encryption Key"
+ icon_state = "binary_key"
+ channels = list(RADIO_CHANNEL_COLONY= TRUE)
+
//ERT, PMC
/obj/item/device/encryptionkey/dutch
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index c3b1eee806c5..7e6b0db056d0 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -451,11 +451,11 @@
icon_state = "req_headset"
initial_keys = list(/obj/item/device/encryptionkey/req/ct)
-/obj/item/device/radio/headset/almayer/ro
- desc = "A headset used by the RO for controlling their slave(s). Channels are as follows: :u - requisitions, :v - marine command, :a - alpha squad, :b - bravo squad, :c - charlie squad, :d - delta squad."
+/obj/item/device/radio/headset/almayer/qm
+ desc = "A headset used by the quartermaster for controlling their slave(s). Channels are as follows: :u - requisitions, :v - marine command, :a - alpha squad, :b - bravo squad, :c - charlie squad, :d - delta squad."
name = "requisition officer radio headset"
icon_state = "ro_headset"
- initial_keys = list(/obj/item/device/encryptionkey/ro)
+ initial_keys = list(/obj/item/device/encryptionkey/qm)
volume = RADIO_VOLUME_CRITICAL
multibroadcast_cooldown = LOW_MULTIBROADCAST_COOLDOWN
@@ -494,6 +494,9 @@
volume = RADIO_VOLUME_CRITICAL
multibroadcast_cooldown = LOW_MULTIBROADCAST_COOLDOWN
+/obj/item/device/radio/headset/almayer/mcom/alt
+ initial_keys = list(/obj/item/device/encryptionkey/mcom/alt)
+
/obj/item/device/radio/headset/almayer/marine/mp_honor/com
name = "marine honor guard command radio headset"
desc = "Given to highly trusted marine honor guard only. It features a non-standard brace. Channels are as follows: :v - marine command, :p - military police, :n - engineering, :m - medbay, :u - requisitions, :a - alpha squad, :b - bravo squad, :c - charlie squad, :d - delta squad."
@@ -1002,6 +1005,17 @@
volume = RADIO_VOLUME_IMPORTANT
ignore_z = TRUE
+/obj/item/device/radio/headset/almayer/sof/survivor_forecon
+ name = "USCM SOF headset"
+ desc = "Issued exclusively to Marine Raiders and members of the USCM's Force Reconnaissance."
+ icon_state = "soc_headset"
+ frequency = SOF_FREQ
+ initial_keys = list(/obj/item/device/encryptionkey/soc/forecon)
+ volume = RADIO_VOLUME_QUIET
+ ignore_z = FALSE
+ has_hud = TRUE
+ hud_type = MOB_HUD_FACTION_USCM
+
/obj/item/device/radio/headset/almayer/mcom/vc
name = "marine vehicle crew radio headset"
desc = "Used by USCM vehicle crew, features a non-standard brace. Channels are as follows: :v - marine command, :n - engineering, :m - medbay, :u - requisitions"
diff --git a/code/game/objects/items/explosives/grenades/marines.dm b/code/game/objects/items/explosives/grenades/marines.dm
index 1892c7543414..a8e0e1803a33 100644
--- a/code/game/objects/items/explosives/grenades/marines.dm
+++ b/code/game/objects/items/explosives/grenades/marines.dm
@@ -547,31 +547,32 @@
ram_distance -- //for max pinballing.
icon_state = inactive_icon
-/obj/item/explosive/grenade/slug/proc/impact_mob(mob/living/M)
- var/direction = Get_Angle(src,M)
- var/target_turf = get_angle_target_turf(src,direction,throw_max)
- var/fling = rand(throw_min,throw_max) //WEEEEEEEEEEEEEEEEEEEE What is going to be put into throw_atom
+/obj/item/explosive/grenade/slug/proc/impact_mob(mob/living/smacked)
+ var/direction = Get_Angle(src, smacked)
+ var/target_turf = get_angle_target_turf(src,direction, throw_max)
+ var/fling = rand(throw_min, throw_max) //WEEEEEEEEEEEEEEEEEEEE What is going to be put into throw_atom
var/random_tile = 0 //random tile for bounce
- playsound(M.loc, impact_sound, 75, 1)
- M.apply_damage(impact_damage, BRUTE)
+ playsound(smacked.loc, impact_sound, 75, 1)
+ smacked.apply_damage(impact_damage, BRUTE)
+ smacked.attack_log += "\[[time_stamp()]\] [src], fired by [fingerprintslast], struck [key_name(smacked)]."
random_tile = get_random_turf_in_range(src,ram_distance,ram_distance) //getting random tile for bounce
src.throw_atom(random_tile,ram_distance,SPEED_FAST,src,TRUE,NORMAL_LAUNCH,NO_FLAGS) //time for a little trolling
- if(isyautja(M)|| issynth(M))
- M.apply_effect(slowdown_time * 0.5, SLOW)
- M.apply_effect(dazed_time * 0.5, DAZE)
+ if(isyautja(smacked)|| issynth(smacked))
+ smacked.apply_effect(slowdown_time * 0.5, SLOW)
+ smacked.apply_effect(dazed_time * 0.5, DAZE)
- if(M.mob_size >= MOB_SIZE_BIG)//big xenos not KO'ed
- M.apply_effect(slowdown_time * 1.2, SLOW)//They are slowed more :trol:
- M.apply_effect(dazed_time * 1.2, DAZE)
+ if(smacked.mob_size >= MOB_SIZE_BIG)//big xenos not KO'ed
+ smacked.apply_effect(slowdown_time * 1.2, SLOW)//They are slowed more :trol:
+ smacked.apply_effect(dazed_time * 1.2, DAZE)
return
- M.apply_effect(knockout_time, WEAKEN)//but little xenos and humans are
- M.throw_atom(target_turf,fling,SPEED_AVERAGE,M,TRUE)
- M.apply_effect(slowdown_time, SLOW)
- M.apply_effect(dazed_time, DAZE)
+ smacked.apply_effect(knockout_time, WEAKEN)//but little xenos and humans are
+ smacked.throw_atom(target_turf, fling, SPEED_AVERAGE, smacked, TRUE)
+ smacked.apply_effect(slowdown_time, SLOW)
+ smacked.apply_effect(dazed_time, DAZE)
return
/obj/item/explosive/grenade/slug/baton
diff --git a/code/game/objects/items/stacks/predator.dm b/code/game/objects/items/stacks/predator.dm
index e500932b08f6..42874b907e02 100644
--- a/code/game/objects/items/stacks/predator.dm
+++ b/code/game/objects/items/stacks/predator.dm
@@ -51,6 +51,8 @@
SPAN_NOTICE("You start hanging [victim] up by the rope..."))
if(!do_after(user, 3 SECONDS, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE, victim))
return
+ if(victim.anchored)
+ return // Just in case weed_food took them during this time
user.visible_message(SPAN_WARNING("[user] hangs [victim] from the ceiling!"), SPAN_NOTICE("You finish hanging [victim]."))
user.stop_pulling()
victim.get_hung()
@@ -106,4 +108,5 @@
apply_transform(A)
pixel_x = 0
pixel_y = 0
+ Moved(loc, NONE, TRUE) // Trigger any movement signals
return COMPONENT_CANCEL_ATTACK
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 34719747bd02..98a7ab036f06 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -144,6 +144,7 @@ var/global/list/datum/stack_recipe/wood_recipes = list ( \
/*
new/datum/stack_recipe("table parts", /obj/item/frame/table/wood, 2), \
*/
+ new/datum/stack_recipe("campfire", /obj/structure/prop/brazier/frame/full/campfire, 5, time = 15, one_per_turf = ONE_TYPE_PER_TURF, on_floor = TRUE), \
new/datum/stack_recipe("wooden chair", /obj/structure/bed/chair/wood/normal, 1, time = 10, one_per_turf = ONE_TYPE_PER_TURF, on_floor = 1), \
new/datum/stack_recipe("wooden barricade", /obj/structure/barricade/wooden, 5, time = 20, one_per_turf = ONE_TYPE_PER_BORDER, on_floor = 1), \
new/datum/stack_recipe("wooden crate", /obj/structure/closet/coffin/woodencrate, 5, time = 15, one_per_turf = ONE_TYPE_PER_TURF, on_floor = 1), \
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index e36225177d91..3bf3656f4bd2 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -437,6 +437,10 @@
xeno_icon_state = "medicpack"
xeno_types = list(/mob/living/carbon/xenomorph/runner, /mob/living/carbon/xenomorph/praetorian, /mob/living/carbon/xenomorph/drone, /mob/living/carbon/xenomorph/warrior, /mob/living/carbon/xenomorph/defender, /mob/living/carbon/xenomorph/sentinel, /mob/living/carbon/xenomorph/spitter)
+/obj/item/storage/backpack/marine/medic/upp
+ name = "\improper UPP corpsman backpack"
+ desc = "Uncommon issue backpack worn by UPP medics from isolated sectors. You can swear you can see a faded USCM symbol."
+
/obj/item/storage/backpack/marine/tech
name = "\improper USCM technician backpack"
desc = "A standard-issue backpack worn by USCM technicians."
@@ -914,6 +918,16 @@ GLOBAL_LIST_EMPTY_TYPED(radio_packs, /obj/item/storage/backpack/marine/satchel/r
max_fuel = 100
worn_accessible = TRUE
+/obj/item/storage/backpack/marine/engineerpack/welder_chestrig
+ name = "\improper Technician Welder Chestrig"
+ desc = "A specialized Chestrig worn by technicians and engineers. It carries one medium fuel tank for quick welder refueling and use."
+ icon_state = "welder_chestrig"
+ item_state = "welder_chestrig"
+ max_storage_space = 12
+ has_gamemode_skin = FALSE
+ max_fuel = 100
+ worn_accessible = TRUE
+
// Pyrotechnician Spec backpack fuel tank
/obj/item/storage/backpack/marine/engineerpack/flamethrower
name = "\improper USCM Pyrotechnician G6-2 fueltank"
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 290460da758f..7721a9643fb5 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -16,6 +16,26 @@
///TRUE Means that it closes a flap over its contents, and therefore update_icon should lift that flap when opened. If it doesn't have _half and _full iconstates, this doesn't matter either way.
var/flap = TRUE
+/obj/item/storage/belt/gun/flaregun/dump_into(obj/item/storage/origin_storage, mob/user)
+
+ if(length(holstered_guns) < 1 && length(contents) >= (storage_slots-1))
+
+ to_chat(user, SPAN_WARNING("[src] is full."))
+ return FALSE
+ return ..()
+
+/obj/item/storage/belt/gun/flaregun/handle_item_insertion(obj/item/new_item, prevent_warning = FALSE, mob/user)
+
+ if(istype(new_item, /obj/item/device/flashlight/flare) && length(holstered_guns) < 1 && length(contents) >= (storage_slots-1))
+ return FALSE
+ return ..()
+
+/obj/item/storage/belt/gun/flaregun/has_room(obj/item/new_item, W_class_override = null)
+
+ if(istype(new_item, /obj/item/device/flashlight/flare) && length(holstered_guns) < 1 && length(contents) >= (storage_slots-1))
+ return FALSE //No slot open because gun in holster.
+ return ..()
+
/obj/item/storage/belt/equipped(mob/user, slot)
switch(slot)
if(WEAR_WAIST, WEAR_J_STORE, WEAR_BACK)
@@ -283,6 +303,19 @@
new /obj/item/storage/pill_bottle/tramadol(src)
new /obj/item/storage/pill_bottle/peridaxon(src)
+/obj/item/storage/belt/medical/lifesaver/upp/partial/fill_preset_inventory()
+ new /obj/item/stack/medical/advanced/bruise_pack(src)
+ new /obj/item/stack/medical/advanced/bruise_pack(src)
+ new /obj/item/stack/medical/advanced/ointment(src)
+ new /obj/item/stack/medical/advanced/ointment(src)
+ new /obj/item/stack/medical/splint(src)
+ new /obj/item/stack/medical/splint(src)
+ new /obj/item/reagent_container/hypospray/autoinjector/oxycodone(src)
+ new /obj/item/storage/pill_bottle/bicaridine(src)
+ new /obj/item/storage/pill_bottle/kelotane(src)
+ new /obj/item/storage/pill_bottle/inaprovaline(src)
+ new /obj/item/storage/pill_bottle/tramadol(src)
+
/obj/item/storage/belt/security
name = "\improper M276 pattern security rig"
desc = "The M276 is the standard load-bearing equipment of the USCM. It consists of a modular belt with various clips. This configuration is commonly seen among USCM Military Police and peacekeepers, though it can hold some light munitions."
@@ -753,7 +786,7 @@
for(var/i = 1 to storage_slots)
new /obj/item/weapon/throwing_knife(src)
-/obj/item/storage/belt/knifepouch/_item_insertion(obj/item/W, prevent_warning = 0)
+/obj/item/storage/belt/knifepouch/_item_insertion(obj/item/new_item, prevent_warning = FALSE)
..()
playsound(src, 'sound/weapons/gun_shotgun_shell_insert.ogg', 15, TRUE)
@@ -900,6 +933,7 @@
for(var/slot in holster_slots)
if(AM == holster_slots[slot]["gun"])
holster_slots[slot]["gun"] = null
+
update_gun_icon(slot)
return
@@ -975,7 +1009,7 @@
to_chat(usr, SPAN_WARNING("[src] can't hold any more ammo."))
return FALSE
-/obj/item/storage/belt/gun/_item_insertion(obj/item/W, prevent_warning = 0)
+/obj/item/storage/belt/gun/_item_insertion(obj/item/W, prevent_warning = FALSE)
if(isgun(W))
holstered_guns += W
for(var/slot in holster_slots)
@@ -1133,6 +1167,22 @@
for(var/i = 1 to storage_slots - 1)
new /obj/item/ammo_magazine/pistol/highpower/black(src)
+/obj/item/storage/belt/gun/m39
+ name = "\improper M276 pattern M39 holster rig"
+ desc = "Special issue variant of the M276 designed to holster a M39 submachine gun and two spare magazines. Uncommonly issued to USCM support and specialist personnel."
+ icon_state = "m39_armor"
+ item_state = "s_marinebelt"
+ storage_slots = 3
+ max_w_class = 5
+ can_hold = list(
+ /obj/item/weapon/gun/smg/m39,
+ /obj/item/ammo_magazine/smg,
+ )
+ holster_slots = list(
+ "1" = list(
+ "icon_x" = -11,
+ "icon_y" = -5))
+
/obj/item/storage/belt/gun/m44
name = "\improper M276 pattern M44 holster rig"
desc = "The M276 is the standard load-bearing equipment of the USCM. It consists of a modular belt with various clips. This version is for the M44 magnum revolver, along with six small pouches for speedloaders. It smells faintly of hay."
diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm
index 2e54d725511d..ea43d6b074b9 100644
--- a/code/game/objects/items/storage/fancy.dm
+++ b/code/game/objects/items/storage/fancy.dm
@@ -12,6 +12,8 @@
* Crayon Box
* Cigarette Box
* Cigar Box
+ * Match Box
+ * Vial Box
*/
/obj/item/storage/fancy
@@ -20,9 +22,10 @@
name = "donut box"
desc = "A box where round, heavenly, holey pastries reside."
var/icon_type = "donut"
+ var/plural = "s"
/obj/item/storage/fancy/update_icon()
- icon_state = "[icon_type]box[contents.len]"
+ icon_state = "[icon_type]box[length(contents)]"
/obj/item/storage/fancy/remove_from_storage(obj/item/W, atom/new_location)
. = ..()
@@ -32,17 +35,14 @@
/obj/item/storage/fancy/get_examine_text(mob/user)
. = ..()
- if(contents.len <= 0)
- . += "There are no [src.icon_type]s left in the box."
- else if(contents.len == 1)
+ if(!length(contents))
+ . += "There are no [src.icon_type][plural] left in the box."
+ else if(length(contents) == 1)
. += "There is one [src.icon_type] left in the box."
else
- . += "There are [src.contents.len] [src.icon_type]s in the box."
-
+ . += "There are [length(src.contents)] [src.icon_type][plural] in the box."
-/*
- * Egg Box
- */
+// EGG BOX
/obj/item/storage/fancy/egg_box
icon = 'icons/obj/items/food.dmi'
@@ -59,9 +59,7 @@
new /obj/item/reagent_container/food/snacks/egg(src)
return
-/*
- * Candle Box
- */
+// CANDLE BOX
/obj/item/storage/fancy/candle_box
name = "candle pack"
@@ -80,9 +78,7 @@
new /obj/item/tool/candle(src)
return
-/*
- * Crayon Box
- */
+// CRAYON BOX
/obj/item/storage/fancy/crayons
name = "box of crayons"
@@ -120,9 +116,8 @@
return
..()
-////////////
-//CIG PACK//
-////////////
+// CIGARETTES BOX
+
/obj/item/storage/fancy/cigarettes
icon = 'icons/obj/items/cigarettes.dmi'
icon_state = "cigpacket"
@@ -153,14 +148,14 @@
icon_state = "[initial(icon_state)]"
/obj/item/storage/fancy/cigarettes/update_icon()
- icon_state = "[initial(icon_state)][contents.len]"
+ icon_state = "[initial(icon_state)][length(contents)]"
return
/obj/item/storage/fancy/cigarettes/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
if(!istype(M, /mob))
return
- if(M == user && user.zone_selected == "mouth" && contents.len > 0 && !user.wear_mask)
+ if(M == user && user.zone_selected == "mouth" && length(contents) > 0 && !user.wear_mask)
var/obj/item/clothing/mask/cigarette/C = locate() in src
if(C)
remove_from_storage(C, get_turf(user))
@@ -223,9 +218,7 @@
default_cig_type = /obj/item/clothing/mask/cigarette/ucigarette
storage_slots = 4
-/////////////
-//CIGAR BOX//
-/////////////
+// CIGAR BOX
/obj/item/storage/fancy/cigar
name = "cigar case"
@@ -253,14 +246,14 @@
icon_state = "[initial(icon_state)]"
/obj/item/storage/fancy/cigar/update_icon()
- icon_state = "[initial(icon_state)][contents.len]"
+ icon_state = "[initial(icon_state)][length(contents)]"
return
/obj/item/storage/fancy/cigar/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
if(!istype(M, /mob))
return
- if(M == user && user.zone_selected == "mouth" && contents.len > 0 && !user.wear_mask)
+ if(M == user && user.zone_selected == "mouth" && length(contents) > 0 && !user.wear_mask)
var/obj/item/clothing/mask/cigarette/cigar/C = locate() in src
if(C)
remove_from_storage(C, get_turf(user))
@@ -286,6 +279,8 @@
storage_slots = 1
default_cigar_type = /obj/item/clothing/mask/cigarette/cigar/tarbacks
+// MATCH BOX
+
/obj/item/storage/fancy/cigar/matchbook
name = "\improper Lucky Strikes matchbook"
desc = "A small book of cheap paper matches. Good luck getting them to light. Made by Lucky Strikes, but you'll be anything but lucky when you burn your hand trying to light a match on this."
@@ -298,6 +293,7 @@
w_class = SIZE_TINY
var/light_chance = 70 //how likely you are to light the match on the book
var/burn_chance = 20 //how likely you are to burn yourself once you light it
+ plural = "es"
/obj/item/storage/fancy/cigar/matchbook/attackby(obj/item/tool/match/W as obj, mob/living/carbon/human/user as mob)
if(!istype(user))
@@ -340,9 +336,7 @@
light_chance = 60
burn_chance = 40
-/*
- * Vial Box
- */
+// VIAL BOX
/obj/item/storage/fancy/vials
icon = 'icons/obj/items/vialbox.dmi'
@@ -401,7 +395,7 @@
req_access = list(ACCESS_MARINE_MEDBAY)
/obj/item/storage/lockbox/vials/update_icon(itemremoved = 0)
- var/total_contents = src.contents.len - itemremoved
+ var/total_contents = length(src.contents) - itemremoved
src.icon_state = "vialbox[total_contents]"
src.overlays.Cut()
if (!broken)
diff --git a/code/game/objects/items/storage/firstaid.dm b/code/game/objects/items/storage/firstaid.dm
index 2717b7c98501..0d976b4cdf57 100644
--- a/code/game/objects/items/storage/firstaid.dm
+++ b/code/game/objects/items/storage/firstaid.dm
@@ -212,6 +212,22 @@
/obj/item/storage/firstaid/surgical/empty/fill_preset_inventory()
return
+//---------TOOLKIT---------
+
+/obj/item/storage/firstaid/toolkit
+ name = "toolkit"
+ desc = "An combat engineering toolkit intended to carry electrical and mechanical supplies into combat."
+ icon_state = "toolkit"
+ item_state = "fulton"
+
+/obj/item/storage/firstaid/toolkit/update_icon()
+ if(content_watchers || !length(contents))
+ icon_state = "toolkit_empty"
+ else
+ icon_state = icon_full
+
+/obj/item/storage/firstaid/toolkit/empty/fill_preset_inventory()
+ return
//---------SYRINGE CASE---------
@@ -265,7 +281,14 @@
/obj/item/storage/surgical_case
name = "surgical case"
- desc = "It's a medical case for storing basic surgical tools."
+ desc = "It's a medical case for storing basic surgical tools. It comes with a brief description for treating common internal bleeds.\
+ \nBefore surgery: Verify correct location and patient is adequately numb to pain.\
+ \nStep one: Open an incision at the site with the scalpel.\
+ \nStep two: Clamp bleeders with the hemostat.\
+ \nStep three: Draw back the skin with the retracter.\
+ \nStep four: Patch the damaged vein with a surgical line.\
+ \nStep five: Close the incision with a surgical line."
+
icon_state = "surgical_case"
throw_speed = SPEED_FAST
throw_range = 8
@@ -448,6 +471,22 @@
..()
update_icon()
+/obj/item/storage/pill_bottle/attack_hand(mob/user, mods)
+ if(loc != user)
+ return ..()
+
+ if(!mods || !mods["alt"])
+ return ..()
+
+ if(!ishuman(user))
+ return ..()
+
+ if(skilllock && !skillcheck(user, SKILL_MEDICAL, SKILL_MEDICAL_MEDIC))
+ error_idlock(user)
+ return FALSE
+
+ return ..()
+
/obj/item/storage/pill_bottle/proc/error_idlock(mob/user)
to_chat(user, SPAN_WARNING("It must have some kind of ID lock..."))
diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm
index aa9d91921fae..30be2f6bc2e2 100644
--- a/code/game/objects/items/storage/lockbox.dm
+++ b/code/game/objects/items/storage/lockbox.dm
@@ -54,11 +54,9 @@
req_access = list(ACCESS_WY_CORPORATE)
/obj/item/storage/lockbox/loyalty/fill_preset_inventory()
- new /obj/item/ammo_magazine/pistol/mod88(src)
- new /obj/item/ammo_magazine/pistol/mod88(src)
- new /obj/item/ammo_magazine/pistol/mod88/rubber(src)
- new /obj/item/ammo_magazine/pistol/mod88/rubber(src)
-
+ new /obj/item/ammo_magazine/pistol/es4(src)
+ new /obj/item/ammo_magazine/pistol/es4(src)
+ new /obj/item/ammo_magazine/pistol/es4(src)
/obj/item/storage/lockbox/cluster
name = "lockbox of cluster flashbangs"
diff --git a/code/game/objects/items/storage/pouch.dm b/code/game/objects/items/storage/pouch.dm
index dc3ee0ba1506..6397c33b76c1 100644
--- a/code/game/objects/items/storage/pouch.dm
+++ b/code/game/objects/items/storage/pouch.dm
@@ -1211,6 +1211,12 @@
new /obj/item/explosive/plastic(src)
new /obj/item/explosive/plastic(src)
+/obj/item/storage/pouch/tools/uppsynth/fill_preset_inventory()
+ new /obj/item/tool/crowbar(src)
+ new /obj/item/tool/wirecutters(src)
+ new /obj/item/tool/weldingtool(src)
+ new /obj/item/tool/wrench(src)
+
/obj/item/storage/pouch/sling
name = "sling strap"
desc = "Keeps a single item attached to a strap."
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm
index 36f946efdfdc..5a6b7d2b9b05 100644
--- a/code/game/objects/items/storage/storage.dm
+++ b/code/game/objects/items/storage/storage.dm
@@ -31,7 +31,6 @@
var/storage_flags = STORAGE_FLAGS_DEFAULT
var/has_gamemode_skin = FALSE ///Whether to use map-variant skins.
-
/obj/item/storage/MouseDrop(obj/over_object as obj)
if(CAN_PICKUP(usr, src))
if(over_object == usr) // this must come before the screen objects only block
@@ -378,12 +377,12 @@ var/list/global/item_storage_box_cache = list()
return
///Returns TRUE if there is room for the given item. W_class_override allows checking for just a generic W_class, meant for checking shotgun handfuls without having to spawn and delete one just to check.
-/obj/item/storage/proc/has_room(obj/item/W as obj, W_class_override = null)
+/obj/item/storage/proc/has_room(obj/item/new_item, W_class_override = null)
if(storage_slots != null && contents.len < storage_slots)
return TRUE //At least one open slot.
//calculate storage space only for containers that don't have slots
if (storage_slots == null)
- var/sum_storage_cost = W_class_override ? W_class_override : W.get_storage_cost() //Takes the override if there is one, the given item otherwise.
+ var/sum_storage_cost = W_class_override ? W_class_override : new_item.get_storage_cost() //Takes the override if there is one, the given item otherwise.
for(var/obj/item/I in contents)
sum_storage_cost += I.get_storage_cost() //Adds up the combined storage costs which will be in the storage item if the item is added to it.
@@ -455,23 +454,23 @@ That's done by can_be_inserted(). Its checks are whether the item exists, is an
The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple
items at once, such as when picking up all the items on a tile with one click.
user can be null, it refers to the potential mob doing the insertion.**/
-/obj/item/storage/proc/handle_item_insertion(obj/item/W, prevent_warning = 0, mob/user)
- if(!istype(W))
+/obj/item/storage/proc/handle_item_insertion(obj/item/new_item, prevent_warning = FALSE, mob/user)
+ if(!istype(new_item))
return FALSE
- if(user && W.loc == user)
- if(!user.drop_inv_item_to_loc(W, src))
+ if(user && new_item.loc == user)
+ if(!user.drop_inv_item_to_loc(new_item, src))
return FALSE
else
- W.forceMove(src)
+ new_item.forceMove(src)
- _item_insertion(W, prevent_warning, user)
+ _item_insertion(new_item, prevent_warning, user)
return TRUE
/**Inserts the item. Separate proc because handle_item_insertion isn't guaranteed to insert
and it therefore isn't safe to override it before calling parent. Updates icon when done.
Can be called directly but only if the item was spawned inside src - handle_item_insertion is safer.
W is always an item. stop_warning prevents messaging. user may be null.**/
-/obj/item/storage/proc/_item_insertion(obj/item/W, prevent_warning = 0, mob/user)
+/obj/item/storage/proc/_item_insertion(obj/item/W, prevent_warning = FALSE, mob/user)
W.on_enter_storage(src)
if(user)
if (user.client && user.s_active != src)
@@ -721,25 +720,26 @@ W is always an item. stop_warning prevents messaging. user may be null.**/
to_chat(user, SPAN_WARNING("[ammo_dumping] is empty."))
return TRUE
-/obj/item/storage/proc/dump_into(obj/item/storage/M, mob/user)
+/obj/item/storage/proc/dump_into(obj/item/storage/origin_storage, mob/user)
+
if(user.action_busy)
return
- if(!M.contents.len)
- to_chat(user, SPAN_WARNING("[M] is empty."))
+ if(!origin_storage.contents.len)
+ to_chat(user, SPAN_WARNING("[origin_storage] is empty."))
return
- if(!has_room(M.contents[1])) //Does it have room for the first item to be inserted?
+ if(!has_room(origin_storage.contents[1])) //Does it have room for the first item to be inserted?
to_chat(user, SPAN_WARNING("[src] is full."))
return
- to_chat(user, SPAN_NOTICE("You start refilling [src] with [M]."))
+ to_chat(user, SPAN_NOTICE("You start refilling [src] with [origin_storage]."))
if(!do_after(user, 1.5 SECONDS, INTERRUPT_ALL, BUSY_ICON_GENERIC))
return
- for(var/obj/item/I in M)
- if(!has_room(I))
+ for(var/obj/item/new_item in origin_storage)
+ if(!has_room(new_item))
break
- M.remove_from_storage(I)
- handle_item_insertion(I, TRUE, user) //quiet insertion
+ origin_storage.remove_from_storage(new_item)
+ handle_item_insertion(new_item, TRUE, user) //quiet insertion
playsound(user.loc, "rustle", 15, TRUE, 6)
return TRUE
diff --git a/code/game/objects/items/tools/misc_tools.dm b/code/game/objects/items/tools/misc_tools.dm
index 1bcebd9f1ea3..505006285881 100644
--- a/code/game/objects/items/tools/misc_tools.dm
+++ b/code/game/objects/items/tools/misc_tools.dm
@@ -276,13 +276,29 @@
pen_colour = "white"
/obj/item/tool/pen/fountain
- desc = "A luxurious fountain pen, embossed with gold accents. Its intricate mechanics allow the user to switch between various ink colors with a simple twist."
+ desc = "A lavish testament to the ingenuity of ARMAT's craftsmanship, this fountain pen is a paragon of design and functionality. Detailed with golden accents and intricate mechanics, the pen allows for a swift change between a myriad of ink colors with a simple twist. A product of precision engineering, each mechanism inside the pen is designed to provide a seamless, effortless transition from one color to the next, creating an instrument of luxurious versatility."
+ desc_lore = "More than just a tool for writing, ARMAT's fountain pen is a symbol of distinction and authority within the ranks of the United States Colonial Marine Corps (USCM). It is a legacy item, exclusively handed out to the top-tier command personnel, each pen a tribute to the recipient's leadership and dedication.\n \nARMAT, renowned for their weapons technology, took a different approach in crafting this piece. The fountain pen, though seemingly a departure from their usual field, is deeply ingrained with the company's engineering philosophy, embodying precision, functionality, and robustness.\n \nThe golden accents are not mere embellishments; they're an identifier, setting apart these pens and their owners from the rest. The gold is meticulously alloyed with a durable metallic substance, granting it resilience to daily wear and tear. Such resilience is symbolic of the tenacity and perseverance required of USCM command personnel.\n \nEach pen is equipped with an intricate color changing mechanism, allowing the user to switch between various ink colors. This feature, inspired by the advanced targeting systems of ARMAT's weaponry, uses miniaturized actuators and precision-ground components to smoothly transition the ink flow. A simple twist of the pen's body activates the change, rotating the internal ink cartridges into place with mechanical grace, ready for the user's command.\n \nThe ink colors are not chosen arbitrarily. Each represents a different echelon within the USCM, allowing the pen's owner to write in the hue that corresponds with their rank or the rank of the recipient of their written orders. This acts as a silent testament to the authority of their words, as if each stroke of the pen echoes through the halls of USCM authority.\n \nDespite its ornate appearance, the pen is as robust as any ARMAT weapon, reflecting the company's commitment to reliability and durability. The metal components are corrosion-resistant, ensuring the pen's longevity, even under the challenging conditions often faced by USCM high command.\n \nThe fusion of luxury and utility, the blend of gold and metal, is an embodiment of the hard-won elegance of command, of the fusion between power and grace. It's more than a writing instrument - it's an emblem of leadership, an accolade to the dedication and strength of those who bear it. ARMAT's fountain pen stands as a monument to the precision, integrity, and courage embodied by the USCM's highest-ranking officers."
name = "fountain pen"
icon_state = "fountain_pen"
item_state = "fountain_pen"
matter = list("metal" = 20, "gold" = 10)
var/static/list/colour_list = list("red", "blue", "green", "yellow", "purple", "pink", "brown", "black", "orange") // Can add more colors as required
var/current_colour_index = 1
+ var/owner = "hard to read text"
+
+/obj/item/tool/pen/fountain/Initialize(mapload, mob/living/carbon/human/user)
+ . = ..()
+ var/turf/current_turf = get_turf(src)
+ var/mob/living/carbon/human/new_owner = locate() in current_turf
+ if(new_owner)
+ owner = new_owner.real_name
+ var/obj/structure/machinery/cryopod/new_owners_pod = locate() in current_turf
+ if(new_owners_pod)
+ owner = new_owners_pod.occupant?.real_name
+
+/obj/item/tool/pen/fountain/get_examine_text(mob/user)
+ . = ..()
+ . += "There's a laser engraving of [owner] on it."
/obj/item/tool/pen/fountain/attack_self(mob/living/carbon/human/user)
if(on)
diff --git a/code/game/objects/items/tools/shovel_tools.dm b/code/game/objects/items/tools/shovel_tools.dm
index 29bc19f65bbf..008b37705fe3 100644
--- a/code/game/objects/items/tools/shovel_tools.dm
+++ b/code/game/objects/items/tools/shovel_tools.dm
@@ -233,7 +233,7 @@
/obj/item/tool/shovel/etool/attack_self(mob/user as mob)
folded = !folded
if(folded)
- w_class = SIZE_MEDIUM
+ w_class = SIZE_SMALL
force = 2
else
w_class = SIZE_LARGE
@@ -242,7 +242,7 @@
/obj/item/tool/shovel/etool/folded
folded = TRUE
- w_class = SIZE_MEDIUM
+ w_class = SIZE_SMALL
force = 2
icon_state = "etool_c"
item_state = "etool_c"
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index c9bfb9b9f757..be7571fa84a1 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -323,13 +323,25 @@
item_state = "syn_breacher"
force_wielded = MELEE_FORCE_VERY_STRONG
really_heavy = TRUE
+ var/move_delay_addition = 1.5
/obj/item/weapon/twohanded/breacher/synth/pickup(mob/user)
if(!(HAS_TRAIT(user, TRAIT_SUPER_STRONG)))
- to_chat(user, SPAN_WARNING("You barely manage to lift \the [src] above your knees. This thing will probably be useless to you."))
+ to_chat(user, SPAN_HIGHDANGER("You barely manage to lift [src] above your knees. This thing will probably be useless to you."))
+ user.apply_effect(3, EYE_BLUR)
+ RegisterSignal(user, COMSIG_HUMAN_POST_MOVE_DELAY, PROC_REF(handle_movedelay))
+
return
..()
+/obj/item/weapon/twohanded/breacher/synth/proc/handle_movedelay(mob/living/M, list/movedata)
+ SIGNAL_HANDLER
+ movedata["move_delay"] += move_delay_addition
+
+/obj/item/weapon/twohanded/breacher/synth/dropped(mob/user, silent)
+ . = ..()
+ UnregisterSignal(user, COMSIG_HUMAN_POST_MOVE_DELAY)
+
/obj/item/weapon/twohanded/breacher/synth/attack(target as mob, mob/living/user as mob)
if(!HAS_TRAIT(user, TRAIT_SUPER_STRONG))
to_chat(user, SPAN_WARNING("\The [src] is too heavy for you to use as a weapon!"))
diff --git a/code/game/objects/structures/barricade/deployable.dm b/code/game/objects/structures/barricade/deployable.dm
index 7ed2eefd75fb..77aa6b7e6816 100644
--- a/code/game/objects/structures/barricade/deployable.dm
+++ b/code/game/objects/structures/barricade/deployable.dm
@@ -23,56 +23,38 @@
. = ..()
. += SPAN_INFO("Drag its sprite onto yourself to undeploy.")
-/obj/structure/barricade/deployable/attackby(obj/item/W, mob/user)
+/obj/structure/barricade/deployable/attackby(obj/item/item, mob/user)
- if(iswelder(W))
- if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH))
+ if(iswelder(item))
+ if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH))
to_chat(user, SPAN_WARNING("You need a stronger blowtorch!"))
return
if(user.action_busy)
return
- var/obj/item/tool/weldingtool/WT = W
+ var/obj/item/tool/weldingtool/welder = item
if(health == maxhealth)
to_chat(user, SPAN_WARNING("[src] doesn't need repairs."))
return
- weld_cade(WT, user)
+ weld_cade(welder, user)
return
- else if(HAS_TRAIT(W, TRAIT_TOOL_WRENCH))
+ else if(HAS_TRAIT(item, TRAIT_TOOL_CROWBAR))
if(user.action_busy)
return
- if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_ENGI))
- to_chat(user, SPAN_WARNING("You do not know where the loosening bolts are on [src]..."))
- return
- else
- if(build_state == BARRICADE_BSTATE_UNSECURED)
- to_chat(user, SPAN_NOTICE("You tighten the bolts on [src]."))
- playsound(src.loc, 'sound/items/Ratchet.ogg', 25, 1)
- build_state = BARRICADE_BSTATE_SECURED
- else
- to_chat(user, SPAN_NOTICE("You loosen the bolts on [src]."))
- playsound(src.loc, 'sound/items/Ratchet.ogg', 25, 1)
- build_state = BARRICADE_BSTATE_UNSECURED
-
- else if(HAS_TRAIT(W, TRAIT_TOOL_CROWBAR))
- if(build_state != BARRICADE_BSTATE_UNSECURED)
- return
- if(user.action_busy)
- return
- if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_ENGI))
+ if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_TRAINED))
to_chat(user, SPAN_WARNING("You do not know how to collapse [src] using a crowbar..."))
return
else
user.visible_message(SPAN_NOTICE("[user] starts collapsing [src]."), \
SPAN_NOTICE("You begin collapsing [src]..."))
- playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
- if(do_after(user, 2 SECONDS, INTERRUPT_ALL, BUSY_ICON_FRIENDLY, src))
+ playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
+ if(do_after(user, 1.5 SECONDS, INTERRUPT_NO_NEEDHAND, BUSY_ICON_FRIENDLY, src))
collapse(usr)
else
to_chat(user, SPAN_WARNING("You stop collapsing [src]."))
- if(try_nailgun_usage(W, user))
+ if(try_nailgun_usage(item, user))
return
. = ..()
@@ -87,20 +69,20 @@
if(over_object == usr && Adjacent(usr))
usr.visible_message(SPAN_NOTICE("[usr] starts collapsing [src]."),
SPAN_NOTICE("You begin collapsing [src]."))
- playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
+ playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
if(do_after(usr, 3 SECONDS, INTERRUPT_NO_NEEDHAND, BUSY_ICON_FRIENDLY, src))
collapse(usr)
else
to_chat(usr, SPAN_WARNING("You stop collapsing [src]."))
/obj/structure/barricade/deployable/proc/collapse(mob/living/carbon/human/user)
- var/obj/item/stack/folding_barricade/FB = new source_type(loc)
- FB.health = health
- FB.maxhealth = maxhealth
+ var/obj/item/stack/folding_barricade/folding = new source_type(loc)
+ folding.stack_health = list(health)
+ folding.maxhealth = maxhealth
if(istype(user))
user.visible_message(SPAN_NOTICE("[user] collapses [src]."),
SPAN_NOTICE("You collapse [src]."))
- user.put_in_active_hand(FB)
+ user.put_in_active_hand(folding)
qdel(src)
/obj/structure/barricade/deployable/initialize_pass_flags(datum/pass_flags_container/PF)
@@ -133,6 +115,14 @@
)
icon = 'icons/obj/items/marine-items.dmi'
+ var/list/stack_health = list()
+
+/obj/item/stack/folding_barricade/Initialize(mapload, init_amount)
+ . = ..()
+ for(var/counter in 1 to amount)
+ stack_health += initial(health)
+
+
/obj/item/stack/folding_barricade/update_icon()
. = ..()
icon_state = "folding-[amount]"
@@ -153,16 +143,16 @@
var/obj/structure/blocker/anti_cade/AC = locate(/obj/structure/blocker/anti_cade) in OT // for M2C HMG, look at smartgun_mount.dm
if(!OT.allow_construction)
- to_chat(usr, SPAN_WARNING("[src.singular_name] must be constructed on a proper surface!"))
+ to_chat(usr, SPAN_WARNING("[singular_name] must be constructed on a proper surface!"))
return
if(AC)
- to_chat(usr, SPAN_WARNING("[src.singular_name] cannot be built here!"))
+ to_chat(usr, SPAN_WARNING("[singular_name] cannot be built here!"))
return
- user.visible_message(SPAN_NOTICE("[user] begins deploying [src.singular_name]."),
- SPAN_NOTICE("You begin deploying [src.singular_name]."))
+ user.visible_message(SPAN_NOTICE("[user] begins deploying [singular_name]."),
+ SPAN_NOTICE("You begin deploying [singular_name]."))
- playsound(src.loc, 'sound/items/Ratchet.ogg', 25, 1)
+ playsound(loc, 'sound/items/Ratchet.ogg', 25, 1)
if(!do_after(user, 1 SECONDS, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD))
to_chat(user, SPAN_WARNING("You were interrupted."))
@@ -173,12 +163,12 @@
to_chat(user, SPAN_WARNING("There is already \a [B] in this direction!"))
return
- user.visible_message(SPAN_NOTICE("[user] has finished deploying [src.singular_name]."),
- SPAN_NOTICE("You finish deploying [src.singular_name]."))
+ user.visible_message(SPAN_NOTICE("[user] has finished deploying [singular_name]."),
+ SPAN_NOTICE("You finish deploying [singular_name]."))
var/obj/structure/barricade/deployable/cade = new(user.loc)
cade.setDir(user.dir)
- cade.health = health
+ cade.health = pop(stack_health)
cade.maxhealth = maxhealth
cade.source_type = singular_type
cade.update_damage_state()
@@ -186,76 +176,90 @@
use(1)
-/obj/item/stack/folding_barricade/attackby(obj/item/W, mob/user)
- if(istype(W, /obj/item/stack/folding_barricade))
- var/obj/item/stack/folding_barricade/F = W
-
- if(health != maxhealth || F.health != F.maxhealth)
- to_chat(user, "You cannot stack damaged [src.singular_name]\s.")
+/obj/item/stack/folding_barricade/attackby(obj/item/item, mob/user)
+ if(istype(item, /obj/item/stack/folding_barricade))
+ var/obj/item/stack/folding_barricade/folding = item
+ if(!ismob(loc)) //gather from ground
+ if(amount >= max_amount)
+ to_chat(user, "You cannot stack more [folding.singular_name]\s.")
+ return
+ var/to_transfer = min(folding.max_amount - folding.amount, amount)
+ for(var/counter in 1 to to_transfer)
+ folding.stack_health += pop(stack_health)
+ use(to_transfer)
+ folding.add(to_transfer)
+ to_chat(user, SPAN_INFO("You transfer [to_transfer] between the stacks."))
return
-
- if(!ismob(src.loc))
- return ..()
-
if(amount >= max_amount)
- to_chat(user, "You cannot stack more [src.singular_name]\s.")
+ to_chat(user, "You cannot stack more [singular_name]\s.")
return
- var/to_transfer = min(max_amount - amount, F.amount)
- F.use(to_transfer)
+ var/to_transfer = min(max_amount - amount, folding.amount)
+ for(var/counter in 1 to to_transfer)
+ stack_health += pop(folding.stack_health)
+ folding.use(to_transfer)
add(to_transfer)
to_chat(user, SPAN_INFO("You transfer [to_transfer] between the stacks."))
return
- else if(iswelder(W))
- if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH))
+ else if(iswelder(item))
+ if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH))
to_chat(user, SPAN_WARNING("You need a stronger blowtorch!"))
return
- if(src != user.get_inactive_hand())
- to_chat(user, SPAN_WARNING("You need to hold [src.singular_name] in hand or deploy to repair it."))
- return
if(user.action_busy)
return
- var/obj/item/tool/weldingtool/WT = W
- if(health == maxhealth)
- to_chat(user, SPAN_WARNING("[src.singular_name] doesn't need repairs."))
+ var/need_repairs = 0
+ for(var/counter in 1 to length(stack_health))
+ if(stack_health[counter] < maxhealth)
+ need_repairs++
+
+ if(!need_repairs)
+ to_chat(user, SPAN_WARNING("[singular_name] doesn't need repairs."))
return
- if(!(WT.remove_fuel(2, user)))
+ var/obj/item/tool/weldingtool/welder = item
+ if(!(welder.remove_fuel(2, user)))
return
user.visible_message(SPAN_NOTICE("[user] begins repairing damage to [src]."),
SPAN_NOTICE("You begin repairing the damage to [src]."))
- playsound(src.loc, 'sound/items/Welder2.ogg', 25, TRUE)
+ playsound(loc, 'sound/items/Welder2.ogg', 25, TRUE)
- var/welding_time = skillcheck(user, SKILL_CONSTRUCTION, 2) ? 5 SECONDS : 10 SECONDS
- if(!do_after(user, welding_time, INTERRUPT_NO_NEEDHAND|BEHAVIOR_IMMOBILE, BUSY_ICON_FRIENDLY, src))
- return
+ var/welding_time = (skillcheck(user, SKILL_CONSTRUCTION, SKILL_CONSTRUCTION_TRAINED) ? 5 SECONDS : 10 SECONDS) * need_repairs
+
+ if(src != user.get_inactive_hand())
+ if(!do_after(user, welding_time, INTERRUPT_NO_NEEDHAND|BEHAVIOR_IMMOBILE, BUSY_ICON_FRIENDLY, src))
+ return
+ else
+ if(!do_after(user, welding_time, (INTERRUPT_ALL & (~INTERRUPT_MOVED)), BUSY_ICON_FRIENDLY, src, INTERRUPT_DIFF_LOC)) //you can move while repairing if you have cade in hand
+ return
user.visible_message(SPAN_NOTICE("[user] repairs some damage on [src]."),
SPAN_NOTICE("You repair [src]."))
user.count_niche_stat(STATISTICS_NICHE_REPAIR_CADES)
- health += 200
- if(health > maxhealth)
- health = maxhealth
+ for(var/counter in 1 to length(stack_health))
+ stack_health[counter] += 200
+ if(stack_health[counter] > maxhealth)
+ stack_health[counter] = maxhealth
- playsound(src.loc, 'sound/items/Welder2.ogg', 25, TRUE)
+ playsound(loc, 'sound/items/Welder2.ogg', 25, TRUE)
return
. = ..()
/obj/item/stack/folding_barricade/attack_hand(mob/user)
- var/mob/living/carbon/human/H = user
- if(!(amount > 1 && H.back == src))
+ var/mob/living/carbon/human/human = user
+ if(!(amount > 1 && (human.back == src || human.get_inactive_hand() == src)))
return ..()
- var/obj/item/stack/F = new singular_type(user, 1)
- transfer_fingerprints_to(F)
- user.put_in_hands(F)
- src.add_fingerprint(user)
- F.add_fingerprint(user)
+ var/obj/item/stack/folding_barricade/folding = new singular_type(user, 1)
+ transfer_fingerprints_to(folding)
+ folding.stack_health = list(pop(stack_health))
+ user.put_in_hands(folding)
+ add_fingerprint(user)
+ folding.add_fingerprint(user)
use(1)
/obj/item/stack/folding_barricade/MouseDrop(obj/over_object as obj)
@@ -277,7 +281,7 @@
/obj/item/stack/folding_barricade/get_examine_text(mob/user)
. = ..()
- if(health < maxhealth)
+ if(round(min(stack_health)/maxhealth * 100) <= 75)
. += SPAN_WARNING("It appears to be damaged.")
/obj/item/stack/folding_barricade/three
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm
index 4d275ee7b9d8..5772db33198d 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm
@@ -134,7 +134,7 @@ GLOBAL_LIST_EMPTY(co_secure_boxes)
new /obj/item/clothing/under/marine/officer/pilot(src)
new /obj/item/clothing/shoes/marine(src)
new /obj/item/clothing/suit/armor/vest/pilot(src)
- new /obj/item/storage/large_holster/m39(src)
+ new /obj/item/storage/belt/gun/m39(src)
new /obj/item/storage/backpack/marine/satchel(src)
new /obj/item/clothing/gloves/yellow(src)
new /obj/item/clothing/glasses/sunglasses(src)
@@ -306,8 +306,8 @@ GLOBAL_LIST_EMPTY(co_secure_boxes)
/obj/structure/closet/secure_closet/req_officer/Initialize()
. = ..()
- new /obj/item/device/radio/headset/almayer/ro(src)
- new /obj/item/clothing/under/rank/ro_suit(src)
+ new /obj/item/device/radio/headset/almayer/qm(src)
+ new /obj/item/clothing/under/rank/qm_suit(src)
new /obj/item/clothing/shoes/marine(src)
new /obj/item/storage/belt/marine(src)
new /obj/item/clothing/head/cmcap/req(src)
diff --git a/code/game/objects/structures/props.dm b/code/game/objects/structures/props.dm
index 1a91650c620a..66598d602691 100644
--- a/code/game/objects/structures/props.dm
+++ b/code/game/objects/structures/props.dm
@@ -639,6 +639,10 @@
icon_state = ""
icon = 'icons/turf/lifeboat.dmi'
+#define STATE_COMPLETE 0
+#define STATE_FUEL 1
+#define STATE_IGNITE 2
+
/obj/structure/prop/brazier
name = "brazier"
desc = "The fire inside the brazier emits a relatively dim glow to flashlights and flares, but nothing can replace the feeling of sitting next to a fireplace with your friends."
@@ -647,6 +651,40 @@
density = TRUE
health = 150
luminosity = 6
+ /// What obj this becomes when it gets to its next stage of construction / ignition
+ var/frame_type
+ /// What is used to progress to the next stage
+ var/state = STATE_COMPLETE
+
+/obj/structure/prop/brazier/get_examine_text(mob/user)
+ . = ..()
+ switch(state)
+ if(STATE_FUEL)
+ . += "[src] requires wood to be fueled."
+ if(STATE_IGNITE)
+ . += "[src] needs to be lit."
+
+/obj/structure/prop/brazier/attackby(obj/item/hit_item, mob/user)
+ switch(state)
+ if(STATE_COMPLETE)
+ return ..()
+ if(STATE_FUEL)
+ if(!istype(hit_item, /obj/item/stack/sheet/wood))
+ return ..()
+ var/obj/item/stack/sheet/wood/wooden_boards = hit_item
+ if(!wooden_boards.use(5))
+ to_chat(user, SPAN_WARNING("Not enough wood!"))
+ return
+ user.visible_message(SPAN_NOTICE("[user] fills [src] with [hit_item]."))
+ if(STATE_IGNITE)
+ if(!hit_item.heat_source)
+ return ..()
+ if(!do_after(user, 3 SECONDS, INTERRUPT_MOVED, BUSY_ICON_BUILD))
+ return
+ user.visible_message(SPAN_NOTICE("[user] ignites [src] with [hit_item]."))
+
+ new frame_type(loc)
+ qdel(src)
/obj/structure/prop/brazier/Destroy()
SetLuminosity(0)
@@ -662,31 +700,15 @@
desc = "An empty brazier."
icon_state = "brazier_frame"
luminosity = 0
+ frame_type = /obj/structure/prop/brazier/frame/full
+ state = STATE_FUEL
-/obj/structure/prop/brazier/frame/attackby(obj/item/hit_item, mob/user)
- if(!istype(hit_item, /obj/item/stack/sheet/wood))
- return ..()
- var/obj/item/stack/wooden_boards = hit_item
- if(wooden_boards.amount < 5)
- to_chat(user, SPAN_WARNING("Not enough wood!"))
- return
- wooden_boards.use(5)
- user.visible_message(SPAN_NOTICE("[user] fills the brazier with wood."))
- new /obj/structure/prop/brazier/frame_woodened(loc)
- qdel(src)
-
-/obj/structure/prop/brazier/frame_woodened
+/obj/structure/prop/brazier/frame/full
name = "empty full brazier"
desc = "An empty brazier. Yet it's also full. What??? Use something hot to ignite it, like a welding tool."
icon_state = "brazier_frame_filled"
- luminosity = 0
-
-/obj/structure/prop/brazier/frame_woodened/attackby(obj/item/hit_item, mob/user)
- if(!hit_item.heat_source)
- return ..()
- user.visible_message(SPAN_NOTICE("[user] ignites the brazier with [hit_item]."))
- new /obj/structure/prop/brazier(loc)
- qdel(src)
+ frame_type = /obj/structure/prop/brazier
+ state = STATE_IGNITE
/obj/structure/prop/brazier/torch
name = "torch"
@@ -695,25 +717,136 @@
density = FALSE
luminosity = 5
-/obj/structure/prop/brazier/torch/frame
+/obj/structure/prop/brazier/frame/full/torch
name = "unlit torch"
desc = "It's a torch, but it's not lit. Use something hot to ignite it, like a welding tool."
icon_state = "torch_frame"
- luminosity = 0
-
-/obj/structure/prop/brazier/torch/frame/attackby(obj/item/hit_item, mob/user)
- if(!hit_item.heat_source)
- return ..()
- user.visible_message(SPAN_NOTICE("[user] ignites the torch with [hit_item]."))
- new /obj/structure/prop/brazier/torch(loc)
- qdel(src)
+ frame_type = /obj/structure/prop/brazier/torch
/obj/item/prop/torch_frame
name = "unlit torch"
icon = 'icons/obj/structures/structures.dmi'
desc = "It's a torch, but it's not lit or placed down. Click on a wall to place it."
icon_state = "torch_frame"
- luminosity = 0
+
+/obj/structure/prop/brazier/frame/full/campfire
+ name = "unlit campfire"
+ desc = "A circle of stones surrounding a pile of wood. If only you were to light it."
+ icon_state = "campfire"
+ frame_type = /obj/structure/prop/brazier/campfire
+ density = FALSE
+
+/obj/structure/prop/brazier/frame/full/campfire/smolder
+ name = "smoldering campfire"
+ desc = "A campfire that used to be lit, but was extinguished. You can still see the embers, and smoke rises from it."
+ state = STATE_FUEL
+ frame_type = /obj/structure/prop/brazier/frame/full/campfire
+
+/obj/structure/prop/brazier/campfire
+ name = "campfire"
+ desc = "A circle of stones surrounding a burning pile of wood. The fire is roaring and you can hear its crackle. You could probably stomp the fire out."
+ icon = 'icons/obj/structures/structures.dmi'
+ icon_state = "campfire_on"
+ density = FALSE
+ ///How many tiles the heating and sound goes
+ var/heating_range = 2
+ /// time between sounds
+ var/time_to_sound = 20
+ /// Time for it to burn through fuel
+ var/fuel_stage_time = 1 MINUTES
+ /// How much fuel it has
+ var/remaining_fuel = 5 //Maxes at 5, but burns one when made
+ /// If the fire can be manually put out
+ var/extinguishable = TRUE
+ /// Make no noise
+ var/quiet = FALSE
+
+/obj/structure/prop/brazier/campfire/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+ fuel_drain(TRUE)
+
+/obj/structure/prop/brazier/campfire/get_examine_text(mob/user)
+ . = ..()
+ switch(remaining_fuel)
+ if(4 to INFINITY)
+ . += "The fire is roaring."
+ if(2 to 3)
+ . += "The fire is burning warm."
+ if(-INFINITY to 1)
+ . += "The embers of the fire barely burns."
+
+/obj/structure/prop/brazier/campfire/process(delta_time)
+ if(!isturf(loc))
+ return
+
+ for(var/mob/living/carbon/human/mob in range(heating_range, src))
+ if(mob.bodytemperature < T20C)
+ mob.bodytemperature += min(round(T20C - mob.bodytemperature)*0.7, 25)
+ mob.recalculate_move_delay = TRUE
+
+ if(quiet)
+ return
+ time_to_sound -= delta_time
+ if(time_to_sound <= 0)
+ playsound(loc, 'sound/machines/firepit_ambience.ogg', 15, FALSE, heating_range)
+ time_to_sound = initial(time_to_sound)
+
+/obj/structure/prop/brazier/campfire/attack_hand(mob/user)
+ . = ..()
+ if(!extinguishable)
+ to_chat(user, SPAN_WARNING("You cannot extinguish [src]."))
+ return
+ to_chat(user, SPAN_NOTICE("You begin to extinguish [src]."))
+ while(remaining_fuel)
+ if(user.action_busy || !do_after(user, 3 SECONDS, INTERRUPT_MOVED, BUSY_ICON_BUILD))
+ return
+ fuel_drain()
+ to_chat(user, SPAN_NOTICE("You continue to extinguish [src]."))
+ visible_message(SPAN_NOTICE("[user] extinguishes [src]."))
+
+/obj/structure/prop/brazier/campfire/attackby(obj/item/attacking_item, mob/user)
+ if(!istype(attacking_item, /obj/item/stack/sheet/wood))
+ to_chat(SPAN_NOTICE("You cannot fuel [src] with [attacking_item]."))
+ return
+ var/obj/item/stack/sheet/wood/fuel = attacking_item
+ if(remaining_fuel >= initial(remaining_fuel))
+ to_chat(user, SPAN_NOTICE("You cannot fuel [src] further."))
+ if(!fuel.use(1))
+ to_chat(SPAN_NOTICE("You do not have enough [attacking_item] to fuel [src]."))
+ visible_message(SPAN_NOTICE("[user] fuels [src] with [fuel]."))
+ remaining_fuel++
+
+/obj/structure/prop/brazier/campfire/attack_alien(mob/living/carbon/xenomorph/xeno)
+ if(!extinguishable)
+ to_chat(xeno, SPAN_WARNING("You cannot extinguish [src]."))
+ return
+ to_chat(xeno, SPAN_NOTICE("You begin to extinguish [src]."))
+ while(remaining_fuel)
+ if(xeno.action_busy || !do_after(xeno, 1 SECONDS, INTERRUPT_MOVED, BUSY_ICON_HOSTILE))
+ return
+ fuel_drain()
+ to_chat(xeno, SPAN_NOTICE("You continue to extinguish [src]."))
+ visible_message(SPAN_WARNING("[xeno] extinguishes [src]!"))
+
+/obj/structure/prop/brazier/campfire/proc/fuel_drain(looping)
+ remaining_fuel--
+ if(!remaining_fuel)
+ new /obj/structure/prop/brazier/frame/full/campfire/smolder(loc)
+ qdel(src)
+ return
+ if(!looping || !fuel_stage_time)
+ return
+ addtimer(CALLBACK(src, PROC_REF(fuel_drain), TRUE), fuel_stage_time)
+
+/obj/structure/prop/brazier/campfire/Destroy()
+ SetLuminosity(0)
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+#undef STATE_COMPLETE
+#undef STATE_FUEL
+#undef STATE_IGNITE
//ICE COLONY PROPS
//Thematically look to Blackmesa's Xen levels. Generic science-y props n' stuff.
@@ -837,8 +970,8 @@
icon_state = "van"
bound_height = 64
bound_width = 64
- unslashable = TRUE
- unacidable = TRUE
+ unslashable = FALSE
+ unacidable = FALSE
/obj/structure/prop/vehicles/crawler
name = "colony crawler"
@@ -883,13 +1016,6 @@
icon_state = "arcadeb"
name = "Spirit Phone, The Game, The Movie: II"
-/obj/structure/prop/maintenance_hatch
- name = "\improper Maintenance Hatch"
- icon = 'icons/obj/structures/structures.dmi'
- icon_state = "hatchclosed"
- desc = "Looks like it's rusted shut. Creepy."
- layer = HATCH_LAYER
-
//INVULNERABLE PROPS
/obj/structure/prop/invuln
@@ -1144,3 +1270,99 @@
/obj/structure/prop/wooden_cross/update_icon()
if(tagged)
overlays += mutable_appearance('icons/obj/structures/props/crosses.dmi', "cross_overlay")
+
+
+/obj/structure/prop/invuln/rope
+ name = "rope"
+ desc = "A secure rope looks like someone might've been hiding out on those rocks."
+ icon = 'icons/obj/structures/props/almayer_props.dmi'
+ icon_state = "rope"
+ density = FALSE
+
+/obj/structure/prop/pred_flight
+ name = "hunter flight console"
+ desc = "A console designed by the Hunters to assist in flight pathing and navigation."
+ icon = 'icons/obj/structures/machinery/computer.dmi'
+ icon_state = "overwatch"
+ density = TRUE
+
+/obj/structure/prop/invuln/joey
+ name = "Workin' Joey"
+ desc = "A defunct Seegson-brand Working Joe lifted from deep storage by a crew of marines after the last shore leave. Attempts have been made to modify the janitorial synthetic to serve as a crude bartender, but with little success."
+ icon = 'icons/obj/structures/props/props.dmi'
+ icon_state = "joey"
+ unslashable = FALSE
+ wrenchable = FALSE
+ /// converted into minutes when used to determine cooldown timer between quips
+ var/quip_delay_minimum = 5
+ /// delay between Quips. Slightly randomized with quip_delay_minimum plus a random number
+ COOLDOWN_DECLARE(quip_delay)
+ /// delay between attack voicelines. Short but done for anti-spam
+ COOLDOWN_DECLARE(damage_delay)
+ /// list of quip emotes, taken from Working Joe
+ var/static/list/quips = list(
+ /datum/emote/living/carbon/human/synthetic/working_joe/quip/alwaysknow_damaged,
+ /datum/emote/living/carbon/human/synthetic/working_joe/quip/not_liking,
+ /datum/emote/living/carbon/human/synthetic/working_joe/greeting/how_can_i_help,
+ /datum/emote/living/carbon/human/synthetic/working_joe/task_update/day_never_done,
+ /datum/emote/living/carbon/human/synthetic/working_joe/task_update/required_by_apollo,
+ /datum/emote/living/carbon/human/synthetic/working_joe/warning/safety_breach
+ )
+ /// list of voicelines to use when damaged
+ var/static/list/damaged = list(
+ /datum/emote/living/carbon/human/synthetic/working_joe/warning/damage,
+ /datum/emote/living/carbon/human/synthetic/working_joe/warning/that_stings,
+ /datum/emote/living/carbon/human/synthetic/working_joe/warning/irresponsible,
+ /datum/emote/living/carbon/human/synthetic/working_joe/warning/this_is_futile,
+ /datum/emote/living/carbon/human/synthetic/working_joe/warning/hysterical,
+ /datum/emote/living/carbon/human/synthetic/working_joe/warning/patience
+ )
+
+/obj/structure/prop/invuln/joey/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+
+/obj/structure/prop/invuln/joey/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/structure/prop/invuln/joey/process()
+ //check if quip_delay cooldown finished. If so, random chance it says a line
+ if(COOLDOWN_FINISHED(src, quip_delay) && prob(10))
+ emote(pick(quips))
+ var/delay = rand(3) + quip_delay_minimum
+ COOLDOWN_START(src, quip_delay, delay MINUTES)
+
+// Advert your eyes.
+/obj/structure/prop/invuln/joey/attackby(obj/item/W, mob/user)
+ attacked()
+ return ..()
+
+/obj/structure/prop/invuln/joey/bullet_act(obj/item/projectile/P)
+ attacked()
+ return ..()
+
+/// A terrible way of handling being hit. If signals would work it should be used.
+/obj/structure/prop/invuln/joey/proc/attacked()
+ if(COOLDOWN_FINISHED(src, damage_delay) && prob(25))
+ emote(pick(damaged))
+ COOLDOWN_START(src, damage_delay, 8 SECONDS)
+
+/// SAY THE LINE JOE
+/obj/structure/prop/invuln/joey/proc/emote(datum/emote/living/carbon/human/synthetic/working_joe/emote)
+ if (!emote)
+ return FALSE
+
+ for(var/mob/mob in hearers(src, null))
+ mob.show_message("[src] says, \"[initial(emote.say_message)]\"", SHOW_MESSAGE_AUDIBLE)
+
+ var/list/viewers = get_mobs_in_view(7, src)
+ for(var/mob/current_mob in viewers)
+ if(!(current_mob.client?.prefs.toggles_langchat & LANGCHAT_SEE_EMOTES))
+ viewers -= current_mob
+ langchat_speech(initial(emote.say_message), viewers, GLOB.all_languages, skip_language_check = TRUE)
+
+ if(initial(emote.sound))
+ playsound(loc, initial(emote.sound), 50, FALSE)
+ return TRUE
+
diff --git a/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm b/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
index c8f5a7f82c0f..7a4274c2c16e 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/xeno_nest.dm
@@ -20,8 +20,6 @@
var/force_nest = FALSE
/// counterpart to buckling_y --> offsets the buckled mob when it buckles
var/list/buckling_x
- /// saves the density of the buckled_mob
- var/buckled_mob_density
/obj/structure/bed/nest/Initialize(mapload, hive)
. = ..()
@@ -52,7 +50,6 @@
resisting_ready = FALSE
if(buckled_mob == current_mob)
- buckled_mob_density = current_mob.density
current_mob.pixel_y = buckling_y["[dir]"]
current_mob.pixel_x = buckling_x["[dir]"]
current_mob.dir = turn(dir, 180)
@@ -70,7 +67,7 @@
current_mob.pixel_y = initial(buckled_mob.pixel_y)
current_mob.pixel_x = initial(buckled_mob.pixel_x)
- current_mob.density = buckled_mob_density
+ current_mob.density = !(current_mob.lying || current_mob.stat == DEAD)
if(dir == SOUTH)
current_mob.layer = initial(current_mob.layer)
if(!ishuman(current_mob))
@@ -281,8 +278,6 @@
buckled_mob.old_y = 0
REMOVE_TRAIT(buckled_mob, TRAIT_NESTED, TRAIT_SOURCE_BUCKLE)
var/mob/living/carbon/human/buckled_human = buckled_mob
- if(buckled_human.stat == DEAD )
- buckled_mob_density = FALSE
var/mob/dead/observer/G = ghost_of_buckled_mob
var/datum/mind/M = G?.mind
@@ -311,7 +306,6 @@
/obj/structure/bed/nest/proc/healthcheck()
if(health <= 0)
- buckled_mob_density = FALSE
deconstruct()
/obj/structure/bed/nest/fire_act()
diff --git a/code/game/turfs/walls/walls.dm b/code/game/turfs/walls/walls.dm
index 3599d5bb980b..2387a2086192 100644
--- a/code/game/turfs/walls/walls.dm
+++ b/code/game/turfs/walls/walls.dm
@@ -403,7 +403,7 @@
if(istype(attacking_item, /obj/item/prop/torch_frame))
to_chat(user, SPAN_NOTICE("You place the torch down on the wall."))
- new /obj/structure/prop/brazier/torch/frame(src)
+ new /obj/structure/prop/brazier/frame/full/torch(src)
qdel(attacking_item)
if(hull)
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index 1c94eb485300..3c964cf4011b 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -57,24 +57,38 @@
display_colour = CONFIG_GET(string/ooc_color_default)
msg = process_chat_markup(msg, list("*"))
-
+ var/ooc_prefix = handle_ooc_prefix()
for(var/client/C in GLOB.clients)
if(C.prefs.toggles_chat & CHAT_OOC)
var/display_name = src.key
- if(prefs.unlock_content)
- if(prefs.toggle_prefs & TOGGLE_MEMBER_PUBLIC)
- var/byond = icon('icons/effects/effects.dmi', "byondlogo")
- display_name = "[icon2html(byond, GLOB.clients)][display_name]"
- if(CONFIG_GET(flag/ooc_country_flags))
- if(prefs.toggle_prefs & TOGGLE_OOC_FLAG)
- display_name = "[country2chaticon(src.country, GLOB.clients)][display_name]"
- to_chat(C, "[src.donator ? "\[D\] " : ""]OOC: [display_name]: [msg]")
+ to_chat(C, "[ooc_prefix]OOC: [display_name]: [msg]")
+
/client/proc/set_ooc_color_global(newColor as color)
set name = "OOC Text Color - Global"
set desc = "Set to yellow for eye burning goodness."
set category = "OOC.OOC"
GLOB.ooc_color_override = newColor
+///Used by OOC chat to generate icons for player prefix. Intended to make it easy to see at a glance if someone is staff, WL Council or Mentor.
+/client/proc/handle_ooc_prefix()
+ var/prefix = ""
+ if(prefs.unlock_content && (prefs.toggle_prefs & TOGGLE_MEMBER_PUBLIC))
+ var/byond = icon('icons/effects/effects.dmi', "byondlogo")
+ prefix += "[icon2html(byond, GLOB.clients)]"
+ if(CONFIG_GET(flag/ooc_country_flags) && (prefs.toggle_prefs & TOGGLE_OOC_FLAG))
+ prefix += "[country2chaticon(src.country, GLOB.clients)]"
+ if(donator)
+ prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, "Donator")]"
+ if(isCouncil(src))
+ prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, "WhitelistCouncil")]"
+ if(admin_holder)
+ var/list/rank_icons = icon_states('icons/ooc.dmi')
+ var/rankname = admin_holder.rank
+ if(rankname in rank_icons)
+ prefix += "[icon2html('icons/ooc.dmi', GLOB.clients, admin_holder.rank)]"
+ if(prefix)
+ prefix = "[prefix] "
+ return prefix
/client/verb/looc(msg as text)
set name = "LOOC" //Gave this shit a shorter name so you only have to time out "ooc" rather than "ooc message" to use it --NeoFite
diff --git a/code/game/verbs/records.dm b/code/game/verbs/records.dm
index 56a440e3558c..f09de72da2e6 100644
--- a/code/game/verbs/records.dm
+++ b/code/game/verbs/records.dm
@@ -53,7 +53,7 @@
if(NOTE_YAUTJA)
color = "#114e11"
- dat += "[N.text] by [admin_ckey] ([N.admin_rank]) on [N.date] "
+ dat += "[N.text] by [admin_ckey] ([N.admin_rank]) on [N.date] [NOTE_ROUND_ID(N)] "
dat += "
"
dat += " "
@@ -168,7 +168,7 @@
continue
var/admin_ckey = N.admin_ckey
- dat += "[N.text] by [admin_ckey] ([N.admin_rank]) on [N.date] "
+ dat += "[N.text] by [admin_ckey] ([N.admin_rank]) on [N.date] [NOTE_ROUND_ID(N)] "
///Can remove notes from anyone other than yourself, unless you're the host. So long as you have deletion access anyway.
if((can_del && target != get_player_from_key(key)) || ishost(usr))
dat += "Remove"
diff --git a/code/game/world.dm b/code/game/world.dm
index 25cd609646da..cff799800a49 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -61,7 +61,7 @@ var/list/reboot_sfx = file2list("config/reboot_sfx.txt")
var/testing_locally = (world.params && world.params["local_test"])
var/running_tests = (world.params && world.params["run_tests"])
- #ifdef UNIT_TESTS
+ #if defined(AUTOWIKI) || defined(UNIT_TESTS)
running_tests = TRUE
#endif
// Only do offline sleeping when the server isn't running unit tests or hosting a local dev test
@@ -84,6 +84,10 @@ var/list/reboot_sfx = file2list("config/reboot_sfx.txt")
HandleTestRun()
#endif
+ #ifdef AUTOWIKI
+ setup_autowiki()
+ #endif
+
update_status()
//Scramble the coords obsfucator
diff --git a/code/global.dm b/code/global.dm
index 28a8926cade4..bdde529a9af8 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -32,6 +32,7 @@
#define CLIENT_HAS_RIGHTS(cli, flags) ((cli?.admin_holder?.rights & flags) == flags)
#define CLIENT_IS_STAFF(cli) (cli?.admin_holder?.rights & (R_MOD|R_ADMIN))
+#define CLIENT_IS_MENTOR(cli) CLIENT_HAS_RIGHTS(cli, R_MENTOR)
#define AHOLD_IS_MOD(ahold) (ahold && (ahold.rights & R_MOD))
#define AHOLD_IS_ADMIN(ahold) (ahold && (ahold.rights & R_ADMIN))
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 36e70d311ac0..bf6d8e261ab3 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -12,7 +12,7 @@
return //don't recheck connected clients.
//Guest Checking
- if(IsGuestKey(key))
+ if(!real_bans_only && CONFIG_GET(flag/guest_ban) && IsGuestKey(key))
log_access("Failed Login: [key] - Guests not allowed")
message_admins("Failed Login: [key] - Guests not allowed")
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 5f24f71c8a50..2c749df71bb7 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -89,7 +89,7 @@
if(N.is_ban)
var/time_d = N.ban_time ? "Banned for [N.ban_time] minutes | " : ""
color = "#880000" //Removed confidential check because we can't make confidential bans
- dat += "[time_d][N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] "
+ dat += "[time_d][N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] [NOTE_ROUND_ID(N)] "
else
if(N.is_confidential)
color = "#AA0055"
@@ -102,7 +102,7 @@
else if(N.note_category == NOTE_YAUTJA)
color = "#114e11"
- dat += "[N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] "
+ dat += "[N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] [NOTE_ROUND_ID(N)] "
if(admin_ckey == usr.ckey || admin_ckey == "Adminbot" || ishost(usr))
dat += "Remove"
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 20ff65ed144e..368e2766ccfc 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -121,6 +121,7 @@ var/list/admin_verbs_minor_event = list(
/client/proc/toggle_sniper_upgrade,
/client/proc/toggle_attack_dead,
/client/proc/toggle_strip_drag,
+ /client/proc/toggle_disposal_mobs,
/client/proc/toggle_uniform_strip,
/client/proc/toggle_strong_defibs,
/client/proc/toggle_blood_optimization,
@@ -343,7 +344,7 @@ var/list/roundstart_mod_verbs = list(
add_verb(src, clan_verbs)
/client/proc/add_admin_whitelists()
- if(CLIENT_HAS_RIGHTS(src, R_MENTOR))
+ if(CLIENT_IS_MENTOR(src))
RoleAuthority.roles_whitelist[ckey] |= WHITELIST_MENTOR
if(CLIENT_IS_STAFF(src))
RoleAuthority.roles_whitelist[ckey] |= WHITELIST_JOE
@@ -576,6 +577,10 @@ var/list/roundstart_mod_verbs = list(
set desc = "Tells everyone about a random statistic in the round."
set category = "OOC"
+ var/prompt = tgui_alert(usr, "Are you sure you want to do this?", "Announce Random Fact", list("No", "Yes"))
+ if(prompt != "Yes")
+ return
+
message_admins("[key_name(usr)] announced a random fact.")
SSticker.mode?.declare_fun_facts()
diff --git a/code/modules/admin/medal_panel/medals_panel_tgui.dm b/code/modules/admin/medal_panel/medals_panel_tgui.dm
index ee8728670544..49c4bb5f96ad 100644
--- a/code/modules/admin/medal_panel/medals_panel_tgui.dm
+++ b/code/modules/admin/medal_panel/medals_panel_tgui.dm
@@ -19,22 +19,22 @@ GLOBAL_DATUM_INIT(medals_panel, /datum/medals_panel_tgui, new)
var/list/xeno_awards = list()
var/list/uscm_award_ckeys = list()
var/list/xeno_award_ckeys = list()
-
+
// Break the medals up by recipient and then pack each medal into a string
for(var/recipient_name as anything in GLOB.medal_awards)
var/datum/recipient_awards/recipient_award = GLOB.medal_awards[recipient_name]
uscm_awards[recipient_name] = list()
uscm_award_ckeys[recipient_name] = recipient_award.recipient_ckey ? " ([recipient_award.recipient_ckey])" : ""
for(var/i in 1 to recipient_award.medal_names.len) // We're assuming everything is same length
- uscm_awards[recipient_name] += "[recipient_award.medal_names[i]]: \'[recipient_award.medal_citations[i]]\' by [recipient_award.giver_rank[i]] [recipient_award.giver_name[i]]."
-
+ uscm_awards[recipient_name] += "[recipient_award.medal_names[i]]: \'[recipient_award.medal_citations[i]]\' by [recipient_award.giver_rank[i] ? "[recipient_award.giver_rank[i]] " : ""][recipient_award.giver_name[i] ? "[recipient_award.giver_name[i]] " : ""]([recipient_award.giver_ckey[i]])."
+
for(var/recipient_name as anything in GLOB.jelly_awards)
var/datum/recipient_awards/recipient_award = GLOB.jelly_awards[recipient_name]
xeno_awards[recipient_name] = list()
xeno_award_ckeys[recipient_name] = recipient_award.recipient_ckey ? " ([recipient_award.recipient_ckey])" : ""
for(var/i in 1 to recipient_award.medal_names.len) // We're assuming everything is same length
- xeno_awards[recipient_name] += "[recipient_award.medal_names[i]]: \'[recipient_award.medal_citations[i]]\'[recipient_award.giver_rank[i] ? " by [recipient_award.giver_rank[i]]" : ""][recipient_award.giver_name[i] ? " ([recipient_award.giver_name[i]])" : ""]."
-
+ xeno_awards[recipient_name] += "[recipient_award.medal_names[i]]: \'[recipient_award.medal_citations[i]]\' by [recipient_award.giver_rank[i] ? "[recipient_award.giver_rank[i]] " : ""][recipient_award.giver_name[i] ? "[recipient_award.giver_name[i]] " : ""]([recipient_award.giver_ckey[i]])."
+
data["uscm_awards"] = uscm_awards
data["xeno_awards"] = xeno_awards
data["uscm_award_ckeys"] = uscm_award_ckeys
@@ -61,8 +61,8 @@ GLOBAL_DATUM_INIT(medals_panel, /datum/medals_panel_tgui, new)
if("delete_medal")
remove_award(params["recipient"], TRUE, params["index"] + 1) // Why is byond not 0 indexed?
return TRUE
-
+
if("delete_jelly")
- remove_award(params["recipient"], FALSE, params["index"] + 1) // Why is byond not 0 indexed?
+ remove_award(params["recipient"], FALSE, params["index"] + 1) // Why is byond not 0 indexed?
return TRUE
-
+
diff --git a/code/modules/admin/player_panel/actions/transform.dm b/code/modules/admin/player_panel/actions/transform.dm
index 185165357e05..91a62b1a1d02 100644
--- a/code/modules/admin/player_panel/actions/transform.dm
+++ b/code/modules/admin/player_panel/actions/transform.dm
@@ -127,7 +127,7 @@ GLOBAL_LIST_INIT(pp_transformables, list(
"Alien Tier 4" = list(
list(
- name = XENO_CASTE_QUEEN+" (Young)",
+ name = XENO_CASTE_QUEEN+" (Immature)",
key = /mob/living/carbon/xenomorph/queen,
color = "purple"
),
diff --git a/code/modules/admin/tabs/admin_tab.dm b/code/modules/admin/tabs/admin_tab.dm
index 5a98faa6ddaa..1298d6150036 100644
--- a/code/modules/admin/tabs/admin_tab.dm
+++ b/code/modules/admin/tabs/admin_tab.dm
@@ -169,12 +169,12 @@
if(N.is_ban)
var/ban_text = N.ban_time ? "Banned for [N.ban_time] | " : ""
color = "#880000"
- dat += "[ban_text][N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] "
+ dat += "[ban_text][N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] [NOTE_ROUND_ID(N)] "
else
if(N.is_confidential)
color = "#AA0055"
- dat += "[N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] "
+ dat += "[N.text] by [admin_ckey] ([N.admin_rank])[confidential_text] on [N.date] [NOTE_ROUND_ID(N)] "
dat += "
"
dat += " "
@@ -255,9 +255,9 @@
log_adminpm("ADMIN: [key_name(src)] : [msg]")
- var/color = "adminsay"
- if(ishost(usr))
- color = "headminsay"
+ var/color = "mod"
+ if(check_rights(R_PERMISSIONS, show_msg = FALSE))
+ color = "adminmod"
var/channel = "ADMIN:"
channel = "[admin_holder.rank]:"
@@ -708,6 +708,20 @@
SSticker.mode.toggleable_flags ^= MODE_NO_ATTACK_DEAD
message_admins("[src] has [MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_ATTACK_DEAD) ? "prevented dead mobs from being" : "allowed dead mobs to be"] attacked.")
+/client/proc/toggle_disposal_mobs()
+ set name = "Toggle Disposable Mobs"
+ set category = "Admin.Flags"
+
+ if(!admin_holder || !check_rights(R_EVENT, FALSE))
+ return
+
+ if(!SSticker.mode)
+ to_chat(usr, SPAN_WARNING("A mode hasn't been selected yet!"))
+ return
+
+ SSticker.mode.toggleable_flags ^= MODE_DISPOSABLE_MOBS
+ message_admins("[src] has [MODE_HAS_TOGGLEABLE_FLAG(MODE_DISPOSABLE_MOBS) ? "allowed mobs to fit" : "prevented mobs fitting"] inside disposals.")
+
/client/proc/toggle_strip_drag()
set name = "Toggle Strip/Drag Dead"
set category = "Admin.Flags"
diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm
index b9eb4fd47ea1..32eaeb674b8d 100644
--- a/code/modules/admin/tabs/event_tab.dm
+++ b/code/modules/admin/tabs/event_tab.dm
@@ -218,21 +218,22 @@
if(!istype(chosen_ert))
return
- var/is_announcing = TRUE
- switch(alert(src, "Would you like to announce the distress beacon to the server population? This will reveal the distress beacon to all players.", "Announce distress beacon?", "Yes", "No", "Cancel"))
- if("Cancel")
- qdel(chosen_ert)
- return
- if("No")
- is_announcing = FALSE
+ var/is_announcing = tgui_alert(usr, "Would you like to announce the distress beacon to the server population? This will reveal the distress beacon to all players.", "Announce distress beacon?", list("Yes", "No"), 20 SECONDS)
+ if(!is_announcing)
+ qdel(chosen_ert)
+ return
+ if(is_announcing == "No")
+ is_announcing = FALSE
+ if (is_announcing == "Yes")
+ is_announcing = TRUE
var/turf/override_spawn_loc
- switch(alert(usr, "Spawn at their assigned spawnpoints, or at your location?", "Spawnpoint Selection", "Assigned Spawnpoint", "Current Location", "Cancel"))
- if("Cancel")
- qdel(chosen_ert)
- return
- if("Current Location")
- override_spawn_loc = get_turf(usr)
+ var/prompt = tgui_alert(usr, "Spawn at their assigned spawn, or at your location?", "Spawnpoint Selection", list("Spawn", "Current Location"), 0)
+ if(!prompt)
+ qdel(chosen_ert)
+ return
+ if(prompt == "Current Location")
+ override_spawn_loc = get_turf(usr)
chosen_ert.activate(is_announcing, override_spawn_loc)
@@ -491,10 +492,10 @@
for(var/obj/structure/machinery/computer/almayer_control/C in machines)
if(!(C.inoperable()))
var/obj/item/paper/P = new /obj/item/paper( C.loc )
- P.name = "'[command_name] Update.'"
+ P.name = "'[customname].'"
P.info = input
P.update_icon()
- C.messagetitle.Add("[command_name] Update")
+ C.messagetitle.Add("[customname]")
C.messagetext.Add(P.info)
if(alert("Press \"Yes\" if you want to announce it to ship crew and marines. Press \"No\" to keep it only as printed report on communication console.",,"Yes","No") == "Yes")
diff --git a/code/modules/admin/topic/topic.dm b/code/modules/admin/topic/topic.dm
index 010802318fbe..a76be10e9c26 100644
--- a/code/modules/admin/topic/topic.dm
+++ b/code/modules/admin/topic/topic.dm
@@ -982,7 +982,7 @@
message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1)
else if(href_list["revive"])
- if(!check_rights(R_MOD))
+ if(!check_rights(R_MOD))
return
var/mob/living/L = locate(href_list["revive"])
@@ -1926,6 +1926,42 @@
log_game("[key_name_admin(usr)] has granted self-destruct, requested by [key_name_admin(ref_person)]")
message_admins("[key_name_admin(usr)] has granted self-destruct, requested by [key_name_admin(ref_person)]", 1)
+ if(href_list["nukeapprove"])
+ var/mob/ref_person = locate(href_list["nukeapprove"])
+ if(!istype(ref_person))
+ return FALSE
+ var/nuketype = "Encrypted Operational Nuke"
+ var/prompt = tgui_alert(usr, "Do you want the nuke to be Encrypted?", "Nuke Type", list("Encrypted", "Decrypted"), 20 SECONDS)
+ if(prompt == "Decrypted")
+ nuketype = "Decrypted Operational Nuke"
+ prompt = tgui_alert(usr, "Are you sure you want to authorize a [nuketype] to the marines? This will greatly affect the round!", "DEFCON 1", list("No", "Yes"))
+ if(prompt != "Yes")
+ return
+
+ //make ASRS order for nuke
+ var/datum/supply_order/new_order = new()
+ new_order.ordernum = supply_controller.ordernum
+ supply_controller.ordernum++
+ new_order.object = supply_controller.supply_packs[nuketype]
+ new_order.orderedby = ref_person
+ new_order.approvedby = "USCM High Command"
+ supply_controller.shoppinglist += new_order
+
+ //Can no longer request a nuke
+ GLOB.ares_link.interface.nuke_available = FALSE
+
+ marine_announcement("A nuclear device has been authorized by High Command and will be delivered to requisitions via ASRS.", "NUCLEAR ORDNANCE AUTHORIZED", 'sound/misc/notice2.ogg', logging = ARES_LOG_MAIN)
+ log_game("[key_name_admin(usr)] has authorized a [nuketype], requested by [key_name_admin(ref_person)]")
+ message_admins("[key_name_admin(usr)] has authorized a [nuketype], requested by [key_name_admin(ref_person)]")
+
+ if(href_list["nukedeny"])
+ var/mob/ref_person = locate(href_list["nukedeny"])
+ if(!istype(ref_person))
+ return FALSE
+ marine_announcement("Your request for nuclear ordnance deployment has been reviewed and denied by USCM High Command for operational security and colonial preservation reasons. Have a good day.", "NUCLEAR ORDNANCE DENIED", 'sound/misc/notice2.ogg', logging = ARES_LOG_MAIN)
+ log_game("[key_name_admin(usr)] has denied nuclear ordnance, requested by [key_name_admin(ref_person)]")
+ message_admins("[key_name_admin(usr)] has dnied nuclear ordnance, requested by [key_name_admin(ref_person)]")
+
if(href_list["sddeny"]) // CentComm-deny. The self-destruct is denied, without any further conditions
var/mob/ref_person = locate(href_list["sddeny"])
marine_announcement("The self-destruct request has not received a response, ARES is now recalculating statistics.", "Self-Destruct System", logging = ARES_LOG_SECURITY)
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 03d2ae517354..3d50b50e414c 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -519,6 +519,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
AddInteraction("Deferred to Mentors by [key_name_admin(usr)].", player_message = "Deferred to Mentors.")
to_chat(initiator, SPAN_ADMINHELP("Your ticket has been deferred to Mentors."))
+ log_admin_private("Ticket [TicketHref("#[id]")] deferred to mentors by [usr.key].")
log_ahelp(id, "Defer", "Deferred to mentors by [usr.key]", null, usr.ckey)
Close(silent = TRUE)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index d73a69f3eb95..4a4f6fa830a9 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -12,7 +12,7 @@
return
if(!CLIENT_IS_STAFF(src))
- if(!CLIENT_HAS_RIGHTS(src, R_MENTOR))
+ if(!CLIENT_IS_MENTOR(src))
to_chat(src, "Only staff members have permission to use this.")
return
if(!CONFIG_GET(flag/mentor_tools))
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index e00f4a2d1263..3f0775c0b65d 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -143,6 +143,7 @@
assets = list(
"wylogo.png" = 'html/images/wylogo.png',
"uscmlogo.png" = 'html/images/uscmlogo.png',
+ "upplogo.png" = 'html/images/upplogo.png',
"faxwylogo.png" = 'html/images/faxwylogo.png',
"faxbackground.jpg" = 'html/images/faxbackground.jpg',
)
@@ -204,10 +205,10 @@
/datum/asset/spritesheet/playtime_rank/register()
var/icon_file = 'icons/mob/hud/hud.dmi'
- var/tier1_state = "hudxenoupgrade1"
- var/tier2_state = "hudxenoupgrade2"
- var/tier3_state = "hudxenoupgrade3"
- var/tier4_state = "hudxenoupgrade4"
+ var/tier1_state = "hudxenoupgrade2"
+ var/tier2_state = "hudxenoupgrade3"
+ var/tier3_state = "hudxenoupgrade4"
+ var/tier4_state = "hudxenoupgrade5"
var/icon/tier1_icon = icon(icon_file, tier1_state, SOUTH)
var/icon/tier2_icon = icon(icon_file, tier2_state, SOUTH)
diff --git a/code/modules/autowiki/autowiki.dm b/code/modules/autowiki/autowiki.dm
new file mode 100644
index 000000000000..8b38ec76706b
--- /dev/null
+++ b/code/modules/autowiki/autowiki.dm
@@ -0,0 +1,36 @@
+/// When the `AUTOWIKI` define is enabled, will generate an output file for tools/autowiki/autowiki.js to consume.
+/// Autowiki code intentionally still *exists* even without the define, to ensure developers notice
+/// when they break it immediately, rather than until CI or worse, call time.
+#if defined(AUTOWIKI) || defined(UNIT_TESTS)
+/proc/setup_autowiki()
+ Master.sleep_offline_after_initializations = FALSE
+ UNTIL(SSticker.current_state == GAME_STATE_PREGAME)
+
+ //trigger things to run the whole process
+ SSticker.request_start()
+ CONFIG_SET(number/round_end_countdown, 0)
+ SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(generate_autowiki)))
+
+/proc/generate_autowiki()
+ var/output = generate_autowiki_output()
+ rustg_file_write(output, "data/autowiki_edits.txt")
+ qdel(world)
+#endif
+
+/// Returns a string of the autowiki output file
+/proc/generate_autowiki_output()
+ var/total_output = ""
+
+ for (var/datum/autowiki/autowiki_type as anything in subtypesof(/datum/autowiki))
+ var/datum/autowiki/autowiki = new autowiki_type
+ var/output = autowiki.generate()
+
+ if (!istext(output))
+ CRASH("[autowiki_type] does not generate a proper output!")
+
+ total_output += json_encode(list(
+ "title" = autowiki.page,
+ "text" = output,
+ )) + "\n"
+
+ return total_output
diff --git a/code/modules/autowiki/pages/_page.dm b/code/modules/autowiki/pages/_page.dm
new file mode 100644
index 000000000000..8e745ace61c2
--- /dev/null
+++ b/code/modules/autowiki/pages/_page.dm
@@ -0,0 +1,54 @@
+/// A representation of an automated wiki page.
+/datum/autowiki
+ /// The page on the wiki to be replaced.
+ /// This should never be a user-facing page, like "Guide to circuits".
+ /// It should always be a template that only Autowiki should touch.
+ /// For example: "Template:Autowiki/CircuitInfo".
+ var/page
+
+/// Override and return the new text of the page.
+/// This proc can be impure, usually to call `upload_file`.
+/datum/autowiki/proc/generate()
+ SHOULD_CALL_PARENT(FALSE)
+ CRASH("[type] does not implement generate()!")
+
+/// Generates an auto formatted template user.
+/// Your autowiki should ideally be a *lot* of these.
+/// It lets wiki editors edit it much easier later, without having to enter repo.
+/// Parameters will be passed in by name. That means your template should expect
+/// something that looks like `{{ Autowiki_Circuit|name=Combiner|description=This combines }}`
+/// Lists, which must be array-like (no keys), will be turned into a flat list with their key and a number,
+/// such that list("food" = list("fruit", "candy")) -> food1=fruit|food2=candy
+/datum/autowiki/proc/include_template(name, parameters)
+ var/template_text = "{{[name]"
+
+ var/list/prepared_parameters = list()
+ for (var/key in parameters)
+ var/value = parameters[key]
+ if (islist(value))
+ for (var/index in 1 to length(value))
+ prepared_parameters["[key][index]"] = "[value[index]]"
+ else
+ prepared_parameters[key] = value
+
+ for (var/parameter_name in prepared_parameters)
+ template_text += "|[parameter_name]="
+ template_text += "[prepared_parameters[parameter_name]]"
+
+ template_text += "}}"
+
+ return template_text
+
+/// Takes an icon and uploads it to Autowiki-name.png.
+/// Do your best to make sure this is unique, so it doesn't clash with other autowiki icons.
+/datum/autowiki/proc/upload_icon(icon/icon, name)
+ // Fuck you
+ if (IsAdminAdvancedProcCall())
+ return
+
+ fcopy(icon, "data/autowiki_files/[name].png")
+
+/// Escape a parameter such that it can be correctly put inside a wiki output
+/datum/autowiki/proc/escape_value(parameter)
+ // | is a special character in MediaWiki, and must be escaped by...using another template.
+ return replacetextEx(parameter, "|", "{{!}}")
diff --git a/code/modules/autowiki/pages/guns.dm b/code/modules/autowiki/pages/guns.dm
new file mode 100644
index 000000000000..0946b552fe31
--- /dev/null
+++ b/code/modules/autowiki/pages/guns.dm
@@ -0,0 +1,118 @@
+/datum/autowiki/guns
+ page = "Template:Autowiki/Content/GunData"
+
+
+/datum/autowiki/guns/generate()
+ var/output = ""
+
+ var/list/gun_to_ammo = list()
+
+ for(var/obj/item/ammo_magazine/typepath as anything in subtypesof(/obj/item/ammo_magazine) - subtypesof(/obj/item/ammo_magazine/internal))
+ LAZYADD(gun_to_ammo[initial(typepath.gun_type)], typepath)
+
+ for(var/typepath in sort_list(subtypesof(/obj/item/weapon/gun), GLOBAL_PROC_REF(cmp_typepaths_asc)))
+ var/obj/item/weapon/gun/generating_gun = new typepath()
+
+ var/filename = SANITIZE_FILENAME(escape_value(format_text(generating_gun.name)))
+
+ var/list/gun_data = generating_gun.ui_data()
+
+ var/list/valid_mag_types = list()
+ for(var/path in gun_to_ammo)
+ if(!istype(generating_gun, path))
+ continue
+
+ valid_mag_types += gun_to_ammo[path]
+
+ var/ammo = ""
+ var/damage_table = ""
+ for(var/ammo_typepath in valid_mag_types)
+ var/obj/item/ammo_magazine/generating_mag = new ammo_typepath()
+
+ var/ammo_filename = SANITIZE_FILENAME(escape_value(format_text(generating_mag.name)))
+
+ if(!fexists("data/autowiki_files/[ammo_filename].png"))
+ upload_icon(getFlatIcon(generating_mag, no_anim = TRUE), ammo_filename)
+
+ var/datum/ammo/current_ammo = GLOB.ammo_list[generating_mag.default_ammo]
+
+ ammo += include_template("Autowiki/AmmoMagazine", list(
+ "icon" = escape_value(ammo_filename),
+ "name" = escape_value(generating_mag.name),
+ "capacity" = escape_value(generating_mag.max_rounds),
+ "damage" = escape_value(current_ammo.damage),
+ "max_range" = escape_value(current_ammo.max_range),
+ "fall_off" = escape_value(current_ammo.damage_falloff),
+ "penetration" = escape_value(current_ammo.penetration),
+ "punch" = escape_value(current_ammo.pen_armor_punch),
+ ))
+
+ generating_gun.current_mag = generating_mag
+
+ var/list/gun_ammo_data = generating_gun.ui_data()
+ var/list/armor_data = list()
+
+ var/iterator = 1
+ for(var/header in gun_ammo_data["damage_armor_profile_headers"])
+ var/damage = gun_ammo_data["damage_armor_profile_marine"][iterator]
+ armor_data["armor-[header]"] = damage
+ iterator++
+
+ var/list/damage = list("ammo_name" = escape_value(generating_mag.name))
+ damage += armor_data
+
+ damage_table += include_template("Autowiki/DamageVersusArmorRow", damage)
+
+ qdel(generating_mag)
+
+ gun_data["ammo_types"] = ammo
+ gun_data["damage_table"] = damage_table
+
+ var/list/attachments_by_slot = list()
+ for(var/obj/item/attachable/attachment_typepath as anything in generating_gun.attachable_allowed)
+ LAZYADD(attachments_by_slot[capitalize(initial(attachment_typepath.slot))], attachment_typepath)
+
+ var/attachments = ""
+ for(var/slot in attachments_by_slot)
+ var/list/attachments_in_slot = ""
+
+ for(var/attachment_typepath in attachments_by_slot[slot])
+ var/obj/item/attachable/generating_attachment = new attachment_typepath()
+
+ var/attachment_filename = SANITIZE_FILENAME(escape_value(format_text(generating_attachment.name)))
+
+ if(!fexists("data/autowiki_files/[attachment_filename].png"))
+ upload_icon(getFlatIcon(generating_attachment, no_anim = TRUE), attachment_filename)
+
+ attachments_in_slot += include_template("Autowiki/AvailableAttachment", list(
+ "icon" = escape_value(attachment_filename),
+ "name" = escape_value(generating_attachment.name),
+ ))
+
+ qdel(generating_attachment)
+
+ attachments += include_template("Autowiki/AttachmentsBySlot", list(
+ "slot" = escape_value(slot),
+ "attachments" = attachments_in_slot,
+ ))
+ gun_data["attachments"] = attachments
+
+
+ upload_icon(getFlatIcon(generating_gun, no_anim = TRUE), filename)
+ gun_data["icon"] = filename
+
+ output += include_template("Autowiki/Gun", gun_data)
+
+ qdel(generating_gun)
+
+ return output
+
+/datum/autowiki/guns/proc/wiki_sanitize_assoc(list/sanitizing_list)
+ var/list/sanitized = list()
+
+ for(var/key in sanitizing_list)
+ var/value = sanitizing_list[key]
+
+ sanitized[escape_value(key)] = escape_value(value)
+
+ return sanitized
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 8f0939474427..2facce7c3a59 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -27,7 +27,7 @@
var/area = null
var/time_died_as_mouse = null //when the client last died as a mouse
- var/donator = 0
+ var/donator = FALSE
var/adminhelped = 0
var/datum/click_intercept = null
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index ad0b6e1d89fb..3722b32fb2b4 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -287,11 +287,6 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
if(!(connection in list("seeker", "web"))) //Invalid connection type.
return null
- if(IsGuestKey(key))
- alert(src,"This server doesn't allow guest accounts to play. Please go to http://www.byond.com/ and register for a key.","Guest","OK")
- qdel(src)
- return
-
GLOB.clients += src
GLOB.directory[ckey] = src
@@ -435,7 +430,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
for(var/line in lines)
if(src.ckey == line)
- src.donator = 1
+ src.donator = TRUE
add_verb(src, /client/proc/set_ooc_color_self)
//if(prefs.window_skin & TOGGLE_WINDOW_SKIN)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 16afa8d1b4f2..4f1161709657 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -1955,11 +1955,17 @@ var/const/MAX_SAVE_SLOTS = 10
load_character(slot_for_job)
/// Transfers both physical characteristics and character information to character
-/datum/preferences/proc/copy_all_to(mob/living/carbon/human/character, job_title, is_late_join = FALSE)
+/datum/preferences/proc/copy_all_to(mob/living/carbon/human/character, job_title, is_late_join = FALSE, check_datacore = FALSE)
if(!istype(character))
return
find_assigned_slot(job_title, is_late_join)
+ if(check_datacore && !(be_random_body && be_random_name))
+ for(var/datum/data/record/record as anything in GLOB.data_core.locked)
+ if(record.fields["name"] == real_name)
+ be_random_body = TRUE
+ be_random_name = TRUE
+ break
if(be_random_name)
real_name = random_name(gender)
@@ -1987,10 +1993,11 @@ var/const/MAX_SAVE_SLOTS = 10
character.flavor_texts["legs"] = flavor_texts["legs"]
character.flavor_texts["feet"] = flavor_texts["feet"]
- character.med_record = strip_html(med_record)
- character.sec_record = strip_html(sec_record)
- character.gen_record = strip_html(gen_record)
- character.exploit_record = strip_html(exploit_record)
+ if(!be_random_name)
+ character.med_record = strip_html(med_record)
+ character.sec_record = strip_html(sec_record)
+ character.gen_record = strip_html(gen_record)
+ character.exploit_record = strip_html(exploit_record)
character.age = age
character.gender = gender
diff --git a/code/modules/client/tgui_macro.dm b/code/modules/client/tgui_macro.dm
index 684cf90942ea..f245f1d657d4 100644
--- a/code/modules/client/tgui_macro.dm
+++ b/code/modules/client/tgui_macro.dm
@@ -45,6 +45,7 @@ GLOBAL_LIST_EMPTY(ui_data_keybindings)
if(!ui)
ui = new(user, src, "KeyBinds", "Keybind Preference")
ui.open()
+ ui.set_autoupdate(FALSE)
/datum/tgui_macro/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
diff --git a/code/modules/clothing/glasses/meson.dm b/code/modules/clothing/glasses/meson.dm
index 859368cd7afc..b0823910365d 100644
--- a/code/modules/clothing/glasses/meson.dm
+++ b/code/modules/clothing/glasses/meson.dm
@@ -16,19 +16,6 @@
desc = "Used for shield the user's eyes from harmful electromagnetic emissions, can also be used as safety googles. Contains prescription lenses."
prescription = TRUE
-/obj/item/clothing/glasses/meson/yautja
- name = "bio-mask x-ray"
- desc = "A vision overlay generated by the Bio-Mask. Used to see through objects."
- icon = 'icons/obj/items/hunter/pred_gear.dmi'
- icon_state = "visor_meson"
- item_state = "securityhud"
- darkness_view = 12
- lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- vision_flags = SEE_TURFS
- flags_inventory = COVEREYES
- flags_item = NODROP|DELONDROP
- actions_types = null
-
/obj/item/clothing/glasses/meson/refurbished
name = "refurbished meson scanner"
desc = "Used to shield the user's eyes from harmful electromagnetic emissions, also used as general safety goggles. A special version with upgraded optics."
diff --git a/code/modules/clothing/glasses/thermal.dm b/code/modules/clothing/glasses/thermal.dm
index 607e53cf65f1..bfc60d271724 100644
--- a/code/modules/clothing/glasses/thermal.dm
+++ b/code/modules/clothing/glasses/thermal.dm
@@ -70,18 +70,6 @@
item_state = "syringe_kit"
toggleable = FALSE
-/obj/item/clothing/glasses/thermal/yautja
- name = "bio-mask thermal"
- desc = "A vision overlay generated by the Bio-Mask. Used to sense the heat of prey."
- icon = 'icons/obj/items/hunter/pred_gear.dmi'
- icon_state = "visor_thermal"
- item_state = "securityhud"
- vision_flags = SEE_MOBS
- invisa_view = 2
- flags_inventory = COVEREYES
- flags_item = NODROP|DELONDROP
- toggleable = FALSE
-
/obj/item/clothing/glasses/thermal/empproof
desc = "Thermals in the shape of glasses. This one is EMP proof."
blinds_on_emp = FALSE
diff --git a/code/modules/clothing/suits/marine_armor.dm b/code/modules/clothing/suits/marine_armor.dm
index c78e6782a833..910bb032349e 100644
--- a/code/modules/clothing/suits/marine_armor.dm
+++ b/code/modules/clothing/suits/marine_armor.dm
@@ -75,6 +75,7 @@
/obj/item/storage/belt/gun/flaregun,
/obj/item/device/motiondetector,
/obj/item/device/walkman,
+ /obj/item/storage/belt/gun/m39,
)
valid_accessory_slots = list(ACCESSORY_SLOT_MEDAL, ACCESSORY_SLOT_PONCHO)
@@ -271,7 +272,7 @@
/obj/item/clothing/suit/storage/marine/rto/intel
name = "\improper XM4 pattern intelligence officer armor"
- uniform_restricted = list(/obj/item/clothing/under/marine/officer, /obj/item/clothing/under/rank/ro_suit, /obj/item/clothing/under/marine/officer/intel)
+ uniform_restricted = list(/obj/item/clothing/under/marine/officer, /obj/item/clothing/under/rank/qm_suit, /obj/item/clothing/under/marine/officer/intel)
specialty = "XM4 pattern intel"
/obj/item/clothing/suit/storage/marine/MP
@@ -347,7 +348,7 @@
icon_state = "officer"
storage_slots = 3
flags_atom = null
- uniform_restricted = list(/obj/item/clothing/under/marine/officer, /obj/item/clothing/under/rank/ro_suit, /obj/item/clothing/under/rank/chief_medical_officer)
+ uniform_restricted = list(/obj/item/clothing/under/marine/officer, /obj/item/clothing/under/rank/qm_suit, /obj/item/clothing/under/rank/chief_medical_officer)
specialty = "M2 pattern officer"
item_state_slots = list(WEAR_JACKET = "officer")
@@ -362,7 +363,7 @@
storage_slots = 3
flags_atom = NO_SNOW_TYPE
flags_inventory = BLOCKSHARPOBJ|SMARTGUN_HARNESS
- uniform_restricted = list(/obj/item/clothing/under/marine, /obj/item/clothing/under/rank/ro_suit)
+ uniform_restricted = list(/obj/item/clothing/under/marine, /obj/item/clothing/under/rank/qm_suit)
specialty = "M3 pattern captain"
item_state_slots = list(WEAR_JACKET = "co_officer")
valid_accessory_slots = list(ACCESSORY_SLOT_MEDAL, ACCESSORY_SLOT_RANK, ACCESSORY_SLOT_DECOR, ACCESSORY_SLOT_PONCHO)
@@ -993,6 +994,12 @@
#undef FULL_CAMOUFLAGE_ALPHA
+/obj/item/clothing/suit/storage/marine/ghillie/forecon
+ name = "UDEP Thermal Poncho"
+ desc = "UDEP or the Ultra Diffusive Environmental Poncho is a camouflaged rain-cover worn to protect against the elements and chemical spills. It's commonly treated with an infrared absorbing coating, making a marine almost invisible in the rain. Favoured by USCM specialists for it's comfort and practicality."
+ icon_state = "mercenary_miner_armor"
+ flags_atom = MOB_LOCK_ON_EQUIP|NO_SNOW_TYPE|NO_NAME_OVERRIDE
+
/obj/item/clothing/suit/storage/marine/sof
name = "\improper SOF Armor"
desc = "A heavily customized suit of M3 armor. Used by Marine Raiders."
@@ -1510,7 +1517,7 @@
pockets.max_storage_space = 8
/obj/item/clothing/suit/storage/RO
- name = "\improper RO jacket"
+ name = "quartermaster jacket"
desc = "A green jacket worn by USCM personnel. The back has the flag of the United Americas on it."
icon_state = "RO_jacket"
blood_overlay_type = "coat"
diff --git a/code/modules/clothing/suits/marine_coat.dm b/code/modules/clothing/suits/marine_coat.dm
index 4442109ec425..4ca2a54af4bf 100644
--- a/code/modules/clothing/suits/marine_coat.dm
+++ b/code/modules/clothing/suits/marine_coat.dm
@@ -178,6 +178,11 @@
desc = "A Navy regulation dress blues coat for high-ranking officers. For those who wish for style and authority."
icon_state = "co_suit"
+/obj/item/clothing/suit/storage/jacket/marine/dress/officer/falcon
+ name = "commanding officer falcon jacket"
+ desc = "A refurbished jacket liner tailor made for a senior officer. This liner has become more of a proper piece of attire, with a new layer of fabric, wrist cuffs, front pockets, and a custom embroidered falcon on the back. This jacket will keep its wearer warm no matter the circumstance, from a cool Sunday drive to chilly autumn's eve."
+ icon_state = "co_falcon"
+
/obj/item/clothing/suit/storage/jacket/marine/dress/general
name = "general's jacket"
desc = "A black trench coat with gold metallic trim. Flashy, highly protective, and over-the-top. Fit for a king - or, in this case, a General. Has quite a few pockets."
@@ -212,6 +217,13 @@
icon_state = "bridge_coat_grey"
valid_accessory_slots = list(ACCESSORY_SLOT_ARMBAND, ACCESSORY_SLOT_RANK, ACCESSORY_SLOT_MEDAL)
+/obj/item/clothing/suit/storage/jacket/marine/service/aso
+ name = "auxiliary support officer jacket"
+ desc = "A comfortable vest for officers who are expected to work long hours staring at rows of numbers and inspecting equipment from knives to torpedos to entire dropships."
+ icon_state = "aso_jacket"
+ blood_overlay_type = "coat"
+ flags_armor_protection = BODY_FLAG_CHEST
+ has_buttons = FALSE
//=========================//PROVOST\\================================\\
diff --git a/code/modules/clothing/under/marine_uniform.dm b/code/modules/clothing/under/marine_uniform.dm
index eca050cc4b88..a4b38b657735 100644
--- a/code/modules/clothing/under/marine_uniform.dm
+++ b/code/modules/clothing/under/marine_uniform.dm
@@ -527,7 +527,7 @@
min_cold_protection_temperature = ICE_PLANET_MIN_COLD_PROT
has_sensor = UNIFORM_HAS_SENSORS
sensor_faction = FACTION_UPP
- suit_restricted = list(/obj/item/clothing/suit/storage/marine/faction/UPP, /obj/item/clothing/suit/gimmick/jason, /obj/item/clothing/suit/storage/snow_suit/soviet, /obj/item/clothing/suit/storage/snow_suit/survivor)
+ suit_restricted = list(/obj/item/clothing/suit/storage/marine/faction/UPP, /obj/item/clothing/suit/gimmick/jason, /obj/item/clothing/suit/storage/snow_suit/soviet, /obj/item/clothing/suit/storage/snow_suit/survivor, /obj/item/clothing/suit/storage/webbing)
flags_jumpsuit = UNIFORM_SLEEVE_ROLLABLE
/obj/item/clothing/under/marine/veteran/UPP/medic
@@ -839,9 +839,9 @@
desc = "A formal white undersuit."
flags_jumpsuit = FALSE
-/obj/item/clothing/under/rank/ro_suit
- name = "requisition officer suit"
- desc = "A nicely-fitting military suit for a requisition officer. It has shards of light Kevlar to help protect against stabbing weapons and bullets."
+/obj/item/clothing/under/rank/qm_suit
+ name = "quartermaster suit"
+ desc = "A nicely-fitting military suit for a quartermaster. It has shards of light Kevlar to help protect against stabbing weapons and bullets."
icon_state = "RO_jumpsuit"
worn_state = "RO_jumpsuit"
flags_jumpsuit = UNIFORM_SLEEVE_ROLLABLE
diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm
index d42e7d17bfd2..329e2055778e 100644
--- a/code/modules/clothing/under/ties.dm
+++ b/code/modules/clothing/under/ties.dm
@@ -359,6 +359,11 @@
desc = "A fire-resistant shoulder patch, worn by the men and women of the USS Hanyut, USCM FORECON."
icon_state = "forecon_patch"
+/obj/item/clothing/accessory/patch/upp
+ name = "UPP Airborne Reconnaissance patch"
+ desc = "A fire-resistant shoulder patch, worn by the men and women of the 173rd Airborne Reconnaissance Platoon."
+ icon_state = "upppatch"
+
/obj/item/clothing/accessory/poncho
name = "USCM Poncho"
desc = "The standard USCM poncho has variations for every climate. Custom fitted to be attached to standard USCM armor variants it is comfortable, warming or cooling as needed, and well-fit. A marine couldn't ask for more. Affectionately referred to as a \"woobie\"."
diff --git a/code/modules/cm_aliens/structures/special/pylon_core.dm b/code/modules/cm_aliens/structures/special/pylon_core.dm
index 993d4f833fa6..068ffeb659eb 100644
--- a/code/modules/cm_aliens/structures/special/pylon_core.dm
+++ b/code/modules/cm_aliens/structures/special/pylon_core.dm
@@ -12,6 +12,7 @@
block_range = 0
var/cover_range = WEED_RANGE_PYLON
var/node_type = /obj/effect/alien/weeds/node/pylon
+ var/obj/effect/alien/weeds/node/node
var/linked_turfs = list()
var/damaged = FALSE
@@ -25,7 +26,7 @@
/obj/effect/alien/resin/special/pylon/Initialize(mapload, hive_ref)
. = ..()
- place_node()
+ node = place_node()
for(var/turf/A in range(round(cover_range*PYLON_COVERAGE_MULT), loc))
LAZYADD(A.linked_pylons, src)
linked_turfs += A
@@ -34,9 +35,8 @@
for(var/turf/A as anything in linked_turfs)
LAZYREMOVE(A.linked_pylons, src)
- var/obj/effect/alien/weeds/node/pylon/W = locate() in loc
- if(W)
- qdel(W)
+ if(node)
+ QDEL_NULL(node)
. = ..()
/obj/effect/alien/resin/special/pylon/attack_alien(mob/living/carbon/xenomorph/M)
@@ -87,8 +87,78 @@
playsound(loc, "alien_resin_build", 25)
/obj/effect/alien/resin/special/pylon/proc/place_node()
- var/obj/effect/alien/weeds/node/pylon/W = new node_type(loc, null, null, linked_hive)
- W.resin_parent = src
+ var/obj/effect/alien/weeds/node/pylon/pylon_node = new node_type(loc, null, null, linked_hive)
+ pylon_node.resin_parent = src
+ return pylon_node
+
+/obj/effect/alien/resin/special/pylon/endgame
+ cover_range = WEED_RANGE_CORE
+ var/activated = FALSE
+
+/obj/effect/alien/resin/special/pylon/endgame/Destroy()
+ if(activated)
+ activated = FALSE
+
+ if(hijack_delete)
+ return ..()
+
+ marine_announcement("ALERT.\n\nEnergy build up around communication relay at [get_area(src)] halted.", "[MAIN_AI_SYSTEM] Biological Scanner")
+
+ for(var/hivenumber in GLOB.hive_datum)
+ var/datum/hive_status/checked_hive = GLOB.hive_datum[hivenumber]
+ if(!length(checked_hive.totalXenos))
+ continue
+
+ if(checked_hive == linked_hive)
+ xeno_announcement(SPAN_XENOANNOUNCE("We have lost our control of the tall's communication relay at [get_area(src)]."), hivenumber, XENO_GENERAL_ANNOUNCE)
+ else
+ xeno_announcement(SPAN_XENOANNOUNCE("Another hive has lost control of the tall's communication relay at [get_area(src)]."), hivenumber, XENO_GENERAL_ANNOUNCE)
+
+ return ..()
+
+/// Checks if all comms towers are connected and then starts end game content on all pylons if they are
+/obj/effect/alien/resin/special/pylon/endgame/proc/comms_relay_connection()
+ marine_announcement("ALERT.\n\nIrregular build up of energy around communication relays at [get_area(src)].", "[MAIN_AI_SYSTEM] Biological Scanner")
+
+ for(var/hivenumber in GLOB.hive_datum)
+ var/datum/hive_status/checked_hive = GLOB.hive_datum[hivenumber]
+ if(!length(checked_hive.totalXenos))
+ continue
+
+ if(checked_hive == linked_hive)
+ xeno_announcement(SPAN_XENOANNOUNCE("We have harnessed the tall's communication relay at [get_area(src)]. Hold it!"), hivenumber, XENO_GENERAL_ANNOUNCE)
+ else
+ xeno_announcement(SPAN_XENOANNOUNCE("Another hive has harnessed the tall's communication relay at [get_area(src)].[linked_hive.faction_is_ally(checked_hive.name) ? "" : " Stop them!"]"), hivenumber, XENO_GENERAL_ANNOUNCE)
+
+ activated = TRUE
+ addtimer(CALLBACK(src, PROC_REF(give_larva)), XENO_PYLON_ACTIVATION_COOLDOWN, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_LOOP|TIMER_DELETE_ME)
+
+#define ENDGAME_LARVA_CAP_MULTIPLIER 0.4
+#define LARVA_ADDITION_MULTIPLIER 0.10
+
+/// Looped proc via timer to give larva after time
+/obj/effect/alien/resin/special/pylon/endgame/proc/give_larva()
+ if(!activated)
+ return
+
+ if(!linked_hive.hive_location || !linked_hive.living_xeno_queen)
+ return
+
+ var/list/hive_xenos = linked_hive.totalXenos
+
+ for(var/mob/living/carbon/xenomorph/xeno in hive_xenos)
+ if(!xeno.counts_for_slots)
+ hive_xenos -= xeno
+
+ if(length(hive_xenos) > (length(GLOB.alive_human_list) * ENDGAME_LARVA_CAP_MULTIPLIER))
+ return
+
+ linked_hive.partial_larva += length(hive_xenos) * LARVA_ADDITION_MULTIPLIER
+ linked_hive.convert_partial_larva_to_full_larva()
+ linked_hive.hive_ui.update_burrowed_larva()
+
+#undef ENDGAME_LARVA_CAP_MULTIPLIER
+#undef LARVA_ADDITION_MULTIPLIER
//Hive Core - Generates strong weeds, supports other buildings
/obj/effect/alien/resin/special/pylon/core
diff --git a/code/modules/cm_aliens/structures/special_structure.dm b/code/modules/cm_aliens/structures/special_structure.dm
index caa729f1df87..69bdcc2438ac 100644
--- a/code/modules/cm_aliens/structures/special_structure.dm
+++ b/code/modules/cm_aliens/structures/special_structure.dm
@@ -39,6 +39,9 @@
plane = FLOOR_PLANE
+ /// Tells the structure if they are being deleted because of hijack
+ var/hijack_delete = FALSE
+
/obj/effect/alien/resin/special/Initialize(mapload, hive_ref)
. = ..()
maxhealth = health
diff --git a/code/modules/cm_aliens/structures/tunnel.dm b/code/modules/cm_aliens/structures/tunnel.dm
index f03e81ccc408..0e1008cfbf12 100644
--- a/code/modules/cm_aliens/structures/tunnel.dm
+++ b/code/modules/cm_aliens/structures/tunnel.dm
@@ -2,6 +2,8 @@
* Tunnels
*/
+#define TUNNEL_COLLAPSING_TIME (60 SECONDS)
+
/obj/structure/tunnel
name = "tunnel"
desc = "A tunnel entrance. Looks like it was dug by some kind of clawed beast."
@@ -83,6 +85,25 @@
/obj/structure/tunnel/attackby(obj/item/W as obj, mob/user as mob)
if(!isxeno(user))
+ if(istype(W, /obj/item/tool/shovel))
+ var/obj/item/tool/shovel/destroying_shovel = W
+
+ if(destroying_shovel.folded)
+ return
+
+ playsound(user.loc, 'sound/effects/thud.ogg', 40, 1, 6)
+
+ user.visible_message(SPAN_NOTICE("[user] starts to collapse [src]!"), SPAN_NOTICE("You start collapsing [src]!"))
+
+ if(user.action_busy || !do_after(user, TUNNEL_COLLAPSING_TIME * ((100 - destroying_shovel.shovelspeed) * 0.01), INTERRUPT_ALL, BUSY_ICON_BUILD))
+ return
+
+ playsound(loc, 'sound/effects/tunnel_collapse.ogg', 50)
+
+ visible_message(SPAN_NOTICE("[src] collapses in on itself."))
+
+ qdel(src)
+
return ..()
return attack_alien(user)
@@ -233,3 +254,12 @@
else
to_chat(M, SPAN_WARNING("\The [src] ended unexpectedly, so you return back up."))
return XENO_NO_DELAY_ACTION
+
+/obj/structure/tunnel/maint_tunnel
+ name = "\improper Maintenance Hatch"
+ desc = "An entrance to a maintenance tunnel. You can see bits of slime and resin within. Pieces of debris keep you from getting a closer look."
+ icon = 'icons/obj/structures/structures.dmi'
+ icon_state = "hatchclosed"
+
+/obj/structure/tunnel/maint_tunnel/no_xeno_desc
+ desc = "An entrance to a maintenance tunnel. Pieces of debris keep you from getting a closer look."
diff --git a/code/modules/cm_aliens/weeds.dm b/code/modules/cm_aliens/weeds.dm
index f20fa842e446..01140beae304 100644
--- a/code/modules/cm_aliens/weeds.dm
+++ b/code/modules/cm_aliens/weeds.dm
@@ -55,7 +55,7 @@
linked_hive = GLOB.hive_datum[hivenumber]
set_hive_data(src, hivenumber)
- if(spread_on_semiweedable)
+ if(spread_on_semiweedable && weed_strength < WEED_LEVEL_HIVE)
if(color)
var/list/RGB = ReadRGB(color)
RGB[1] = Clamp(RGB[1] + 35, 0, 255)
@@ -588,9 +588,13 @@
weed_strength = WEED_LEVEL_HIVE
node_range = WEED_RANGE_PYLON
overlay_node = FALSE
+ spread_on_semiweedable = TRUE
var/obj/effect/alien/resin/special/resin_parent
/obj/effect/alien/weeds/node/pylon/proc/set_parent_damaged()
+ if(!resin_parent)
+ return
+
var/obj/effect/alien/resin/special/pylon/parent_pylon = resin_parent
parent_pylon.damaged = TRUE
@@ -616,7 +620,13 @@
/obj/effect/alien/weeds/node/pylon/acid_spray_act()
return
+/obj/effect/alien/weeds/node/pylon/cluster
+ spread_on_semiweedable = FALSE
+
/obj/effect/alien/weeds/node/pylon/cluster/set_parent_damaged()
+ if(!resin_parent)
+ return
+
var/obj/effect/alien/resin/special/cluster/parent_cluster = resin_parent
parent_cluster.damaged = TRUE
diff --git a/code/modules/cm_marines/Donator_Items.dm b/code/modules/cm_marines/Donator_Items.dm
index 47380ca2c84a..58b8d448a92f 100644
--- a/code/modules/cm_marines/Donator_Items.dm
+++ b/code/modules/cm_marines/Donator_Items.dm
@@ -55,6 +55,8 @@
//DON'T GRAB STUFF BETWEEN THIS LINE
flags_inventory = ALLOWREBREATH
flags_inv_hide = HIDEEARS|HIDEEYES|HIDEFACE
+ flags_cold_protection = BODY_FLAG_HEAD
+ min_cold_protection_temperature = ICE_PLANET_MIN_COLD_PROT
//AND THIS LINE
//END MASK TEMPLATE
diff --git a/code/modules/cm_marines/dropship_equipment.dm b/code/modules/cm_marines/dropship_equipment.dm
index 89d33134bdb8..3568f001c977 100644
--- a/code/modules/cm_marines/dropship_equipment.dm
+++ b/code/modules/cm_marines/dropship_equipment.dm
@@ -289,7 +289,7 @@
density = FALSE
equip_categories = list(DROPSHIP_WEAPON, DROPSHIP_CREW_WEAPON)
icon_state = "mg_system"
- point_cost = 300
+ point_cost = 50
var/deployment_cooldown
var/obj/structure/machinery/m56d_hmg/mg_turret/dropship/deployed_mg
combat_equipment = FALSE
@@ -449,7 +449,7 @@
icon_state = "spotlights"
desc = "A set of high-powered spotlights to illuminate large areas. Fits on electronics attach points of dropships. Moving this will require a powerloader."
is_interactable = TRUE
- point_cost = 300
+ point_cost = 50
var/spotlights_cooldown
var/brightness = 11
@@ -513,7 +513,7 @@
name = "\improper LZ detector"
desc = "An electronic device linked to the dropship's camera system that lets you observe your landing zone mid-flight."
icon_state = "lz_detector"
- point_cost = 400
+ point_cost = 50
var/obj/structure/machinery/computer/cameras/dropship/linked_cam_console
/obj/structure/dropship_equipment/electronics/landing_zone_detector/update_equipment()
@@ -1137,7 +1137,7 @@
name = "rappel deployment system"
equip_categories = list(DROPSHIP_CREW_WEAPON)
icon_state = "rappel_module_packaged"
- point_cost = 500
+ point_cost = 50
combat_equipment = FALSE
var/harness = /obj/item/rappel_harness
diff --git a/code/modules/cm_marines/equipment/guncases.dm b/code/modules/cm_marines/equipment/guncases.dm
index ff4d8397be26..a9a3855a53e4 100644
--- a/code/modules/cm_marines/equipment/guncases.dm
+++ b/code/modules/cm_marines/equipment/guncases.dm
@@ -58,13 +58,15 @@
/obj/item/storage/box/guncase/lmg
name = "\improper M41AE2 heavy pulse rifle case"
desc = "A gun case containing the M41AE2 heavy pulse rifle. You can get additional ammunition at requisitions."
- storage_slots = 3
+ storage_slots = 5
can_hold = list(/obj/item/weapon/gun/rifle/lmg, /obj/item/ammo_magazine/rifle/lmg)
/obj/item/storage/box/guncase/lmg/fill_preset_inventory()
new /obj/item/weapon/gun/rifle/lmg(src)
new /obj/item/ammo_magazine/rifle/lmg(src)
new /obj/item/ammo_magazine/rifle/lmg/holo_target(src)
+ new /obj/item/attachable/flashlight
+ new /obj/item/attachable/bipod
//------------
/obj/item/storage/box/guncase/m41aMK1
@@ -293,3 +295,47 @@
new /obj/item/weapon/gun/shotgun/double/cane(src)
new /obj/item/ammo_magazine/handful/revolver/marksman/six_rounds(src)
new /obj/item/ammo_magazine/handful/revolver/marksman/six_rounds(src)
+
+//Handgun case for Military police vendor three mag , a railflashligh and the handgun.
+
+//88 Mod 4 Combat Pistol
+/obj/item/storage/box/guncase/mod88
+ name = "\improper 88 Mod 4 Combat Pistol case"
+ desc = "A gun case containing an 88 Mod 4 Combat Pistol."
+ storage_slots = 5
+ can_hold = list(/obj/item/attachable/flashlight, /obj/item/weapon/gun/pistol/mod88, /obj/item/ammo_magazine/pistol/mod88)
+
+/obj/item/storage/box/guncase/mod88/fill_preset_inventory()
+ new /obj/item/attachable/flashlight(src)
+ new /obj/item/weapon/gun/pistol/mod88(src)
+ new /obj/item/ammo_magazine/pistol/mod88(src)
+ new /obj/item/ammo_magazine/pistol/mod88(src)
+ new /obj/item/ammo_magazine/pistol/mod88(src)
+
+//M44 Combat Revolver
+/obj/item/storage/box/guncase/m44
+ name = "\improper M44 Combat Revolver case"
+ desc = "A gun case containing an M44 Combat Revolver."
+ storage_slots = 5
+ can_hold = list(/obj/item/attachable/flashlight, /obj/item/weapon/gun/revolver/m44, /obj/item/ammo_magazine/revolver)
+
+/obj/item/storage/box/guncase/m44/fill_preset_inventory()
+ new /obj/item/attachable/flashlight(src)
+ new /obj/item/weapon/gun/revolver/m44(src)
+ new /obj/item/ammo_magazine/revolver(src)
+ new /obj/item/ammo_magazine/revolver(src)
+ new /obj/item/ammo_magazine/revolver(src)
+
+//M4A3 Service Pistol
+/obj/item/storage/box/guncase/m4a3
+ name = "\improper M4A3 Service Pistol case"
+ desc = "A gun case containing an M4A3 Service Pistol."
+ storage_slots = 5
+ can_hold = list(/obj/item/attachable/flashlight, /obj/item/weapon/gun/pistol/m4a3, /obj/item/ammo_magazine/pistol)
+
+/obj/item/storage/box/guncase/m4a3/fill_preset_inventory()
+ new /obj/item/attachable/flashlight(src)
+ new /obj/item/weapon/gun/pistol/m4a3(src)
+ new /obj/item/ammo_magazine/pistol(src)
+ new /obj/item/ammo_magazine/pistol(src)
+ new /obj/item/ammo_magazine/pistol(src)
diff --git a/code/modules/cm_marines/equipment/kit_boxes.dm b/code/modules/cm_marines/equipment/kit_boxes.dm
index 4ce6be802f3d..a552b8eb0927 100644
--- a/code/modules/cm_marines/equipment/kit_boxes.dm
+++ b/code/modules/cm_marines/equipment/kit_boxes.dm
@@ -207,7 +207,7 @@
for(var/allowed_role in allowed_roles_list)
if(user.job == allowed_role)
- if(!skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_DEFAULT) && !skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_ALL))
+ if(!skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_TRAINED) && !skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_ALL))
to_chat(user, SPAN_WARNING("You already have specialization, give this kit to someone else!"))
return FALSE
return TRUE
@@ -216,7 +216,7 @@
var/selection = tgui_input_list(user, "Pick your specialist equipment type.", "Specialist Kit Selection", available_specialist_kit_boxes)
if(!selection || QDELETED(src))
return FALSE
- if(!skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_DEFAULT) && !skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_ALL))
+ if(!skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_TRAINED) && !skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_ALL))
to_chat(user, SPAN_WARNING("You already unwrapped your [name], give this one to someone else!"))
return
if(!available_specialist_kit_boxes[selection] || available_specialist_kit_boxes[selection] <= 0)
diff --git a/code/modules/cm_marines/equipment/maps.dm b/code/modules/cm_marines/equipment/maps.dm
index 98ad238fe055..f41c8d6f971b 100644
--- a/code/modules/cm_marines/equipment/maps.dm
+++ b/code/modules/cm_marines/equipment/maps.dm
@@ -82,7 +82,7 @@
/obj/item/map/big_red_map
name = "\improper Solaris Ridge Map"
desc = "A censored blueprint of the Solaris Ridge facility"
- html_link = "images/c/c5/Big_Red.png"
+ html_link = "images/9/9e/Solaris_Ridge.png"
color = "#e88a10"
/obj/item/map/FOP_map
@@ -104,10 +104,11 @@
color = "#ad8d0e"
/obj/item/map/sorokyne_map
- name = "\improper Sorokyne Outpost Map"
- desc = "A labelled schematic of the Sorokyne Outpost and the surrounding caves."
+ name = "\improper Sorokyne Strata map"
+ desc = "A map of the Weyland-Yutani colony Sorokyne Outpost, commonly known as Sorokyne Strata."
html_link = "images/2/21/Sorokyne_Wiki_Map.jpg" //The fact that this is just a wiki-link makes me sad and amused.
color = "cyan"
+
/obj/item/map/corsat
name = "\improper CORSAT map"
desc = "A blueprint of CORSAT station"
@@ -125,83 +126,46 @@
desc = "An overview of LV-522 schematics."
html_link = "images/b/bb/C_claim.png"
color = "cyan"
+
/obj/item/map/new_varadero
name = "\improper New Varadero map"
desc = "A labeled blueprint of the UA outpost New Varadero"
html_link = "images/9/94/New_Varadero.png"
color = "red"
+GLOBAL_LIST_INIT_TYPED(map_type_list, /obj/item/map, setup_all_maps())
+
+/proc/setup_all_maps()
+ return list(
+ MAP_LV_624 = new /obj/item/map/lazarus_landing_map(),
+ MAP_ICE_COLONY = new /obj/item/map/ice_colony_map(),
+ MAP_ICE_COLONY_V3 = new /obj/item/map/ice_colony_map_v3(),
+ MAP_WHISKEY_OUTPOST = new /obj/item/map/whiskey_outpost_map(),
+ MAP_BIG_RED = new /obj/item/map/big_red_map(),
+ MAP_PRISON_STATION = new /obj/item/map/FOP_map(),
+ MAP_PRISON_STATION_V3 = new /obj/item/map/FOP_map_v3(),
+ MAP_DESERT_DAM = new /obj/item/map/desert_dam(),
+ MAP_SOROKYNE_STRATA = new /obj/item/map/sorokyne_map(),
+ MAP_CORSAT = new /obj/item/map/corsat(),
+ MAP_KUTJEVO = new /obj/item/map/kutjevo_map(),
+ MAP_LV522_CHANCES_CLAIM = new /obj/item/map/lv522_map(),
+ MAP_NEW_VARADERO = new /obj/item/map/new_varadero()
+ )
+
//used by marine equipment machines to spawn the correct map.
/obj/item/map/current_map
/obj/item/map/current_map/Initialize(mapload, ...)
. = ..()
- switch(SSmapping.configs[GROUND_MAP].map_name)
- if(MAP_LV_624)
- name = "\improper Lazarus Landing Map"
- desc = "A satellite printout of the Lazarus Landing colony on LV-624."
- html_link = "images/6/6f/LV624.png"
- if(MAP_ICE_COLONY)
- name = "\improper Ice Colony map"
- desc = "A satellite printout of the Ice Colony."
- html_link = "images/1/18/Map_icecolony.png"
- color = "cyan"
- if(MAP_ICE_COLONY_V3)
- name = "\improper Shivas Snowball map"
- desc = "A labelled print out of the anterior scan of the UA colony Shivas Snowball."
- html_link = "images/1/18/Map_icecolony.png"//needs to be replaced at some point
- color = "cyan"
- if(MAP_BIG_RED)
- name = "\improper Solaris Ridge Map"
- desc = "A censored blueprint of the Solaris Ridge facility"
- html_link = "images/9/9e/Solaris_Ridge.png"
- color = "#e88a10"
- if(MAP_PRISON_STATION)
- name = "\improper Fiorina Orbital Penitentiary Map"
- desc = "A labelled interior scan of Fiorina Orbital Penitentiary"
- html_link = "images/4/4c/Map_Prison.png"
- color = "#e88a10"
- if(MAP_PRISON_STATION_V3)
- name = "\improper Fiorina Orbital Penitentiary Map"
- desc = "A scan produced by the Almayer's sensor array of the Fiorina Orbital Penitentiary Civilian Annex. It appears to have broken off from the rest of the station and is now in free geo-sync orbit around the planet."
- html_link = "images/e/e0/Prison_Station_Science_Annex.png"
- color = "#e88a10"
- if(MAP_DESERT_DAM)
- name = "\improper Trijent Dam map"
- desc = "A map of Trijent Dam"
- html_link = "images/9/92/Trijent_Dam.png"
- color = "#cec13f"
- //did only the basics todo change later
- if(MAP_SOROKYNE_STRATA)
- name = "\improper Sorokyne Strata map"
- desc = "A map of the Weyland-Yutani colony Sorokyne Outpost, commonly known as Sorokyne Strata."
- html_link = "images/1/1c/Sorokyne_map.png"
- color = "cyan"
- if (MAP_CORSAT)
- name = "\improper CORSAT map"
- desc = "A blueprint of CORSAT station"
- html_link = "images/8/8e/CORSAT_Satellite.png"
- color = "red"
- if (MAP_KUTJEVO)
- name = "\improper Kutjevo Refinery map"
- desc = "An orbital scan of Kutjevo Refinery"
- html_link = "images/0/0d/Kutjevo_a1.jpg"
- color = "red"
- if (MAP_LV522_CHANCES_CLAIM)
- name = "\improper LV-522 Map"
- desc = "An overview of LV-522 schematics."
- html_link = "images/b/bb/C_claim.png"
- color = "cyan"
- if (MAP_NEW_VARADERO)
- name = "\improper New Varadero map"
- desc = "The blueprint and readout of the UA outpost New Varadero"
- html_link = "images/9/94/New_Varadero.png"//replace later
- color = "red"
-
- else
- return INITIALIZE_HINT_QDEL
-
+ var/map_name = SSmapping.configs[GROUND_MAP].map_name
+ var/obj/item/map/map = GLOB.map_type_list[map_name]
+ if (!map && (map_name == MAP_RUNTIME || map_name == MAP_CHINOOK || map_name == MAIN_SHIP_DEFAULT_NAME))
+ return // "Maps" we don't have maps for so we don't need to throw a runtime for (namely in unit_testing)
+ name = map.name
+ desc = map.desc
+ html_link = map.html_link
+ color = map.color
// Landmark - Used for mapping. Will spawn the appropriate map for each gamemode (LV map items will spawn when LV is the gamemode, etc)
/obj/effect/landmark/map_item
diff --git a/code/modules/cm_marines/marines_consoles.dm b/code/modules/cm_marines/marines_consoles.dm
index 36535a0b5141..ad0ebac8307b 100644
--- a/code/modules/cm_marines/marines_consoles.dm
+++ b/code/modules/cm_marines/marines_consoles.dm
@@ -679,11 +679,13 @@
idle_power_usage = 250
active_power_usage = 500
var/faction = FACTION_MARINE
+ /// What type of /datum/crewmonitor this will create
+ var/crewmonitor_type = /datum/crewmonitor
/obj/structure/machinery/computer/crew/Initialize()
. = ..()
if(!GLOB.crewmonitor[faction])
- GLOB.crewmonitor[faction] = new /datum/crewmonitor(faction)
+ GLOB.crewmonitor[faction] = new crewmonitor_type(faction)
/obj/structure/machinery/computer/crew/attack_remote(mob/living/user)
attack_hand(user)
@@ -714,6 +716,12 @@
icon_state = "cmonitor"
density = FALSE
+/obj/structure/machinery/computer/crew/alt/yautja
+ name = "\improper Yautja health monitor"
+ desc = "Used to monitor active health sensors of all Yautja in the system. You can see that the console highlights the human's ship areas with BLUE and the hunting locations with RED."
+ faction = FACTION_YAUTJA
+ crewmonitor_type = /datum/crewmonitor/yautja
+
/obj/structure/machinery/computer/crew/upp
faction = FACTION_UPP
@@ -790,7 +798,7 @@ GLOBAL_LIST_EMPTY_TYPED(crewmonitor, /datum/crewmonitor)
/datum/crewmonitor/ui_data(mob/user)
. = list(
"sensors" = update_data(),
- "link_allowed" = isAI(user)
+ "link_allowed" = isAI(user),
)
/datum/crewmonitor/proc/update_data()
@@ -902,10 +910,11 @@ GLOBAL_LIST_EMPTY_TYPED(crewmonitor, /datum/crewmonitor)
RAIDER_OFFICER_SQUAD = 11,
JOB_SO = 12,
JOB_SEA = 13,
- // 20-29: Aux Command (Synth isn't Aux head, but important - make him bold)
- JOB_SYNTH = 20,
- JOB_PILOT = 21,
- JOB_DROPSHIP_CREW_CHIEF = 22,
+ // 20-29: Aux Command
+ JOB_AUXILIARY_OFFICER = 20,
+ JOB_SYNTH = 21,
+ JOB_PILOT = 22,
+ JOB_DROPSHIP_CREW_CHIEF = 23,
JOB_INTEL = 24,
// 30-39: Security
JOB_CHIEF_POLICE = 30,
@@ -929,11 +938,11 @@ GLOBAL_LIST_EMPTY_TYPED(crewmonitor, /datum/crewmonitor)
// 60-69: Cargo
JOB_CHIEF_REQUISITION = 60,
JOB_CARGO_TECH = 61,
+ JOB_MESS_SERGEANT = 62,
// 70-139: SQUADS (look below)
// 140+: Civilian/other
JOB_CORPORATE_LIAISON = 140,
- JOB_MESS_SERGEANT = 141,
- JOB_PASSENGER = 142,
+ JOB_PASSENGER = 141,
// Non Almayer jobs lower then registered
JOB_SYNTH_SURVIVOR = 150,
JOB_SURVIVOR = 151,
@@ -1102,6 +1111,51 @@ GLOBAL_LIST_EMPTY_TYPED(crewmonitor, /datum/crewmonitor)
else
jobs = list()
+/datum/crewmonitor/yautja
+ faction = FACTION_YAUTJA
+
+/datum/crewmonitor/yautja/update_data()
+ var/list/results = list()
+ for(var/mob/living/carbon/human/human_mob as anything in GLOB.human_mob_list)
+
+ if(!isyautja(human_mob))
+ continue
+
+ if(faction != human_mob.faction)
+ continue
+
+ // Check if z-level is correct
+ var/turf/pos = get_turf(human_mob)
+ if(!pos)
+ continue
+
+ // The entry for this human
+ var/list/entry = list(
+ "ref" = REF(human_mob),
+ "name" = human_mob.real_name,
+ "ijob" = UNKNOWN_JOB_ID,
+ "assignment" = "Hunter",
+ "oxydam" = round(human_mob.getOxyLoss(), 1),
+ "toxdam" = round(human_mob.getToxLoss(), 1),
+ "burndam" = round(human_mob.getFireLoss(), 1),
+ "brutedam" = round(human_mob.getBruteLoss(), 1),
+ "can_track" = TRUE,
+ )
+
+ if(is_mainship_level(pos.z))
+ entry["side"] = "Almayer"
+
+ var/area/mob_area = get_area(human_mob)
+ entry["area"] = sanitize_area(mob_area.name)
+
+ results[++results.len] = entry
+
+ // Cache result
+ data = results
+ last_update = world.time
+
+ return results
+
#undef SENSOR_LIVING
#undef SENSOR_VITALS
#undef SENSOR_COORDS
diff --git a/code/modules/cm_phone/phone.dm b/code/modules/cm_phone/phone.dm
index ac00e717f79a..b3e0ecd87206 100644
--- a/code/modules/cm_phone/phone.dm
+++ b/code/modules/cm_phone/phone.dm
@@ -312,6 +312,7 @@ GLOBAL_LIST_EMPTY_TYPED(transmitters, /obj/structure/transmitter)
P.handle_hear(message, L, speaking)
attached_to.handle_hear(message, L, speaking)
+ log_say("TELEPHONE: [key_name(speaking)] on Phone '[phone_id]' to '[T.phone_id]' said '[message]'")
/obj/structure/transmitter/attackby(obj/item/W, mob/user)
if(W == attached_to)
diff --git a/code/modules/cm_preds/falcon.dm b/code/modules/cm_preds/falcon.dm
index 7b369d6b1032..4461f9b4f7e7 100644
--- a/code/modules/cm_preds/falcon.dm
+++ b/code/modules/cm_preds/falcon.dm
@@ -11,12 +11,27 @@
)
flags_equip_slot = SLOT_EAR
flags_item = ITEM_PREDATOR
+ flags_atom = FPRINT|USES_HEARING
+
+
+/obj/item/falcon_drone/hear_talk(mob/living/sourcemob, message, verb, datum/language/language, italics)
+ var/mob/hologram/falcon/hologram = loc
+ if(!istype(hologram))
+ return FALSE
+ var/mob/living/carbon/human/user = hologram.owned_bracers.loc
+ if(!ishuman(user) || user == sourcemob)
+ return FALSE
+
+ to_chat(user, SPAN_YAUTJABOLD("Falcon Relay: [sourcemob.name] [verb], \"[message]\""))
+ if(user && user.client && user.client.prefs && !user.client.prefs.lang_chat_disabled \
+ && !user.ear_deaf && user.say_understands(sourcemob, language))
+ sourcemob.langchat_display_image(user)
+
+ return TRUE
/obj/item/falcon_drone/get_examine_location(mob/living/carbon/human/wearer, mob/examiner, slot, t_he = "They", t_his = "their", t_him = "them", t_has = "have", t_is = "are")
switch(slot)
- if(WEAR_L_EAR)
- return "on [t_his] shoulder"
- if(WEAR_R_EAR)
+ if(WEAR_L_EAR, WEAR_R_EAR)
return "on [t_his] shoulder"
return ..()
@@ -53,6 +68,7 @@
var/obj/item/falcon_drone/parent_drone
var/obj/item/clothing/gloves/yautja/owned_bracers
desc = "An agile drone used by Yautja to survey the hunting grounds."
+ motion_sensed = TRUE
/mob/hologram/falcon/Initialize(mapload, mob/M, obj/item/falcon_drone/drone, obj/item/clothing/gloves/yautja/bracers)
. = ..()
diff --git a/code/modules/cm_preds/yaut_bracers.dm b/code/modules/cm_preds/yaut_bracers.dm
index f33d5f9a5554..a94cde9887c3 100644
--- a/code/modules/cm_preds/yaut_bracers.dm
+++ b/code/modules/cm_preds/yaut_bracers.dm
@@ -46,6 +46,8 @@
var/mob/living/carbon/human/owner //Pred spawned on, or thrall given to.
var/obj/item/clothing/gloves/yautja/linked_bracer //Bracer linked to this one (thrall or mentor).
COOLDOWN_DECLARE(bracer_recharge)
+ /// What minimap icon this bracer should have
+ var/minimap_icon = "predator"
/obj/item/clothing/gloves/yautja/equipped(mob/user, slot)
. = ..()
@@ -54,6 +56,8 @@
if(!owner)
owner = user
toggle_lock_internal(user, TRUE)
+ RegisterSignal(user, list(COMSIG_MOB_STAT_SET_ALIVE, COMSIG_MOB_DEATH), PROC_REF(update_minimap_icon))
+ INVOKE_NEXT_TICK(src, PROC_REF(update_minimap_icon), user)
/obj/item/clothing/gloves/yautja/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -65,6 +69,8 @@
/obj/item/clothing/gloves/yautja/dropped(mob/user)
STOP_PROCESSING(SSobj, src)
flags_item = initial(flags_item)
+ UnregisterSignal(user, list(COMSIG_MOB_STAT_SET_ALIVE, COMSIG_MOB_DEATH))
+ SSminimaps.remove_marker(user)
..()
/obj/item/clothing/gloves/yautja/pickup(mob/living/user)
@@ -94,7 +100,7 @@
return
if(human_holder.stat == DEAD)
decloak(human_holder, TRUE)
- if(!HAS_TRAIT(human_holder, TRAIT_YAUTJA_TECH) && !human_holder.hunter_data.thralled && prob(15))
+ if(!HAS_TRAIT(human_holder, TRAIT_YAUTJA_TECH) && !human_holder.hunter_data.thralled && prob(2))
decloak(human_holder)
shock_user(human_holder)
@@ -102,6 +108,27 @@
/obj/item/clothing/gloves/yautja/proc/decloak()
return
+/// Called to update the minimap icon of the predator
+/obj/item/clothing/gloves/yautja/proc/update_minimap_icon()
+ if(!ishuman(owner))
+ return
+
+ var/mob/living/carbon/human/human_owner = owner
+ var/turf/wearer_turf = get_turf(owner)
+ SSminimaps.remove_marker(owner)
+ if(!isyautja(owner))
+ if(owner.stat >= DEAD)
+ if(human_owner.undefibbable)
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, "bracer_stolen", 'icons/ui_icons/map_blips.dmi', overlay_iconstates = list("undefibbable"))
+ else
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, "bracer_stolen", 'icons/ui_icons/map_blips.dmi', overlay_iconstates = list("defibbable"))
+ else
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, "bracer_stolen", 'icons/ui_icons/map_blips.dmi')
+ else
+ if(owner?.stat >= DEAD)
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, minimap_icon, 'icons/ui_icons/map_blips.dmi', overlay_iconstates = list("undefibbable")) //defib/undefib status doesn't really matter because they're gonna explode in the end regardless
+ else
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, minimap_icon, 'icons/ui_icons/map_blips.dmi')
/*
*This is the main proc for checking AND draining the bracer energy. It must have human passed as an argument.
*It can take a negative value in amount to restore energy.
@@ -193,8 +220,23 @@
desc = "A pair of strange alien bracers, adapted for human biology."
color = "#b85440"
+ minimap_icon = "thrall"
+/obj/item/clothing/gloves/yautja/thrall/update_minimap_icon()
+ if(!ishuman(owner))
+ return
+
+ var/mob/living/carbon/human/human_owner = owner
+ var/turf/wearer_turf = get_turf(owner)
+ if(owner.stat >= DEAD)
+ if(human_owner.undefibbable)
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, minimap_icon, overlay_iconstates = list("undefibbable"))
+ else
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, minimap_icon, overlay_iconstates = list("defibbable"))
+ else
+ SSminimaps.add_marker(owner, wearer_turf.z, MINIMAP_FLAG_YAUTJA, minimap_icon)
+
/obj/item/clothing/gloves/yautja/hunter
name = "clan bracers"
desc = "An extremely complex, yet simple-to-operate set of armored bracers worn by the Yautja. It has many functions, activate them to use some."
@@ -293,7 +335,7 @@
var/mob/living/carbon/human/human = loc
//Non-Yautja have a chance to get stunned with each power drain
- if((!HAS_TRAIT(human, TRAIT_YAUTJA_TECH) && !human.hunter_data.thralled) && prob(15))
+ if((!HAS_TRAIT(human, TRAIT_YAUTJA_TECH) && !human.hunter_data.thralled) && prob(4))
if(cloaked)
decloak(human, TRUE, DECLOAK_SPECIES)
shock_user(human)
@@ -955,19 +997,10 @@
if(.)
return
- for(var/obj/item/weapon/yautja/combistick/C in range(7))
- if(C in caller.contents) //Can't yank if they are wearing it
- return FALSE
- if(caller.put_in_active_hand(C))//Try putting it in our active hand, or, if it's full...
- if(!drain_power(caller, 70)) //We should only drain power if we actually yank the chain back. Failed attempts can quickly drain the charge away.
- return TRUE
- caller.visible_message(SPAN_WARNING("[caller] yanks [C]'s chain back!"), SPAN_WARNING("You yank [C]'s chain back!"))
- else if(caller.put_in_inactive_hand(C))///...Try putting it in our inactive hand.
- if(!drain_power(caller, 70)) //We should only drain power if we actually yank the chain back. Failed attempts can quickly drain the charge away.
- return TRUE
- caller.visible_message(SPAN_WARNING("[caller] yanks [C]'s chain back!"), SPAN_WARNING("You yank [C]'s chain back!"))
- else //If neither hand can hold it, you must not have a free hand.
- to_chat(caller, SPAN_WARNING("You need a free hand to do this!"))
+ for(var/datum/effects/tethering/tether in caller.effects_list)
+ if(istype(tether.tethered.affected_atom, /obj/item/weapon/yautja/combistick))
+ var/obj/item/weapon/yautja/combistick/stick = tether.tethered.affected_atom
+ stick.recall()
/obj/item/clothing/gloves/yautja/hunter/verb/translate()
set name = "Translator"
diff --git a/code/modules/cm_preds/yaut_mask.dm b/code/modules/cm_preds/yaut_mask.dm
index 5d4d21c46c80..be0aa8ed761a 100644
--- a/code/modules/cm_preds/yaut_mask.dm
+++ b/code/modules/cm_preds/yaut_mask.dm
@@ -1,7 +1,5 @@
#define VISION_MODE_OFF 0
#define VISION_MODE_NVG 1
-#define VISION_MODE_THERMAL 2
-#define VISION_MODE_MESON 3
///parent type
/obj/item/clothing/mask/gas/yautja
@@ -65,8 +63,32 @@
/obj/item/clothing/mask/gas/yautja/Destroy()
remove_from_missing_pred_gear(src)
+ STOP_PROCESSING(SSobj, src)
return ..()
+/obj/item/clothing/mask/gas/yautja/process()
+ if(!ishuman(loc))
+ return PROCESS_KILL
+ var/mob/living/carbon/human/human_holder = loc
+
+ if(current_goggles && !drain_power(human_holder, 3))
+ to_chat(human_holder, SPAN_WARNING("Your bracers lack sufficient power to operate the visor."))
+ current_goggles = VISION_MODE_OFF
+ var/obj/item/visor = human_holder.glasses
+ if(istype(visor, /obj/item/clothing/glasses/night/yautja))//To change if any new vision modes are made
+ human_holder.temp_drop_inv_item(visor)
+ qdel(visor)
+ human_holder.update_inv_glasses()
+ human_holder.update_sight()
+
+/obj/item/clothing/mask/gas/yautja/proc/drain_power(mob/living/carbon/human/human_holder, drain_amount)
+ var/obj/item/clothing/gloves/yautja/bracer = human_holder.gloves
+ if(!bracer || !istype(bracer))
+ return FALSE
+ if(!(bracer.drain_power(human_holder, drain_amount)))
+ return FALSE
+ return TRUE
+
/obj/item/clothing/mask/gas/yautja/verb/toggle_zoom()
set name = "Toggle Mask Zoom"
set desc = "Toggle your mask's zoom function."
@@ -82,40 +104,36 @@
set src in usr
if(!usr || usr.stat)
return
- var/mob/living/carbon/human/M = usr
- if(!istype(M))
+ var/mob/living/carbon/human/user = usr
+ if(!istype(user))
return
- if(!HAS_TRAIT(M, TRAIT_YAUTJA_TECH) && !M.hunter_data.thralled)
- to_chat(M, SPAN_WARNING("You have no idea how to work this thing!"))
+ if(!HAS_TRAIT(user, TRAIT_YAUTJA_TECH) && !user.hunter_data.thralled)
+ to_chat(user, SPAN_WARNING("You have no idea how to work this thing!"))
return
- if(src != M.wear_mask) //sanity
- to_chat(M, SPAN_WARNING("You must wear \the [src]!"))
+ if(src != user.wear_mask) //sanity
+ to_chat(user, SPAN_WARNING("You must wear \the [src]!"))
return
- var/obj/item/clothing/gloves/yautja/Y = M.gloves //Doesn't actually reduce power, but needs the bracers anyway.
- if(!Y || !istype(Y))
- to_chat(M, SPAN_WARNING("You must be wearing your bracers, as they have the power source."))
+ var/obj/item/clothing/gloves/yautja/bracer = user.gloves
+ if(!bracer || !istype(bracer))
+ to_chat(user, SPAN_WARNING("You must be wearing your bracers, as they have the power source."))
return
- var/obj/item/G = M.glasses
- if(G)
- if(!istype(G,/obj/item/clothing/glasses/night/yautja) && !istype(G,/obj/item/clothing/glasses/meson/yautja) && !istype(G,/obj/item/clothing/glasses/thermal/yautja))
- to_chat(M, SPAN_WARNING("You need to remove your glasses first. Why are you even wearing these?"))
+ var/obj/item/visor = user.glasses
+ if(visor)
+ if(!istype(visor, /obj/item/clothing/glasses/night/yautja))
+ to_chat(user, SPAN_WARNING("You need to remove your glasses first. Why are you even wearing these?"))
return
- M.temp_drop_inv_item(G) //Get rid of ye existing maicerinho goggles
- qdel(G)
- M.update_inv_glasses()
- M.update_sight()
+ user.temp_drop_inv_item(visor) //Get rid of ye existing maicerinho goggles
+ qdel(visor)
+ user.update_inv_glasses()
+ user.update_sight()
switch_vision_mode()
- add_vision(M)
+ add_vision(user)
/obj/item/clothing/mask/gas/yautja/proc/switch_vision_mode() //switches to the next one
switch(current_goggles)
if(VISION_MODE_OFF)
current_goggles = VISION_MODE_NVG
if(VISION_MODE_NVG)
- current_goggles = VISION_MODE_THERMAL
- if(VISION_MODE_THERMAL)
- current_goggles = VISION_MODE_MESON
- if(VISION_MODE_MESON)
current_goggles = VISION_MODE_OFF
/obj/item/clothing/mask/gas/yautja/proc/add_vision(mob/living/carbon/human/user) //applies current_goggles
@@ -123,38 +141,40 @@
if(VISION_MODE_NVG)
user.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/yautja(user), WEAR_EYES)
to_chat(user, SPAN_NOTICE("Low-light vision module: activated."))
- if(VISION_MODE_THERMAL)
- user.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal/yautja(user), WEAR_EYES)
- to_chat(user, SPAN_NOTICE("Thermal vision module: activated."))
- if(VISION_MODE_MESON)
- user.equip_to_slot_or_del(new /obj/item/clothing/glasses/meson/yautja(user), WEAR_EYES)
- to_chat(user, SPAN_NOTICE("Material vision module: activated."))
if(VISION_MODE_OFF)
to_chat(user, SPAN_NOTICE("You deactivate your visor."))
playsound(src, 'sound/effects/pred_vision.ogg', 15, 1)
user.update_inv_glasses()
+#undef VISION_MODE_OFF
+#undef VISION_MODE_NVG
+
/obj/item/clothing/mask/gas/yautja/dropped(mob/living/carbon/human/user) //Clear the gogglors if the helmet is removed.
+ STOP_PROCESSING(SSobj, src)
if(istype(user) && user.wear_mask == src) //inventory reference is only cleared after dropped().
for(var/listed_hud in mask_huds)
var/datum/mob_hud/H = huds[listed_hud]
H.remove_hud_from(user)
- var/obj/item/G = user.glasses
- if(G) //make your hud fuck off
- if(istype(G,/obj/item/clothing/glasses/night/yautja) || istype(G,/obj/item/clothing/glasses/meson/yautja) || istype(G,/obj/item/clothing/glasses/thermal/yautja))
- user.temp_drop_inv_item(G)
- qdel(G)
+ var/obj/item/visor = user.glasses
+ if(visor) //make your hud fuck off
+ if(istype(visor, /obj/item/clothing/glasses/night/yautja))
+ user.temp_drop_inv_item(visor)
+ qdel(visor)
user.update_inv_glasses()
user.update_sight()
..()
/obj/item/clothing/mask/gas/yautja/equipped(mob/living/carbon/human/user, slot)
if(slot == WEAR_FACE)
+ START_PROCESSING(SSobj, src)
for(var/listed_hud in mask_huds)
var/datum/mob_hud/H = huds[listed_hud]
H.add_hud_to(user)
if(current_goggles)
+ var/obj/item/clothing/gloves/yautja/bracer = user.gloves
+ if(!bracer || !istype(bracer))
+ return FALSE
add_vision(user)
..()
@@ -205,30 +225,6 @@
name = "ancient alien mask"
desc = "A beautifully designed metallic face mask, both ornate and functional. This one seems to be old and degraded."
-/obj/item/clothing/mask/gas/yautja/damaged/switch_vision_mode()
- switch(current_goggles)
- if(VISION_MODE_OFF)
- current_goggles = VISION_MODE_NVG
- if(VISION_MODE_NVG)
- current_goggles = VISION_MODE_OFF
-
-/obj/item/clothing/mask/gas/yautja/damaged/add_vision(mob/living/carbon/human/user)
- switch(current_goggles)
- if(VISION_MODE_NVG)
- user.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/yautja(user), WEAR_EYES)
- to_chat(user, SPAN_NOTICE("You activate your visor."))
- if(VISION_MODE_OFF)
- to_chat(user, SPAN_NOTICE("You deactivate your visor."))
-
- playsound(src, 'sound/effects/pred_vision.ogg', 15, 1)
- user.update_inv_glasses()
-
-#undef VISION_MODE_OFF
-#undef VISION_MODE_NVG
-#undef VISION_MODE_THERMAL
-#undef VISION_MODE_MESON
-
-
//flavor, not a subtype
/obj/item/clothing/mask/yautja_flavor
name = "alien stone mask"
diff --git a/code/modules/cm_preds/yaut_weapons.dm b/code/modules/cm_preds/yaut_weapons.dm
index fbbe6c183aaa..5ff13c843ee3 100644
--- a/code/modules/cm_preds/yaut_weapons.dm
+++ b/code/modules/cm_preds/yaut_weapons.dm
@@ -254,6 +254,17 @@
var/force_wielded = MELEE_FORCE_TIER_6
var/force_unwielded = MELEE_FORCE_TIER_2
var/force_storage = MELEE_FORCE_TIER_1
+ /// Ref to the tether effect when thrown
+ var/datum/effects/tethering/chain
+
+/obj/item/weapon/yautja/combistick/Destroy()
+ cleanup_chain()
+ return ..()
+
+/obj/item/weapon/yautja/combistick/dropped(mob/user)
+ . = ..()
+ if(on && isturf(loc))
+ setup_chain(user)
/obj/item/weapon/yautja/combistick/try_to_throw(mob/living/user)
if(!charged)
@@ -262,8 +273,66 @@
charged = FALSE
remove_filter("combistick_charge")
unwield(user) //Otherwise stays wielded even when thrown
+ if(on)
+ setup_chain(user)
return TRUE
+/obj/item/weapon/yautja/combistick/proc/setup_chain(mob/living/user)
+ var/list/tether_effects = apply_tether(user, src, range = 6, resistable = FALSE)
+ chain = tether_effects["tetherer_tether"]
+ RegisterSignal(chain, COMSIG_PARENT_QDELETING, PROC_REF(cleanup_chain))
+ RegisterSignal(src, COMSIG_ITEM_PICKUP, PROC_REF(on_pickup))
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+
+/// The chain normally breaks if it's put into a container, so let's yank it back if that's the case
+/obj/item/weapon/yautja/combistick/proc/on_move(datum/source, atom/moved, dir, forced)
+ SIGNAL_HANDLER
+ if(!z && !is_type_in_list(loc, list(/obj/structure/surface, /mob))) // I rue for the day I can remove the surface snowflake check
+ recall()
+
+/// Clean up the chain, deleting/nulling/unregistering as needed
+/obj/item/weapon/yautja/combistick/proc/cleanup_chain()
+ SIGNAL_HANDLER
+ if(!QDELETED(chain))
+ QDEL_NULL(chain)
+
+ else
+ chain = null
+
+ UnregisterSignal(src, COMSIG_ITEM_PICKUP)
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
+
+/obj/item/weapon/yautja/combistick/proc/on_pickup(datum/source, mob/user)
+ SIGNAL_HANDLER
+ if(user != chain.affected_atom)
+ to_chat(chain.affected_atom, SPAN_WARNING("You feel the chain of [src] be torn from your grasp!")) // Recall the fuckin combi my man
+
+ cleanup_chain()
+
+/// recall the combistick to the pred's hands or to be at their feet
+/obj/item/weapon/yautja/combistick/proc/recall()
+ SIGNAL_HANDLER
+ if(!chain)
+ return
+
+ var/mob/living/carbon/human/user = chain.affected_atom
+ if((src in user.contents) || !istype(user.gloves, /obj/item/clothing/gloves/yautja/hunter))
+ cleanup_chain()
+ return
+
+ var/obj/item/clothing/gloves/yautja/hunter/pred_gloves = user.gloves
+
+ if(user.put_in_hands(src, TRUE))
+ if(!pred_gloves.drain_power(user, 70))
+ return TRUE
+ user.visible_message(SPAN_WARNING("[user] yanks [src]'s chain back, catching it in [user.p_their()] hand!"), SPAN_WARNING("You yank [src]'s chain back, catching it inhand!"))
+ cleanup_chain()
+
+ else
+ if(!pred_gloves.drain_power(user, 70))
+ return TRUE
+ user.visible_message(SPAN_WARNING("[user] yanks [src]'s chain back, letting [src] fall at [user.p_their()]!"), SPAN_WARNING("You yank [src]'s chain back, letting it drop at your feet!"))
+ cleanup_chain()
/obj/item/weapon/yautja/combistick/IsShield()
return on
@@ -823,7 +892,7 @@
/obj/item/weapon/gun/energy/yautja/plasmarifle
name = "plasma rifle"
- desc = "A long-barreled heavy plasma weapon capable of taking down large game. It has a mounted scope for distant shots and an integrated battery."
+ desc = "A long-barreled heavy plasma weapon. Intended for combat, not hunting. Has an integrated battery that allows for a functionally unlimited amount of shots to be discharged. Equipped with an internal gyroscopic stabilizer allowing its operator to fire the weapon one-handed if desired"
icon_state = "plasmarifle"
item_state = "plasmarifle"
unacidable = TRUE
@@ -836,7 +905,7 @@
var/charge_time = 0
var/last_regen = 0
flags_gun_features = GUN_UNUSUAL_DESIGN
- flags_item = ITEM_PREDATOR
+ flags_item = ITEM_PREDATOR|TWOHANDED
/obj/item/weapon/gun/energy/yautja/plasmarifle/Initialize(mapload, spawn_empty)
. = ..()
@@ -889,12 +958,8 @@
return ..()
/obj/item/weapon/gun/energy/yautja/plasmarifle/load_into_chamber()
- if(charge_time >= 80)
- ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/rifle/blast]
- charge_time -= 80
- else
- ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/rifle/bolt]
- charge_time -= 10
+ ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/rifle/bolt]
+ charge_time -= 10
var/obj/item/projectile/projectile = create_bullet(ammo, initial(name))
projectile.SetLuminosity(1)
in_chamber = projectile
diff --git a/code/modules/cm_tech/hologram.dm b/code/modules/cm_tech/hologram.dm
index 83cc0937b46e..5c0e986f45b2 100644
--- a/code/modules/cm_tech/hologram.dm
+++ b/code/modules/cm_tech/hologram.dm
@@ -1,4 +1,4 @@
-GLOBAL_LIST_EMPTY(hologram_list)
+GLOBAL_LIST_EMPTY_TYPED(hologram_list, /mob/hologram)
/mob/hologram
name = "Hologram"
@@ -16,6 +16,8 @@ GLOBAL_LIST_EMPTY(hologram_list)
var/mob/linked_mob
var/datum/action/leave_hologram/leave_button
+ ///If can be detected on motion detectors.
+ var/motion_sensed = FALSE
/mob/hologram/movement_delay()
. = -2 // Very fast speed, so they can navigate through easily, they can't ever have movement delay whilst as a hologram
diff --git a/code/modules/cm_tech/implements/medical_czsp.dm b/code/modules/cm_tech/implements/medical_czsp.dm
index e0b00ebf5afd..3eed2fc9f619 100644
--- a/code/modules/cm_tech/implements/medical_czsp.dm
+++ b/code/modules/cm_tech/implements/medical_czsp.dm
@@ -83,10 +83,11 @@
/obj/item/device/defibrillator/upgraded
name = "upgraded emergency defibrillator"
icon_state = "adv_defib"
- desc = "An advanced rechargeable defibrillator using induction to deliver shocks through metallic objects, such as armor, and does so with much greater efficiency than the standard variant."
+ desc = "An advanced rechargeable defibrillator using induction to deliver shocks through metallic objects, such as armor, and does so with much greater efficiency than the standard variant, not damaging the heart."
blocked_by_suit = FALSE
- heart_damage_to_deal = 0
+ min_heart_damage_dealt = 0
+ max_heart_damage_dealt = 0
damage_heal_threshold = 35
/obj/item/ammo_magazine/internal/pillgun
diff --git a/code/modules/defenses/sentry.dm b/code/modules/defenses/sentry.dm
index fc52c254c0b8..86464d97f5a3 100644
--- a/code/modules/defenses/sentry.dm
+++ b/code/modules/defenses/sentry.dm
@@ -1,6 +1,5 @@
#define SENTRY_FIREANGLE 135
#define SENTRY_RANGE 5
-#define SENTRY_MUZZLELUM 3
#define SENTRY_ENGAGED_TIMEOUT 60 SECONDS
#define SENTRY_LOW_AMMO_TIMEOUT 20 SECONDS
#define SENTRY_LOW_AMMO_ALERT_PERCENTAGE 0.25
@@ -336,9 +335,6 @@
if(isnull(angle))
return
- SetLuminosity(SENTRY_MUZZLELUM)
- addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, SetLuminosity), -SENTRY_MUZZLELUM), 10)
-
var/image_layer = layer + 0.1
var/offset = 13
@@ -726,4 +722,3 @@
#undef SENTRY_FIREANGLE
#undef SENTRY_RANGE
-#undef SENTRY_MUZZLELUM
diff --git a/code/modules/gear_presets/_select_equipment.dm b/code/modules/gear_presets/_select_equipment.dm
index fc70e5fc90bb..94a628553252 100644
--- a/code/modules/gear_presets/_select_equipment.dm
+++ b/code/modules/gear_presets/_select_equipment.dm
@@ -952,3 +952,53 @@ var/list/rebel_rifles = list(
list("Shoulder Holster", 10, /obj/item/clothing/accessory/storage/holster, null, VENDOR_ITEM_REGULAR),
list("Webbing", 10, /obj/item/clothing/accessory/storage/webbing, null, VENDOR_ITEM_REGULAR)
)
+
+/datum/equipment_preset/proc/add_upp_weapon(mob/living/carbon/human/new_human)
+ var/random_gun = rand(1,3)
+ switch(random_gun)
+ if(1)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/type71(new_human), WEAR_L_HAND)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/type71(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/type71(new_human), WEAR_IN_BACK)
+ if(2)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/type71/carbine(new_human), WEAR_L_HAND)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/type71(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/type71(new_human), WEAR_IN_BACK)
+ if(3)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/shotgun/type23(new_human), WEAR_L_HAND)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/handful/shotgun/heavy/buckshot(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/handful/shotgun/heavy/buckshot(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/handful/shotgun/heavy/buckshot(new_human), WEAR_IN_BACK)
+
+/datum/equipment_preset/proc/spawn_random_upp_headgear(mob/living/carbon/human/new_human)
+ var/random_hat = rand(1,10)
+ switch(random_hat)
+ if (1, 2)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/veteran/UPP(new_human), WEAR_HEAD)
+ if (3, 4, 5)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/uppcap(new_human), WEAR_HEAD)
+ if (6, 7)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/uppcap/beret(new_human), WEAR_HEAD)
+ if (8, 9)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/uppcap/ushanka(new_human), WEAR_HEAD)
+
+/datum/equipment_preset/proc/spawn_random_upp_armor(mob/living/carbon/human/new_human)
+ var/random_gear = rand(1, 4)
+ switch(random_gear)
+ if (1, 2, 3)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/veteran/UPP (new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/webbing(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_J_STORE)
+ if (4)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/veteran/UPP (new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/faction/UPP(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar(new_human), WEAR_IN_JACKET)
+
+/datum/equipment_preset/proc/spawn_random_upp_belt(mob/living/carbon/human/new_human)
+ var/random_gun = rand(1, 3)
+ switch(random_gun)
+ if (1, 2)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/type47/NY(new_human), WEAR_WAIST)
+ if (3)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/marine/upp(new_human), WEAR_WAIST)
diff --git a/code/modules/gear_presets/corpses.dm b/code/modules/gear_presets/corpses.dm
index 7e9dd5b841a0..18903ef491ee 100644
--- a/code/modules/gear_presets/corpses.dm
+++ b/code/modules/gear_presets/corpses.dm
@@ -1035,3 +1035,43 @@
/datum/equipment_preset/corpse/gladiator/burst
name = "Corpse - Burst Gladiator"
xenovictim = TRUE
+
+
+//FORECON
+
+/datum/equipment_preset/corpse/forecon_spotter
+ name = "Corpse - USCM Reconnaissance Spotter"
+ assignment = "Reconnaissance Spotter"
+ xenovictim = FALSE
+ paygrade = "ME5"
+ idtype = /obj/item/card/id/dogtag
+ role_comm_title = "FORECON"
+ faction_group = list(FACTION_USCM, FACTION_SURVIVOR)
+ access = list(
+ ACCESS_CIVILIAN_PUBLIC,
+ ACCESS_CIVILIAN_ENGINEERING,
+ ACCESS_CIVILIAN_LOGISTICS,
+ )
+
+/datum/equipment_preset/corpse/forecon_spotter/load_gear(mob/living/carbon/human/new_human)
+ var/obj/item/clothing/under/marine/reconnaissance/uniform = new()
+ var/obj/item/clothing/accessory/storage/droppouch/pouch = new()
+ var/obj/item/clothing/accessory/ranks/marine/e5/pin = new()
+ var/obj/item/clothing/accessory/patch/patch_uscm = new()
+ var/obj/item/clothing/accessory/patch/forecon/patch_forecon = new()
+ uniform.attach_accessory(new_human,pouch)
+ uniform.attach_accessory(new_human,patch_uscm)
+ uniform.attach_accessory(new_human,pin)
+ uniform.attach_accessory(new_human,patch_forecon)
+ new_human.equip_to_slot_or_del(uniform, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/webbing(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/satchel(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full/alternate(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/reagent_container/food/drinks/flask/marine(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/facepaint/sniper(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/box/MRE(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio(new_human), WEAR_IN_BACK)
diff --git a/code/modules/gear_presets/forcon_survivors.dm b/code/modules/gear_presets/forcon_survivors.dm
index a883759a2f7f..825228f29735 100644
--- a/code/modules/gear_presets/forcon_survivors.dm
+++ b/code/modules/gear_presets/forcon_survivors.dm
@@ -1,6 +1,5 @@
///*****************************LV-522 Force Recon Survivors*******************************************************/
//Nanu told me to put them here so they dont clutter up survivors.dm
-
/datum/equipment_preset/survivor/forecon
paygrade = "ME5"
idtype = /obj/item/card/id/dogtag
@@ -36,6 +35,7 @@
new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/device/radio(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/sof/survivor_forecon(new_human), WEAR_L_EAR)
GLOB.character_traits[/datum/character_trait/skills/spotter].apply_trait(new_human)
/datum/equipment_preset/survivor/forecon/add_survivor_weapon_security(mob/living/carbon/human/new_human)
@@ -102,7 +102,7 @@
/datum/equipment_preset/survivor/forecon/standard
name = "Survivor - USCM Reconnaissance Marine"
- assignment = "Reconnaissance Rifleman"
+ assignment = JOB_FORECON_RIFLEMAN
skills = /datum/skills/military/survivor/forecon_standard
/datum/equipment_preset/survivor/forecon/standard/load_gear(mob/living/carbon/human/new_human)
@@ -116,7 +116,7 @@
/datum/equipment_preset/survivor/forecon/tech
name = "Survivor - USCM Reconnaissance Support Technician"
- assignment = "Reconnaissance Support Technician"
+ assignment = JOB_FORECON_SUPPORT
skills = /datum/skills/military/survivor/forecon_techician
/datum/equipment_preset/survivor/forecon/tech/load_gear(mob/living/carbon/human/new_human)
@@ -138,7 +138,7 @@
/datum/equipment_preset/survivor/forecon/marksman
name = "Survivor - USCM Reconnaissance Designated Marksman"
- assignment = "Reconnaissance Marksman"
+ assignment = JOB_FORECON_MARKSMAN
skills = /datum/skills/military/survivor/forecon_marksman
/datum/equipment_preset/survivor/forecon/marksman/load_gear(mob/living/carbon/human/new_human)
@@ -154,7 +154,7 @@
/datum/equipment_preset/survivor/forecon/smartgunner
name = "Survivor - USCM Reconnaissance Smartgunner"
- assignment = "Reconnaissance Smartgunner"
+ assignment = JOB_FORECON_SMARTGUNNER
skills = /datum/skills/military/survivor/forecon_smartgunner
/datum/equipment_preset/survivor/forecon/smartgunner/load_gear(mob/living/carbon/human/new_human)
@@ -163,33 +163,50 @@
new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/pistol/m1911(new_human), WEAR_IN_BELT)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BELT)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BELT)
+ new_human.equip_to_slot(new /obj/item/smartgun_battery(new_human), WEAR_IN_BACK)
add_forecon_weapon(new_human)
spawn_random_headgear(new_human)
add_forecon_equipment(new_human)
///*****************************//
-/datum/equipment_preset/survivor/forecon/grenadier
- name = "Survivor - USCM Reconnaissance Grenadier"
- assignment = "Reconnaissance Grenadier"
- skills = /datum/skills/military/survivor/forecon_grenadier
+/datum/equipment_preset/survivor/forecon/sniper
+ name = "Survivor - USCM Reconnaissance Sniper"
+ assignment = JOB_FORECON_SNIPER
+ skills = /datum/skills/military/survivor/forecon_sniper
-/datum/equipment_preset/survivor/forecon/grenadier/load_gear(mob/living/carbon/human/new_human)
- new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/launcher/grenade/m81/m79(new_human), WEAR_L_HAND)
- new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/pistol/m1911(new_human), WEAR_R_HAND)
- ..()
- new_human.equip_to_slot_or_del(new /obj/item/storage/belt/grenade(new_human), WEAR_WAIST)
- new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BACK)
- new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BACK)
- new_human.equip_to_slot_or_del(new /obj/item/storage/box/packet/incendiary(new_human), WEAR_IN_BACK)
+/datum/equipment_preset/survivor/forecon/sniper/load_gear(mob/living/carbon/human/new_human)
+ var/obj/item/clothing/under/marine/reconnaissance/uniform = new()
+ var/obj/item/clothing/accessory/storage/droppouch/pouch = new()
+ var/obj/item/clothing/accessory/ranks/marine/e5/pin = new()
+ var/obj/item/clothing/accessory/patch/patch_uscm = new()
+ var/obj/item/clothing/accessory/patch/forecon/patch_forecon = new()
+ uniform.attach_accessory(new_human,pouch)
+ uniform.attach_accessory(new_human,patch_uscm)
+ uniform.attach_accessory(new_human,pin)
+ uniform.attach_accessory(new_human,patch_forecon)
+ new_human.equip_to_slot_or_del(uniform, WEAR_BODY)
+ new_human.equip_to_slot(new /obj/item/clothing/suit/storage/marine/ghillie/forecon(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/satchel(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full/alternate(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/reagent_container/food/drinks/flask/marine(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/facepaint/sniper(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/box/MRE(new_human), WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/general_belt(new_human), WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar/tactical(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/sof/survivor_forecon(new_human), WEAR_L_EAR)
spawn_random_headgear(new_human)
add_forecon_equipment(new_human)
+ add_forecon_weapon_pistol(new_human)
//---------------------------\\
/datum/equipment_preset/survivor/forecon/squad_leader
name = "Survivor - USCM Reconnaissance Squad Leader"
- assignment = "Reconnaissance Squad Leader"
+ assignment = JOB_FORECON_SL
skills = /datum/skills/military/survivor/forecon_squad_leader
paygrade = "MO1"
@@ -215,7 +232,7 @@
/datum/equipment_preset/survivor/forecon/major
name = "Survivor - USCM Reconnaissance Major"
- assignment = "Reconnaissance Commander"
+ assignment = JOB_FORECON_CO
skills = /datum/skills/commander
paygrade = "MO4"
idtype = /obj/item/card/id/gold
@@ -248,7 +265,7 @@
/datum/equipment_preset/synth/survivor/forecon
name = "Survivor - USCM Synthetic"
- assignment = "Reconnaissance Synthetic"
+ assignment = JOB_FORECON_SYN
faction_group = list(FACTION_MARINE, FACTION_SURVIVOR)
idtype = /obj/item/card/id/gold
@@ -261,3 +278,4 @@
preset_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/regular(preset_human), WEAR_R_HAND)
preset_human.equip_to_slot_or_del(new /obj/item/storage/pouch/tools/full(preset_human), WEAR_R_STORE)
preset_human.equip_to_slot_or_del(new /obj/item/storage/pouch/survival/synth/full(preset_human), WEAR_L_STORE)
+ preset_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/sof/survivor_forecon(preset_human), WEAR_L_EAR)
diff --git a/code/modules/gear_presets/survivors.dm b/code/modules/gear_presets/survivors.dm
index 8c6a118d24d4..9ab5e7c8d22c 100644
--- a/code/modules/gear_presets/survivors.dm
+++ b/code/modules/gear_presets/survivors.dm
@@ -1562,3 +1562,159 @@
new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/metal/med_small_stack(new_human), WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/m4a3/m1911(new_human), WEAR_WAIST)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap(new_human), WEAR_HEAD)
+
+ ..()
+
+/datum/equipment_preset/survivor/upp
+ name = "UPP Soldier"
+ paygrade = "UE1"
+ origin_override = ORIGIN_UPP
+ rank = JOB_SURVIVOR
+ skills = /datum/skills/military/survivor/upp_private
+ languages = list(LANGUAGE_RUSSIAN, LANGUAGE_GERMAN, LANGUAGE_CHINESE)
+ faction = FACTION_UPP
+ faction_group = list(FACTION_UPP, FACTION_SURVIVOR)
+ role_comm_title = "UPP 173RD RECON"
+ idtype = /obj/item/card/id/dogtag
+ flags = EQUIPMENT_PRESET_EXTRA
+ uses_special_name = TRUE
+ access = list(
+ ACCESS_CIVILIAN_PUBLIC,
+ )
+
+/datum/equipment_preset/survivor/upp/load_name(mob/living/carbon/human/new_human, randomise)
+ var/random_name = capitalize(pick(new_human.gender == MALE ? first_names_male_upp : first_names_female_upp)) + " " + capitalize(pick(last_names_upp))
+ new_human.change_real_name(new_human, random_name)
+
+/datum/equipment_preset/survivor/upp/load_gear(mob/living/carbon/human/new_human)
+ var/obj/item/clothing/under/marine/veteran/UPP/uniform = new()
+ var/random_number = rand(1,2)
+ switch(random_number)
+ if(1)
+ uniform.roll_suit_jacket(new_human)
+ if(2)
+ uniform.roll_suit_sleeves(new_human)
+ new_human.equip_to_slot_or_del(uniform, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/upp (new_human), WEAR_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/upp_knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/flare(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/full/alternate(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/metal/med_small_stack(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine(new_human), WEAR_HANDS)
+
+/datum/equipment_preset/survivor/upp/soldier
+ name = "Survivor - UPP Soldier"
+ paygrade = "UE2"
+ assignment = "UPP Soldier"
+ skills = /datum/skills/military/survivor/upp_private
+
+/datum/equipment_preset/survivor/upp/soldier/load_gear(mob/living/carbon/human/new_human)
+ var/obj/item/clothing/under/marine/veteran/UPP/uniform = new()
+ var/random_number = rand(1,2)
+ switch(random_number)
+ if(1)
+ uniform.roll_suit_jacket(new_human)
+ if(2)
+ uniform.roll_suit_sleeves(new_human)
+ new_human.equip_to_slot_or_del(uniform, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot(new_human), WEAR_BACK)
+ add_upp_weapon(new_human)
+ spawn_random_upp_headgear(new_human)
+ spawn_random_upp_armor(new_human)
+ spawn_random_upp_belt(new_human)
+
+ ..()
+
+/datum/equipment_preset/survivor/upp/sapper
+ name = "Survivor - UPP Sapper"
+ paygrade = "UE3S"
+ assignment = "UPP Sapper"
+ skills = /datum/skills/military/survivor/upp_sapper
+
+/datum/equipment_preset/survivor/upp/sapper/load_gear(mob/living/carbon/human/new_human)
+
+ var/obj/item/clothing/under/marine/veteran/UPP/engi/uniform = new()
+ var/R = rand(1,2)
+ switch(R)
+ if(1)
+ uniform.roll_suit_jacket(new_human)
+ if(2)
+ uniform.roll_suit_sleeves(new_human)
+ new_human.equip_to_slot_or_del(uniform, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine/insulated(new_human), WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/utility/full(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/welding(new_human), WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot(new_human), WEAR_BACK)
+ spawn_random_upp_armor(new_human)
+ add_upp_weapon(new_human)
+ spawn_random_upp_headgear(new_human)
+
+ ..()
+
+/datum/equipment_preset/survivor/upp/medic
+ name = "Survivor - UPP Medic"
+ paygrade = "UE3M"
+ assignment = "UPP Medic"
+ skills = /datum/skills/military/survivor/upp_medic
+
+/datum/equipment_preset/survivor/upp/medic/load_gear(mob/living/carbon/human/new_human)
+ var/obj/item/clothing/under/marine/veteran/UPP/medic/uniform = new()
+ var/random_number = rand(1,2)
+ switch(random_number)
+ if(1)
+ uniform.roll_suit_jacket(new_human)
+ if(2)
+ uniform.roll_suit_sleeves(new_human)
+ new_human.equip_to_slot_or_del(uniform, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new/obj/item/clothing/glasses/hud/health(new_human), WEAR_EYES)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/lifesaver/upp/partial(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/medic/upp(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/extinguisher/mini(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/healthanalyzer(new_human), WEAR_IN_BACK)
+ spawn_random_upp_armor(new_human)
+ add_upp_weapon(new_human)
+ spawn_random_upp_headgear(new_human)
+
+ ..()
+
+/datum/equipment_preset/survivor/upp/specialist
+ name = "Survivor - UPP Specialist"
+ paygrade = "UE4"
+ assignment = "UPP Specialist"
+ skills = /datum/skills/military/survivor/upp_spec
+
+/datum/equipment_preset/survivor/upp/specialist/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/veteran/UPP/heavy(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/veteran/UPP (new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/faction/UPP/heavy (new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/type71/flamer(new_human), WEAR_L_HAND)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/type71(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/type71(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/type47/PK9(new_human), WEAR_WAIST)
+
+ ..()
+
+/datum/equipment_preset/survivor/upp/squad_leader
+ name = "Survivor - UPP Squad Leader"
+ paygrade = "UE5"
+ languages = list(LANGUAGE_RUSSIAN, LANGUAGE_ENGLISH, LANGUAGE_GERMAN, LANGUAGE_CHINESE)
+ assignment = "UPP Squad Leader"
+ role_comm_title = "UPP 173Rd RECON SL"
+ skills = /datum/skills/military/survivor/upp_sl
+
+/datum/equipment_preset/survivor/upp/squad_leader/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/veteran/UPP/officer (new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/faction/UPP/officer (new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar(new_human), WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/uppcap/beret(new_human), WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/five_slot(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/type47/PK9(new_human), WEAR_WAIST)
+ add_upp_weapon(new_human)
+
+ ..()
diff --git a/code/modules/gear_presets/synths.dm b/code/modules/gear_presets/synths.dm
index eaf6299b88a8..eaab23630ed2 100644
--- a/code/modules/gear_presets/synths.dm
+++ b/code/modules/gear_presets/synths.dm
@@ -455,13 +455,51 @@
survivor_variant = ENGINEERING_SURVIVOR
- faction = FACTION_SURVIVOR
- faction_group = list(FACTION_SURVIVOR)
- access = list(ACCESS_CIVILIAN_PUBLIC, ACCESS_CIVILIAN_COMMAND)
+/datum/equipment_preset/synth/survivor/upp
+ name = "Survivor - Synthetic - UPP Synth"
+ flags = EQUIPMENT_PRESET_EXTRA
+ languages = ALL_SYNTH_LANGUAGES_UPP
+ assignment = JOB_UPP_COMBAT_SYNTH
+ rank = JOB_SURVIVOR
+ faction = FACTION_UPP
+ faction_group = list(FACTION_UPP, FACTION_SURVIVOR)
+ skills = /datum/skills/colonial_synthetic
+ paygrade = "SYN"
+ idtype = /obj/item/card/id/dogtag
+ role_comm_title = "UPP 173Rd RECON Syn"
+
+/datum/equipment_preset/synth/survivor/upp/load_gear(mob/living/carbon/human/new_human)
+ var/obj/item/clothing/under/marine/veteran/UPP/medic/uniform = new()
+ var/random_number = rand(1,2)
+ switch(random_number)
+ if(1)
+ uniform.roll_suit_jacket(new_human)
+ if(2)
+ uniform.roll_suit_sleeves(new_human)
+ new_human.equip_to_slot_or_del(uniform, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/uppcap/beret, WEAR_HEAD)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/screwdriver, WEAR_R_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress, WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/upp, WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/roller, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/multitool, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/cable_coil, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/metal/small_stack, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/healthanalyzer, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/webbing, WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/flashlight, WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/lifesaver/upp/partial, WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/upp, WEAR_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine/veteran, WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/tools/uppsynth, WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/upp, WEAR_FEET)
/datum/equipment_preset/synth/survivor/pmc
name = "Survivor - Synthetic - PMC Support Synth"
-
+ faction = FACTION_SURVIVOR
+ faction_group = list(FACTION_SURVIVOR)
+ access = list(ACCESS_CIVILIAN_PUBLIC, ACCESS_CIVILIAN_COMMAND)
idtype = /obj/item/card/id/pmc
assignment = JOB_PMC_SYNTH
rank = JOB_PMC_SYNTH
@@ -507,6 +545,7 @@
new_human.equip_to_slot_or_del(new /obj/item/tool/weldingtool/hugetank, WEAR_IN_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/construction/full_barbed_wire, WEAR_R_STORE)
+
//*****************************************************************************************************/
/datum/equipment_preset/synth/working_joe
diff --git a/code/modules/gear_presets/upp.dm b/code/modules/gear_presets/upp.dm
index 2f7148302485..a43404c59450 100644
--- a/code/modules/gear_presets/upp.dm
+++ b/code/modules/gear_presets/upp.dm
@@ -1777,7 +1777,7 @@
name = "UPP Combat Synthetic"
flags = EQUIPMENT_PRESET_EXTRA
- languages = ALL_SYNTH_LANGUAGES
+ languages = ALL_SYNTH_LANGUAGES_UPP
skills = /datum/skills/synthetic
assignment = JOB_UPP_COMBAT_SYNTH
diff --git a/code/modules/gear_presets/uscm_ship.dm b/code/modules/gear_presets/uscm_ship.dm
index 7aa9eabb3042..457ef71e9a46 100644
--- a/code/modules/gear_presets/uscm_ship.dm
+++ b/code/modules/gear_presets/uscm_ship.dm
@@ -193,7 +193,6 @@
ACCESS_MARINE_ENGINEERING,
ACCESS_MARINE_COMMAND,
ACCESS_CIVILIAN_ENGINEERING,
- ACCESS_MARINE_SENIOR,
ACCESS_MARINE_DATABASE,
ACCESS_MARINE_MAINT,
ACCESS_MARINE_OT,
@@ -202,7 +201,7 @@
)
assignment = JOB_CHIEF_ENGINEER
rank = JOB_CHIEF_ENGINEER
- paygrade = "MO2"
+ paygrade = "MO1"
role_comm_title = "CE"
minimum_age = 27
skills = /datum/skills/CE
@@ -261,6 +260,7 @@
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/medium(new_human), WEAR_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/device/demo_scanner(new_human), WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/bag/trash(new_human), WEAR_L_HAND)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/engineerpack/welder_chestrig, (new_human), WEAR_R_HAND)
/datum/equipment_preset/uscm_ship/maint/load_rank(mob/living/carbon/human/new_human)
if(new_human.client)
@@ -307,8 +307,8 @@
//*****************************************************************************************************/
-/datum/equipment_preset/uscm_ship/ro
- name = "USCM Requisitions Officer (RO)"
+/datum/equipment_preset/uscm_ship/qm
+ name = "USCM Quartermaster (QM)"
flags = EQUIPMENT_PRESET_START_OF_ROUND|EQUIPMENT_PRESET_MARINE
idtype = /obj/item/card/id/silver
@@ -317,7 +317,6 @@
ACCESS_MARINE_RO,
ACCESS_MARINE_COMMAND,
ACCESS_MARINE_DATABASE,
- ACCESS_MARINE_SENIOR,
ACCESS_MARINE_ALPHA,
ACCESS_MARINE_BRAVO,
ACCESS_MARINE_CHARLIE,
@@ -326,23 +325,23 @@
)
assignment = JOB_CHIEF_REQUISITION
rank = JOB_CHIEF_REQUISITION
- paygrade = "MO2"
- role_comm_title = "RO"
+ paygrade = "ME7"
+ role_comm_title = "QM"
minimum_age = 27
skills = /datum/skills/RO
minimap_background = MINIMAP_ICON_BACKGROUND_CIC
minimap_icon = list("ct" = MINIMAP_ICON_COLOR_HEAD)
- utility_under = list(/obj/item/clothing/under/rank/ro_suit)
+ utility_under = list(/obj/item/clothing/under/rank/qm_suit)
-/datum/equipment_preset/uscm_ship/ro/load_gear(mob/living/carbon/human/new_human)
+/datum/equipment_preset/uscm_ship/qm/load_gear(mob/living/carbon/human/new_human)
var/back_item = /obj/item/storage/backpack/marine/satchel/tech
if (new_human.client && new_human.client.prefs && (new_human.client.prefs.backbag == 1))
back_item = /obj/item/storage/backpack/industrial
- new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/ro(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/ro_suit(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/qm(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/qm_suit(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/yellow(new_human), WEAR_HANDS)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap/req(new_human), WEAR_HEAD)
@@ -476,6 +475,13 @@
role_comm_title = "CO"
minimum_age = 35
+ dress_over = list(
+ /obj/item/clothing/suit/storage/jacket/marine/dress/officer/white,
+ /obj/item/clothing/suit/storage/jacket/marine/dress/officer/black,
+ /obj/item/clothing/suit/storage/jacket/marine/dress/officer/suit,
+ /obj/item/clothing/suit/storage/jacket/marine/dress/officer/falcon,
+ )
+
/datum/equipment_preset/uscm_ship/commander/council/load_gear(mob/living/carbon/human/new_human)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/bridge(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/beret/marine/commander/council(new_human), WEAR_HEAD)
@@ -608,6 +614,63 @@
//*****************************************************************************************************/
+/datum/equipment_preset/uscm_ship/auxiliary_officer
+ name = "USCM Auxiliary Support Officer (ASO)"
+ flags = EQUIPMENT_PRESET_START_OF_ROUND|EQUIPMENT_PRESET_MARINE
+
+ idtype = /obj/item/card/id/silver
+ assignment = JOB_AUXILIARY_OFFICER
+ rank = JOB_AUXILIARY_OFFICER
+ paygrade = "MO2"
+ role_comm_title = "ASO"
+ minimum_age = 27
+ skills = /datum/skills/auxiliary_officer
+
+ minimap_icon = list("cic" = MINIMAP_ICON_COLOR_SILVER)
+ minimap_background = MINIMAP_ICON_BACKGROUND_CIC
+
+/datum/equipment_preset/uscm_ship/auxiliary_officer/New()
+ . = ..()
+ access = list(
+ ACCESS_MARINE_SENIOR,
+ ACCESS_MARINE_ASO,
+ ACCESS_MARINE_COMMAND,
+ ACCESS_MARINE_BRIG,
+ ACCESS_MARINE_ARMORY,
+ ACCESS_MARINE_MEDBAY,
+ ACCESS_MARINE_CE,
+ ACCESS_MARINE_ENGINEERING,
+ ACCESS_MARINE_MAINT,
+ ACCESS_MARINE_OT,
+ ACCESS_MARINE_RO,
+ ACCESS_MARINE_CARGO,
+ ACCESS_MARINE_PREP,
+ ACCESS_MARINE_ALPHA,
+ ACCESS_MARINE_BRAVO,
+ ACCESS_MARINE_CHARLIE,
+ ACCESS_MARINE_DELTA,
+ ACCESS_MARINE_PILOT,
+ ACCESS_MARINE_DROPSHIP,
+ ACCESS_MARINE_KITCHEN,
+ ACCESS_PRESS,
+ )
+
+/datum/equipment_preset/uscm_ship/auxiliary_officer/load_gear(mob/living/carbon/human/new_human)
+ var/back_item = /obj/item/storage/backpack/satchel
+ if (new_human.client && new_human.client.prefs && (new_human.client.prefs.backbag == 1))
+ back_item = /obj/item/storage/backpack/marine
+
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcom/alt(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/bridge(new_human), WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/jacket/marine/service/aso(new_human), WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range(new_human), WEAR_L_STORE)
+
+//*****************************************************************************************************/
+
/datum/equipment_preset/uscm_ship/po
name = "USCM Pilot (DP) (Cryo)"
flags = EQUIPMENT_PRESET_START_OF_ROUND|EQUIPMENT_PRESET_MARINE
diff --git a/code/modules/hydroponics/hydro_tray.dm b/code/modules/hydroponics/hydro_tray.dm
index 65cf76b19a4a..9549b8fa59c1 100644
--- a/code/modules/hydroponics/hydro_tray.dm
+++ b/code/modules/hydroponics/hydro_tray.dm
@@ -210,11 +210,14 @@
// Make sure the plant is not starving or thirsty. Adequate
// water and nutrients will cause a plant to become healthier.
+ // Checks if there are sufficient enough nutrients, if not the plant dies.
var/healthmod = rand(1,3) * HYDRO_SPEED_MULTIPLIER
if(seed.requires_nutrients && prob(35))
plant_health += (nutrilevel < 2 ? -healthmod : healthmod)
if(seed.requires_water && prob(35))
plant_health += (waterlevel < 10 ? -healthmod : healthmod)
+ if(nutrilevel < 1)
+ plant_health = 0
// Check that pressure, heat and light are all within bounds.
// First, handle an open system or an unconnected closed system.
diff --git a/code/modules/law/laws/minor_crime.dm b/code/modules/law/laws/minor_crime.dm
index 3fc257cf1cbf..651b9dd42d4f 100644
--- a/code/modules/law/laws/minor_crime.dm
+++ b/code/modules/law/laws/minor_crime.dm
@@ -50,3 +50,8 @@
desc = "To take items (or property) from another person or entity without their express permission, or to retain possession of items that have been taken without permission. This includes the removal of vendors from department areas without permission."
special_punishment = "Return of Stolen Item"
brig_time = 10
+
+/datum/law/minor_law/false_testimony
+ name = "False Testimony"
+ desc = "To intentionally and willingly lie to an MP, or deputized officer, during a legal investigation with the intent to disrupt said investigation."
+ brig_time = 5
diff --git a/code/modules/law/laws/optional.dm b/code/modules/law/laws/optional.dm
index b553d5530a12..942f824aae56 100644
--- a/code/modules/law/laws/optional.dm
+++ b/code/modules/law/laws/optional.dm
@@ -2,8 +2,8 @@
severity = OPTIONAL_CRIME
/datum/law/optional_law/minor_unruly
- name = "Minor Disordely Conduct in Confinement"
- desc = "To cause disruption in a minor manner while in Brig and under arrest. Disruption is considered breaking Minor Law. This can be added on to any charge."
+ name = "Minor Disorderly Conduct in Confinement"
+ desc = "To cause disruption in a minor manner while in the Brig and under arrest. Disruption is considered breaking a Minor Law. This can be added on to any charge."
brig_time = 7.5
/datum/law/optional_law/aiding
name = "Aiding and Abetting"
@@ -17,6 +17,6 @@
brig_time = 10
/datum/law/optional_law/major_unruly
- name = "Major Disordely Conduct in Confinement"
- desc = "To cause disruption in a major manner while in Brig and under arrest. Disruption is considered breaking Major Law. This can be added on to any charge."
+ name = "Major Disorderly Conduct in Confinement"
+ desc = "To cause disruption in a major manner while in the Brig and under arrest. Disruption is considered breaking a Major Law. This can be added on to any charge."
brig_time = 15
diff --git a/code/modules/mentor/mentorhelp.dm b/code/modules/mentor/mentorhelp.dm
index 84dacf4f8bfb..9aaf1cae517b 100644
--- a/code/modules/mentor/mentorhelp.dm
+++ b/code/modules/mentor/mentorhelp.dm
@@ -64,7 +64,7 @@
if(to_thread_mentor && mentor)
hitlist |= mentor
for(var/client/candidate in GLOB.admins)
- if(to_mentors && CLIENT_HAS_RIGHTS(candidate, R_MENTOR))
+ if(to_mentors && CLIENT_IS_MENTOR(candidate))
hitlist |= candidate
else if(to_staff && CLIENT_IS_STAFF(candidate))
hitlist |= candidate
@@ -137,7 +137,7 @@
if(!sender || !check_open(sender))
return
if(sender != author)
- if(!CLIENT_HAS_RIGHTS(sender, R_MENTOR))
+ if(!CLIENT_IS_MENTOR(sender))
return
// If the mentor forgot to mark the mentorhelp, mark it for them
@@ -201,7 +201,7 @@
return
// Not a mentor/staff
- if(!CLIENT_HAS_RIGHTS(thread_mentor, R_MENTOR))
+ if(!CLIENT_IS_MENTOR(thread_mentor))
return
mentor = thread_mentor
@@ -294,7 +294,7 @@
if(!check_open(responder))
return
- if(!CLIENT_HAS_RIGHTS(responder, R_MENTOR))
+ if(!CLIENT_IS_MENTOR(responder))
return
// If the mentor forgot to mark the mentorhelp, mark it for them
@@ -314,7 +314,7 @@
if(!check_open(responder))
return
- if(!CLIENT_HAS_RIGHTS(responder, R_MENTOR))
+ if(!CLIENT_IS_MENTOR(responder))
return
// Re-mark if they unmarked it while the dialog was open (???)
diff --git a/code/modules/mob/camera/imaginary_friend.dm b/code/modules/mob/camera/imaginary_friend.dm
index 054dd6ea8ff4..a78de70a7e15 100644
--- a/code/modules/mob/camera/imaginary_friend.dm
+++ b/code/modules/mob/camera/imaginary_friend.dm
@@ -200,6 +200,7 @@
to_chat(owner, "[rendered]")
to_chat(src, "[rendered]")
+ log_say("Imaginary Friend: [dead_rendered]")
if(!hidden)
var/list/send_to = list()
if(!owner.client?.prefs.lang_chat_disabled)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 9e8fa264af1d..ee36d25fc687 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -544,6 +544,27 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(!istype(target))
return
+ if(check_client_rights(client, R_MOD, FALSE))
+ view_health_scan(target)
+ return
+
+ if(!mind.original)
+ view_health_scan(target)
+ return
+
+ if(!ishuman(mind.original))
+ view_health_scan(target)
+ return
+
+ var/mob/living/carbon/human/original_human = mind.original
+
+ if(!original_human.check_tod() || !original_human.is_revivable() || !can_reenter_corpse)
+ view_health_scan(target)
+ return
+
+ to_chat(src, SPAN_NOTICE("You must be permanently unrevivable or unable to reenter your body to use the scan health verb."))
+
+/mob/dead/observer/proc/view_health_scan(mob/living/target)
if (!last_health_display)
last_health_display = new(target)
else
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 61848eda251c..a6062276bd8e 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -29,27 +29,35 @@
b_volume = 0
else if(chem_effect_flags & CHEM_EFFECT_ORGAN_STASIS)
b_volume *= 1
- else if(heart.damage > 1 && heart.damage < heart.min_bruised_damage)
- b_volume *= 0.8
- else if(heart.damage >= heart.min_bruised_damage && heart.damage < heart.min_broken_damage)
- b_volume *= 0.6
- else if(heart.damage >= heart.min_broken_damage && heart.damage < INFINITY)
- b_volume *= 0.3
+ else if(heart.damage >= heart.organ_status >= ORGAN_BRUISED)
+ b_volume *= Clamp(100 - (2 * heart.damage), 30, 100) / 100
//Effects of bloodloss
+ if(b_volume <= BLOOD_VOLUME_SAFE)
+ /// The blood volume turned into a %, with BLOOD_VOLUME_NORMAL being 100%
+ var/blood_percentage = b_volume / (BLOOD_VOLUME_NORMAL / 100)
+ /// How much oxyloss will there be from the next time blood processes
+ var/additional_oxyloss = (100 - blood_percentage) / 5
+ /// The limit of the oxyloss gained, ignoring oxyloss from the switch statement
+ var/maximum_oxyloss = Clamp((100 - blood_percentage) / 2, oxyloss, 100)
+ if(oxyloss < maximum_oxyloss)
+ oxyloss += round(max(additional_oxyloss, 0))
+
+ //Bloodloss effects on nutrition
+ if(nutrition >= 300)
+ nutrition -= 10
+ else if(nutrition >= 200)
+ nutrition -= 3
+
switch(b_volume)
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
if(prob(1))
var/word = pick("dizzy","woozy","faint")
to_chat(src, SPAN_DANGER("You feel [word]."))
- if(oxyloss < 20)
- oxyloss += 3
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
if(eye_blurry < 50)
AdjustEyeBlur(6)
- if(oxyloss < 50)
- oxyloss += 10
- oxyloss += 2
+ oxyloss += 3
if(prob(15))
apply_effect(rand(1,3), PARALYZE)
var/word = pick("dizzy","woozy","faint")
@@ -57,7 +65,7 @@
if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD)
if(eye_blurry < 50)
AdjustEyeBlur(6)
- oxyloss += 5
+ oxyloss += 8
toxloss += 3
if(prob(15))
apply_effect(rand(1,3), PARALYZE)
@@ -66,13 +74,6 @@
if(0 to BLOOD_VOLUME_SURVIVE)
death(create_cause_data("blood loss"))
- // Without enough blood you slowly go hungry.
- if(blood_volume < BLOOD_VOLUME_SAFE)
- if(nutrition >= 300)
- nutrition -= 10
- else if(nutrition >= 200)
- nutrition -= 3
-
// Xeno blood regeneration
/mob/living/carbon/xenomorph/handle_blood()
if(stat != DEAD) //Only living xenos regenerate blood
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index a68286e6c970..a4eb3b99a13a 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -238,6 +238,10 @@
else
hud_used.l_hand_hud_object.icon_state = "hand_inactive"
hud_used.r_hand_hud_object.icon_state = "hand_active"
+ if(l_hand)
+ l_hand.hands_swapped(src)
+ if(r_hand)
+ r_hand.hands_swapped(src)
return
/mob/living/carbon/proc/activate_hand(selhand) //0 or "r" or "right" for right hand; 1 or "l" or "left" for left hand.
@@ -279,9 +283,9 @@
playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 5)
return
- adjust_effect(-3, PARALYZE)
- adjust_effect(-3, STUN)
- adjust_effect(-3, WEAKEN)
+ adjust_effect(-6, PARALYZE)
+ adjust_effect(-6, STUN)
+ adjust_effect(-6, WEAKEN)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 5)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 23c947e53063..d8008b1e3f78 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -529,8 +529,11 @@
if(U == w_uniform)
U.remove_accessory(usr, A)
else
+ if(HAS_TRAIT(src, TRAIT_UNSTRIPPABLE) && !is_mob_incapacitated()) //Can't strip the unstrippable!
+ to_chat(usr, SPAN_DANGER("[src] has an unbreakable grip on their equipment!"))
+ return
visible_message(SPAN_DANGER("[usr] is trying to take off \a [A] from [src]'s [U]!"), null, null, 5)
- if(do_after(usr, HUMAN_STRIP_DELAY, INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
+ if(do_after(usr, get_strip_delay(usr, src), INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
if(U == w_uniform)
U.remove_accessory(usr, A)
@@ -549,7 +552,7 @@
else
var/oldsens = U.has_sensor
visible_message(SPAN_DANGER("[usr] is trying to modify [src]'s sensors!"), null, null, 4)
- if(do_after(usr, HUMAN_STRIP_DELAY, INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
+ if(do_after(usr, get_strip_delay(usr, src), INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
if(U == w_uniform)
if(U.has_sensor >= UNIFORM_FORCED_SENSORS)
to_chat(usr, "The controls are locked.")
@@ -1407,6 +1410,7 @@
lighting_alpha = default_lighting_alpha
sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = species.darksight
+ sight |= species.flags_sight
if(glasses)
process_glasses(glasses)
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index 8a1b7203c1ca..35097a8e5c79 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -227,9 +227,9 @@
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 5)
return
- adjust_effect(-3, PARALYZE)
- adjust_effect(-3, STUN)
- adjust_effect(-3, WEAKEN)
+ adjust_effect(-6, PARALYZE)
+ adjust_effect(-6, STUN)
+ adjust_effect(-6, WEAKEN)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 835e03c8e042..45fb65771069 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -486,63 +486,82 @@
return WEAR_LEGCUFFS
return ..()
-
-
-/mob/living/carbon/human/stripPanelUnequip(obj/item/I, mob/M, slot_to_process)
- if(I.flags_item & ITEM_ABSTRACT)
+/mob/living/carbon/human/proc/get_strip_delay(mob/living/carbon/human/user, mob/living/carbon/human/target)
+ /// Default delay
+ var/target_delay = HUMAN_STRIP_DELAY
+ /// Multiplier for how quickly the user can strip things.
+ var/user_speed = user.get_skill_duration_multiplier(SKILL_CQC)
+ /// The total skill level of CQC & Police
+ var/target_skills = (target.skills.get_skill_level(SKILL_CQC) + target.skills.get_skill_level(SKILL_POLICE))
+
+ /// Delay then gets + 0.5s per skill level, so long as not dead or cuffed.
+ if(!(target.stat || target.handcuffed))
+ target_delay += (target_skills * 5)
+
+ /// Final result is overall delay * speed multiplier
+ return target_delay * user_speed
+
+/mob/living/carbon/human/stripPanelUnequip(obj/item/interact_item, mob/target_mob, slot_to_process)
+ if(HAS_TRAIT(target_mob, TRAIT_UNSTRIPPABLE) && !target_mob.is_mob_incapacitated()) //Can't strip the unstrippable!
+ to_chat(src, SPAN_DANGER("[target_mob] has an unbreakable grip on their equipment!"))
+ return
+ if(interact_item.flags_item & ITEM_ABSTRACT)
+ return
+ if(interact_item.flags_item & NODROP)
+ to_chat(src, SPAN_WARNING("You can't remove \the [interact_item.name], it appears to be stuck!"))
return
- if(I.flags_item & NODROP)
- to_chat(src, SPAN_WARNING("You can't remove \the [I.name], it appears to be stuck!"))
+ if(interact_item.flags_inventory & CANTSTRIP)
+ to_chat(src, SPAN_WARNING("You're having difficulty removing \the [interact_item.name]."))
return
- if(I.flags_inventory & CANTSTRIP)
- to_chat(src, SPAN_WARNING("You're having difficulty removing \the [I.name]."))
+ target_mob.attack_log += "\[[time_stamp()]\] Has had their [interact_item.name] ([slot_to_process]) attempted to be removed by [key_name(src)]"
+ attack_log += "\[[time_stamp()]\] Attempted to remove [key_name(target_mob)]'s [interact_item.name] ([slot_to_process])"
+ log_interact(src, target_mob, "[key_name(src)] tried to remove [key_name(target_mob)]'s [interact_item.name] ([slot_to_process]).")
+
+ src.visible_message(SPAN_DANGER("[src] tries to remove [target_mob]'s [interact_item.name]."), \
+ SPAN_DANGER("You are trying to remove [target_mob]'s [interact_item.name]."), null, 5)
+ interact_item.add_fingerprint(src)
+ if(do_after(src, get_strip_delay(src, target_mob), INTERRUPT_ALL, BUSY_ICON_GENERIC, target_mob, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
+ if(interact_item && Adjacent(target_mob) && interact_item == target_mob.get_item_by_slot(slot_to_process))
+ target_mob.drop_inv_item_on_ground(interact_item)
+ log_interact(src, target_mob, "[key_name(src)] removed [key_name(target_mob)]'s [interact_item.name] ([slot_to_process]) successfully.")
+
+ if(target_mob)
+ if(interactee == target_mob && Adjacent(target_mob))
+ target_mob.show_inv(src)
+
+
+/mob/living/carbon/human/stripPanelEquip(obj/item/interact_item, mob/target_mob, slot_to_process)
+ if(HAS_TRAIT(target_mob, TRAIT_UNSTRIPPABLE) && !target_mob.is_mob_incapacitated())
+ to_chat(src, SPAN_DANGER("[target_mob] is too strong to force [interact_item.name] onto them!"))
return
- M.attack_log += "\[[time_stamp()]\] Has had their [I.name] ([slot_to_process]) attempted to be removed by [key_name(src)]"
- attack_log += "\[[time_stamp()]\] Attempted to remove [key_name(M)]'s [I.name] ([slot_to_process])"
- log_interact(src, M, "[key_name(src)] tried to remove [key_name(M)]'s [I.name] ([slot_to_process]).")
-
- src.visible_message(SPAN_DANGER("[src] tries to remove [M]'s [I.name]."), \
- SPAN_DANGER("You are trying to remove [M]'s [I.name]."), null, 5)
- I.add_fingerprint(src)
- if(do_after(src, HUMAN_STRIP_DELAY * src.get_skill_duration_multiplier(SKILL_CQC), INTERRUPT_ALL, BUSY_ICON_GENERIC, M, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
- if(I && Adjacent(M) && I == M.get_item_by_slot(slot_to_process))
- M.drop_inv_item_on_ground(I)
- log_interact(src, M, "[key_name(src)] removed [key_name(M)]'s [I.name] ([slot_to_process]) successfully.")
-
- if(M)
- if(interactee == M && Adjacent(M))
- M.show_inv(src)
-
-
-/mob/living/carbon/human/stripPanelEquip(obj/item/I, mob/M, slot_to_process)
- if(I && !(I.flags_item & ITEM_ABSTRACT))
- if(I.flags_item & NODROP)
- to_chat(src, SPAN_WARNING("You can't put \the [I.name] on [M], it's stuck to your hand!"))
+ if(interact_item && !(interact_item.flags_item & ITEM_ABSTRACT))
+ if(interact_item.flags_item & NODROP)
+ to_chat(src, SPAN_WARNING("You can't put \the [interact_item.name] on [target_mob], it's stuck to your hand!"))
return
- if(I.flags_inventory & CANTSTRIP)
- to_chat(src, SPAN_WARNING("You're having difficulty putting \the [I.name] on [M]."))
+ if(interact_item.flags_inventory & CANTSTRIP)
+ to_chat(src, SPAN_WARNING("You're having difficulty putting \the [interact_item.name] on [target_mob]."))
return
- if(I.flags_item & WIELDED)
- I.unwield(src)
- if(!I.mob_can_equip(M, slot_to_process, TRUE))
- to_chat(src, SPAN_WARNING("You can't put \the [I.name] on [M]!"))
+ if(interact_item.flags_item & WIELDED)
+ interact_item.unwield(src)
+ if(!interact_item.mob_can_equip(target_mob, slot_to_process, TRUE))
+ to_chat(src, SPAN_WARNING("You can't put \the [interact_item.name] on [target_mob]!"))
return
- visible_message(SPAN_NOTICE("[src] tries to put \the [I.name] on [M]."), null, null, 5)
- if(do_after(src, HUMAN_STRIP_DELAY * src.get_skill_duration_multiplier(SKILL_CQC), INTERRUPT_ALL, BUSY_ICON_GENERIC, M, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
- if(I == get_active_hand() && !M.get_item_by_slot(slot_to_process) && Adjacent(M))
- if(I.flags_item & WIELDED) //to prevent re-wielding it during the do_after
- I.unwield(src)
- if(I.mob_can_equip(M, slot_to_process, TRUE))//Placing an item on the mob
- drop_inv_item_on_ground(I)
- if(I && !QDELETED(I)) //Might be self-deleted?
- M.equip_to_slot_if_possible(I, slot_to_process, 1, 0, 1, 1)
- if(ishuman(M) && M.stat == DEAD)
- var/mob/living/carbon/human/H = M
- H.disable_lights() // take that powergamers -spookydonut
-
- if(M)
- if(interactee == M && Adjacent(M))
- M.show_inv(src)
+ visible_message(SPAN_NOTICE("[src] tries to put \the [interact_item.name] on [target_mob]."), null, null, 5)
+ if(do_after(src, get_strip_delay(src, target_mob), INTERRUPT_ALL, BUSY_ICON_GENERIC, target_mob, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
+ if(interact_item == get_active_hand() && !target_mob.get_item_by_slot(slot_to_process) && Adjacent(target_mob))
+ if(interact_item.flags_item & WIELDED) //to prevent re-wielding it during the do_after
+ interact_item.unwield(src)
+ if(interact_item.mob_can_equip(target_mob, slot_to_process, TRUE))//Placing an item on the mob
+ drop_inv_item_on_ground(interact_item)
+ if(interact_item && !QDELETED(interact_item)) //Might be self-deleted?
+ target_mob.equip_to_slot_if_possible(interact_item, slot_to_process, 1, 0, 1, 1)
+ if(ishuman(target_mob) && target_mob.stat == DEAD)
+ var/mob/living/carbon/human/human_target = target_mob
+ human_target.disable_lights() // take that powergamers -spookydonut
+
+ if(target_mob)
+ if(interactee == target_mob && Adjacent(target_mob))
+ target_mob.show_inv(src)
/mob/living/carbon/human/drop_inv_item_on_ground(obj/item/I, nomoveupdate, force)
remember_dropped_object(I)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index df9f0c259e4f..778218ef577f 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -41,7 +41,7 @@
handle_chemicals_in_body(delta_time)
//Organs and blood
- handle_organs()
+ handle_organs(delta_time)
handle_blood()
//Random events (vomiting etc)
diff --git a/code/modules/mob/living/carbon/human/life/handle_organs.dm b/code/modules/mob/living/carbon/human/life/handle_organs.dm
index 8d0a0dbc3e15..2c978f2295ed 100644
--- a/code/modules/mob/living/carbon/human/life/handle_organs.dm
+++ b/code/modules/mob/living/carbon/human/life/handle_organs.dm
@@ -1,11 +1,11 @@
// Takes care of organ & limb related updates, such as broken and missing limbs
-/mob/living/carbon/human/proc/handle_organs()
+/mob/living/carbon/human/proc/handle_organs(delta_time)
last_dam = getBruteLoss() + getFireLoss() + getToxLoss()
// Processing internal organs is pretty cheap, do that first.
for(var/datum/internal_organ/I as anything in internal_organs)
- I.process()
+ I.process(delta_time)
for(var/obj/limb/E as anything in limbs_to_process)
if(!E)
diff --git a/code/modules/mob/living/carbon/human/species/emote-yautja.dm b/code/modules/mob/living/carbon/human/species/emote-yautja.dm
deleted file mode 100644
index 8c959329c953..000000000000
--- a/code/modules/mob/living/carbon/human/species/emote-yautja.dm
+++ /dev/null
@@ -1,145 +0,0 @@
-/datum/emote/living/carbon/human/yautja
- species_type_allowed_typecache = list(/datum/species/yautja)
- keybind_category = CATEGORY_YAUTJA_EMOTE
-
-/datum/emote/living/carbon/human/yautja/anytime
- key = "anytime"
- sound = 'sound/voice/pred_anytime.ogg'
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/click
- key = "click"
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/click/get_sound(mob/living/user)
- if(rand(0,100) < 50)
- return 'sound/voice/pred_click1.ogg'
- else
- return 'sound/voice/pred_click2.ogg'
-
-/datum/emote/living/carbon/human/yautja/helpme
- key = "helpme"
- sound = 'sound/voice/pred_helpme.ogg'
- volume = 25
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/malescream
- key = "malescream"
- emote_type = EMOTE_AUDIBLE
- sound = "male_scream"
-
-/datum/emote/living/carbon/human/yautja/femalescream
- key = "femalescream"
- emote_type = EMOTE_AUDIBLE
- sound = "female_scream"
-
-/datum/emote/living/carbon/human/yautja/iseeyou
- key = "iseeyou"
- sound = 'sound/hallucinations/i_see_you2.ogg'
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/itsatrap
- key = "itsatrap"
- sound = 'sound/voice/pred_itsatrap.ogg'
- volume = 25
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/laugh1
- key = "laugh1"
- sound = 'sound/voice/pred_laugh1.ogg'
- volume = 25
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/laugh2
- key = "laugh2"
- sound = 'sound/voice/pred_laugh2.ogg'
- volume = 25
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/laugh3
- key = "laugh3"
- sound = 'sound/voice/pred_laugh3.ogg'
- volume = 25
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/overhere
- key = "overhere"
- sound = 'sound/voice/pred_overhere.ogg'
- volume = 25
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/roar
- key = "roar"
- message = "roars!"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/yautja/roar/get_sound(mob/living/user)
- return pick('sound/voice/pred_roar1.ogg', 'sound/voice/pred_roar2.ogg')
-
-/datum/emote/living/carbon/human/yautja/roar2
- key = "roar2"
- sound = 'sound/voice/pred_roar3.ogg'
- message = "roars!"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/yautja/loudroar
- key = "loudroar"
- message = "roars loudly!"
- volume = 60
- cooldown = 120 SECONDS
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/yautja/loudroar/get_sound(mob/living/user)
- return pick('sound/voice/pred_roar4.ogg', 'sound/voice/pred_roar5.ogg')
-
-/datum/emote/living/carbon/human/yautja/loudroar/run_emote(mob/user, params, type_override, intentional)
- . = ..()
- if(!.)
- return
-
- for(var/mob/current_mob as anything in get_mobs_in_z_level_range(get_turf(user), 18) - user)
- var/relative_dir = get_dir(current_mob, user)
- var/final_dir = dir2text(relative_dir)
- to_chat(current_mob, SPAN_HIGHDANGER("You hear a loud roar coming from [final_dir ? "the [final_dir]" : "nearby"]!"))
-
-/datum/emote/living/carbon/human/yautja/turnaround
- key = "turnaround"
- sound = 'sound/voice/pred_turnaround.ogg'
- volume = 25
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/click2
- key = "click2"
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/click2/get_sound(mob/living/user)
- return pick('sound/voice/pred_click3.ogg', 'sound/voice/pred_click4.ogg')
-
-/datum/emote/living/carbon/human/yautja/aliengrowl
- key = "aliengrowl"
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/aliengrowl/get_sound(mob/living/user)
- return pick('sound/voice/alien_growl1.ogg', 'sound/voice/alien_growl2.ogg')
-
-/datum/emote/living/carbon/human/yautja/alienhelp
- key = "alienhelp"
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/alienhelp/get_sound(mob/living/user)
- return pick('sound/voice/alien_help1.ogg', 'sound/voice/alien_help2.ogg')
-
-/datum/emote/living/carbon/human/yautja/comeonout
- key = "comeonout"
- sound = 'sound/voice/pred_come_on_out.ogg'
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/overthere
- key = "overthere"
- sound = 'sound/voice/pred_over_there.ogg'
- emote_type = EMOTE_AUDIBLE
-
-/datum/emote/living/carbon/human/yautja/uglyfreak
- key = "uglyfreak"
- sound = 'sound/voice/pred_ugly_freak.ogg'
- emote_type = EMOTE_AUDIBLE
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index a6d0355bfbe5..f6487d1faf1c 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -63,6 +63,7 @@
var/darksight = 2
var/default_lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
+ var/flags_sight = 0
var/brute_mod = null // Physical damage reduction/malus.
var/burn_mod = null // Burn damage reduction/malus.
@@ -123,6 +124,10 @@
/datum/species/proc/larva_impregnated(obj/item/alien_embryo/embryo)
return
+/// Override to add an emote panel to a species
+/datum/species/proc/open_emote_panel()
+ return
+
/datum/species/proc/handle_npc(mob/living/carbon/human/H)
set waitfor = FALSE
return
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm b/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm
index 63cc79a57dae..e66fec576f64 100644
--- a/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm
+++ b/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm
@@ -2,7 +2,7 @@
species_type_allowed_typecache = list(/datum/species/synthetic/colonial/working_joe)
keybind_category = CATEGORY_SYNTH_EMOTE
volume = 75
- /// A general category for the emote, for use in the WJ emote panel. See [code/__DEFINES/wj_emotes.dm] for categories.
+ /// A general category for the emote, for use in the WJ emote panel. See [code/__DEFINES/emote_panels.dm] for categories.
var/category = ""
/// Override text for the emote to be displayed in the WJ emote panel
var/override_say = ""
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
index b9044becb6f5..292c302f9317 100644
--- a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
@@ -4,7 +4,7 @@
death_message = "violently gargles fluid and seizes up, the glow in their eyes dimming..."
uses_ethnicity = FALSE
burn_mod = 0.65 // made for hazardous environments, withstanding temperatures up to 1210 degrees
- mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT, TRAIT_CANNOT_EAT)
+ mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT, TRAIT_CANNOT_EAT, TRAIT_UNSTRIPPABLE)
slowdown = 0.45
hair_color = "#000000"
@@ -22,7 +22,7 @@
return ..()
/// Open the WJ's emote panel, which allows them to use voicelines
-/datum/species/synthetic/colonial/working_joe/proc/open_emote_panel()
+/datum/species/synthetic/colonial/working_joe/open_emote_panel()
var/datum/joe_emote_panel/ui = new(usr)
ui.ui_interact(usr)
diff --git a/code/modules/mob/living/carbon/human/species/yautja/_emote.dm b/code/modules/mob/living/carbon/human/species/yautja/_emote.dm
new file mode 100644
index 000000000000..0202eece1620
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/yautja/_emote.dm
@@ -0,0 +1,10 @@
+/datum/emote/living/carbon/human/yautja
+ species_type_allowed_typecache = list(/datum/species/yautja)
+ keybind_category = CATEGORY_YAUTJA_EMOTE
+ emote_type = EMOTE_AUDIBLE
+ /// A general category for the emote, for use in the Yautja emote panel. See [code/__DEFINES/emote_panels.dm] for categories.
+ var/category = ""
+ /// Override text for the emote to be displayed in the Yautja emote panel
+ var/override_say = ""
+ /// Override for being in panel or not
+ var/no_panel = FALSE
diff --git a/code/modules/mob/living/carbon/human/species/yautja.dm b/code/modules/mob/living/carbon/human/species/yautja/_species.dm
similarity index 68%
rename from code/modules/mob/living/carbon/human/species/yautja.dm
rename to code/modules/mob/living/carbon/human/species/yautja/_species.dm
index a4c87b5da4e1..f8ecb3e0591b 100644
--- a/code/modules/mob/living/carbon/human/species/yautja.dm
+++ b/code/modules/mob/living/carbon/human/species/yautja/_species.dm
@@ -25,6 +25,8 @@
speech_chance = 100
death_message = "clicks in agony and falls still, motionless and completely lifeless..."
darksight = 5
+ default_lighting_alpha = LIGHTING_PLANE_ALPHA_YAUTJA
+ flags_sight = SEE_MOBS
slowdown = -0.5
total_health = 175 //more health than regular humans
timed_hug = FALSE
@@ -180,16 +182,16 @@
limb.max_damage = 35
limb.time_to_knit = -1
-/datum/species/yautja/handle_post_spawn(mob/living/carbon/human/H)
- GLOB.alive_human_list -= H
- H.universal_understand = 1
+/datum/species/yautja/handle_post_spawn(mob/living/carbon/human/hunter)
+ GLOB.alive_human_list -= hunter
+ hunter.universal_understand = 1
- H.blood_type = "Y*"
- H.h_style = "Standard"
+ hunter.blood_type = "Y*"
+ hunter.h_style = "Standard"
#ifndef UNIT_TESTS // Since this is a hard ref, we shouldn't confuse create_and_destroy
- GLOB.yautja_mob_list += H
+ GLOB.yautja_mob_list += hunter
#endif
- for(var/obj/limb/limb in H.limbs)
+ for(var/obj/limb/limb in hunter.limbs)
switch(limb.name)
if("groin","chest")
limb.min_broken_damage = 145
@@ -208,7 +210,8 @@
limb.max_damage = 150
limb.time_to_knit = 600 // 1 minute to self heal bone break, time is in tenths of a second
- H.set_languages(list(LANGUAGE_YAUTJA))
+ hunter.set_languages(list(LANGUAGE_YAUTJA))
+ give_action(hunter, /datum/action/yautja_emote_panel)
return ..()
/datum/species/yautja/get_hairstyle(style)
@@ -220,3 +223,105 @@
/datum/species/yautja/handle_paygrades()
return ""
+
+/// Open the Yautja emote panel, which allows them to use their emotes easier.
+/datum/species/yautja/open_emote_panel()
+ var/datum/yautja_emote_panel/ui = new(usr)
+ ui.ui_interact(usr)
+
+/datum/action/yautja_emote_panel
+ name = "Open Emote Panel"
+ action_icon_state = "looc_toggle"
+
+/datum/action/yautja_emote_panel/can_use_action()
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!isyautja(owner))
+ return FALSE
+
+ return TRUE
+
+/datum/action/yautja_emote_panel/action_activate()
+ if(!can_use_action())
+ return
+
+ var/mob/living/carbon/human/human_owner = owner
+ var/datum/species/yautja/yautja_species = human_owner.species
+ yautja_species.open_emote_panel()
+
+/datum/yautja_emote_panel
+ /// Static dict ("category" : (emotes)) of every yautja emote typepath
+ var/static/list/yautja_emotes
+ /// Static list of categories
+ var/static/list/yautja_categories = list()
+ /// Panel allows you to spam, so a manual CD is added here
+ COOLDOWN_DECLARE(panel_emote_cooldown)
+
+/datum/yautja_emote_panel/New()
+ if(!length(yautja_emotes))
+ var/list/emotes_to_add = list()
+ for(var/datum/emote/living/carbon/human/yautja/emote as anything in subtypesof(/datum/emote/living/carbon/human/yautja))
+ if(!initial(emote.key) || initial(emote.no_panel))
+ continue
+
+ if(!(initial(emote.category) in yautja_categories))
+ yautja_categories += initial(emote.category)
+ emotes_to_add += emote
+ yautja_emotes = emotes_to_add
+
+/datum/yautja_emote_panel/proc/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "YautjaEmotes")
+ ui.open()
+
+/datum/yautja_emote_panel/ui_data(mob/user)
+ var/list/data = list()
+
+ data["on_cooldown"] = !COOLDOWN_FINISHED(src, panel_emote_cooldown)
+
+ return data
+
+/datum/yautja_emote_panel/ui_state(mob/user)
+ return GLOB.conscious_state
+
+/datum/yautja_emote_panel/ui_static_data(mob/user)
+ var/list/data = list()
+
+ data["categories"] = yautja_categories
+ data["emotes"] = list()
+
+ for(var/datum/emote/living/carbon/human/yautja/emote as anything in yautja_emotes)
+ data["emotes"] += list(list(
+ "id" = initial(emote.key),
+ "text" = (initial(emote.override_say) || initial(emote.say_message) || initial(emote.key)),
+ "category" = initial(emote.category),
+ "path" = "[emote]",
+ ))
+
+ return data
+
+/datum/yautja_emote_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("emote")
+ var/datum/emote/living/carbon/human/yautja/path
+ if(!params["emotePath"])
+ return FALSE
+
+ path = text2path(params["emotePath"])
+
+ if(!path || !COOLDOWN_FINISHED(src, panel_emote_cooldown))
+ return
+
+ if(!(path in subtypesof(/datum/emote/living/carbon/human/yautja)))
+ return FALSE
+
+ COOLDOWN_START(src, panel_emote_cooldown, 2.5 SECONDS)
+ usr.emote(initial(path.key))
+ return TRUE
diff --git a/code/modules/mob/living/carbon/human/species/yautja/fake_sounds.dm b/code/modules/mob/living/carbon/human/species/yautja/fake_sounds.dm
new file mode 100644
index 000000000000..80aa853c663f
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/yautja/fake_sounds.dm
@@ -0,0 +1,26 @@
+/datum/emote/living/carbon/human/yautja/fake_sound
+ category = YAUTJA_EMOTE_CATEGORY_FAKESOUND
+
+/datum/emote/living/carbon/human/yautja/fake_sound/aliengrowl
+ override_say = "Xenomorph growl"
+ key = "aliengrowl"
+
+/datum/emote/living/carbon/human/yautja/fake_sound/aliengrowl/get_sound(mob/living/user)
+ return pick('sound/voice/alien_growl1.ogg', 'sound/voice/alien_growl2.ogg')
+
+/datum/emote/living/carbon/human/yautja/fake_sound/alienhelp
+ override_say = "Xenomorph needs help"
+ key = "alienhelp"
+
+/datum/emote/living/carbon/human/yautja/fake_sound/alienhelp/get_sound(mob/living/user)
+ return pick('sound/voice/alien_help1.ogg', 'sound/voice/alien_help2.ogg')
+
+/datum/emote/living/carbon/human/yautja/fake_sound/malescream
+ override_say = "Human scream (male)"
+ key = "malescream"
+ sound = "male_scream"
+
+/datum/emote/living/carbon/human/yautja/fake_sound/femalescream
+ override_say = "Human scream (female)"
+ key = "femalescream"
+ sound = "female_scream"
diff --git a/code/modules/mob/living/carbon/human/species/yautja/fake_voice.dm b/code/modules/mob/living/carbon/human/species/yautja/fake_voice.dm
new file mode 100644
index 000000000000..7e2c73fd41e4
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/yautja/fake_voice.dm
@@ -0,0 +1,59 @@
+/datum/emote/living/carbon/human/yautja/voice
+ category = YAUTJA_EMOTE_CATEGORY_VOICE
+
+/datum/emote/living/carbon/human/yautja/voice/anytime
+ override_say = "Anytime."
+ key = "anytime"
+ sound = 'sound/voice/pred_anytime.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/helpme
+ override_say = "Help me!"
+ key = "helpme"
+ sound = 'sound/voice/pred_helpme.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/iseeyou
+ override_say = "I see you."
+ key = "iseeyou"
+ sound = 'sound/hallucinations/i_see_you2.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/itsatrap
+ override_say = "It's a trap."
+ key = "itsatrap"
+ sound = 'sound/voice/pred_itsatrap.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/overhere
+ override_say = "Over here."
+ key = "overhere"
+ sound = 'sound/voice/pred_overhere.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/turnaround
+ override_say = "Turn around."
+ key = "turnaround"
+ sound = 'sound/voice/pred_turnaround.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/comeonout
+ override_say = "Come on out, motherfucker."
+ key = "comeonout"
+ sound = 'sound/voice/pred_come_on_out.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/overthere
+ override_say = "Over there."
+ key = "overthere"
+ sound = 'sound/voice/pred_over_there.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/uglyfreak
+ override_say = "Come on, you ugly freak."
+ key = "uglyfreak"
+ sound = 'sound/voice/pred_ugly_freak.ogg'
diff --git a/code/modules/mob/living/carbon/human/species/yautja/yautja_sound.dm b/code/modules/mob/living/carbon/human/species/yautja/yautja_sound.dm
new file mode 100644
index 000000000000..a6a9a659f215
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/yautja/yautja_sound.dm
@@ -0,0 +1,70 @@
+/datum/emote/living/carbon/human/yautja/species_sound
+ category = YAUTJA_EMOTE_CATEGORY_SPECIES
+
+/datum/emote/living/carbon/human/yautja/species_sound/click
+ key = "click"
+
+/datum/emote/living/carbon/human/yautja/species_sound/click/get_sound(mob/living/user)
+ if(rand(0,100) < 50)
+ return 'sound/voice/pred_click1.ogg'
+ else
+ return 'sound/voice/pred_click2.ogg'
+
+/datum/emote/living/carbon/human/yautja/species_sound/click2
+ key = "click2"
+
+/datum/emote/living/carbon/human/yautja/species_sound/click2/get_sound(mob/living/user)
+ return pick('sound/voice/pred_click3.ogg', 'sound/voice/pred_click4.ogg')
+
+// Laughing Emotes
+/datum/emote/living/carbon/human/yautja/species_sound/laugh1
+ key = "laugh1"
+ sound = 'sound/voice/pred_laugh1.ogg'
+ volume = 25
+
+/datum/emote/living/carbon/human/yautja/species_sound/laugh2
+ key = "laugh2"
+ sound = 'sound/voice/pred_laugh2.ogg'
+ volume = 25
+
+/datum/emote/living/carbon/human/yautja/species_sound/laugh3
+ key = "laugh3"
+ sound = 'sound/voice/pred_laugh3.ogg'
+ volume = 25
+
+
+// Roar Emotes
+/datum/emote/living/carbon/human/yautja/species_sound/roar
+ key = "roar"
+ message = "roars!"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/yautja/species_sound/roar/get_sound(mob/living/user)
+ return pick('sound/voice/pred_roar1.ogg', 'sound/voice/pred_roar2.ogg')
+
+/datum/emote/living/carbon/human/yautja/species_sound/roar2
+ key = "roar2"
+ sound = 'sound/voice/pred_roar3.ogg'
+ message = "roars!"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/yautja/species_sound/loudroar
+ key = "loudroar"
+ message = "roars loudly!"
+ volume = 60
+ cooldown = 120 SECONDS
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+ no_panel = TRUE
+
+/datum/emote/living/carbon/human/yautja/species_sound/loudroar/get_sound(mob/living/user)
+ return pick('sound/voice/pred_roar4.ogg', 'sound/voice/pred_roar5.ogg')
+
+/datum/emote/living/carbon/human/yautja/species_sound/loudroar/run_emote(mob/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return
+
+ for(var/mob/current_mob as anything in get_mobs_in_z_level_range(get_turf(user), 18) - user)
+ var/relative_dir = get_dir(current_mob, user)
+ var/final_dir = dir2text(relative_dir)
+ to_chat(current_mob, SPAN_HIGHDANGER("You hear a loud roar coming from [final_dir ? "the [final_dir]" : "nearby"]!"))
diff --git a/code/modules/mob/living/carbon/human/species/zombie.dm b/code/modules/mob/living/carbon/human/species/zombie.dm
index 532d9413102c..07fe8f5e1255 100644
--- a/code/modules/mob/living/carbon/human/species/zombie.dm
+++ b/code/modules/mob/living/carbon/human/species/zombie.dm
@@ -1,8 +1,12 @@
+// DEFINES
+///Time until a zombie rises from the dead
+#define ZOMBIE_REVIVE_TIME 1 MINUTES
+
/datum/species/zombie
group = SPECIES_HUMAN
name = SPECIES_ZOMBIE
name_plural = "Zombies"
- slowdown = 1
+ slowdown = 0.75
blood_color = BLOOD_COLOR_ZOMBIE
icobase = 'icons/mob/humans/species/r_goo_zed.dmi'
deform = 'icons/mob/humans/species/r_goo_zed.dmi'
@@ -12,7 +16,7 @@
death_message = "seizes up and falls limp..."
flags = NO_BREATHE|NO_CLONE_LOSS|NO_POISON|NO_NEURO|NO_SHRAPNEL
mob_inherent_traits = list(TRAIT_FOREIGN_BIO)
- brute_mod = 0.25 //EXTREME BULLET RESISTANCE
+ brute_mod = 0.6 //Minor bullet resistance
burn_mod = 0.8 //Lowered burn damage since it would 1-shot zombies from 2 to 0.8.
speech_chance = 5
cold_level_1 = -1 //zombies don't mind the cold
@@ -96,7 +100,7 @@
zombie.play_screen_text("You are dead... You will rise again in one minute.", /atom/movable/screen/text/screen_text/command_order, rgb(155, 0, 200))
to_chat(zombie, SPAN_XENOWARNING("You fall... but your body is slowly regenerating itself."))
var/weak_ref = WEAKREF(zombie)
- to_revive[weak_ref] = addtimer(CALLBACK(src, PROC_REF(revive_from_death), zombie, "[REF(zombie)]"), 1 MINUTES, TIMER_STOPPABLE|TIMER_OVERRIDE|TIMER_UNIQUE|TIMER_NO_HASH_WAIT)
+ to_revive[weak_ref] = addtimer(CALLBACK(src, PROC_REF(revive_from_death), zombie, "[REF(zombie)]"), ZOMBIE_REVIVE_TIME, TIMER_STOPPABLE|TIMER_OVERRIDE|TIMER_UNIQUE|TIMER_NO_HASH_WAIT)
revive_times[weak_ref] = world.time + 1 MINUTES
else
if(zombie.client)
diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
index e390cd15dca2..4ce266f70596 100644
--- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
@@ -100,6 +100,14 @@
switch(stage)
if(2)
+ if(prob(4))
+ if(affected_mob.knocked_out < 1)
+ affected_mob.pain.apply_pain(PAIN_CHESTBURST_WEAK)
+ affected_mob.visible_message(SPAN_DANGER("[affected_mob] starts shaking uncontrollably!"), \
+ SPAN_DANGER("You feel something moving inside you! You start shaking uncontrollably!"))
+ affected_mob.apply_effect(1, PARALYZE)
+ affected_mob.make_jittery(105)
+ affected_mob.take_limb_damage(1)
if(prob(2))
var/message = SPAN_WARNING("[pick("Your chest hurts a little bit", "Your stomach hurts")].")
to_chat(affected_mob, message)
@@ -113,15 +121,15 @@
affected_mob.take_limb_damage(1)
else if(prob(2))
affected_mob.emote("[pick("sneeze", "cough")]")
- if(4)
- if(prob(1))
+ if(prob(5))
if(affected_mob.knocked_out < 1)
affected_mob.pain.apply_pain(PAIN_CHESTBURST_WEAK)
affected_mob.visible_message(SPAN_DANGER("\The [affected_mob] starts shaking uncontrollably!"), \
- SPAN_DANGER("You start shaking uncontrollably!"))
- affected_mob.apply_effect(10, PARALYZE)
+ SPAN_DANGER("You feel something moving inside you! You start shaking uncontrollably!"))
+ affected_mob.apply_effect(2, PARALYZE)
affected_mob.make_jittery(105)
affected_mob.take_limb_damage(1)
+ if(4)
if(prob(2))
affected_mob.pain.apply_pain(PAIN_CHESTBURST_WEAK)
var/message = pick("Your chest hurts badly", "It becomes difficult to breathe", "Your heart starts beating rapidly, and each beat is painful")
@@ -129,61 +137,102 @@
to_chat(affected_mob, message)
if(prob(50))
affected_mob.emote("scream")
+ if(prob(6))
+ if(affected_mob.knocked_out < 1)
+ affected_mob.pain.apply_pain(PAIN_CHESTBURST_WEAK)
+ affected_mob.visible_message(SPAN_DANGER("[affected_mob] starts shaking uncontrollably!"), \
+ SPAN_DANGER("You feel something moving inside you! You start shaking uncontrollably!"))
+ affected_mob.apply_effect(3, PARALYZE)
+ affected_mob.make_jittery(105)
+ affected_mob.take_limb_damage(1)
if(5)
become_larva()
- if(6)
+ if(7) // Stage 6 is while we are trying to find a candidate in become_larva
larva_autoburst_countdown--
if(!larva_autoburst_countdown)
var/mob/living/carbon/xenomorph/larva/larva_embryo = locate() in affected_mob
if(larva_embryo)
larva_embryo.chest_burst(affected_mob)
-//We look for a candidate. If found, we spawn the candidate as a larva
-//Order of priority is bursted individual (if xeno is enabled), then random candidate, and then it's up for grabs and spawns braindead
+///We look for a candidate. If found, we spawn the candidate as a larva
+///Order of priority is bursted individual (if xeno is enabled), then player hugger, then random candidate, and then it's up for grabs and spawns braindead
/obj/item/alien_embryo/proc/become_larva()
// We do not allow chest bursts on the Centcomm Z-level, to prevent
// stranded players from admin experiments and other issues
if(!affected_mob || is_admin_level(affected_mob.z))
return
- var/datum/hive_status/hive = GLOB.hive_datum[hivenumber]
+ stage = 6 // Increase the stage value to prevent this proc getting repeated
+ var/datum/hive_status/hive = GLOB.hive_datum[hivenumber]
var/mob/picked
+ var/mob/dead/observer/hugger = null
+ var/is_nested = istype(affected_mob.buckled, /obj/structure/bed/nest)
+
// If the bursted person themselves has Xeno enabled, they get the honor of first dibs on the new larva.
- if((!isyautja(affected_mob) || (isyautja(affected_mob) && prob(20))) && istype(affected_mob.buckled, /obj/structure/bed/nest))
+ if((!isyautja(affected_mob) || (isyautja(affected_mob) && prob(20))) && is_nested)
if(affected_mob.first_xeno || (affected_mob.client?.prefs?.be_special & BE_ALIEN_AFTER_DEATH && !jobban_isbanned(affected_mob, JOB_XENOMORPH)))
picked = affected_mob
else if(affected_mob.mind?.ghost_mob && affected_mob.client?.prefs?.be_special & BE_ALIEN_AFTER_DEATH && !jobban_isbanned(affected_mob, JOB_XENOMORPH))
picked = affected_mob.mind.ghost_mob // This currently doesn't look possible
else if(affected_mob.persistent_ckey)
for(var/mob/dead/observer/cur_obs as anything in GLOB.observer_list)
+ if(!cur_obs)
+ continue
if(cur_obs.ckey != affected_mob.persistent_ckey)
continue
- if(cur_obs?.client?.prefs?.be_special & BE_ALIEN_AFTER_DEATH && !jobban_isbanned(cur_obs, JOB_XENOMORPH))
+ if(cur_obs.client?.prefs?.be_special & BE_ALIEN_AFTER_DEATH && !jobban_isbanned(cur_obs, JOB_XENOMORPH))
picked = cur_obs
break
if(!picked)
// Get a candidate from observers
var/list/candidates = get_alien_candidates(hive)
- if(candidates && candidates.len)
+ if(candidates && length(candidates))
// If they were facehugged by a player thats still in queue, they get second dibs on the new larva.
if(hugger_ckey)
for(var/mob/dead/observer/cur_obs as anything in candidates)
if(cur_obs.ckey == hugger_ckey)
- picked = cur_obs
- candidates -= cur_obs
- message_alien_candidates(candidates, dequeued = 0)
- for(var/obj/item/alien_embryo/embryo as anything in GLOB.player_embryo_list)
- if(embryo.hugger_ckey == cur_obs.ckey && embryo != src)
- // Skipping src just in case an admin wants to quickly check before this thing fully deletes
- // If this nulls out any embryo, wow
- embryo.hugger_ckey = null
+ hugger = cur_obs
+ if(!is_nested)
+ cur_obs.ManualFollow(affected_mob)
+ if(cur_obs.client.prefs?.toggles_flashing & FLASH_POOLSPAWN)
+ window_flash(cur_obs.client)
+ if(is_nested || tgui_alert(cur_obs, "An unnested host you hugged is about to burst! Do you want to control the new larva?", "Larva maturation", list("Yes", "No"), 10 SECONDS) == "Yes")
+ picked = cur_obs
+ candidates -= cur_obs
+ message_alien_candidates(candidates, dequeued = 0)
+ for(var/obj/item/alien_embryo/embryo as anything in GLOB.player_embryo_list)
+ if(!embryo)
+ continue
+ if(embryo.hugger_ckey == cur_obs.ckey && embryo != src)
+ // Skipping src just in case an admin wants to quickly check before this thing fully deletes
+ // If this nulls out any embryo, wow
+ embryo.hugger_ckey = null
break
+ // Get a candidate from the front of the queue
if(!picked)
- picked = candidates[1]
- message_alien_candidates(candidates, dequeued = 1)
+ if(is_nested)
+ picked = candidates[1]
+ message_alien_candidates(candidates, dequeued = 1)
+ else
+ // Make up to 5 attempts from the queue for an unnested host
+ // At 10s per candidate, for 6 candidates (facehugger is the +1) this means we may have delayed an unnested autoburst up to 60 seconds
+ for(var/i in 1 to min(5, length(candidates)))
+ var/mob/dead/observer/cur_candidate = candidates[i]
+ if(!cur_candidate?.client) // Make sure they are still a valid candidate since tgui_alerts may have delayed us to this point
+ continue
+ if(cur_candidate == hugger)
+ continue // They were already asked
+ cur_candidate.ManualFollow(affected_mob)
+ if(cur_candidate.client.prefs?.toggles_flashing & FLASH_POOLSPAWN)
+ window_flash(cur_candidate.client)
+ if(tgui_alert(cur_candidate, "An unnested host is about to burst! Do you want to control the new larva?", "Larva maturation", list("Yes", "No"), 10 SECONDS) == "Yes")
+ picked = cur_candidate
+ candidates -= cur_candidate
+ message_alien_candidates(candidates, dequeued = 0)
+ break
// Spawn the larva
var/mob/living/carbon/xenomorph/larva/new_xeno
@@ -211,6 +260,8 @@
if(new_xeno.client)
new_xeno.client.change_view(world_view_size)
+ if(new_xeno.client.prefs?.toggles_flashing & FLASH_POOLSPAWN)
+ window_flash(new_xeno.client)
SSround_recording.recorder.track_player(new_xeno)
@@ -231,7 +282,7 @@
to_chat(observer, SPAN_DEADSAY("A [new_xeno.hive.prefix]Larva is about to chestburst out of [affected_mob]! [OBSERVER_JMP(observer, affected_mob)]"))
to_chat(src, SPAN_DEADSAY("A [new_xeno.hive.prefix]Larva is about to chestburst out of [affected_mob]!"))
- stage = 6
+ stage = 7 // Begin the autoburst countdown
/mob/living/carbon/xenomorph/larva/proc/cause_unbearable_pain(mob/living/carbon/victim)
if(loc != victim)
@@ -286,8 +337,8 @@
if(larva_embryo.client)
larva_embryo.set_lighting_alpha_from_prefs(larva_embryo.client)
- larva_embryo.attack_log += "\[[time_stamp()]\] chestbursted from [key_name(victim)]"
- victim.attack_log += "\[[time_stamp()]\] Was chestbursted, larva was [key_name(larva_embryo)]"
+ larva_embryo.attack_log += "\[[time_stamp()]\] chestbursted from [key_name(victim)] in [get_area_name(larva_embryo)] at X[victim.x], Y[victim.y], Z[victim.z]"
+ victim.attack_log += "\[[time_stamp()]\] Was chestbursted in [get_area_name(larva_embryo)] at X[victim.x], Y[victim.y], Z[victim.z]. The larva was [key_name(larva_embryo)]."
if(burstcount)
step(larva_embryo, pick(cardinal))
@@ -308,7 +359,7 @@
if(!victim.first_xeno)
to_chat(larva_embryo, SPAN_XENOHIGHDANGER("The Queen's will overwhelms your instincts..."))
to_chat(larva_embryo, SPAN_XENOHIGHDANGER("\"[hive.hive_orders]\""))
- log_attack("[key_name(victim)] chestbursted, the larva was [key_name(larva_embryo)].") //this is so that admins are not spammed with los logs
+ log_attack("[key_name(victim)] chestbursted in [get_area_name(larva_embryo)] at X[victim.x], Y[victim.y], Z[victim.z]. The larva was [key_name(larva_embryo)].") //this is so that admins are not spammed with los logs
for(var/obj/item/alien_embryo/AE in victim)
qdel(AE)
diff --git a/code/modules/mob/living/carbon/xenomorph/Evolution.dm b/code/modules/mob/living/carbon/xenomorph/Evolution.dm
index 4806f7528582..d8767ffce87a 100644
--- a/code/modules/mob/living/carbon/xenomorph/Evolution.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Evolution.dm
@@ -39,7 +39,7 @@
if(!evolve_checks())
return
- if((!hive.living_xeno_queen) && castepick != XENO_CASTE_QUEEN && !islarva(src) && !hive.allow_no_queen_actions)
+ if((!hive.living_xeno_queen) && castepick != XENO_CASTE_QUEEN && !islarva(src) && !hive.allow_no_queen_evo)
to_chat(src, SPAN_WARNING("The Hive is shaken by the death of the last Queen. You can't find the strength to evolve."))
return
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
index a2db0a0da636..8c16f6e9ff37 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
@@ -18,7 +18,7 @@
if(SSticker.mode && SSticker.mode.xenomorphs.len) //Send to only xenos in our gamemode list. This is faster than scanning all mobs
for(var/datum/mind/L in SSticker.mode.xenomorphs)
var/mob/living/carbon/M = L.current
- if(M && istype(M) && !M.stat && M.client && (!hivenumber || M.ally_of_hivenumber(hivenumber))) //Only living and connected xenos
+ if(M && istype(M) && !M.stat && M.client && (!hivenumber || M.hivenumber == hivenumber)) //Only living and connected xenos
to_chat(M, SPAN_XENODANGER(" [message]"))
//Sends a maptext alert to our currently selected squad. Does not make sound.
@@ -71,7 +71,7 @@
if(caste && caste.evolution_allowed)
evolve_progress = "[min(stored_evolution, evolution_threshold)]/[evolution_threshold]"
- if(hive && !hive.allow_no_queen_actions && !caste?.evolve_without_queen)
+ if(hive && !hive.allow_no_queen_evo && !caste?.evolve_without_queen)
if(!hive.living_xeno_queen)
evolve_progress += " (NO QUEEN)"
else if(!(hive.living_xeno_queen.ovipositor || hive.evolution_without_ovipositor))
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoUpgrade.dm b/code/modules/mob/living/carbon/xenomorph/XenoUpgrade.dm
index a89a3ad32e85..ae03d0646657 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoUpgrade.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoUpgrade.dm
@@ -3,6 +3,7 @@
return
age = XENO_NORMAL
+ var/hours_as_xeno = client.get_total_xeno_playtime()
var/hours_as_caste = get_job_playtime(client, caste.caste_type)
switch(hours_as_caste)
@@ -15,11 +16,16 @@
if(JOB_PLAYTIME_TIER_4 to INFINITY)
age = XENO_PRIME
+ if(hours_as_xeno < JOB_PLAYTIME_TIER_1)
+ age = XENO_YOUNG
+
// For people who wish to remain anonymous
if(!client.prefs.playtime_perks)
age = XENO_NORMAL
switch(age)
+ if(XENO_YOUNG)
+ age_prefix = "Young "
if(XENO_NORMAL)
age_prefix = ""
if(XENO_MATURE)
diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
index 7cd5c6ce2dd8..66174021890b 100644
--- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
@@ -1125,3 +1125,8 @@
SPAN_WARNING("You squeeze and scuttle underneath [current_structure]."), max_distance = 5)
forceMove(current_structure.loc)
return TRUE
+
+/mob/living/carbon/xenomorph/knocked_down_callback()
+ . = ..()
+ if(!resting) // !resting because we dont wanna prematurely update wounds if they're just trying to rest
+ update_wounds()
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm
index 9f5de1c631cd..91bda707ec45 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/facehugger/facehugger_abilities.dm
@@ -12,7 +12,7 @@
knockdown = TRUE
knockdown_duration = 0.5
windup = TRUE
- windup_duration = 10
+ windup_duration = FACEHUGGER_WINDUP_DURATION
freeze_self = TRUE
freeze_time = 5
freeze_play_sound = FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
index a999836c00db..a9cffb196cb3 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
@@ -506,7 +506,9 @@
var/mob/living/carbon/xenomorph/xeno = owner
if(!xeno.check_state(TRUE))
return
- if (!action_cooldown_check())
+ if(!action_cooldown_check())
+ return
+ if(xeno.action_busy)
return
if(xeno.layer != XENO_HIDING_LAYER)
xeno.layer = XENO_HIDING_LAYER
diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
index 246e2d2809db..8e64afa3f733 100644
--- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm
@@ -982,3 +982,12 @@
var/matrix/A = matrix()
apply_transform(A)
stat &= ~BROKEN //Remove broken. MAGICAL REPAIRS
+
+//Misc
+/obj/structure/prop/invuln/joey/attack_alien(mob/living/carbon/xenomorph/alien)
+ alien.animation_attack_on(src)
+ alien.visible_message(SPAN_DANGER("[alien] [alien.slashes_verb] [src]!"), \
+ SPAN_DANGER("You [alien.slash_verb] [src]!"), null, 5)
+ playsound(loc, "alien_claw_metal", 25, 1)
+ attacked()
+ return XENO_ATTACK_ACTION
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index d82e43db2aaf..ac03389994c9 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -133,7 +133,7 @@
to_chat(src, SPAN_WARNING("You can't infect \the [human]..."))
return
visible_message(SPAN_WARNING("\The [src] starts climbing onto \the [human]'s face..."), SPAN_XENONOTICE("You start climbing onto \the [human]'s face..."))
- if(!do_after(src, 6 SECONDS, INTERRUPT_ALL, BUSY_ICON_HOSTILE, human, INTERRUPT_MOVED, BUSY_ICON_HOSTILE))
+ if(!do_after(src, FACEHUGGER_WINDUP_DURATION, INTERRUPT_ALL, BUSY_ICON_HOSTILE, human, INTERRUPT_MOVED, BUSY_ICON_HOSTILE))
return
if(!human.lying)
to_chat(src, SPAN_WARNING("You can't reach \the [human], they need to be lying down."))
@@ -149,7 +149,7 @@
/mob/living/carbon/xenomorph/facehugger/proc/handle_hug(mob/living/carbon/human/human)
var/obj/item/clothing/mask/facehugger/hugger = new /obj/item/clothing/mask/facehugger(loc, hivenumber)
- var/did_hug = hugger.attach(human, TRUE, 0.5, src)
+ var/did_hug = hugger.attach(human, TRUE, 1, src)
if(client)
client.player_data?.adjust_stat(PLAYER_STAT_FACEHUGS, STAT_CATEGORY_XENO, 1)
qdel(src)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
index f3a354b42ad0..8a7425e2071a 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
@@ -68,6 +68,7 @@
/mob/hologram/queen
name = "Queen Eye"
action_icon_state = "queen_exit"
+ motion_sensed = TRUE
color = "#a800a8"
@@ -412,17 +413,24 @@
if(queen_aged)
age_xeno()
switch(age)
- if(XENO_NORMAL) name = "[name_prefix]Queen" //Young
- if(XENO_MATURE) name = "[name_prefix]Elder Queen" //Mature
- if(XENO_ELDER) name = "[name_prefix]Elder Empress" //Elite
- if(XENO_ANCIENT) name = "[name_prefix]Ancient Empress" //Ancient
- if(XENO_PRIME) name = "[name_prefix]Prime Empress" //Primordial
+ if(XENO_YOUNG)
+ name = "[name_prefix]Young Queen" //Young
+ if(XENO_NORMAL)
+ name = "[name_prefix]Queen" //Regular
+ if(XENO_MATURE)
+ name = "[name_prefix]Elder Queen" //Mature
+ if(XENO_ELDER)
+ name = "[name_prefix]Elder Empress" //Elite
+ if(XENO_ANCIENT)
+ name = "[name_prefix]Ancient Empress" //Ancient
+ if(XENO_PRIME)
+ name = "[name_prefix]Prime Empress" //Primordial
else
age = XENO_NORMAL
if(client)
hud_update()
- name = "[name_prefix]Young Queen"
+ name = "[name_prefix]Immature Queen"
var/name_client_prefix = ""
var/name_client_postfix = ""
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
index 69e5b82aa307..f946ec44b5b7 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm
@@ -71,7 +71,7 @@
/mob/living/carbon/xenomorph/runner/initialize_pass_flags(datum/pass_flags_container/PF)
..()
if (PF)
- PF.flags_pass = PASS_FLAGS_CRAWLER
+ PF.flags_pass |= PASS_FLAGS_CRAWLER
/datum/behavior_delegate/runner_base
name = "Base Runner Behavior Delegate"
diff --git a/code/modules/mob/living/carbon/xenomorph/damage_procs.dm b/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
index c26e38202200..51ceee153368 100644
--- a/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/damage_procs.dm
@@ -29,6 +29,8 @@
user.put_in_hands(iff_tag)
iff_tag = null
user.visible_message(SPAN_NOTICE("[user] removes \the [src]'s IFF tag."), SPAN_NOTICE("You remove \the [src]'s IFF tag."), max_distance = 3)
+ if(hive.hivenumber == XENO_HIVE_RENEGADE) //it's important to know their IFF settings for renegade
+ to_chat(src, SPAN_NOTICE("With the removal of the device, your instincts have returned to normal."))
return
return ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm
index 12c9b3e37c9b..56f9460f5c71 100644
--- a/code/modules/mob/living/carbon/xenomorph/death.dm
+++ b/code/modules/mob/living/carbon/xenomorph/death.dm
@@ -69,10 +69,11 @@
if(!QDELETED(Q) && Q != src && Q.hivenumber == hivenumber)
hive.set_living_xeno_queen(Q)
break
+ hive.on_queen_death()
hive.handle_xeno_leader_pheromones()
if(SSticker.mode)
INVOKE_ASYNC(SSticker.mode, TYPE_PROC_REF(/datum/game_mode, check_queen_status), hivenumber)
- LAZYADD(SSticker.mode.dead_queens, " [!isnull(src.key) ? src.key : "?"] was [src] [SPAN_BOLDNOTICE("(DIED)")]")
+ LAZYADD(SSticker.mode.dead_queens, " [!isnull(full_designation) ? full_designation : "?"] was [src] [SPAN_BOLDNOTICE("(DIED)")]")
else if(ispredalien(src))
playsound(loc,'sound/voice/predalien_death.ogg', 25, TRUE)
diff --git a/code/modules/mob/living/carbon/xenomorph/hive_faction.dm b/code/modules/mob/living/carbon/xenomorph/hive_faction.dm
index 1eb5818674fd..e16a5cccd915 100644
--- a/code/modules/mob/living/carbon/xenomorph/hive_faction.dm
+++ b/code/modules/mob/living/carbon/xenomorph/hive_faction.dm
@@ -56,5 +56,5 @@ GLOBAL_LIST_INIT(hive_alliable_factions, generate_alliable_factions())
return
var/should_ally = text2num(params["should_ally"])
- assoc_hive.allies[params["target_faction"]] = should_ally
+ assoc_hive.change_stance(params["target_faction"], should_ally)
. = TRUE
diff --git a/code/modules/mob/living/carbon/xenomorph/items/iff_tag.dm b/code/modules/mob/living/carbon/xenomorph/items/iff_tag.dm
index cde9cd072a95..4c63fc2b5923 100644
--- a/code/modules/mob/living/carbon/xenomorph/items/iff_tag.dm
+++ b/code/modules/mob/living/carbon/xenomorph/items/iff_tag.dm
@@ -19,6 +19,8 @@
injector.visible_message(SPAN_NOTICE("[injector] forces \the [src] into [xeno]'s carapace!"), SPAN_NOTICE("You force \the [src] into [xeno]'s carapace!"))
xeno.iff_tag = src
injector.drop_inv_item_to_loc(src, xeno)
+ if(xeno.hive.hivenumber == XENO_HIVE_RENEGADE) //it's important to know their IFF settings for renegade
+ to_chat(xeno, SPAN_NOTICE("With the insertion of the device into your carapace, your instincts have changed compelling you to protect [english_list(faction_groups, "no one")]."))
return
return ..()
@@ -48,6 +50,8 @@
if("Remove")
faction_groups = list()
to_chat(programmer, SPAN_NOTICE("You [option] the IFF group data, the IFF group on the tag now reads as: [english_list(faction_groups, "None")]"))
+ if(xeno?.hive.hivenumber == XENO_HIVE_RENEGADE) //it's important to know their IFF settings for renegade
+ to_chat(xeno, SPAN_NOTICE("Your instincts have changed, you seem compelled to protect [english_list(faction_groups, "no one")]."))
return TRUE
/obj/item/iff_tag/pmc_handler
diff --git a/code/modules/mob/living/carbon/xenomorph/life.dm b/code/modules/mob/living/carbon/xenomorph/life.dm
index 842e3b45a76d..6f9a667642e3 100644
--- a/code/modules/mob/living/carbon/xenomorph/life.dm
+++ b/code/modules/mob/living/carbon/xenomorph/life.dm
@@ -42,7 +42,7 @@
var/progress_amount = 1
if(SSxevolution)
progress_amount = SSxevolution.get_evolution_boost_power(hive.hivenumber)
- var/ovipositor_check = (hive.allow_no_queen_actions || hive.evolution_without_ovipositor || (hive.living_xeno_queen && hive.living_xeno_queen.ovipositor))
+ var/ovipositor_check = (hive.allow_no_queen_evo || hive.evolution_without_ovipositor || (hive.living_xeno_queen && hive.living_xeno_queen.ovipositor))
if(caste && caste.evolution_allowed && (ovipositor_check || caste?.evolve_without_queen))
if(evolution_stored >= evolution_threshold)
if(!got_evolution_message)
@@ -334,11 +334,6 @@ Make sure their actual health updates immediately.*/
if(!T || !istype(T))
return
- var/is_runner_hiding
-
- if(isrunner(src) && layer != initial(layer))
- is_runner_hiding = 1
-
if(caste)
if(caste.innate_healing || check_weeds_for_healing())
if(!hive) return // can't heal if you have no hive, sorry bud
@@ -369,9 +364,8 @@ Make sure their actual health updates immediately.*/
if(armor_integrity > armor_integrity_max)
armor_integrity = armor_integrity_max
- else //Xenos restore plasma VERY slowly off weeds, regardless of health, as long as they are not using special abilities
- if(prob(50) && !is_runner_hiding && !current_aura)
- plasma_stored += 0.1 * plasma_max / 100
+ else if(prob(50) && !current_aura) //Xenos restore plasma VERY slowly off weeds, regardless of health, as long as they are not using special abilities
+ plasma_stored += 0.1 * plasma_max / 100
for(var/datum/action/xeno_action/action in src.actions)
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
index 7a2196a3c209..7b64ae9f69d7 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
@@ -159,7 +159,12 @@
new /obj/effect/particle_effect/smoke/acid_runner_harmless(T)
playsound(bound_xeno, 'sound/effects/blobattack.ogg', 75)
if(bound_xeno.client && bound_xeno.hive)
- addtimer(CALLBACK(bound_xeno.hive, TYPE_PROC_REF(/datum/hive_status, free_respawn), bound_xeno.client), 5 SECONDS)
+ var/datum/hive_status/hive_status = bound_xeno.hive
+ var/turf/spawning_turf = get_turf(bound_xeno)
+ if(!hive_status.hive_location)
+ addtimer(CALLBACK(bound_xeno.hive, TYPE_PROC_REF(/datum/hive_status, respawn_on_turf), bound_xeno.client, spawning_turf), 0.5 SECONDS)
+ else
+ addtimer(CALLBACK(bound_xeno.hive, TYPE_PROC_REF(/datum/hive_status, free_respawn), bound_xeno.client), 5 SECONDS)
bound_xeno.gib()
/mob/living/carbon/xenomorph/runner/ventcrawl_carry()
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
index 69fd2793c406..c2bf91edde6e 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
@@ -295,8 +295,9 @@
var/evolution_bonus = 0
var/allow_no_queen_actions = FALSE
+ var/allow_no_queen_evo = FALSE
var/evolution_without_ovipositor = TRUE //Temporary for the roundstart.
- /// Set to true if you want to prevent evolutions into Queens
+ /// Set to false if you want to prevent evolutions into Queens
var/allow_queen_evolve = TRUE
/// Set to true if you want to prevent bursts and spawns of new xenos. Will also prevent healing if the queen no longer exists
var/hardcore = FALSE
@@ -316,6 +317,7 @@
XENO_STRUCTURE_EGGMORPH = 6,
XENO_STRUCTURE_EVOPOD = 2,
XENO_STRUCTURE_RECOVERY = 6,
+ XENO_STRUCTURE_PYLON = 2,
)
var/global/list/hive_structure_types = list(
@@ -352,6 +354,8 @@
/// How many lesser drones the hive can support
var/lesser_drone_limit = 0
+ /// Slots available for lesser drones will never go below this number
+ var/lesser_drone_minimum = 3
var/datum/tacmap/xeno/tacmap
var/minimap_type = MINIMAP_FLAG_XENO
@@ -367,17 +371,25 @@
if(hivenumber != XENO_HIVE_NORMAL)
return
- RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(setup_evolution_announcements))
+ RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(post_setup))
-/datum/hive_status/proc/setup_evolution_announcements()
+/datum/hive_status/proc/post_setup()
SIGNAL_HANDLER
+ setup_evolution_announcements()
+ setup_pylon_limits()
+
+/datum/hive_status/proc/setup_evolution_announcements()
for(var/time in GLOB.xeno_evolve_times)
if(time == "0")
continue
addtimer(CALLBACK(src, PROC_REF(announce_evolve_available), GLOB.xeno_evolve_times[time]), text2num(time))
+/// Sets up limits on pylons in New() for potential futureproofing with more static comms
+/datum/hive_status/proc/setup_pylon_limits()
+ hive_structures_limit[XENO_STRUCTURE_PYLON] = length(GLOB.all_static_telecomms_towers) || 2
+
/datum/hive_status/proc/announce_evolve_available(list/datum/caste_datum/available_castes)
var/list/castes_available = list()
@@ -880,6 +892,7 @@
for(var/obj/effect/alien/resin/special/S in hive_structures[name_ref])
if(get_area(S) == hijacked_dropship)
continue
+ S.hijack_delete = TRUE
hive_structures[name_ref] -= S
qdel(S)
for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos)
@@ -917,10 +930,14 @@
embryo.hivenumber = XENO_HIVE_FORSAKEN
potential_host.update_med_icon()
for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list)
- if((isspecieshuman(current_human) || isspeciessynth(current_human)) && current_human.job)
- var/turf/turf = get_turf(current_human)
- if(is_mainship_level(turf?.z))
- shipside_humans_weighted_count += RoleAuthority.calculate_role_weight(current_human.job)
+ if(!(isspecieshuman(current_human) || isspeciessynth(current_human)))
+ continue
+ var/datum/job/job = RoleAuthority.roles_for_mode[current_human.job]
+ if(!job)
+ continue
+ var/turf/turf = get_turf(current_human)
+ if(is_mainship_level(turf?.z))
+ shipside_humans_weighted_count += RoleAuthority.calculate_role_weight(job)
hijack_burrowed_surge = TRUE
hijack_burrowed_left = max(n_ceil(shipside_humans_weighted_count * 0.5) - xenos_count, 5)
hivecore_cooldown = FALSE
@@ -933,6 +950,24 @@
else
hive_ui.update_burrowed_larva()
+/datum/hive_status/proc/respawn_on_turf(client/xeno_client, turf/spawning_turf)
+ var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_hivenumber_larva(spawning_turf, hivenumber)
+ if(isnull(new_xeno))
+ return FALSE
+
+ if(!SSticker.mode.transfer_xeno(xeno_client.mob, new_xeno))
+ qdel(new_xeno)
+ return FALSE
+
+ new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly emerges from a dead husk!"),
+ SPAN_XENOANNOUNCE("The hive has no core! You manage to emerge from your old husk as a larva!"))
+ msg_admin_niche("[key_name(new_xeno)] respawned at \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]")
+ playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1)
+ if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN)
+ window_flash(new_xeno.client)
+
+ hive_ui.update_burrowed_larva()
+
/datum/hive_status/proc/do_buried_larva_spawn(mob/xeno_candidate)
var/spawning_area
if(hive_location)
@@ -1019,6 +1054,10 @@
//This is to prevent people from joining as Forsaken Huggers on the pred ship
to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!"))
return FALSE
+ for(var/mob_name in banished_ckeys)
+ if(banished_ckeys[mob_name] == user.ckey)
+ to_chat(user, SPAN_WARNING("You are banished from the [name], you may not rejoin unless the Queen re-admits you or dies."))
+ return FALSE
update_hugger_limit()
@@ -1044,9 +1083,10 @@
hugger.visible_message(SPAN_XENODANGER("A facehugger suddenly emerges out of \the [A]!"), SPAN_XENODANGER("You emerge out of \the [A] and awaken from your slumber. For the Hive!"))
playsound(hugger, 'sound/effects/xeno_newlarva.ogg', 25, TRUE)
hugger.generate_name()
+ hugger.timeofdeath = user.timeofdeath // Keep old death time
/datum/hive_status/proc/update_lesser_drone_limit()
- lesser_drone_limit = Ceiling(totalXenos.len / 3)
+ lesser_drone_limit = lesser_drone_minimum + Ceiling(length(totalXenos) / 3)
/datum/hive_status/proc/can_spawn_as_lesser_drone(mob/dead/observer/user)
if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber])
@@ -1080,11 +1120,11 @@
if(islesserdrone(mob))
current_lesser_drone_count++
- if(lesser_drone_limit <= current_lesser_drone_count)
- to_chat(user, SPAN_WARNING("[GLOB.hive_datum[hivenumber]] cannot support more lesser drones! Limit: [current_lesser_drone_count]/[lesser_drone_limit]"))
+ if(tgui_alert(user, "Are you sure you want to become a lesser drone?", "Confirmation", list("Yes", "No")) != "Yes")
return FALSE
- if(tgui_alert(user, "Are you sure you want to become a lesser drone?", "Confirmation", list("Yes", "No")) != "Yes")
+ if(lesser_drone_limit <= current_lesser_drone_count)
+ to_chat(user, SPAN_WARNING("[GLOB.hive_datum[hivenumber]] cannot support more lesser drones! Limit: [current_lesser_drone_count]/[lesser_drone_limit]"))
return FALSE
if(!user.client)
@@ -1096,6 +1136,10 @@
/datum/hive_status/proc/increase_larva_after_burst()
var/extra_per_burst = CONFIG_GET(number/extra_larva_per_burst)
partial_larva += extra_per_burst
+ convert_partial_larva_to_full_larva()
+
+///Called after times when partial larva are added to process them to stored larva
+/datum/hive_status/proc/convert_partial_larva_to_full_larva()
for(var/i = 1 to partial_larva)
partial_larva--
stored_larva++
@@ -1111,13 +1155,15 @@
need_round_end_check = TRUE
-/datum/hive_status/corrupted/add_xeno(mob/living/carbon/xenomorph/X)
+ var/list/defectors = list()
+
+/datum/hive_status/corrupted/add_xeno(mob/living/carbon/xenomorph/xeno)
. = ..()
- X.add_language(LANGUAGE_ENGLISH)
+ xeno.add_language(LANGUAGE_ENGLISH)
-/datum/hive_status/corrupted/remove_xeno(mob/living/carbon/xenomorph/X, hard)
+/datum/hive_status/corrupted/remove_xeno(mob/living/carbon/xenomorph/xeno, hard)
. = ..()
- X.remove_language(LANGUAGE_ENGLISH)
+ xeno.remove_language(LANGUAGE_ENGLISH)
/datum/hive_status/corrupted/can_delay_round_end(mob/living/carbon/xenomorph/xeno)
if(!faction_is_ally(FACTION_MARINE, TRUE))
@@ -1180,6 +1226,7 @@
destruction_allowed = XENO_NOBODY
dynamic_evolution = FALSE
allow_no_queen_actions = TRUE
+ allow_no_queen_evo = TRUE
allow_queen_evolve = FALSE
ignore_slots = TRUE
latejoin_burrowed = FALSE
@@ -1194,6 +1241,7 @@
dynamic_evolution = FALSE
allow_no_queen_actions = TRUE
+ allow_no_queen_evo = TRUE
allow_queen_evolve = FALSE
ignore_slots = TRUE
latejoin_burrowed = FALSE
@@ -1211,6 +1259,7 @@
dynamic_evolution = FALSE
allow_no_queen_actions = TRUE
+ allow_no_queen_evo = TRUE
allow_queen_evolve = FALSE
ignore_slots = TRUE
latejoin_burrowed = FALSE
@@ -1240,6 +1289,7 @@
dynamic_evolution = FALSE
allow_no_queen_actions = TRUE
+ allow_no_queen_evo = TRUE
allow_queen_evolve = FALSE
ignore_slots = TRUE
latejoin_burrowed = FALSE
@@ -1295,6 +1345,145 @@
return ..()
+/datum/hive_status/corrupted/renegade
+ name = "Renegade Hive"
+ reporting_id = "renegade"
+ hivenumber = XENO_HIVE_RENEGADE
+ prefix = "Renegade "
+ color = "#9c7a4d"
+ ui_color ="#80705c"
+
+ dynamic_evolution = FALSE
+ allow_queen_evolve = FALSE
+ allow_no_queen_evo = TRUE
+ latejoin_burrowed = FALSE
+
+/datum/hive_status/corrupted/renegade/New()
+ . = ..()
+ hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0
+ hive_structures_limit[XENO_STRUCTURE_EVOPOD] = 0
+ for(var/faction in FACTION_LIST_HUMANOID) //renegades allied to all humanoids, but it mostly affects structures. Their ability to attack humanoids and other xenos (including of the same hive) depends on iff settings
+ allies[faction] = TRUE
+
+/datum/hive_status/corrupted/renegade/can_spawn_as_hugger(mob/dead/observer/user)
+ to_chat(user, SPAN_WARNING("The [name] cannot support facehuggers."))
+ return FALSE
+
+/datum/hive_status/corrupted/renegade/proc/iff_protection_check(mob/living/carbon/xenomorph/xeno, mob/living/carbon/attempt_harm_mob)
+ if(xeno == attempt_harm_mob)
+ return TRUE //you cannot hurt yourself...
+ if(!xeno.iff_tag)
+ return FALSE //can attack anyone if you don't have iff tag
+ if(isxeno(attempt_harm_mob))
+ var/mob/living/carbon/xenomorph/target_xeno = attempt_harm_mob
+ if(!target_xeno.iff_tag)
+ return FALSE //can attack any xeno who don't have iff tag
+ for(var/faction in xeno.iff_tag.faction_groups)
+ if(faction in target_xeno.iff_tag.faction_groups)
+ return TRUE //cannot attack xenos with same iff setting
+ return FALSE
+ for(var/faction in xeno.iff_tag.faction_groups)
+ if(faction in attempt_harm_mob.faction_group)
+ return TRUE //cannot attack mob if iff is set to at least one of its factions
+ return FALSE
+
+/datum/hive_status/corrupted/renegade/faction_is_ally(faction, ignore_queen_check = TRUE)
+ return ..()
+
+/datum/hive_status/proc/on_queen_death() //break alliances on queen's death
+ if(allow_no_queen_actions || living_xeno_queen)
+ return
+ var/broken_alliances = FALSE
+ for(var/faction in allies)
+ if(!allies[faction])
+ continue
+ change_stance(faction, FALSE)
+ broken_alliances = TRUE
+
+
+ if(broken_alliances)
+ xeno_message(SPAN_XENOANNOUNCE("With the death of the Queen, all alliances have been broken."), 3, hivenumber)
+
+/datum/hive_status/proc/change_stance(faction, should_ally)
+ if(faction == name)
+ return
+ if(allies[faction] == should_ally)
+ return
+ allies[faction] = should_ally
+
+ if(living_xeno_queen)
+ if(allies[faction])
+ xeno_message(SPAN_XENOANNOUNCE("Your Queen set up an alliance with [faction]!"), 3, hivenumber)
+ else
+ xeno_message(SPAN_XENOANNOUNCE("Your Queen broke the alliance with [faction]!"), 3, hivenumber)
+
+ for(var/number in GLOB.hive_datum)
+ var/datum/hive_status/target_hive = GLOB.hive_datum[number]
+ if(target_hive.name != faction)
+ continue
+ if(!target_hive.living_xeno_queen && !target_hive.allow_no_queen_actions)
+ return
+ if(allies[faction])
+ xeno_message(SPAN_XENOANNOUNCE("You sense that [name] [living_xeno_queen ? "Queen " : ""]set up an alliance with us!"), 3, target_hive.hivenumber)
+ return
+
+ xeno_message(SPAN_XENOANNOUNCE("You sense that [name] [living_xeno_queen ? "Queen " : ""]broke the alliance with us!"), 3, target_hive.hivenumber)
+ if(target_hive.allies[name]) //autobreak alliance on betrayal
+ target_hive.change_stance(name, FALSE)
+
+
+/datum/hive_status/corrupted/change_stance(faction, should_ally)
+ . = ..()
+ if(allies[faction])
+ return
+ if(!(faction in FACTION_LIST_HUMANOID))
+ return
+
+ for(var/mob/living/carbon/xenomorph/xeno in totalXenos) // handle defecting xenos on betrayal
+ if(!xeno.iff_tag)
+ continue
+ if(!(faction in xeno.iff_tag.faction_groups))
+ continue
+ if(xeno in defectors)
+ continue
+ if(xeno.caste_type == XENO_CASTE_QUEEN)
+ continue
+ INVOKE_ASYNC(src, PROC_REF(give_defection_choice), xeno, faction)
+ addtimer(CALLBACK(src, PROC_REF(handle_defectors), faction), 11 SECONDS)
+
+/datum/hive_status/corrupted/proc/give_defection_choice(mob/living/carbon/xenomorph/xeno, faction)
+ if(tgui_alert(xeno, "Your Queen has broken the alliance with the [faction]. The device inside your carapace begins to suppress your connection with the Hive. Do you remove it and stay loyal to her?", "Alliance broken!", list("Stay loyal", "Obey the talls"), 10 SECONDS) == "Obey the talls")
+ if(!xeno.iff_tag)
+ to_chat(xeno, SPAN_XENOWARNING("It's too late now. The device is gone and your service to the Queen continues."))
+ return
+ defectors += xeno
+ xeno.set_hive_and_update(XENO_HIVE_RENEGADE)
+ to_chat(xeno, SPAN_XENOANNOUNCE("You lost the connection with your Hive. Now you have no Queen, only your masters."))
+ to_chat(xeno, SPAN_NOTICE("Your instincts have changed, you seem compelled to protect [english_list(xeno.iff_tag.faction_groups, "no one")]."))
+ return
+ xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("You rip out [xeno.iff_tag]! For the Hive!"))
+ xeno.adjustBruteLoss(50)
+ xeno.iff_tag.forceMove(get_turf(xeno))
+ xeno.iff_tag = null
+
+/datum/hive_status/corrupted/proc/handle_defectors(faction)
+ for(var/mob/living/carbon/xenomorph/xeno in totalXenos)
+ if(!xeno.iff_tag)
+ continue
+ if(xeno in defectors)
+ continue
+ if(!(faction in xeno.iff_tag.faction_groups))
+ continue
+ xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("You rip out [xeno.iff_tag]! For the hive!"))
+ xeno.adjustBruteLoss(50)
+ xeno.iff_tag.forceMove(get_turf(xeno))
+ xeno.iff_tag = null
+ if(!length(defectors))
+ return
+
+ xeno_message(SPAN_XENOANNOUNCE("You sense that [english_list(defectors)] turned their backs against their sisters and the Queen in favor of their slavemasters!"), 3, hivenumber)
+ defectors.Cut()
+
//Xeno Resin Mark Shit, the very best place for it too :0)
//Defines at the bottom of this list here will show up at the top in the mark menu
/datum/xeno_mark_define
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
index 6b37145ad7a1..583d26de3ee5 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm
@@ -25,6 +25,13 @@
if(!hive)
return
+ if(hive.hivenumber == XENO_HIVE_RENEGADE) //Renegade's ability to attack someone depends on IFF settings, not on alliance
+ if(!iff_tag)
+ to_chat(src, SPAN_NOTICE("You are not obligated to protect anyone."))
+ return
+ to_chat(src, SPAN_NOTICE("You seem compelled to protect [english_list(iff_tag.faction_groups, "no one")]."))
+ return
+
if((!hive.living_xeno_queen || Check_WO()) && !hive.allow_no_queen_actions) //No Hive status on WO
to_chat(src, SPAN_WARNING("There is no Queen. You are alone."))
return
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 0c182fb0fe9c..06df71427e4f 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -594,8 +594,8 @@ below 100 is not dizzy
if(!istype(src, /mob/living/carbon/human)) // for the moment, only humans get dizzy
return
- dizziness = min(1000, dizziness + amount) // store what will be new value
- // clamped to max 1000
+ dizziness = min(500, dizziness + amount) // store what will be new value
+ // clamped to max 500
if(dizziness > 100 && !is_dizzy)
INVOKE_ASYNC(src, PROC_REF(dizzy_process))
@@ -609,16 +609,22 @@ note dizziness decrements automatically in the mob's Life() proc.
is_dizzy = 1
while(dizziness > 100)
if(client)
- var/amplitude = dizziness*(sin(dizziness * 0.044 * world.time) + 1) / 70
- client.pixel_x = amplitude * sin(0.008 * dizziness * world.time)
- client.pixel_y = amplitude * cos(0.008 * dizziness * world.time)
-
+ if(buckled || resting)
+ client.pixel_x = 0
+ client.pixel_y = 0
+ else
+ var/amplitude = dizziness*(sin(dizziness * 0.044 * world.time) + 1) / 70
+ client.pixel_x = amplitude * sin(0.008 * dizziness * world.time)
+ client.pixel_y = amplitude * cos(0.008 * dizziness * world.time)
+ if(prob(1))
+ to_chat(src, "The dizziness is becoming unbearable! It should pass faster if you lie down.")
sleep(1)
//endwhile - reset the pixel offsets to zero
is_dizzy = 0
if(client)
client.pixel_x = 0
client.pixel_y = 0
+ to_chat(src, "The dizziness has passed, you're starting to feel better.")
// jitteriness - copy+paste of dizziness
diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm
index f8c86f9c5171..1839fe191627 100644
--- a/code/modules/mob/new_player/preferences_setup.dm
+++ b/code/modules/mob/new_player/preferences_setup.dm
@@ -245,6 +245,8 @@
return /datum/equipment_preset/uscm_ship/so
if(JOB_XO)
return /datum/equipment_preset/uscm_ship/xo
+ if(JOB_AUXILIARY_OFFICER)
+ return /datum/equipment_preset/uscm_ship/auxiliary_officer
if(JOB_INTEL)
return /datum/equipment_preset/uscm/intel/full
if(JOB_PILOT)
@@ -277,7 +279,7 @@
if(JOB_MAINT_TECH)
return /datum/equipment_preset/uscm_ship/maint
if(JOB_CHIEF_REQUISITION)
- return /datum/equipment_preset/uscm_ship/ro
+ return /datum/equipment_preset/uscm_ship/qm
if(JOB_CARGO_TECH)
return /datum/equipment_preset/uscm_ship/cargo
if(JOB_CMO)
diff --git a/code/modules/organs/organ_internal.dm b/code/modules/organs/organ_internal.dm
index 1038f2a86e8a..2178e2d53f5c 100644
--- a/code/modules/organs/organ_internal.dm
+++ b/code/modules/organs/organ_internal.dm
@@ -247,6 +247,21 @@
robotic_type = /obj/item/organ/brain/prosthetic
vital = 1
+/datum/internal_organ/brain/process(delta_time)
+ ..()
+
+ if(organ_status >= ORGAN_BRUISED && prob(5 * delta_time))
+ var/dir_choice = pick(list(NORTH, SOUTH, EAST, WEST))
+ owner.drop_held_items()
+ owner.Move(get_step(get_turf(owner), dir_choice))
+ to_chat(owner, SPAN_DANGER("Your mind wanders and goes blank for a moment..."))
+
+ if(organ_status >= ORGAN_BROKEN && prob(5 * delta_time))
+ owner.apply_effect(1, PARALYZE)
+ if(owner.jitteriness < 100)
+ owner.make_jittery(50)
+ to_chat(owner, SPAN_DANGER("Your body seizes up!"))
+
/datum/internal_organ/brain/prosthetic //used by synthetic species
robotic = ORGAN_ROBOT
removed_type = /obj/item/organ/brain/prosthetic
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 7665e0a5d7ab..3b10c757f2f4 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -256,6 +256,7 @@
t = replacetext(t, "\[logo\]", "")
t = replacetext(t, "\[wy\]", "")
t = replacetext(t, "\[uscm\]", "")
+ t = replacetext(t, "\[upp\]", "")
t = "[t]"
else // If it is a crayon, and he still tries to use these, make them empty!
@@ -606,6 +607,15 @@
color = "green"
info = "
I could not do it, the fucking marshals, the minions of THEM, have gotten a whiff of my co-workers plans and started raiding us pre-emptively. We managed to get word of it and erected a few barricades to slow them down, but it is too late. Our plan, my plan to save humanity has turned to dust.
As I lay and write this, they are gassing the entire area with tear gas, while gunshots echo around the caves. \n They have gotten to my mind already, their voices are... laughing, saying that, \" it's over \" and that \n “we have risen\". Their voices are mocking me as I could do nothing to prevent their rise \n Just as I am about to finish my final entry, I overhear a few panicked radio calls from a dead officer's radio, about a code red lambda breach, and \" X-RAYS OUT OF CONTAINMENT\". \n However, not a single one of their cries has been met with a response as their fellow officers are too preoccupied with beating up poor miners... \n They have won.... they have PLANNED THIS all along.... \n only God may save us now..."
+/obj/item/paper/bigred/upp
+ name = "UPP Orders"
+
+/obj/item/paper/bigred/upp/Initialize(mapload, photo_list)
+ . = ..()
+
+ var/datum/asset/asset = get_asset_datum(/datum/asset/simple/paper)
+ info = "
Union Of Progressive People's Fourth Fleet Orders For 173rd Airborne Reconnaissance: 2nd Platoon No.52
Order of Military Officer of the UPP Kolonel Ganbaatar Commander of MV-35 Date: 2182 On Special Mission The actions of the hostile Weyland-Yutani corporation on the fringes of the Neroid sector have grown increasingly intolerable. However, evidence suggesting they are researching into the creation and deployment of some form of biological weapons program represent an unacceptable risk to the security of UPP interests in this sector. The risk of these items falling into UA/USCM hands is unacceptable.
Orders for the Boris squad of the 173rd Airborne Recon are as follows. Initiate airborne reconnaissance of WY colony Oxley's Buttle, Trijent Dam, location on planet Raijin (UA Code: LV-670). Ascertain veracity of onsight biological weapons program. If positive confirmation of the weapons program is identified, authorization for rapid assault and recovery is granted. Avoid all contact with UA/USCM military forces, abort missions if UA/USCM forces are encountered.
Authorizing Officer: Gaanbatar Name and Rank: Kolonel