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/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/ci_suite.yml b/.github/workflows/ci_suite.yml
index a2eeca30134a..683f3909b447 100644
--- a/.github/workflows/ci_suite.yml
+++ b/.github/workflows/ci_suite.yml
@@ -8,7 +8,7 @@ jobs:
run_linters:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
name: Run Linters
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
concurrency:
group: run_linters-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
@@ -63,7 +63,7 @@ jobs:
compile_all_maps:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
name: Compile Maps
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore BYOND cache
@@ -80,7 +80,7 @@ jobs:
find_all_maps:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
name: Find Maps to Test
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
outputs:
maps: ${{ steps.map_finder.outputs.maps }}
alternate_tests: ${{ steps.alternate_test_finder.outputs.alternate_tests }}
@@ -138,7 +138,7 @@ jobs:
if: "!contains(github.event.head_commit.message, '[ci skip]') && needs.find_all_maps.outputs.alternate_tests != '[]'"
name: Check Alternate Tests
needs: [run_alternate_tests]
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- run: echo Alternate tests passed.
diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml
index 1f6e72092627..85947f08b18b 100644
--- a/.github/workflows/compile_changelogs.yml
+++ b/.github/workflows/compile_changelogs.yml
@@ -8,7 +8,7 @@ on:
jobs:
compile:
name: "Compile changelogs"
- runs-on: ubuntu-20.04
+ 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
diff --git a/.github/workflows/conflicts.yml b/.github/workflows/conflicts.yml
index 9e70bf488482..b65a5213ab4a 100644
--- a/.github/workflows/conflicts.yml
+++ b/.github/workflows/conflicts.yml
@@ -7,9 +7,11 @@ on:
types: [ready_for_review, opened, synchronize, reopened]
jobs:
triage:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- uses: eps1lon/actions-label-merge-conflict@v2.1.0
with:
dirtyLabel: 'Merge Conflict'
+ commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
+ commentOnClean: "Conflicts have been resolved. A maintainer will review the pull request shortly."
repoToken: ${{ secrets.BOT_TOKEN_CM || secrets.GITHUB_TOKEN }}
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/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml
index 59e6d1144c68..c2898dfa9c57 100644
--- a/.github/workflows/generate_documentation.yml
+++ b/.github/workflows/generate_documentation.yml
@@ -6,7 +6,7 @@ on:
jobs:
generate_documentation:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
concurrency: gen-docs
steps:
- uses: actions/checkout@v3
diff --git a/.github/workflows/run_approval.yml b/.github/workflows/run_approval.yml
index 1708d41949f4..1d452c5f8aa7 100644
--- a/.github/workflows/run_approval.yml
+++ b/.github/workflows/run_approval.yml
@@ -1,11 +1,11 @@
name: Automatic Approve Workflows
on:
- schedule:
+ schedule:
- cron: "*/10 * * * *"
jobs:
automatic-approve:
name: Automatic Approve Workflows
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- name: Automatic Approve
uses: mheap/automatic-approve-action@v1
diff --git a/.github/workflows/run_unit_tests.yml b/.github/workflows/run_unit_tests.yml
index 8c2e7421b6a5..539b0fa01082 100644
--- a/.github/workflows/run_unit_tests.yml
+++ b/.github/workflows/run_unit_tests.yml
@@ -15,7 +15,7 @@ on:
type: string
jobs:
run_unit_tests:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Restore BYOND cache
@@ -27,7 +27,7 @@ jobs:
run: |
sudo dpkg --add-architecture i386
sudo apt update || true
- sudo apt install -o APT::Immediate-Configure=false libssl1.1:i386
+ sudo apt install -o APT::Immediate-Configure=false zlib1g-dev:i386 libssl-dev:i386
bash tools/ci/install_rust_g.sh
- name: Configure version
run: |
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index fe2417ca3eb8..13cb50704387 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -7,7 +7,7 @@ on:
jobs:
stale:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
diff --git a/.github/workflows/update_changelog.yml b/.github/workflows/update_changelog.yml
index b7d8702c5cc7..0b30a268d4aa 100644
--- a/.github/workflows/update_changelog.yml
+++ b/.github/workflows/update_changelog.yml
@@ -7,7 +7,7 @@ on:
jobs:
update-changelog:
concurrency: changelog
- runs-on: ubuntu-20.04
+ 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
diff --git a/.github/workflows/update_tgs_dmapi.yml b/.github/workflows/update_tgs_dmapi.yml
index d530718d12e3..b197e62f392f 100644
--- a/.github/workflows/update_tgs_dmapi.yml
+++ b/.github/workflows/update_tgs_dmapi.yml
@@ -7,7 +7,7 @@ on:
jobs:
update-dmapi:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
name: Update the TGS DMAPI
steps:
- name: Clone
diff --git a/code/__DEFINES/ARES.dm b/code/__DEFINES/ARES.dm
index 05a42738c499..ec84a6ab5992 100644
--- a/code/__DEFINES/ARES.dm
+++ b/code/__DEFINES/ARES.dm
@@ -43,11 +43,20 @@
#define APOLLO_ACCESS_DEBUG 5
/// Ticket statuses, both for Access and Maintenance
+/// Pending assignment/rejection
#define TICKET_PENDING "pending"
+/// Assigned to a WJ
#define TICKET_ASSIGNED "assigned"
-#define TICKET_REJECTED "rejected"
+/// Cancelled by reporter
#define TICKET_CANCELLED "cancelled"
-#define TICKET_COMPLETED "complete"
+/// Rejected by WJs
+#define TICKET_REJECTED "rejected"
+/// Completed by WJs
+#define TICKET_COMPLETED "completed"
+
+/// Checks for if buttons can be used, these may yet be removed and internalised to the UI programming
+#define TICKET_OPEN "OPEN"
+#define TICKET_CLOSED "CLOSED"
/// Cooldowns
#define COOLDOWN_ARES_SENSOR 60 SECONDS
diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm
index a40675e0560e..dcc8e4bf6c4b 100644
--- a/code/__DEFINES/access.dm
+++ b/code/__DEFINES/access.dm
@@ -71,3 +71,11 @@ most of them are tied into map-placed objects. This should be reworked in the fu
#define ACCESS_WY_CORPORATE_DS 202
#define ACCESS_PRESS 203
//=================================================
+
+// Yautja Access Levels
+/// Requires a visible ID chip to open
+#define ACCESS_YAUTJA_SECURE 250
+/// Elders+ only
+#define ACCESS_YAUTJA_ELDER 251
+/// Ancients only
+#define ACCESS_YAUTJA_ANCIENT 252
diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index 5869650da273..fcc3e7784cb5 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -37,3 +37,7 @@
/// This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage
var/MAX_EXPLOSION_RANGE = 14
+
+/// Used in /obj/structure/pipes/vents/proc/create_gas
+#define VENT_GAS_SMOKE "Smoke"
+#define VENT_GAS_CN20 "CN20 Nerve Gas"
diff --git a/code/__DEFINES/autofire.dm b/code/__DEFINES/autofire.dm
new file mode 100644
index 000000000000..934cdcd7dc79
--- /dev/null
+++ b/code/__DEFINES/autofire.dm
@@ -0,0 +1,4 @@
+// Controls how many buckets should be kept, each representing a tick. Max is ten seconds, to have better perf.
+#define AUTOFIRE_BUCKET_LEN (world.fps * 10)
+/// Helper for getting the correct bucket
+#define AUTOFIRE_BUCKET_POS(next_fire) (((round((next_fire - SSautomatedfire.head_offset) / world.tick_lag) + 1) % AUTOFIRE_BUCKET_LEN) || AUTOFIRE_BUCKET_LEN)
diff --git a/code/__DEFINES/clans.dm b/code/__DEFINES/clans.dm
index 54c9d11616e5..1b95d11c030c 100644
--- a/code/__DEFINES/clans.dm
+++ b/code/__DEFINES/clans.dm
@@ -49,14 +49,14 @@
/// Scales with clan size
#define CLAN_LIMIT_SIZE 2
-var/global/list/datum/rank/clan_ranks = list(
- CLAN_RANK_UNBLOODED = new /datum/rank/unblooded(),
- CLAN_RANK_YOUNG = new /datum/rank/young(),
- CLAN_RANK_BLOODED = new /datum/rank/blooded(),
- CLAN_RANK_ELITE = new /datum/rank/elite(),
- CLAN_RANK_ELDER = new /datum/rank/elder(),
- CLAN_RANK_LEADER = new /datum/rank/leader(),
- CLAN_RANK_ADMIN = new /datum/rank/ancient()
+var/global/list/datum/yautja_rank/clan_ranks = list(
+ CLAN_RANK_UNBLOODED = new /datum/yautja_rank/unblooded(),
+ CLAN_RANK_YOUNG = new /datum/yautja_rank/young(),
+ CLAN_RANK_BLOODED = new /datum/yautja_rank/blooded(),
+ CLAN_RANK_ELITE = new /datum/yautja_rank/elite(),
+ CLAN_RANK_ELDER = new /datum/yautja_rank/elder(),
+ CLAN_RANK_LEADER = new /datum/yautja_rank/leader(),
+ CLAN_RANK_ADMIN = new /datum/yautja_rank/ancient()
)
var/global/list/clan_ranks_ordered = list(
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 60c4116df330..31df07648fc0 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -20,3 +20,18 @@
#define DEFAULT_MESSAGE_RANGE 7
#define BAYONET_DRAW_DELAY (1 SECONDS)
+
+//Predator decloak multpliers based on the standard.
+#define DECLOAK_STANDARD (10 SECONDS)
+/// Forced for any unspecified reason.
+#define DECLOAK_FORCED 1
+/// Caused by being worn by non humans.
+#define DECLOAK_SPECIES 0.75
+/// Caused by fire extinguisher.
+#define DECLOAK_EXTINGUISHER 1.5
+/// Caused by predalien screech.
+#define DECLOAK_PREDALIEN 2
+/// Caused by being in a body of water.
+#define DECLOAK_SUBMERGED 2
+/// Caused by an EMP.
+#define DECLOAK_EMP 3
diff --git a/code/__DEFINES/conflict.dm b/code/__DEFINES/conflict.dm
index fc8ca4e03a9f..7a1b322a19ee 100644
--- a/code/__DEFINES/conflict.dm
+++ b/code/__DEFINES/conflict.dm
@@ -54,30 +54,24 @@
#define GUN_TRIGGER_SAFETY (1<<1)
#define GUN_UNUSUAL_DESIGN (1<<2)
#define GUN_SILENCED (1<<3)
-#define GUN_AUTOMATIC (1<<4)
///If checking for ammo with current.mag you have to check it against numerical values, as booleans will not trigger.
-#define GUN_INTERNAL_MAG (1<<5)
-#define GUN_AUTO_EJECTOR (1<<6)
-#define GUN_AMMO_COUNTER (1<<7)
-#define GUN_BURST_ON (1<<8)
-#define GUN_BURST_FIRING (1<<9)
-#define GUN_FLASHLIGHT_ON (1<<10)
-#define GUN_WY_RESTRICTED (1<<11)
-#define GUN_SPECIALIST (1<<12)
-#define GUN_WIELDED_FIRING_ONLY (1<<13)
-#define GUN_HAS_FULL_AUTO (1<<14)
-#define GUN_FULL_AUTO_ON (1<<15)
+#define GUN_INTERNAL_MAG (1<<4)
+#define GUN_AUTO_EJECTOR (1<<5)
+#define GUN_AMMO_COUNTER (1<<6)
+#define GUN_BURST_FIRING (1<<7)
+#define GUN_FLASHLIGHT_ON (1<<8)
+#define GUN_WY_RESTRICTED (1<<9)
+#define GUN_SPECIALIST (1<<10)
+#define GUN_WIELDED_FIRING_ONLY (1<<11)
/// removes unwielded accuracy and scatter penalties (not recoil)
-#define GUN_ONE_HAND_WIELDED (1<<16)
-#define GUN_ANTIQUE (1<<17)
+#define GUN_ONE_HAND_WIELDED (1<<12)
+#define GUN_ANTIQUE (1<<13)
/// Whether the gun has been fired by its current user (reset upon `dropped()`)
-#define GUN_RECOIL_BUILDUP (1<<18)
+#define GUN_RECOIL_BUILDUP (1<<14)
/// support weapon, bipod will grant IFF
-#define GUN_SUPPORT_PLATFORM (1<<19)
-#define GUN_BURST_ONLY (1<<20)
-#define GUN_FULL_AUTO_ONLY (1<<21)
+#define GUN_SUPPORT_PLATFORM (1<<15)
/// No gun description, only base desc
-#define GUN_NO_DESCRIPTION (1<<22)
+#define GUN_NO_DESCRIPTION (1<<16)
// NOTE: Don't add flags past 1<<23, it'll break things due to BYOND limitations. You can usually use a Component instead.
#define USES_STREAKS (1<<0)
@@ -85,15 +79,17 @@
#define MOVES_WHEN_LEVERING (1<<2)
//Gun attachable related flags.
-#define ATTACH_REMOVABLE 1
-#define ATTACH_ACTIVATION 2
+#define ATTACH_REMOVABLE (1<<0)
+#define ATTACH_ACTIVATION (1<<1)
/// for attachments that fire bullets
-#define ATTACH_PROJECTILE 4
-#define ATTACH_RELOADABLE 8
+#define ATTACH_PROJECTILE (1<<2)
+#define ATTACH_RELOADABLE (1<<3)
/// is a weapon that fires stuff
-#define ATTACH_WEAPON 16
+#define ATTACH_WEAPON (1<<4)
+/// This attachment should override ignore if it is empty
+#define ATTACH_IGNORE_EMPTY (1<<5)
/// This attachment should activate if you attack() with it attached.
-#define ATTACH_MELEE 32
+#define ATTACH_MELEE (1<<6)
//Ammo magazine defines, for flags_magazine
diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
index 347623798b74..323e0ee6966c 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
@@ -110,3 +110,9 @@
#define COMSIG_MOB_STAT_SET_DEAD "mob_stat_set_dead"
#define COMSIG_GHOST_MOVED "ghost_moved"
+
+#define COMSIG_MOB_MOUSEDOWN "mob_mousedown" //from /client/MouseDown(): (atom/object, turf/location, control, params)
+#define COMSIG_MOB_MOUSEUP "mob_mouseup" //from /client/MouseUp(): (atom/object, turf/location, control, params)
+#define COMSIG_MOB_MOUSEDRAG "mob_mousedrag" //from /client/MouseDrag(): (atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params)
+ #define COMSIG_MOB_CLICK_CANCELED (1<<0)
+ #define COMSIG_MOB_CLICK_HANDLED (1<<1)
diff --git a/code/__DEFINES/dcs/signals/atom/signals_item.dm b/code/__DEFINES/dcs/signals/atom/signals_item.dm
index 9c2f3b92ba05..138e88d21746 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_item.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_item.dm
@@ -38,3 +38,19 @@
#define COMSIG_ITEM_ZOOM "item_zoom"
/// from /obj/item/proc/unzoom() : (mob/user)
#define COMSIG_ITEM_UNZOOM "item_unzoom"
+
+//Signals for automatic fire at component
+#define COMSIG_AUTOMATIC_SHOOTER_START_SHOOTING_AT "start_shooting_at"
+#define COMSIG_AUTOMATIC_SHOOTER_STOP_SHOOTING_AT "stop_shooting_at"
+#define COMSIG_AUTOMATIC_SHOOTER_SHOOT "shoot"
+
+//Signals for gun auto fire component
+#define COMSIG_GET_BURST_FIRE "get_burst_fire"
+ #define BURST_FIRING (1<<0)
+
+#define COMSIG_GUN_FIRE "gun_fire"
+#define COMSIG_GUN_STOP_FIRE "gun_stop_fire"
+#define COMSIG_GUN_FIRE_MODE_TOGGLE "gun_fire_mode_toggle"
+#define COMSIG_GUN_AUTOFIREDELAY_MODIFIED "gun_autofiredelay_modified"
+#define COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED "gun_burst_shots_to_fire_modified"
+#define COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED "gun_burst_shot_delay_modified"
diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm
index 9781f61ec95c..a288ac2c8be7 100644
--- a/code/__DEFINES/dcs/signals/signals_global.dm
+++ b/code/__DEFINES/dcs/signals/signals_global.dm
@@ -56,3 +56,6 @@
/// From /datum/game_mode/colonialmarines/proc/check_ground_humans()
#define COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING "!groundside_forsaken_handling"
+
+/// From
+#define COMSIG_GLOB_YAUTJA_ARMORY_OPENED "yautja_armory_opened"
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/equipment.dm b/code/__DEFINES/equipment.dm
index 4212f6a52301..461eae27a2a3 100644
--- a/code/__DEFINES/equipment.dm
+++ b/code/__DEFINES/equipment.dm
@@ -82,7 +82,6 @@
#define NOTABLEMERGE (1<<13)
/// Has heat source but isn't 'on fire' and thus can be stored
#define IGNITING_ITEM (1<<14)
-
//==========================================================================================
diff --git a/code/__DEFINES/guns.dm b/code/__DEFINES/guns.dm
index a89d98805e73..b29f7c7439f2 100644
--- a/code/__DEFINES/guns.dm
+++ b/code/__DEFINES/guns.dm
@@ -37,3 +37,11 @@
#define REVOLVER_TIP_COLOR_INCENDIARY AMMO_BAND_COLOR_INCENDIARY
#define REVOLVER_TIP_COLOR_PENETRATING AMMO_BAND_COLOR_PENETRATING
#define REVOLVER_TIP_COLOR_TOXIN AMMO_BAND_COLOR_TOXIN
+
+#define GUN_FIREMODE_SEMIAUTO "semi-auto fire mode"
+#define GUN_FIREMODE_BURSTFIRE "burst-fire mode"
+#define GUN_FIREMODE_AUTOMATIC "automatic fire mode"
+
+//autofire component fire callback return flags
+#define AUTOFIRE_CONTINUE (1<<0)
+#define AUTOFIRE_SUCCESS (1<<1)
diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm
index 422edb05508b..1878ca63f34e 100644
--- a/code/__DEFINES/keybinding.dm
+++ b/code/__DEFINES/keybinding.dm
@@ -189,6 +189,7 @@
#define COMSIG_KB_OBSERVER_JOIN_XENO "keybinding_observer_join_as_xeno"
#define COMSIG_KB_OBSERVER_JOIN_ERT "keybinding_observer_join_ert"
#define COMSIG_KB_OBSERVER_JOIN_PREDATOR "keybinding_observer_join_pred"
+#define COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE "keybinding_observer_join_lesser_drone"
#define CATEGORY_CLIENT "CLIENT"
#define CATEGORY_EMOTE "EMOTE"
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index bed2ceeced7d..b024f22ebfff 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -113,7 +113,7 @@
// These behaviors are either of the person performing the action or any targets.
/// You cannot move the person while this action is being performed
-#define BEHAVIOR_IMMOBILE (1<<18)
+#define BEHAVIOR_IMMOBILE (1<<19)
// *************************************** //
// END DO_AFTER FLAGS //
@@ -284,6 +284,8 @@
#define COOLDOWN_COMM_CENTRAL 30 SECONDS
#define COOLDOWN_COMM_DESTRUCT 5 MINUTES
+///Cooldown for pred recharge
+#define COOLDOWN_BRACER_CHARGE 3 MINUTES
// magic value to use for indicating a proc slept
#define PROC_RETURN_SLEEP -1
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index c8078d3fb673..c0886ab871f9 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -222,6 +222,7 @@
//Mob sizes
#define MOB_SIZE_SMALL 0
#define MOB_SIZE_HUMAN 1
+#define MOB_SIZE_XENO_VERY_SMALL 1.5
#define MOB_SIZE_XENO_SMALL 2
#define MOB_SIZE_XENO 3
#define MOB_SIZE_BIG 4
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 662bcb458c55..3a25d865fb60 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -173,6 +173,7 @@
#define SS_PRIORITY_INPUT 1000
#define SS_PRIORITY_TIMER 700
+#define SS_PRIORITY_AUTOFIRE 450
#define SS_PRIORITY_SOUND 250
#define SS_PRIORITY_TICKER 200
#define SS_PRIORITY_NIGHTMARE 180
diff --git a/code/__DEFINES/typecheck/humanoids.dm b/code/__DEFINES/typecheck/humanoids.dm
index 58d245c486e4..7076cf67c95c 100644
--- a/code/__DEFINES/typecheck/humanoids.dm
+++ b/code/__DEFINES/typecheck/humanoids.dm
@@ -25,7 +25,7 @@
#define isspeciessynth(A) (A.species?.group == SPECIES_SYNTHETIC)
//Size checks for carbon to use instead of typechecks. (Hellhounds are deprecated)
-#define iscarbonsizexeno(A) (A.mob_size >= MOB_SIZE_XENO_SMALL)
+#define iscarbonsizexeno(A) (A.mob_size >= MOB_SIZE_XENO_VERY_SMALL)
#define iscarbonsizehuman(A) (A.mob_size <= MOB_SIZE_HUMAN)
//job/role helpers
diff --git a/code/__DEFINES/typecheck/mobs_generic.dm b/code/__DEFINES/typecheck/mobs_generic.dm
index 49e4463435a6..1d848decda4e 100644
--- a/code/__DEFINES/typecheck/mobs_generic.dm
+++ b/code/__DEFINES/typecheck/mobs_generic.dm
@@ -1,4 +1,5 @@
#define isAI(A) (istype(A, /mob/living/silicon/ai))
+#define isARES(A) (istype(A, /mob/living/silicon/decoy/ship_ai))
#define isSilicon(A) (istype(A, /mob/living/silicon))
#define isRemoteControlling(M) (M && M.client && M.client.remote_control)
#define isRemoteControllingOrAI(M) ((M && M.client && M.client.remote_control) || (istype(M, /mob/living/silicon/ai)))
diff --git a/code/__DEFINES/typecheck/xenos.dm b/code/__DEFINES/typecheck/xenos.dm
index 504a929a7f02..34b70ac92f45 100644
--- a/code/__DEFINES/typecheck/xenos.dm
+++ b/code/__DEFINES/typecheck/xenos.dm
@@ -14,6 +14,7 @@
#define islarva(A) (istype(A, /mob/living/carbon/xenomorph/larva))
#define ispredalienlarva(A) (istype(A, /mob/living/carbon/xenomorph/larva/predalien))
#define isfacehugger(A) (istype(A, /mob/living/carbon/xenomorph/facehugger))
+#define islesserdrone(A) (istype(A, /mob/living/carbon/xenomorph/lesser_drone))
#define ispraetorian(A) (istype(A, /mob/living/carbon/xenomorph/praetorian))
#define isqueen(A) (istype(A, /mob/living/carbon/xenomorph/queen))
#define isravager(A) (istype(A, /mob/living/carbon/xenomorph/ravager))
diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm
index ac783b6f426e..82237cd793b2 100644
--- a/code/__DEFINES/xeno.dm
+++ b/code/__DEFINES/xeno.dm
@@ -70,6 +70,7 @@
#define HUD_ARMOR_STATES_XENO 10
/// Multiplier for time taken for a xeno to place down a resin structure
+#define BUILD_TIME_MULT_LESSER_DRONE 2
#define BUILD_TIME_MULT_XENO 1
#define BUILD_TIME_MULT_BUILDER 1
#define BUILD_TIME_MULT_HIVELORD 0.5
@@ -206,6 +207,7 @@
// Health bands
#define XENO_HEALTH_LARVA 35 * XENO_UNIVERSAL_HPMULT
+#define XENO_HEALTH_LESSER_DRONE 160 * XENO_UNIVERSAL_HPMULT
#define XENO_HEALTH_RUNNER 230 * XENO_UNIVERSAL_HPMULT // Killed by 1 PB
#define XENO_HEALTH_TIER_1 250 * XENO_UNIVERSAL_HPMULT
#define XENO_HEALTH_TIER_2 300 * XENO_UNIVERSAL_HPMULT
@@ -603,7 +605,8 @@
#define XENO_CASTE_LARVA "Bloody Larva"
#define XENO_CASTE_PREDALIEN_LARVA "Predalien Larva"
#define XENO_CASTE_FACEHUGGER "Facehugger"
-#define XENO_T0_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER)
+#define XENO_CASTE_LESSER_DRONE "Lesser Drone"
+#define XENO_T0_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE)
//t1
#define XENO_CASTE_DRONE "Drone"
@@ -631,7 +634,7 @@
#define XENO_CASTE_HELLHOUND "Hellhound"
#define XENO_SPECIAL_CASTES list(XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)
-#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)
+#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)
// Checks if two hives are allied to each other.
// PARAMETERS:
@@ -695,3 +698,6 @@
#define TAILSTAB_AIRLOCK_DAMAGE_MULTIPLIER 2
#define FRENZY_DAMAGE_MULTIPLIER 2
+
+#define JOIN_AS_FACEHUGGER_DELAY (3 MINUTES)
+#define JOIN_AS_LESSER_DRONE_DELAY (30 SECONDS)
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 09b55accf16d..ef539b8459c7 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -108,24 +108,18 @@ DEFINE_BITFIELD(flags_gun_features, list(
"GUN_TRIGGER_SAFETY" = GUN_TRIGGER_SAFETY,
"GUN_UNUSUAL_DESIGN" = GUN_UNUSUAL_DESIGN,
"GUN_SILENCED" = GUN_SILENCED,
- "GUN_AUTOMATIC" = GUN_AUTOMATIC,
"GUN_INTERNAL_MAG" = GUN_INTERNAL_MAG,
"GUN_AUTO_EJECTOR" = GUN_AUTO_EJECTOR,
"GUN_AMMO_COUNTER" = GUN_AMMO_COUNTER,
- "GUN_BURST_ON" = GUN_BURST_ON,
"GUN_BURST_FIRING" = GUN_BURST_FIRING,
"GUN_FLASHLIGHT_ON" = GUN_FLASHLIGHT_ON,
"GUN_WY_RESTRICTED" = GUN_WY_RESTRICTED,
"GUN_SPECIALIST" = GUN_SPECIALIST,
"GUN_WIELDED_FIRING_ONLY" = GUN_WIELDED_FIRING_ONLY,
- "GUN_HAS_FULL_AUTO" = GUN_HAS_FULL_AUTO,
- "GUN_FULL_AUTO_ON" = GUN_FULL_AUTO_ON,
"GUN_ONE_HAND_WIELDED" = GUN_ONE_HAND_WIELDED,
"GUN_ANTIQUE" = GUN_ANTIQUE,
"GUN_RECOIL_BUILDUP" = GUN_RECOIL_BUILDUP,
"GUN_SUPPORT_PLATFORM" = GUN_SUPPORT_PLATFORM,
- "GUN_BURST_ONLY" = GUN_BURST_ONLY,
- "GUN_FULL_AUTO_ONLY" = GUN_FULL_AUTO_ONLY,
))
DEFINE_BITFIELD(flags_magazine, list(
diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm
index 138bc1fcbc96..36058a44fc37 100644
--- a/code/_globalvars/global_lists.dm
+++ b/code/_globalvars/global_lists.dm
@@ -50,6 +50,12 @@ GLOBAL_LIST_EMPTY(mainship_pipes)
// Resin constructions parameters
GLOBAL_LIST_INIT_TYPED(resin_constructions_list, /datum/resin_construction, setup_resin_constructions())
+GLOBAL_LIST_INIT(resin_build_order_lesser_drone, list(
+ /datum/resin_construction/resin_turf/wall,
+ /datum/resin_construction/resin_turf/membrane,
+ /datum/resin_construction/resin_obj/door,
+))
+
GLOBAL_LIST_INIT(resin_build_order_drone, list(
/datum/resin_construction/resin_turf/wall,
/datum/resin_construction/resin_turf/membrane,
diff --git a/code/_onclick/click_hold.dm b/code/_onclick/click_hold.dm
index c68beb52fc9e..f65dd33c2eea 100644
--- a/code/_onclick/click_hold.dm
+++ b/code/_onclick/click_hold.dm
@@ -30,6 +30,9 @@
mouse_trace_history = null
LAZYADD(mouse_trace_history, A)
+ if(SEND_SIGNAL(mob, COMSIG_MOB_MOUSEDOWN, A, T, skin_ctl, params) & COMSIG_MOB_CLICK_CANCELED)
+ return
+
var/list/mods = params2list(params)
if(mods["left"])
SEND_SIGNAL(src, COMSIG_CLIENT_LMB_DOWN, A, mods)
@@ -62,6 +65,9 @@
params += ";click_catcher=1"
holding_click = FALSE
+ if(SEND_SIGNAL(mob, COMSIG_MOB_MOUSEUP, A, T, skin_ctl, params) & COMSIG_MOB_CLICK_CANCELED)
+ return
+
var/list/mods = params2list(params)
if(mods["left"])
SEND_SIGNAL(src, COMSIG_CLIENT_LMB_UP, A, params)
@@ -75,6 +81,9 @@
if(click_catcher_click)
params += ";click_catcher=1"
+ if(SEND_SIGNAL(mob, COMSIG_MOB_MOUSEDRAG, src_obj, over_obj, src_loc, over_loc, src_ctl, over_ctl, params) & COMSIG_MOB_CLICK_CANCELED)
+ return
+
var/list/mods = params2list(params)
if(mods["left"])
SEND_SIGNAL(src, COMSIG_CLIENT_LMB_DRAG, src_obj, over_obj, params)
diff --git a/code/controllers/subsystem/autofire.dm b/code/controllers/subsystem/autofire.dm
new file mode 100644
index 000000000000..3d3abbd2669f
--- /dev/null
+++ b/code/controllers/subsystem/autofire.dm
@@ -0,0 +1,85 @@
+/**
+ * # Autofire Subsystem
+ *
+ * Maintains a timer-like system to handle autofiring. Much of this code is modeled
+ * after or adapted from TGMC's runechat subsytem.
+ *
+ * Note that this has the same structure for storing and queueing shooter component as the timer subsystem does
+ * for handling timers: the bucket_list is a list of autofire component, each of which are the head
+ * of a linked list. Any given index in bucket_list could be null, representing an empty bucket.
+ *
+ * Doesn't support any event scheduled for more than 100 ticks in the future, as it has no secondary queue by design
+ */
+SUBSYSTEM_DEF(automatedfire)
+ name = "Automated fire"
+ flags = SS_TICKER | SS_NO_INIT
+ wait = 1
+ priority = SS_PRIORITY_AUTOFIRE
+
+ /// world.time of the first entry in the bucket list, effectively the 'start time' of the current buckets
+ var/head_offset = 0
+ /// Index of the first non-empty bucket
+ var/practical_offset = 1
+ ///How many buckets for every frame of world.fps
+ var/bucket_resolution = 0
+ /// How many shooter are in the buckets
+ var/shooter_count = 0
+ /// List of buckets, each bucket holds every shooter that has to shoot this byond tick
+ var/list/bucket_list = list()
+ /// Reference to the next shooter before we clean shooter.next
+ var/datum/component/automatedfire/next_shooter
+
+/datum/controller/subsystem/automatedfire/PreInit()
+ bucket_list.len = AUTOFIRE_BUCKET_LEN
+ head_offset = world.time
+ bucket_resolution = world.tick_lag
+
+/datum/controller/subsystem/automatedfire/stat_entry(msg = "ActShooters: [shooter_count]")
+ return ..()
+
+/datum/controller/subsystem/automatedfire/fire(resumed = FALSE)
+ // Check for when we need to loop the buckets, this occurs when
+ // the head_offset is approaching AUTOFIRE_BUCKET_LEN ticks in the past
+ if (practical_offset > AUTOFIRE_BUCKET_LEN)
+ head_offset += TICKS2DS(AUTOFIRE_BUCKET_LEN)
+ practical_offset = 1
+ resumed = FALSE
+
+ // Check for when we have to reset buckets, typically from auto-reset
+ if ((length(bucket_list) != AUTOFIRE_BUCKET_LEN) || (world.tick_lag != bucket_resolution))
+ reset_buckets()
+ bucket_list = src.bucket_list
+ resumed = FALSE
+
+ // Store a reference to the 'working' shooter so that we can resume if the MC
+ // has us stop mid-way through processing
+ var/static/datum/component/automatedfire/shooter
+ if (!resumed)
+ shooter = null
+
+ // Iterate through each bucket starting from the practical offset
+ while (practical_offset <= AUTOFIRE_BUCKET_LEN && head_offset + ((practical_offset - 1) * world.tick_lag) <= world.time)
+ if(!shooter)
+ shooter = bucket_list[practical_offset]
+ bucket_list[practical_offset] = null
+
+ while (shooter)
+ next_shooter = shooter.next
+ INVOKE_ASYNC(shooter, TYPE_PROC_REF(/datum/component/automatedfire, process_shot))
+
+ SSautomatedfire.shooter_count--
+ shooter = next_shooter
+ if (MC_TICK_CHECK)
+ return
+
+ // Move to the next bucket
+ practical_offset++
+
+/datum/controller/subsystem/automatedfire/Recover()
+ bucket_list |= SSautomatedfire.bucket_list
+
+///In the event of a change of world.tick_lag, we refresh the size of the bucket and the bucket resolution
+/datum/controller/subsystem/automatedfire/proc/reset_buckets()
+ bucket_list.len = AUTOFIRE_BUCKET_LEN
+ head_offset = world.time
+ bucket_resolution = world.tick_lag
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index ed6e3cda15f5..afecabd74be0 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -123,10 +123,14 @@ SUBSYSTEM_DEF(mapping)
++i
// load the maps
- for (var/P in parsed_maps)
- var/datum/parsed_map/pm = P
- if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE))
+ for (var/datum/parsed_map/pm as anything in parsed_maps)
+ var/cur_z = start_z + parsed_maps[pm]
+ if (!pm.load(1, 1, cur_z, no_changeturf = TRUE))
errorList |= pm.original_path
+ if(istype(z_list[cur_z], /datum/space_level))
+ var/datum/space_level/cur_level = z_list[cur_z]
+ cur_level.x_bounds = pm.bounds[MAP_MAXX]
+ cur_level.y_bounds = pm.bounds[MAP_MAXY]
if(!silent)
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
return parsed_maps
diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm
index c3b2d36b8177..b154c7673855 100644
--- a/code/controllers/subsystem/minimap.dm
+++ b/code/controllers/subsystem/minimap.dm
@@ -493,6 +493,16 @@ SUBSYSTEM_DEF(minimaps)
else
return UI_CLOSE
+/datum/tacmap/xeno/ui_status(mob/user)
+ if(!isxeno(user))
+ return UI_CLOSE
+
+ var/mob/living/carbon/xenomorph/xeno = user
+ if(!xeno.hive?.living_xeno_queen?.ovipositor)
+ return UI_CLOSE
+
+ return UI_INTERACTIVE
+
/datum/tacmap_holder
var/map_ref
var/atom/movable/screen/minimap/map
diff --git a/code/datums/agents/tools/tranq_gun.dm b/code/datums/agents/tools/tranq_gun.dm
index e812aa57037e..e95f853cef82 100644
--- a/code/datums/agents/tools/tranq_gun.dm
+++ b/code/datums/agents/tools/tranq_gun.dm
@@ -6,11 +6,10 @@
item_state = "pk9r"
current_mag = /obj/item/ammo_magazine/pistol/tranq
- burst_amount = 1
/obj/item/weapon/gun/pistol/tranquilizer/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6
+ set_fire_delay(FIRE_DELAY_TIER_6)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_7
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_10
diff --git a/code/datums/components/autofire/_automated_fire.dm b/code/datums/components/autofire/_automated_fire.dm
new file mode 100644
index 000000000000..9abd6a152199
--- /dev/null
+++ b/code/datums/components/autofire/_automated_fire.dm
@@ -0,0 +1,49 @@
+/datum/component/automatedfire
+ ///The owner of this component
+ var/atom/shooter
+ /// Contains the scheduled fire time, used for scheduling EOL
+ var/next_fire
+ /// Contains the reference to the next component in the bucket, used by autofire subsystem
+ var/datum/component/automatedfire/next
+ /// Contains the reference to the previous component in the bucket, used by autofire subsystem
+ var/datum/component/automatedfire/prev
+
+
+/// schedule the shooter into the system, inserting it into the next fire queue
+/datum/component/automatedfire/proc/schedule_shot()
+ //We move to another bucket, so we clean the reference from the former linked list
+ next = null
+ prev = null
+ var/list/bucket_list = SSautomatedfire.bucket_list
+
+ // Ensure the next_fire time is properly bound to avoid missing a scheduled event
+ next_fire = max(CEILING(next_fire, world.tick_lag), world.time + world.tick_lag)
+
+ // Get bucket position and a local reference to the datum var, it's faster to access this way
+ var/bucket_pos = AUTOFIRE_BUCKET_POS(next_fire)
+
+ // Get the bucket head for that bucket, increment the bucket count
+ var/datum/component/automatedfire/bucket_head = bucket_list[bucket_pos]
+ SSautomatedfire.shooter_count++
+
+ // If there is no existing head of this bucket, we can set this shooter to be that head
+ if (!bucket_head)
+ bucket_list[bucket_pos] = src
+ return
+
+ // Otherwise it's a simple insertion into the double-linked list
+ if (bucket_head.next)
+ next = bucket_head.next
+ next.prev = src
+
+ bucket_head.next = src
+ prev = bucket_head
+
+ //Something went wrong, probably a lag spike or something. To prevent infinite loops, we reschedule it to a another next fire
+ if(prev == src)
+ next_fire += 1
+ schedule_shot()
+
+///Handle the firing of the autofire component
+/datum/component/automatedfire/proc/process_shot()
+ return
diff --git a/code/datums/components/autofire/autofire.dm b/code/datums/components/autofire/autofire.dm
new file mode 100644
index 000000000000..31ca255f1b88
--- /dev/null
+++ b/code/datums/components/autofire/autofire.dm
@@ -0,0 +1,137 @@
+/datum/component/automatedfire/autofire
+ ///The current fire mode of the shooter
+ var/fire_mode
+ ///Delay between two shots when in full auto
+ var/auto_fire_shot_delay
+ ///Delay between two burst shots
+ var/burstfire_shot_delay
+ ///How many bullets are fired in burst mode
+ var/burst_shots_to_fire
+ ///Count the shots fired when bursting
+ var/shots_fired = 0
+ ///If the shooter is currently shooting
+ var/shooting = FALSE
+ ///If TRUE, the shooter will reset its references at the end of the burst
+ var/have_to_reset_at_burst_end = FALSE
+ ///If we are in a burst
+ var/bursting = FALSE
+ ///Callback to set bursting mode on the parent
+ var/datum/callback/callback_bursting
+ ///Callback to ask the parent to reset its firing vars
+ var/datum/callback/callback_reset_fire
+ ///Callback to ask the parent to fire
+ var/datum/callback/callback_fire
+ ///Callback to ask the parent to display ammo
+ var/datum/callback/callback_display_ammo
+ ///Callback to set parent's fa_firing
+ var/datum/callback/callback_set_firing
+
+/datum/component/automatedfire/autofire/Initialize(auto_fire_shot_delay = 0.3 SECONDS, burstfire_shot_delay, burst_shots_to_fire = 3, fire_mode = GUN_FIREMODE_SEMIAUTO, datum/callback/callback_bursting, datum/callback/callback_reset_fire, datum/callback/callback_fire, datum/callback/callback_display_ammo, datum/callback/callback_set_firing)
+ . = ..()
+
+ RegisterSignal(parent, COMSIG_GUN_FIRE_MODE_TOGGLE, PROC_REF(modify_fire_mode))
+ RegisterSignal(parent, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, PROC_REF(modify_fire_shot_delay))
+ RegisterSignal(parent, COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED, PROC_REF(modify_burst_shots_to_fire))
+ RegisterSignal(parent, COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED, PROC_REF(modify_burstfire_shot_delay))
+ RegisterSignal(parent, COMSIG_GUN_FIRE, PROC_REF(initiate_shot))
+ RegisterSignal(parent, COMSIG_GUN_STOP_FIRE, PROC_REF(stop_firing))
+
+ src.auto_fire_shot_delay = auto_fire_shot_delay
+ src.burstfire_shot_delay = burstfire_shot_delay
+ src.burst_shots_to_fire = burst_shots_to_fire
+ src.fire_mode = fire_mode
+ src.callback_bursting = callback_bursting
+ src.callback_reset_fire = callback_reset_fire
+ src.callback_fire = callback_fire
+ src.callback_display_ammo = callback_display_ammo
+ src.callback_set_firing = callback_set_firing
+
+/datum/component/automatedfire/autofire/Destroy(force, silent)
+ QDEL_NULL(callback_fire)
+ QDEL_NULL(callback_reset_fire)
+ QDEL_NULL(callback_bursting)
+ QDEL_NULL(callback_display_ammo)
+ QDEL_NULL(callback_set_firing)
+ return ..()
+
+///Setter for fire mode
+/datum/component/automatedfire/autofire/proc/modify_fire_mode(datum/source, fire_mode)
+ SIGNAL_HANDLER
+ src.fire_mode = fire_mode
+
+///Setter for auto fire shot delay
+/datum/component/automatedfire/autofire/proc/modify_fire_shot_delay(datum/source, auto_fire_shot_delay)
+ SIGNAL_HANDLER
+ src.auto_fire_shot_delay = auto_fire_shot_delay
+
+///Setter for the number of shots in a burst
+/datum/component/automatedfire/autofire/proc/modify_burst_shots_to_fire(datum/source, burst_shots_to_fire)
+ SIGNAL_HANDLER
+ src.burst_shots_to_fire = burst_shots_to_fire
+
+///Setter for burst shot delay
+/datum/component/automatedfire/autofire/proc/modify_burstfire_shot_delay(datum/source, burstfire_shot_delay)
+ SIGNAL_HANDLER
+ src.burstfire_shot_delay = burstfire_shot_delay
+
+///Insert the component in the bucket system if it was not in already
+/datum/component/automatedfire/autofire/proc/initiate_shot()
+ SIGNAL_HANDLER
+ if(shooting)//if we are already shooting, it means the shooter is still on cooldown
+ return
+ shooting = TRUE
+ process_shot()
+
+///Remove the component from the bucket system if it was in
+/datum/component/automatedfire/autofire/proc/stop_firing()
+ SIGNAL_HANDLER
+ if(!shooting)
+ return
+ ///We are burst firing, we can't clean the state now. We will do it when the burst is over
+ if(bursting)
+ have_to_reset_at_burst_end = TRUE
+ return
+ shooting = FALSE
+ shots_fired = 0
+
+///Hard reset the autofire, happens when the shooter fall/is thrown, at the end of a burst or when it runs out of ammunition
+/datum/component/automatedfire/autofire/proc/hard_reset()
+ callback_reset_fire.Invoke() //resets the gun
+ shots_fired = 0
+ have_to_reset_at_burst_end = FALSE
+ if(bursting)
+ bursting = FALSE
+ callback_bursting.Invoke(FALSE)
+ shooting = FALSE
+
+
+///Ask the shooter to fire and schedule the next shot if need
+/datum/component/automatedfire/autofire/process_shot()
+ if(!shooting)
+ return
+ if(next_fire > world.time)//This mean duplication somewhere, we abort now
+ return
+ if(!(callback_fire.Invoke() & AUTOFIRE_CONTINUE))//reset fire if we want to stop
+ hard_reset()
+ return
+ switch(fire_mode)
+ if(GUN_FIREMODE_BURSTFIRE)
+ shots_fired++
+ if(shots_fired == burst_shots_to_fire)
+ callback_bursting.Invoke(FALSE)
+ callback_display_ammo.Invoke()
+ bursting = FALSE
+ stop_firing()
+ if(have_to_reset_at_burst_end)//We failed to reset because we were bursting, we do it now
+ callback_reset_fire.Invoke()
+ have_to_reset_at_burst_end = FALSE
+ return
+ callback_bursting.Invoke(TRUE)
+ bursting = TRUE
+ next_fire = world.time + burstfire_shot_delay
+ if(GUN_FIREMODE_AUTOMATIC)
+ callback_set_firing.Invoke(TRUE)
+ next_fire = world.time + auto_fire_shot_delay
+ if(GUN_FIREMODE_SEMIAUTO)
+ return
+ schedule_shot()
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/emergency_calls/goons.dm b/code/datums/emergency_calls/goons.dm
index 8bb2ee515c69..8a0b00968807 100644
--- a/code/datums/emergency_calls/goons.dm
+++ b/code/datums/emergency_calls/goons.dm
@@ -47,12 +47,12 @@
/datum/emergency_call/goon/chem_retrieval/New()
..()
dispatch_message = "[MAIN_SHIP_NAME], this is USCSS Royce. Our squad is boarding to retrieve all samples of a chemical recently scanned from your research department. You should already have received a significant sum of money for your department's discovery. In return we ask that you cooperate and provide everything related to the chemical to our retrieval team."
- objectives = "Secure all documents, samples, and chemicals containing the property DNA_Disintegrating from [MAIN_SHIP_NAME] research department."
+ objectives = "Secure all documents, samples, and chemicals containing the property DNA_Disintegrating from [MAIN_SHIP_NAME] research department and return them to Response Team Station."
/datum/emergency_call/goon/chem_retrieval/proc/check_objective_info()
if(objective_info)
- objectives = "Secure all documents, samples and chemicals related to [objective_info] from [MAIN_SHIP_NAME] research department."
- objectives += "Assume at least 30 units are located within the department. If they can not make more that should be all. Cooperate with the onboard CL to ensure all who know the complete recipe are kept silenced with a contract of confidentiality. All humans who have ingested the chemical must be brought back dead or alive. Viral scan is required for any humans who is suspected of ingestion. The professor may call for PMC back up if things get out of hand."
+ objectives = "Secure all documents, samples and chemicals related to [objective_info] from [MAIN_SHIP_NAME] research department and return them to Response Team Station."
+ objectives += "Assume at least 30 units are located within the department. If they can not make more that should be all. Cooperate with the onboard CL to ensure all who know the complete recipe are kept silenced with a contract of confidentiality. All humans who have ingested the chemical must be brought back dead or alive. Viral scan is required for any humans who is suspected of ingestion. You must not deploy to the colony without explicit permission from PMC Dispatch. The professor may call for PMC back up if things get out of hand."
checked_objective = TRUE
/datum/emergency_call/goon/chem_retrieval/create_member(datum/mind/M, turf/override_spawn_loc)
diff --git a/code/datums/factions/uscm.dm b/code/datums/factions/uscm.dm
index 1fb1df278ab0..4d13e7d285ca 100644
--- a/code/datums/factions/uscm.dm
+++ b/code/datums/factions/uscm.dm
@@ -2,15 +2,15 @@
name = "United States Colonial Marines"
faction_tag = FACTION_MARINE
-/datum/faction/uscm/modify_hud_holder(image/holder, mob/living/carbon/human/H)
- var/datum/squad/squad = H.assigned_squad
+/datum/faction/uscm/modify_hud_holder(image/holder, mob/living/carbon/human/current_human)
+ var/datum/squad/squad = current_human.assigned_squad
if(istype(squad))
- var/squad_clr = squad_colors[H.assigned_squad.color]
+ var/squad_clr = current_human.assigned_squad.equipment_color
var/marine_rk
- var/obj/item/card/id/I = H.get_idcard()
+ var/obj/item/card/id/I = current_human.get_idcard()
var/_role
- if(H.job)
- _role = H.job
+ if(current_human.job)
+ _role = current_human.job
else if(I)
_role = I.rank
switch(GET_DEFAULT_ROLE(_role))
@@ -29,42 +29,42 @@
if(JOB_MARINE_RAIDER) marine_rk = "soc"
if(JOB_MARINE_RAIDER_SL) marine_rk = "soctl"
if(JOB_MARINE_RAIDER_CMD) marine_rk = "soccmd"
- if(squad.squad_leader == H)
+ if(squad.squad_leader == current_human)
switch(squad.squad_type)
if("Squad") marine_rk = "leader_a"
if("Team") marine_rk = "soctl_a"
- H.langchat_styles = "langchat_bolded" // bold text for bold leaders
+ current_human.langchat_styles = "langchat_bolded" // bold text for bold leaders
else
- H.langchat_styles = initial(H.langchat_styles)
+ current_human.langchat_styles = initial(current_human.langchat_styles)
- H.langchat_color = squad_colors_chat[H.assigned_squad.color]
+ current_human.langchat_color = current_human.assigned_squad.chat_color
- if(!marine_rk) marine_rk = H.rank_fallback
+ if(!marine_rk) marine_rk = current_human.rank_fallback
if(marine_rk)
- var/image/IMG = image('icons/mob/hud/marine_hud.dmi', H, "hudsquad")
+ var/image/IMG = image('icons/mob/hud/marine_hud.dmi', current_human, "hudsquad")
if(squad_clr)
IMG.color = squad_clr
else
IMG.color = "#5A934A"
holder.overlays += IMG
- holder.overlays += image('icons/mob/hud/marine_hud.dmi', H, "hudsquad_[marine_rk]")
- if(H.assigned_squad && H.assigned_fireteam)
- var/image/IMG2 = image('icons/mob/hud/marine_hud.dmi', H, "hudsquad_[H.assigned_fireteam]")
+ holder.overlays += image('icons/mob/hud/marine_hud.dmi', current_human, "hudsquad_[marine_rk]")
+ if(current_human.assigned_squad && current_human.assigned_fireteam)
+ var/image/IMG2 = image('icons/mob/hud/marine_hud.dmi', current_human, "hudsquad_[current_human.assigned_fireteam]")
IMG2.color = squad_clr
holder.overlays += IMG2
- if(H.assigned_squad.fireteam_leaders[H.assigned_fireteam] == H)
- var/image/IMG3 = image('icons/mob/hud/marine_hud.dmi', H, "hudsquad_ftl")
+ if(current_human.assigned_squad.fireteam_leaders[current_human.assigned_fireteam] == current_human)
+ var/image/IMG3 = image('icons/mob/hud/marine_hud.dmi', current_human, "hudsquad_ftl")
IMG3.color = squad_clr
holder.overlays += IMG3
else
var/marine_rk
var/border_rk
var/icon_prefix = "hudsquad_"
- var/obj/item/card/id/ID = H.get_idcard()
+ var/obj/item/card/id/ID = current_human.get_idcard()
var/_role
- if(H.mind)
- _role = H.job
+ if(current_human.mind)
+ _role = current_human.job
else if(ID)
_role = ID.rank
switch(_role)
@@ -188,9 +188,9 @@
icon_prefix = "cmb_"
if(marine_rk)
- var/image/I = image('icons/mob/hud/marine_hud.dmi', H, "hudsquad")
+ var/image/I = image('icons/mob/hud/marine_hud.dmi', current_human, "hudsquad")
I.color = "#5A934A"
holder.overlays += I
- holder.overlays += image('icons/mob/hud/marine_hud.dmi', H, "[icon_prefix][marine_rk]")
+ holder.overlays += image('icons/mob/hud/marine_hud.dmi', current_human, "[icon_prefix][marine_rk]")
if(border_rk)
- holder.overlays += image('icons/mob/hud/marine_hud.dmi', H, "hudmarineborder[border_rk]")
+ holder.overlays += image('icons/mob/hud/marine_hud.dmi', current_human, "hudmarineborder[border_rk]")
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/recipe.dm b/code/datums/recipe.dm
index ec0c90f427ca..47752fd59400 100644
--- a/code/datums/recipe.dm
+++ b/code/datums/recipe.dm
@@ -769,7 +769,7 @@
/datum/recipe/syntikabob
items = list(
/obj/item/stack/rods,
- /obj/item/reagent_container/food/snacks/meat/syntiflesh,
+ /obj/item/reagent_container/food/snacks/meat/synthmeat,
)
result = /obj/item/reagent_container/food/snacks/monkeykabob
diff --git a/code/datums/supply_packs/operations.dm b/code/datums/supply_packs/operations.dm
index 610503d6c25d..6d5e5d14756c 100644
--- a/code/datums/supply_packs/operations.dm
+++ b/code/datums/supply_packs/operations.dm
@@ -94,7 +94,7 @@
group = "Operations"
/datum/supply_packs/nuclearbomb
- name = "Operational Nuke"
+ name = "Decrypted Operational Nuke"
cost = 0
containertype = /obj/structure/machinery/nuclearbomb
buyable = 0
@@ -102,7 +102,7 @@
iteration_needed = null
/datum/supply_packs/technuclearbomb
- name = "Intel Operational Nuke"
+ name = "Encrypted Operational Nuke"
cost = 0
containertype = /obj/structure/machinery/nuclearbomb/tech
buyable = 0
diff --git a/code/defines/procs/announcement.dm b/code/defines/procs/announcement.dm
index 323fb526d527..5223d63b8e59 100644
--- a/code/defines/procs/announcement.dm
+++ b/code/defines/procs/announcement.dm
@@ -46,7 +46,7 @@
targets.Remove(H)
var/datum/ares_link/link = GLOB.ares_link
- if(link.interface && !(link.interface.inoperable()))
+ if(ares_can_log())
switch(logging)
if(ARES_LOG_MAIN)
link.log_ares_announcement(title, message)
@@ -99,7 +99,7 @@
INVOKE_ASYNC(AI, TYPE_PROC_REF(/mob/living/silicon/decoy/ship_ai, say), message)
var/datum/ares_link/link = GLOB.ares_link
- if(link.interface && !(link.interface.inoperable()))
+ if(ares_can_log())
switch(logging)
if(ARES_LOG_MAIN)
link.log_ares_announcement("[MAIN_AI_SYSTEM] Comms Update", message)
@@ -151,7 +151,7 @@
targets.Remove(T)
var/datum/ares_link/link = GLOB.ares_link
- if(link.interface && !(link.interface.inoperable()))
+ if(ares_can_log())
link.log_ares_announcement("[title] Shipwide Update", message)
announcement_helper(message, title, targets, sound_to_play)
diff --git a/code/game/bioscans.dm b/code/game/bioscans.dm
index 5f07b307751a..62c801a02d29 100644
--- a/code/game/bioscans.dm
+++ b/code/game/bioscans.dm
@@ -110,7 +110,7 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
/// This will do something after Project ARES.
-/datum/bioscan_data/proc/can_ares_bioscan()
+/datum/bioscan_data/proc/ares_can_bioscan()
var/datum/ares_link/link = GLOB.ares_link
if(!istype(link))
return FALSE
@@ -120,8 +120,15 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
/// The announcement to all Humans. Slightly off for the planet and elsewhere, accurate for the ship.
/datum/bioscan_data/proc/ares_bioscan(forced = FALSE, variance = 2)
- if(!forced && !can_ares_bioscan())
- message_admins("BIOSCAN: An ARES bioscan has failed.")
+ var/datum/ares_link/link = GLOB.ares_link
+ if(!forced && !ares_can_bioscan())
+ message_admins("An ARES Bioscan has failed.")
+ var/name = "[MAIN_AI_SYSTEM] Bioscan Status"
+ var/input = "Bioscan failed. \n\nInvestigation into Bioscan subsystem recommended."
+ if(ares_can_log())
+ link.log_ares_bioscan(name, input)
+ if(ares_can_interface())
+ marine_announcement(input, name, 'sound/misc/interference.ogg', logging = ARES_LOG_NONE)
return
//Adjust the randomness there so everyone gets the same thing
var/fake_xenos_on_planet = max(0, xenos_on_planet + rand(-variance, variance))
@@ -130,10 +137,12 @@ GLOBAL_DATUM_INIT(bioscan_data, /datum/bioscan_data, new)
log_game("BIOSCAN: ARES bioscan completed. [input]")
- var/datum/ares_link/link = GLOB.ares_link
- link.log_ares_bioscan(name, input)
- if(forced || (link.p_interface && !link.p_interface.inoperable()))
+ if(forced || ares_can_log())
+ link.log_ares_bioscan(name, input) //if interface is down, bioscan still logged, just have to go read it.
+ if(forced || ares_can_interface())
marine_announcement(input, name, 'sound/AI/bioscan.ogg', logging = ARES_LOG_NONE)
+ else
+ message_admins("An ARES Bioscan has succeeded, but was not announced.")
/// The announcement to all Xenos. Slightly off for the human ship, accurate otherwise.
/datum/bioscan_data/proc/qm_bioscan(variance = 2)
diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm
index a42ff3f22e59..a7e8ab612bb4 100644
--- a/code/game/gamemodes/cm_initialize.dm
+++ b/code/game/gamemodes/cm_initialize.dm
@@ -548,6 +548,38 @@ Additional game mode variables.
return TRUE
+/datum/game_mode/proc/attempt_to_join_as_lesser_drone(mob/xeno_candidate)
+ var/list/active_hives = list()
+ var/datum/hive_status/hive
+ var/last_active_hive = 0
+ for(var/hivenumber in GLOB.hive_datum)
+ hive = GLOB.hive_datum[hivenumber]
+ if(hive.totalXenos.len <= 0)
+ continue
+ active_hives[hive.name] = hive.hivenumber
+ last_active_hive = hive.hivenumber
+
+ if(active_hives.len <= 0)
+ to_chat(xeno_candidate, SPAN_WARNING("There aren't any Hives active at this point for you to join."))
+ return FALSE
+
+ if(active_hives.len > 1)
+ var/hive_picked = tgui_input_list(xeno_candidate, "Select which Hive to attempt joining.", "Hive Choice", active_hives, theme="hive_status")
+ if(!hive_picked)
+ to_chat(xeno_candidate, SPAN_ALERT("Hive choice error. Aborting."))
+ return
+ hive = GLOB.hive_datum[active_hives[hive_picked]]
+ else
+ hive = GLOB.hive_datum[last_active_hive]
+
+ if(!hive.hive_location)
+ to_chat(xeno_candidate, SPAN_WARNING("The selected hive does not have a hive core to spawn from!"))
+ return
+
+ hive.hive_location.spawn_lesser_drone(xeno_candidate)
+
+ return TRUE
+
/datum/game_mode/proc/transfer_xeno(xeno_candidate, mob/living/new_xeno)
if(!xeno_candidate || !isxeno(new_xeno) || QDELETED(new_xeno))
return FALSE
diff --git a/code/game/gamemodes/cm_process.dm b/code/game/gamemodes/cm_process.dm
index e4e0e32adff4..82f0902e2f8c 100644
--- a/code/game/gamemodes/cm_process.dm
+++ b/code/game/gamemodes/cm_process.dm
@@ -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..067201277e38 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/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 55eee2e521bf..54f79ce32881 100644
--- a/code/game/jobs/access.dm
+++ b/code/game/jobs/access.dm
@@ -2,17 +2,28 @@
//returns FALSE otherwise
/obj/proc/allowed(mob/M)
//check if it doesn't require any access at all
- if(check_access()) return TRUE
- if(isRemoteControlling(M)) return TRUE //AI can do whatever he wants
+ if(check_access() || isRemoteControlling(M))
+ return TRUE
- else if(ishuman(M))
+ if(ishuman(M))
var/mob/living/carbon/human/H = M
//if they are holding or wearing a card that has access, that works
- if(check_access(H.get_active_hand()) || check_access(H.wear_id)) return TRUE
- else if(istype(M, /mob/living/carbon/xenomorph))
+ if(check_access(H.get_active_hand()) || check_access(H.wear_id))
+ return TRUE
+ return check_yautja_access(H)
+ if(istype(M, /mob/living/carbon/xenomorph))
var/mob/living/carbon/C = M
- if(check_access(C.get_active_hand())) return TRUE
- return FALSE
+ if(check_access(C.get_active_hand()))
+ return TRUE
+ return FALSE
+
+/obj/proc/check_yautja_access(mob/living/carbon/human/yautja)
+ if(!istype(yautja))
+ return FALSE
+ var/obj/item/clothing/gloves/yautja/hunter/bracer = yautja.gloves
+ if(!istype(bracer) || !bracer.embedded_id || !check_access(bracer.embedded_id))
+ return FALSE
+ return TRUE
/obj/item/proc/GetAccess() return list()
diff --git a/code/game/jobs/job/marine/marine.dm b/code/game/jobs/job/marine/marine.dm
index a64701b8bfc7..e07c1edd3138 100644
--- a/code/game/jobs/job/marine/marine.dm
+++ b/code/game/jobs/job/marine/marine.dm
@@ -5,15 +5,15 @@
spawn_positions = 8
allow_additional = 1
-/datum/job/marine/generate_entry_message(mob/living/carbon/human/H)
- if(H.assigned_squad)
- entry_message_intro = "You are a [title]!
You have been assigned to: [lowertext(H.assigned_squad.name)] squad.[Check_WO() ? "" : " Make your way to the cafeteria for some post-cryosleep chow, and then get equipped in your squad's prep room." ]"
+/datum/job/marine/generate_entry_message(mob/living/carbon/human/current_human)
+ if(current_human.assigned_squad)
+ entry_message_intro = "You are a [title]!
You have been assigned to: [lowertext(current_human.assigned_squad.name)] squad.[Check_WO() ? "" : " Make your way to the cafeteria for some post-cryosleep chow, and then get equipped in your squad's prep room." ]"
return ..()
-/datum/job/marine/generate_entry_conditions(mob/living/carbon/human/H)
+/datum/job/marine/generate_entry_conditions(mob/living/carbon/human/current_human)
..()
if(!Check_WO())
- H.nutrition = rand(NUTRITION_VERYLOW, NUTRITION_LOW) //Start hungry for the default marine.
+ current_human.nutrition = rand(NUTRITION_VERYLOW, NUTRITION_LOW) //Start hungry for the default marine.
/datum/timelock/squad
name = "Squad Roles"
diff --git a/code/game/jobs/job/marine/squad_info.dm b/code/game/jobs/job/marine/squad_info.dm
index 1dc4ad7181cd..7e7dfcc0229a 100644
--- a/code/game/jobs/job/marine/squad_info.dm
+++ b/code/game/jobs/job/marine/squad_info.dm
@@ -17,7 +17,7 @@
update_squad_leader()
var/list/data = squad_info_data.Copy()
data["squad"] = name
- data["squad_color"] = squad_colors[color]
+ data["squad_color"] = equipment_color
data["is_lead"] = get_leadership(user)
data["objective"] = list(
"primary" = primary_objective,
diff --git a/code/game/jobs/job/marine/squads.dm b/code/game/jobs/job/marine/squads.dm
index 9f4068e8450f..fb85be012d30 100644
--- a/code/game/jobs/job/marine/squads.dm
+++ b/code/game/jobs/job/marine/squads.dm
@@ -25,29 +25,51 @@
sub_leader = "Strike Leader"
/datum/squad
- var/name //Name of the squad
+ /// Name of the squad
+ var/name
+ /// Squads ID that is set on New()
var/tracking_id = null //Used for the tracking subsystem
- var/max_positions = -1 //Maximum number allowed in a squad. Defaults to infinite
- var/color = 0 //Color for helmets, etc.
- var/list/access = list() //Which special access do we grant them
- var/omni_squad_vendor = FALSE /// Can use any squad vendor regardless of squad connection
- var/max_engineers = 3 //maximum # of engineers allowed in squad
- var/max_medics = 4 //Ditto, squad medics
+ /// Maximum number allowed in a squad. Defaults to infinite
+ var/max_positions = -1
+ /// Color for the squad marines gear overlays
+ var/equipment_color = "#FFFFFF"
+ /// The alpha for the armor overlay used by equipment color
+ var/armor_alpha = 125
+ /// Color for the squad marines langchat
+ var/chat_color = "#FFFFFF"
+ /// Which special access do we grant them
+ var/list/access = list()
+ /// Can use any squad vendor regardless of squad connection
+ var/omni_squad_vendor = FALSE
+ /// maximum # of engineers allowed in the squad
+ var/max_engineers = 3
+ /// maximum # of squad medics allowed in the squad
+ var/max_medics = 4
+ /// maximum # of specs allowed in the squad
var/max_specialists = 1
+ /// maximum # of fireteam leaders allowed in the suqad
var/max_tl = 2
+ /// maximum # of smartgunners allowed in the squad
var/max_smartgun = 1
+ /// maximum # of squad leaders allowed in the squad
var/max_leaders = 1
- var/radio_freq = 1461 //Squad radio headset frequency.
-
- ///Variables for showing up in various places
- var/usable = FALSE //Is it used in-game?
- var/roundstart = TRUE /// Whether this squad can be picked at roundstart
- var/locked = FALSE //Is it available for squad management?
- var/active = FALSE //Is it visible in overwatch?
- var/faction = FACTION_MARINE //What faction runs the squad?
-
- ///Squad Type Specifics
+ /// Squad headsets default radio frequency
+ var/radio_freq = 1461
+
+ /// Whether this squad can be used by marines
+ var/usable = FALSE
+ /// Whether this squad can be picked at roundstart
+ var/roundstart = TRUE
+ // Whether the squad is available for squad management
+ var/locked = FALSE
+ /// Whether it is visible in overwatch
+ var/active = FALSE
+ /// Which faction the squad is in
+ var/faction = FACTION_MARINE
+
+ /// What will the assistant squad leader be called
var/squad_type = "Squad" //Referenced for aSL details. Squad/Team/Cell etc.
+ /// Squad leaders icon
var/lead_icon //Referenced for SL's 'L' icon. If nulled, won't override icon for aSLs.
//vvv Do not set these in squad defines
@@ -96,35 +118,40 @@
/datum/squad/marine/alpha
name = SQUAD_MARINE_1
- color = 1
+ equipment_color = "#e61919"
+ chat_color = "#e67d7d"
access = list(ACCESS_MARINE_ALPHA)
radio_freq = ALPHA_FREQ
minimap_color = MINIMAP_SQUAD_ALPHA
/datum/squad/marine/bravo
name = SQUAD_MARINE_2
- color = 2
+ equipment_color = "#ffc32d"
+ chat_color = "#ffe650"
access = list(ACCESS_MARINE_BRAVO)
radio_freq = BRAVO_FREQ
minimap_color = MINIMAP_SQUAD_BRAVO
/datum/squad/marine/charlie
name = SQUAD_MARINE_3
- color = 3
+ equipment_color = "#c864c8"
+ chat_color = "#ff96ff"
access = list(ACCESS_MARINE_CHARLIE)
radio_freq = CHARLIE_FREQ
minimap_color = MINIMAP_SQUAD_CHARLIE
/datum/squad/marine/delta
name = SQUAD_MARINE_4
- color = 4
+ equipment_color = "#4148c8"
+ chat_color = "#828cff"
access = list(ACCESS_MARINE_DELTA)
radio_freq = DELTA_FREQ
minimap_color = MINIMAP_SQUAD_DELTA
/datum/squad/marine/echo
name = SQUAD_MARINE_5
- color = 5
+ equipment_color = "#67d692"
+ chat_color = "#67d692"
access = list(ACCESS_MARINE_ALPHA, ACCESS_MARINE_BRAVO, ACCESS_MARINE_CHARLIE, ACCESS_MARINE_DELTA)
radio_freq = ECHO_FREQ
omni_squad_vendor = TRUE
@@ -136,7 +163,8 @@
/datum/squad/marine/cryo
name = SQUAD_MARINE_CRYO
- color = 6
+ equipment_color = "#c47a50"
+ chat_color = "#c47a50"
access = list(ACCESS_MARINE_ALPHA, ACCESS_MARINE_BRAVO, ACCESS_MARINE_CHARLIE, ACCESS_MARINE_DELTA)
minimap_color = MINIMAP_SQUAD_FOXTROT
@@ -149,7 +177,8 @@
/datum/squad/marine/sof
name = SQUAD_SOF
- color = 7
+ equipment_color = "#400000"
+ chat_color = "#400000"
radio_freq = SOF_FREQ
squad_type = "Team"
lead_icon = "soctl"
@@ -168,23 +197,28 @@
/datum/squad/upp/one
name = "UPPS1"
- color = 1
+ equipment_color = "#e61919"
+ chat_color = "#e67d7d"
/datum/squad/upp/twp
name = "UPPS2"
- color = 2
+ equipment_color = "#ffc32d"
+ chat_color = "#ffe650"
/datum/squad/upp/three
name = "UPPS3"
- color = 3
+ equipment_color = "#c864c8"
+ chat_color = "#ff96ff"
/datum/squad/upp/four
name = "UPPS4"
- color = 4
+ equipment_color = "#4148c8"
+ chat_color = "#828cff"
/datum/squad/upp/kdo
name = "UPPKdo"
- color = 6
+ equipment_color = "#c47a50"
+ chat_color = "#c47a50"
squad_type = "Team"
locked = TRUE
//###############################
@@ -197,11 +231,13 @@
/datum/squad/pmc/one
name = "Team Upsilon"
- color = 3
+ equipment_color = "#c864c8"
+ chat_color = "#ff96ff"
/datum/squad/pmc/two
name = "Team Gamma"
- color = 6
+ equipment_color = "#c47a50"
+ chat_color = "#c47a50"
/datum/squad/pmc/wo
name = "Taskforce White"
@@ -331,7 +367,7 @@
/// Displays a message to squad members directly on the game map
/datum/squad/proc/send_maptext(text = "", title_text = "", only_leader = 0)
- var/message_colour = squad_colors_chat[color]
+ var/message_colour = chat_color
if(only_leader)
if(squad_leader)
var/mob/living/carbon/human/SL = squad_leader
diff --git a/code/game/jobs/role_authority.dm b/code/game/jobs/role_authority.dm
index b909c38cd9e6..e7697d54f0de 100644
--- a/code/game/jobs/role_authority.dm
+++ b/code/game/jobs/role_authority.dm
@@ -762,6 +762,8 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou
M = /mob/living/carbon/xenomorph/larva/predalien
if(XENO_CASTE_FACEHUGGER)
M = /mob/living/carbon/xenomorph/facehugger
+ if(XENO_CASTE_LESSER_DRONE)
+ M = /mob/living/carbon/xenomorph/lesser_drone
if(XENO_CASTE_RUNNER)
M = /mob/living/carbon/xenomorph/runner
if(XENO_CASTE_DRONE)
diff --git a/code/game/machinery/ARES/ARES.dm b/code/game/machinery/ARES/ARES.dm
index f8a7351d123e..fd85aff215db 100644
--- a/code/game/machinery/ARES/ARES.dm
+++ b/code/game/machinery/ARES/ARES.dm
@@ -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 79c49818595c..6c5bd1dec08a 100644
--- a/code/game/machinery/ARES/ARES_procs.dm
+++ b/code/game/machinery/ARES/ARES_procs.dm
@@ -1,4 +1,18 @@
GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
+GLOBAL_LIST_INIT(maintenance_categories, list(
+ "Broken Light",
+ "Shattered Glass",
+ "Minor Structural Damage",
+ "Major Structural Damage",
+ "Janitorial",
+ "Chemical Spill",
+ "Fire",
+ "Communications Failure",
+ "Power Generation Failure",
+ "Electrical Fault",
+ "Support",
+ "Other"
+ ))
/datum/ares_link
var/link_id = MAIN_SHIP_DEFAULT_NAME
@@ -35,14 +49,12 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
speaker = "Unknown"
var/datum/ares_link/link = GLOB.ares_link
if(!link.p_apollo || link.p_apollo.inoperable())
- return
+ return FALSE
if(!link.p_interface || link.p_interface.inoperable())
- return
+ return FALSE
link.apollo_log.Add("[worldtime2text()]: [speaker], '[message]'")
/datum/ares_link/proc/log_ares_bioscan(title, input)
- if(!p_bioscan || p_bioscan.inoperable() || !interface)
- return FALSE
interface.records_bioscan.Add(new /datum/ares_record/bioscan(title, input))
/datum/ares_link/proc/log_ares_bombardment(mob/living/user, ob_name, coordinates)
@@ -61,6 +73,32 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
interface.records_security.Add(new /datum/ares_record/security(title, details))
// ------ End ARES Logging Procs ------ //
+/proc/ares_apollo_talk(broadcast_message)
+ var/datum/language/apollo/apollo = GLOB.all_languages[LANGUAGE_APOLLO]
+ for(var/mob/living/silicon/decoy/ship_ai/ai in ai_mob_list)
+ if(ai.stat == DEAD)
+ return FALSE
+ apollo.broadcast(ai, broadcast_message)
+ for(var/mob/listener in (GLOB.human_mob_list + GLOB.dead_mob_list))
+ if(listener.hear_apollo())//Only plays sound to mobs and not observers, to reduce spam.
+ playsound_client(listener.client, sound('sound/misc/interference.ogg'), listener, vol = 45)
+
+/proc/ares_can_interface()
+ var/obj/structure/machinery/ares/processor/interface/processor = GLOB.ares_link.p_interface
+ if(!istype(GLOB.ares_link))
+ return FALSE
+ if(processor && !processor.inoperable())
+ return TRUE
+ return FALSE //interface processor not found or is broken
+
+/proc/ares_can_log()
+ var/obj/structure/machinery/computer/ares_console/interface = GLOB.ares_link.interface
+ if(!istype(GLOB.ares_link))
+ return FALSE
+ if(interface && !interface.inoperable())
+ return TRUE
+ return FALSE //ares interface not found or is broken
+
// ------ ARES Interface Procs ------ //
/obj/structure/machinery/computer/proc/get_ares_access(obj/item/card/id/card)
if(ACCESS_ARES_DEBUG in card.access)
@@ -463,6 +501,19 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
current_menu = "read_deleted"
// -- Emergency Buttons -- //
+ if("general_quarters")
+ if(security_level == SEC_LEVEL_RED || security_level == SEC_LEVEL_DELTA)
+ to_chat(usr, SPAN_WARNING("Alert level is already red or above, General Quarters cannot be called."))
+ playsound(src, 'sound/machines/buzz-two.ogg', 15, 1)
+ return FALSE
+ set_security_level(2, no_sound = TRUE, announce = FALSE)
+ shipwide_ai_announcement("ATTENTION! GENERAL QUARTERS. ALL HANDS, MAN YOUR BATTLESTATIONS.", MAIN_AI_SYSTEM, 'sound/effects/GQfullcall.ogg')
+ log_game("[key_name(usr)] has called for general quarters via ARES.")
+ message_admins("[key_name_admin(usr)] has called for general quarters via ARES.")
+ var/datum/ares_link/link = GLOB.ares_link
+ link.log_ares_security("General Quarters", "[last_login] has called for general quarters via ARES.")
+ . = TRUE
+
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."))
@@ -525,15 +576,22 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
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 ------ //
@@ -551,7 +609,7 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
return APOLLO_ACCESS_AUTHED
if(ACCESS_MARINE_AI_TEMP in card.access)
return APOLLO_ACCESS_TEMP
- if((ACCESS_MARINE_COMMAND in card.access ) || (ACCESS_MARINE_ENGINEERING in card.access) || (ACCESS_WY_CORPORATE in card.access))
+ if((ACCESS_MARINE_SENIOR in card.access ) || (ACCESS_MARINE_ENGINEERING in card.access) || (ACCESS_WY_CORPORATE in card.access))
return APOLLO_ACCESS_REPORTER
else
return APOLLO_ACCESS_REQUEST
@@ -577,7 +635,8 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
set name = "Eject ID Card"
set src in oview(1)
- if(!usr || usr.stat || usr.lying) return
+ if(!usr || usr.stat || usr.lying)
+ return FALSE
if(authenticator_id)
authenticator_id.loc = get_turf(src)
@@ -604,11 +663,14 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
/obj/structure/machinery/computer/working_joe/attackby(obj/object, mob/user)
if(istype(object, /obj/item/card/id))
+ if(!ticket_console)
+ to_chat(user, SPAN_WARNING("This console doesn't have an ID port!"))
+ return FALSE
if(!operable())
to_chat(user, SPAN_NOTICE("You try to insert [object] but [src] remains silent."))
- return
+ return FALSE
var/obj/item/card/id/idcard = object
- if((ACCESS_MARINE_AI in idcard.access) || (ACCESS_ARES_DEBUG in idcard.access))
+ if((idcard.assignment == JOB_WORKING_JOE) || (ACCESS_ARES_DEBUG in idcard.access))
if(!authenticator_id)
if(user.drop_held_item())
object.forceMove(src)
@@ -620,7 +682,7 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
target_id = object
else
to_chat(user, "Both slots are full already. Remove a card first.")
- return
+ return FALSE
else
if(!target_id)
if(user.drop_held_item())
@@ -628,7 +690,7 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
target_id = object
else
to_chat(user, "Both slots are full already. Remove a card first.")
- return
+ return FALSE
else
..()
@@ -689,26 +751,42 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
for(var/datum/ares_ticket/maintenance/maint_ticket as anything in link.tickets_maintenance)
if(!istype(maint_ticket))
continue
+ var/lock_status = TICKET_OPEN
+ switch(maint_ticket.ticket_status)
+ if(TICKET_REJECTED, TICKET_CANCELLED, TICKET_COMPLETED)
+ lock_status = TICKET_CLOSED
+
var/list/current_maint = list()
+ current_maint["id"] = maint_ticket.ticket_id
current_maint["time"] = maint_ticket.ticket_time
- current_maint["title"] = maint_ticket.ticket_name
+ current_maint["priority_status"] = maint_ticket.ticket_priority
+ current_maint["category"] = maint_ticket.ticket_name
current_maint["details"] = maint_ticket.ticket_details
current_maint["status"] = maint_ticket.ticket_status
current_maint["submitter"] = maint_ticket.ticket_submitter
current_maint["assignee"] = maint_ticket.ticket_assignee
+ current_maint["lock_status"] = lock_status
current_maint["ref"] = "\ref[maint_ticket]"
logged_maintenance += list(current_maint)
data["maintenance_tickets"] = logged_maintenance
var/list/logged_access = list()
- for(var/datum/ares_ticket/access_ticket/access_ticket as anything in link.tickets_access)
+ for(var/datum/ares_ticket/access/access_ticket as anything in link.tickets_access)
+ var/lock_status = TICKET_OPEN
+ switch(access_ticket.ticket_status)
+ if(TICKET_REJECTED, TICKET_CANCELLED, TICKET_COMPLETED)
+ lock_status = TICKET_CLOSED
+
var/list/current_ticket = list()
+ current_ticket["id"] = access_ticket.ticket_id
current_ticket["time"] = access_ticket.ticket_time
+ current_ticket["priority_status"] = access_ticket.ticket_priority
current_ticket["title"] = access_ticket.ticket_name
current_ticket["details"] = access_ticket.ticket_details
current_ticket["status"] = access_ticket.ticket_status
current_ticket["submitter"] = access_ticket.ticket_submitter
current_ticket["assignee"] = access_ticket.ticket_assignee
+ current_ticket["lock_status"] = lock_status
current_ticket["ref"] = "\ref[access_ticket]"
logged_access += list(current_ticket)
data["access_tickets"] = logged_access
@@ -758,7 +836,7 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
authentication = get_ares_access(idcard)
last_login = idcard.registered_name
else
- to_chat(usr, SPAN_WARNING("You require an ID card to access this terminal!"))
+ to_chat(operator, SPAN_WARNING("You require an ID card to access this terminal!"))
playsound(src, 'sound/machines/buzz-two.ogg', 15, 1)
return FALSE
if(authentication)
@@ -796,20 +874,113 @@ GLOBAL_DATUM_INIT(ares_link, /datum/ares_link, new)
current_menu = "maint_claim"
if("new_report")
- var/name = tgui_input_text(usr, "What is the type of maintenance item you wish to report?\n\nExample:\n 'Broken light in Aft Hallway.'", "Ticket Name", encode = FALSE)
- if(!name)
+ var/priority_report = FALSE
+ var/maint_type = tgui_input_list(operator, "What is the type of maintenance item you wish to report?", "Report Category", GLOB.maintenance_categories, 30 SECONDS)
+ switch(maint_type)
+ if("Major Structural Damage", "Fire", "Communications Failure", "Power Generation Failure")
+ priority_report = TRUE
+
+ if(!maint_type)
return FALSE
- var/details = tgui_input_text(usr, "What are the details for this report?", "Ticket Details", encode = FALSE)
+ var/details = tgui_input_text(operator, "What are the details for this report?", "Ticket Details", encode = FALSE)
if(!details)
return FALSE
- var/confirm = tgui_alert(usr, "Please confirm the submission of your maintenance report. \n\n [name] \n\n [details] \n\n Is this correct?", "Confirmation", list("Yes", "No"))
+
+ if((authentication >= APOLLO_ACCESS_REPORTER) && !priority_report)
+ var/is_priority = tgui_alert(operator, "Is this a priority report?", "Priority designation", list("Yes", "No"))
+ if(is_priority == "Yes")
+ priority_report = TRUE
+
+ var/confirm = alert(operator, "Please confirm the submission of your maintenance report. \n\n Priority: [priority_report ? "Yes" : "No"] \n Category: '[maint_type]' \n Details: '[details]' \n\n Is this correct?", "Confirmation", "Yes", "No")
if(confirm == "Yes")
if(link)
- var/datum/ares_ticket/maintenance/maint_ticket = new(last_login, name, details)
+ var/datum/ares_ticket/maintenance/maint_ticket = new(last_login, maint_type, details, priority_report)
link.tickets_maintenance += maint_ticket
- log_game("ARES: Maintenance Ticket created by [key_name(operator)] as [last_login] with Header '[name]' and Details of '[details]'.")
+ if(priority_report)
+ ares_apollo_talk("Priority Maintenance Report: [maint_type] - ID [maint_ticket.ticket_id]. Seek and resolve.")
+ log_game("ARES: Maintenance Ticket '\ref[maint_ticket]' created by [key_name(operator)] as [last_login] with Category '[maint_type]' and Details of '[details]'.")
return TRUE
return FALSE
+ if("claim_ticket")
+ var/datum/ares_ticket/ticket = locate(params["ticket"])
+ if(!istype(ticket))
+ return FALSE
+ var/claim = TRUE
+ var/assigned = ticket.ticket_assignee
+ if(assigned)
+ if(assigned == last_login)
+ var/prompt = tgui_alert(usr, "You already claimed this ticket! Do you wish to drop your claim?", "Unclaim ticket", list("Yes", "No"))
+ if(prompt != "Yes")
+ return FALSE
+ /// set ticket back to pending
+ ticket.ticket_assignee = null
+ ticket.ticket_status = TICKET_PENDING
+ return claim
+ var/choice = tgui_alert(usr, "This ticket has already been claimed by [assigned]! Do you wish to override their claim?", "Claim Override", list("Yes", "No"))
+ if(choice != "Yes")
+ claim = FALSE
+ if(claim)
+ ticket.ticket_assignee = last_login
+ ticket.ticket_status = TICKET_ASSIGNED
+ return claim
+
+ if("cancel_ticket")
+ var/datum/ares_ticket/ticket = locate(params["ticket"])
+ if(!istype(ticket))
+ return FALSE
+ if(ticket.ticket_submitter != last_login)
+ to_chat(usr, SPAN_WARNING("You cannot cancel a ticket that does not belong to you!"))
+ return FALSE
+ to_chat(usr, SPAN_WARNING("[ticket.ticket_type] [ticket.ticket_id] has been cancelled."))
+ ticket.ticket_status = TICKET_CANCELLED
+ if(ticket.ticket_priority)
+ ares_apollo_talk("Priority [ticket.ticket_type] [ticket.ticket_id] has been cancelled.")
+ return TRUE
+
+ if("mark_ticket")
+ var/datum/ares_ticket/ticket = locate(params["ticket"])
+ if(!istype(ticket))
+ return FALSE
+ if(ticket.ticket_assignee != last_login && ticket.ticket_assignee) //must be claimed by you or unclaimed.)
+ to_chat(usr, SPAN_WARNING("You cannot update a ticket that is not assigned to you!"))
+ return FALSE
+ var/choice = tgui_alert(usr, "What do you wish to mark the ticket as?", "Mark", list(TICKET_COMPLETED, TICKET_REJECTED), 20 SECONDS)
+ switch(choice)
+ if(TICKET_COMPLETED)
+ ticket.ticket_status = TICKET_COMPLETED
+ if(TICKET_REJECTED)
+ ticket.ticket_status = TICKET_REJECTED
+ else
+ return FALSE
+ if(ticket.ticket_priority)
+ ares_apollo_talk("Priority [ticket.ticket_type] [ticket.ticket_id] has been [choice] by [last_login].")
+ to_chat(usr, SPAN_NOTICE("[ticket.ticket_type] [ticket.ticket_id] marked as [choice]."))
+ return TRUE
+
+ if("new_access")
+ var/priority_report = FALSE
+ var/ticket_holder = tgui_input_text(operator, "Who is the ticket for?", "Ticket Holder", encode = FALSE)
+ if(!ticket_holder)
+ return FALSE
+ var/details = tgui_input_text(operator, "What is the purpose of this access ticket?", "Ticket Details", encode = FALSE)
+ if(!details)
+ return FALSE
+
+ if(authentication >= APOLLO_ACCESS_AUTHED)
+ var/is_priority = tgui_alert(operator, "Is this a priority request?", "Priority designation", list("Yes", "No"))
+ if(is_priority == "Yes")
+ priority_report = TRUE
+
+ var/confirm = alert(operator, "Please confirm the submission of your access ticket request. \n\n Priority: [priority_report ? "Yes" : "No"] \n Holder: '[ticket_holder]' \n Details: '[details]' \n\n Is this correct?", "Confirmation", "Yes", "No")
+ if(confirm != "Yes" || !link)
+ return FALSE
+ var/datum/ares_ticket/access/access_ticket = new(last_login, ticket_holder, details, priority_report)
+ link.tickets_access += access_ticket
+ if(priority_report)
+ ares_apollo_talk("Priority Access Request: [ticket_holder] - ID [access_ticket.ticket_id]. Seek and resolve.")
+ log_game("ARES: Access Ticket '\ref[access_ticket]' created by [key_name(operator)] as [last_login] with Holder '[ticket_holder]' and Details of '[details]'.")
+ return TRUE
+
if(playsound)
playsound(src, "keyboard_alt", 15, 1)
diff --git a/code/game/machinery/ARES/ARES_records.dm b/code/game/machinery/ARES/ARES_records.dm
index 9cb8574e58f7..4e2b479e71a2 100644
--- a/code/game/machinery/ARES/ARES_records.dm
+++ b/code/game/machinery/ARES/ARES_records.dm
@@ -77,6 +77,8 @@
var/ticket_status = TICKET_PENDING
/// Name of who is handling the ticket. Derived from last login.
var/ticket_assignee
+ /// Numerical designation of the ticket.
+ var/ticket_id = "1111"
/// World time in text format.
var/ticket_time
/// Who submitted the ticket. Derived from last login.
@@ -85,15 +87,23 @@
var/ticket_name
/// The content of the ticket, usually an explanation of what it is for.
var/ticket_details
+ /// Whether or not the tickey is a priority.
+ var/ticket_priority = FALSE
+
+/datum/ares_ticket/New(user, name, details, priority)
+ var/ref_holder = "\ref[src]"
+ var/pos = length(ref_holder)
+ var/new_id = "#[copytext("\ref[src]", pos - 4, pos)]"
-/datum/ares_ticket/New(user, name, details)
ticket_time = worldtime2text()
ticket_submitter = user
ticket_details = details
ticket_name = name
+ ticket_priority = priority
+ ticket_id = new_id
/datum/ares_ticket/maintenance
ticket_type = ARES_RECORD_MAINTENANCE
-/datum/ares_ticket/access_ticket
+/datum/ares_ticket/access
ticket_type = ARES_RECORD_ACCESS
diff --git a/code/game/machinery/ARES/ARES_step_triggers.dm b/code/game/machinery/ARES/ARES_step_triggers.dm
index 1562f1badaab..a50aa40abd90 100644
--- a/code/game/machinery/ARES/ARES_step_triggers.dm
+++ b/code/game/machinery/ARES/ARES_step_triggers.dm
@@ -74,12 +74,7 @@
return FALSE
to_chat(passer, SPAN_BOLDWARNING("You hear a soft beeping sound as you cross the threshold."))
- var/datum/language/apollo/apollo = GLOB.all_languages[LANGUAGE_APOLLO]
- for(var/mob/living/silicon/decoy/ship_ai/ai in ai_mob_list)
- apollo.broadcast(ai, broadcast_message)
- for(var/mob/listener as anything in (GLOB.human_mob_list + GLOB.dead_mob_list))
- if(listener.hear_apollo())//Only plays sound to mobs and not observers, to reduce spam.
- playsound_client(listener.client, sound('sound/misc/interference.ogg'), listener, vol = 45)
+ ares_apollo_talk(broadcast_message)
COOLDOWN_START(src, sensor_cooldown, cooldown_duration)
if(alert_id && link)
for(var/obj/effect/step_trigger/ares_alert/sensor in link.linked_alerts)
@@ -166,12 +161,7 @@
return FALSE
to_chat(passer, SPAN_BOLDWARNING("You hear a harsh buzzing sound as you cross the threshold!"))
- var/datum/language/apollo/apollo = GLOB.all_languages[LANGUAGE_APOLLO]
- for(var/mob/living/silicon/decoy/ship_ai/ai in ai_mob_list)
- apollo.broadcast(ai, broadcast_message)
- for(var/mob/listener in (GLOB.human_mob_list + GLOB.dead_mob_list))
- if(listener.hear_apollo())//Only plays sound to mobs and not observers, to reduce spam.
- playsound_client(listener.client, sound('sound/misc/interference.ogg'), listener, vol = 45)
+ ares_apollo_talk(broadcast_message)
if(idcard)
idcard.access -= ACCESS_MARINE_AI_TEMP
COOLDOWN_START(src, sensor_cooldown, COOLDOWN_ARES_ACCESS_CONTROL)
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index 899f9f5a0e1c..373c74767bb4 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -267,6 +267,36 @@
/obj/structure/machinery/door/airlock/strata/mining/autoname
autoname = TRUE
+//YAUTJA SHIP - CURRENTLY USES STRATA DOORS
+/obj/structure/machinery/door/airlock/yautja
+ name = "\improper Airlock"
+ icon = 'icons/obj/structures/doors/strata/strata_doors.dmi'
+ openspeed = 5
+ req_access = null
+ req_one_access = null
+ tiles_with = list(
+ /obj/structure/window/framed/strata,
+ /obj/structure/machinery/door/airlock,
+ )
+ masterkey_resist = TRUE
+ no_panel = TRUE
+ not_weldable = TRUE
+ unacidable = TRUE
+
+/obj/structure/machinery/door/airlock/yautja/autoname
+ autoname = TRUE
+
+/obj/structure/machinery/door/airlock/yautja/secure
+ heavy = TRUE
+ req_one_access = list(ACCESS_YAUTJA_SECURE, ACCESS_YAUTJA_ELDER, ACCESS_YAUTJA_ANCIENT)
+
+/obj/structure/machinery/door/airlock/yautja/secure/elder
+ req_one_access = list(ACCESS_YAUTJA_ELDER, ACCESS_YAUTJA_ANCIENT)
+
+/obj/structure/machinery/door/airlock/yautja/secure/ancient
+ req_one_access = list(ACCESS_YAUTJA_ANCIENT)
+ unslashable = TRUE
+
//FIORINA PENITENTIARY (PRISON_FOP) MAINTENANCE HATCHES
/obj/structure/machinery/door/airlock/prison_hatch
diff --git a/code/game/machinery/doors/shutters.dm b/code/game/machinery/doors/shutters.dm
index 68b0464f87f8..39ecbd806e64 100644
--- a/code/game/machinery/doors/shutters.dm
+++ b/code/game/machinery/doors/shutters.dm
@@ -87,6 +87,17 @@
. = ..()
relativewall_neighbours()
+/obj/structure/machinery/door/poddoor/shutters/almayer/yautja
+ name = "Armory Shutter"
+ id = "Yautja Armory"
+ needs_power = FALSE
+ unacidable = TRUE
+ indestructible = TRUE
+
+/obj/structure/machinery/door/poddoor/shutters/almayer/yautja/Initialize()
+ . = ..()
+ RegisterSignal(SSdcs, COMSIG_GLOB_YAUTJA_ARMORY_OPENED, PROC_REF(open))
+
/obj/structure/machinery/door/poddoor/shutters/almayer/containment
unacidable = TRUE
diff --git a/code/game/machinery/nuclearbomb.dm b/code/game/machinery/nuclearbomb.dm
index 6c0ec4cc0389..743f53e4f03b 100644
--- a/code/game/machinery/nuclearbomb.dm
+++ b/code/game/machinery/nuclearbomb.dm
@@ -12,7 +12,7 @@ var/bomb_set = FALSE
var/timing = FALSE
var/deployable = FALSE
var/explosion_time = null
- var/timeleft = 4800
+ var/timeleft = 8 MINUTES
var/safety = TRUE
var/being_used = FALSE
var/end_round = TRUE
@@ -152,6 +152,7 @@ var/bomb_set = FALSE
data["command_lockout"] = command_lockout
data["allowed"] = allowed
data["being_used"] = being_used
+ data["decryption_complete"] = TRUE //this is overriden by techweb nuke UI_data later, this just makes it default to true
return data
diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm
index 50abb701145e..57d0e49a58bc 100644
--- a/code/game/machinery/vending/cm_vending.dm
+++ b/code/game/machinery/vending/cm_vending.dm
@@ -1182,6 +1182,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_corresponding_types_list, list(
sleep(15)
vendor.stat &= ~IN_USE
+ vendor.icon_state = initial(vendor.icon_state)
vendor.update_icon()
/proc/vendor_successful_vend_one(obj/structure/machinery/cm_vending/vendor, prod_type, mob/living/carbon/human/user, turf/target_turf, insignas_override)
diff --git a/code/game/machinery/vending/vendor_types/crew/medical.dm b/code/game/machinery/vending/vendor_types/crew/medical.dm
index ccf4abe03282..f1574c8104ff 100644
--- a/code/game/machinery/vending/vendor_types/crew/medical.dm
+++ b/code/game/machinery/vending/vendor_types/crew/medical.dm
@@ -145,7 +145,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_researcher, 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/doc, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY),
+ list("Headset", 0, /obj/item/device/radio/headset/almayer/research, 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("Reagent Scanner HUD Goggles", 0, /obj/item/clothing/glasses/science, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_RECOMMENDED),
diff --git a/code/game/machinery/vending/vendor_types/crew/synthetic.dm b/code/game/machinery/vending/vendor_types/crew/synthetic.dm
index be6939a6f9dd..dd11cea0f242 100644
--- a/code/game/machinery/vending/vendor_types/crew/synthetic.dm
+++ b/code/game/machinery/vending/vendor_types/crew/synthetic.dm
@@ -295,7 +295,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_synth_snowflake, list(
/obj/structure/machinery/cm_vending/own_points/experimental_tools/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/coin/marine/synth))
if(user.drop_inv_item_to_loc(W, src))
- available_points = available_points + 45
+ available_points = 45
available_points_to_display = available_points
to_chat(user, SPAN_NOTICE("You insert \the [W] into \the [src]."))
return
diff --git a/code/game/machinery/vending/vendor_types/requisitions.dm b/code/game/machinery/vending/vendor_types/requisitions.dm
index b8e3e55f1d89..f85657e887a8 100644
--- a/code/game/machinery/vending/vendor_types/requisitions.dm
+++ b/code/game/machinery/vending/vendor_types/requisitions.dm
@@ -92,26 +92,27 @@
list("POUCHES", -1, null, null),
list("Autoinjector Pouch", round(scale * 2), /obj/item/storage/pouch/autoinjector, VENDOR_ITEM_REGULAR),
- list("Bayonet Pouch", round(scale * 2), /obj/item/storage/pouch/bayonet, VENDOR_ITEM_REGULAR),
+ list("Medkit Pouch", round(scale * 2), /obj/item/storage/pouch/medkit, VENDOR_ITEM_REGULAR),
+ list("Medical Pouch", round(scale * 2), /obj/item/storage/pouch/medical, VENDOR_ITEM_REGULAR),
+ list("First-Aid Pouch (Full)", round(scale * 5), /obj/item/storage/pouch/firstaid/full, VENDOR_ITEM_REGULAR),
+ list("First Responder Pouch", round(scale * 2), /obj/item/storage/pouch/first_responder, VENDOR_ITEM_REGULAR),
+ list("Syringe Pouch", round(scale * 2), /obj/item/storage/pouch/syringe, VENDOR_ITEM_REGULAR),
+ list("Tools Pouch (Full)", round(scale * 2), /obj/item/storage/pouch/tools/full, VENDOR_ITEM_REGULAR),
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("Electronics Pouch", round(scale * 2), /obj/item/storage/pouch/electronics, 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, VENDOR_ITEM_REGULAR),
- list("First Responder Pouch", round(scale * 2), /obj/item/storage/pouch/first_responder, VENDOR_ITEM_REGULAR),
list("Flare Pouch (Full)", round(scale * 5), /obj/item/storage/pouch/flare/full, VENDOR_ITEM_REGULAR),
- list("Fuel Tank Strap Pouch", round(scale * 4), /obj/item/storage/pouch/flamertank, VENDOR_ITEM_REGULAR),
- list("Large Pistol Magazine Pouch", round(scale * 5), /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("Shotgun Shell Pouch", round(scale * 5), /obj/item/storage/pouch/shotgun, VENDOR_ITEM_REGULAR),
+ list("Document Pouch", round(scale * 2), /obj/item/storage/pouch/document/small, VENDOR_ITEM_REGULAR),
+ list("Sling Pouch", round(scale * 2), /obj/item/storage/pouch/sling, VENDOR_ITEM_REGULAR),
list("Machete Pouch (Full)", round(scale * 4), /obj/item/storage/pouch/machete/full, VENDOR_ITEM_REGULAR),
- list("Medical Pouch", round(scale * 2), /obj/item/storage/pouch/medical, VENDOR_ITEM_REGULAR),
+ list("Bayonet Pouch", round(scale * 2), /obj/item/storage/pouch/bayonet, VENDOR_ITEM_REGULAR),
list("Medium General Pouch", round(scale * 2), /obj/item/storage/pouch/general/medium, VENDOR_ITEM_REGULAR),
- list("Medkit Pouch", round(scale * 2), /obj/item/storage/pouch/medkit, VENDOR_ITEM_REGULAR),
+ list("Magazine Pouch", round(scale * 5), /obj/item/storage/pouch/magazine, VENDOR_ITEM_REGULAR),
+ list("Shotgun Shell Pouch", round(scale * 5), /obj/item/storage/pouch/shotgun, VENDOR_ITEM_REGULAR),
list("Sidearm Pouch", round(scale * 5), /obj/item/storage/pouch/pistol, VENDOR_ITEM_REGULAR),
- list("Syringe Pouch", round(scale * 2), /obj/item/storage/pouch/syringe, VENDOR_ITEM_REGULAR),
- list("Tools Pouch (Full)", round(scale * 2), /obj/item/storage/pouch/tools/full, VENDOR_ITEM_REGULAR),
- list("Sling Pouch", round(scale * 2), /obj/item/storage/pouch/sling, VENDOR_ITEM_REGULAR),
+ list("Large Pistol Magazine Pouch", round(scale * 5), /obj/item/storage/pouch/magazine/pistol/large, VENDOR_ITEM_REGULAR),
+ list("Fuel Tank Strap Pouch", round(scale * 4), /obj/item/storage/pouch/flamertank, VENDOR_ITEM_REGULAR),
+ list("Large General Pouch", round(scale * 2), /obj/item/storage/pouch/general/large, VENDOR_ITEM_REGULAR),
list("Large Magazine Pouch", round(scale * 2), /obj/item/storage/pouch/magazine/large, VENDOR_ITEM_REGULAR),
list("Large Shotgun Shell Pouch", round(scale * 2), /obj/item/storage/pouch/shotgun/large, 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..05784ec3c161 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),
diff --git a/code/game/objects/effects/effect_system/smoke.dm b/code/game/objects/effects/effect_system/smoke.dm
index 9f7a9c8143c7..2eb36930c542 100644
--- a/code/game/objects/effects/effect_system/smoke.dm
+++ b/code/game/objects/effects/effect_system/smoke.dm
@@ -180,18 +180,20 @@
/obj/effect/particle_effect/smoke/mustard/Move()
. = ..()
- for(var/mob/living/carbon/human/R in get_turf(src))
- affect(R)
+ for(var/mob/living/carbon/human/creature in get_turf(src))
+ affect(creature)
-/obj/effect/particle_effect/smoke/mustard/affect(mob/living/carbon/human/R)
- ..()
- R.burn_skin(0.75)
- if(R.coughedtime != 1)
- R.coughedtime = 1
- if(ishuman(R)) //Humans only to avoid issues
- R.emote("gasp")
- addtimer(VARSET_CALLBACK(R, coughedtime, 0), 2 SECONDS)
- R.updatehealth()
+/obj/effect/particle_effect/smoke/mustard/affect(mob/living/carbon/human/creature)
+ if(!istype(creature) || issynth(creature))
+ return FALSE
+
+ creature.burn_skin(0.75)
+ if(creature.coughedtime != 1)
+ creature.coughedtime = 1
+ if(ishuman(creature)) //Humans only to avoid issues
+ creature.emote("gasp")
+ addtimer(VARSET_CALLBACK(creature, coughedtime, 0), 2 SECONDS)
+ creature.updatehealth()
return
/////////////////////////////////////////////
@@ -244,6 +246,55 @@
M.updatehealth()
+/////////////////////////////////////////////
+// CN20 Nerve Gas
+/////////////////////////////////////////////
+
+/obj/effect/particle_effect/smoke/cn20
+ name = "CN20 nerve gas"
+ smokeranking = SMOKE_RANK_HIGH
+ color = "#80c7e4"
+
+/obj/effect/particle_effect/smoke/cn20/Move()
+ . = ..()
+ for(var/mob/living/carbon/human/creature in get_turf(src))
+ affect(creature)
+
+/obj/effect/particle_effect/smoke/cn20/affect(mob/living/carbon/human/creature)
+ if(!istype(creature) || issynth(creature) || creature.stat == DEAD)
+ return FALSE
+ if(isyautja(creature) && prob(75))
+ return FALSE
+
+ if (creature.wear_mask && (creature.wear_mask.flags_inventory & BLOCKGASEFFECT))
+ return FALSE
+
+ var/effect_amt = round(6 + amount*6)
+
+ creature.apply_damage(12, OXY)
+ creature.SetEarDeafness(max(creature.ear_deaf, round(effect_amt*1.5))) //Paralysis of hearing system, aka deafness
+ if(!creature.eye_blind) //Eye exposure damage
+ to_chat(creature, SPAN_DANGER("Your eyes sting. You can't see!"))
+ creature.SetEyeBlind(round(effect_amt/3))
+ if(creature.coughedtime != 1 && !creature.stat) //Coughing/gasping
+ creature.coughedtime = 1
+ if(prob(50))
+ creature.emote("cough")
+ else
+ creature.emote("gasp")
+ addtimer(VARSET_CALLBACK(creature, coughedtime, 0), 1.5 SECONDS)
+ if (prob(20))
+ creature.apply_effect(1, WEAKEN)
+
+ //Topical damage (neurotoxin on exposed skin)
+ to_chat(creature, SPAN_DANGER("Your body is going numb, almost as if paralyzed!"))
+ if(prob(60 + round(amount*15))) //Highly likely to drop items due to arms/hands seizing up
+ creature.drop_held_item()
+ if(ishuman(creature))
+ creature.temporary_slowdown = max(creature.temporary_slowdown, 4) //One tick every two second
+ creature.recalculate_move_delay = TRUE
+ return TRUE
+
//////////////////////////////////////
// FLASHBANG SMOKE
////////////////////////////////////
@@ -541,6 +592,9 @@
/datum/effect_system/smoke_spread/phosphorus/weak
smoke_type = /obj/effect/particle_effect/smoke/phosphorus/weak
+/datum/effect_system/smoke_spread/cn20
+ smoke_type = /obj/effect/particle_effect/smoke/cn20
+
// XENO SMOKES
/datum/effect_system/smoke_spread/xeno_acid
diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm
index 55f3c32cebba..3b84d2433e88 100644
--- a/code/game/objects/items/bodybag.dm
+++ b/code/game/objects/items/bodybag.dm
@@ -122,25 +122,27 @@
/obj/structure/closet/bodybag/store_mobs(stored_units) // overriding this
var/list/dead_mobs = list()
- for(var/mob/living/M in loc)
- if(M.buckled)
+ for(var/mob/living/mob in loc)
+ if(mob.buckled)
continue
- if(M.stat != DEAD) // covers alive mobs
+ if(mob.stat != DEAD) // covers alive mobs
continue
- if(!ishuman(M)) // all the dead other shit
- dead_mobs += M
+ if(!ishuman(mob)) // all the dead other shit
+ dead_mobs += mob
continue
- var/mob/living/carbon/human/H = M
- if(H.check_tod() || issynth(H) || H.is_revivable() && H.get_ghost()) // revivable
+ var/mob/living/carbon/human/human = mob
+ if(issynth(human))
continue
- dead_mobs += M
+ if(human.check_tod() && human.is_revivable()) // revivable
+ continue
+ dead_mobs += mob
var/mob/living/mob_to_store
if(dead_mobs.len)
mob_to_store = pick(dead_mobs)
mob_to_store.forceMove(src)
stored_units += mob_size
- for(var/obj/item/limb/L in loc)
- L.forceMove(src)
+ for(var/obj/item/limb/limb in loc)
+ limb.forceMove(src)
return stored_units
/obj/structure/closet/bodybag/attack_hand(mob/living/user)
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 6293abb67339..d45b4e8b8212 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -92,6 +92,11 @@
icon_state = "med_key"
channels = list(RADIO_CHANNEL_MEDSCI = TRUE)
+/obj/item/device/encryptionkey/medres
+ name = "Research Radio Encryption Key"
+ icon_state = "med_key"
+ channels = list(RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+
// MARINE MILITARY POLICE
/obj/item/device/encryptionkey/cmpcom
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index e8dcdac34222..c3b1eee806c5 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -439,6 +439,12 @@
icon_state = "med_headset"
initial_keys = list(/obj/item/device/encryptionkey/med)
+/obj/item/device/radio/headset/almayer/research
+ name = "researcher radio headset"
+ desc = "A headset used by medbay's skilled researchers. Channels are as follows: :m - medical, :t - intel."
+ icon_state = "med_headset"
+ initial_keys = list(/obj/item/device/encryptionkey/medres)
+
/obj/item/device/radio/headset/almayer/ct
name = "supply radio headset"
desc = "Used by the lowly Cargo Technicians of the USCM, light weight and portable. To access the supply channel, use :u."
diff --git a/code/game/objects/items/explosives/plastic.dm b/code/game/objects/items/explosives/plastic.dm
index 4c2a1774d4db..830df9659070 100644
--- a/code/game/objects/items/explosives/plastic.dm
+++ b/code/game/objects/items/explosives/plastic.dm
@@ -213,23 +213,6 @@
return TRUE
-/obj/item/explosive/plastic/breaching_charge/can_place(mob/user, atom/target)
- if(!is_type_in_list(target, breachable))//only items on the list are allowed
- to_chat(user, SPAN_WARNING("You cannot plant \the [name] on \the [target]!"))
- return FALSE
-
- if(SSinterior.in_interior(target))// vehicle checks again JUST IN CASE
- to_chat(user, SPAN_WARNING("It's too cramped in here to deploy \the [src]."))
- return FALSE
-
- if(istype(target, /obj/structure/window))//no breaching charges on the briefing windows / brig / CIC e.e
- var/obj/structure/window/W = target
- if(W.not_damageable)
- to_chat(user, SPAN_WARNING("[W] is much too tough for you to do anything to it with [src].")) //On purpose to mimic wall message
- return FALSE
-
- return TRUE
-
/obj/item/explosive/plastic/proc/calculate_pixel_offset(mob/user, atom/target)
switch(get_dir(user, target))
if(NORTH)
@@ -312,13 +295,6 @@
cell_explosion(target_turf, 120, 30, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data)
qdel(src)
-/obj/item/explosive/plastic/breaching_charge/handle_explosion(turf/target_turf, dir, cause_data)
- var/explosion_target = get_step(target_turf, dir)
- create_shrapnel(explosion_target, 40, dir, angle,/datum/ammo/bullet/shrapnel/metal, cause_data)
- sleep(1)// prevents explosion from eating shrapnel
- cell_explosion(target_turf, 60, 60, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, dir, cause_data)
- qdel(src)
-
/obj/item/explosive/plastic/proc/delayed_prime(turf/target_turf)
prime(TRUE)
@@ -342,3 +318,65 @@
min_timer = 3
penetration = 0.60
deploying_time = 10
+ var/shrapnel_volume = 40
+
+/obj/item/explosive/plastic/breaching_charge/can_place(mob/user, atom/target)
+ if(!is_type_in_list(target, breachable))//only items on the list are allowed
+ to_chat(user, SPAN_WARNING("You cannot plant [name] on [target]!"))
+ return FALSE
+
+ if(SSinterior.in_interior(target))// vehicle checks again JUST IN CASE
+ to_chat(user, SPAN_WARNING("It's too cramped in here to deploy [src]."))
+ return FALSE
+
+ if(istype(target, /obj/structure/window))//no breaching charges on the briefing windows / brig / CIC e.e
+ var/obj/structure/window/window = target
+ if(window.not_damageable)
+ to_chat(user, SPAN_WARNING("[window] is much too tough for you to do anything to it with [src].")) //On purpose to mimic wall message
+ return FALSE
+
+ if(istype(target, /turf/closed/wall))
+ var/turf/closed/wall/targeted_wall = target
+ if(targeted_wall.hull)
+ to_chat(user, SPAN_WARNING("You are unable to stick [src] to [targeted_wall]!"))
+ return FALSE
+
+ return TRUE
+
+/obj/item/explosive/plastic/breaching_charge/handle_explosion(turf/target_turf, dir, cause_data)
+ var/explosion_target = get_step(target_turf, dir)
+ create_shrapnel(explosion_target, shrapnel_volume, dir, angle,/datum/ammo/bullet/shrapnel/metal, cause_data)
+ addtimer(CALLBACK(src, PROC_REF(trigger_explosion), target_turf, dir, cause_data), 1)
+
+/obj/item/explosive/plastic/breaching_charge/proc/trigger_explosion(turf/target_turf, dir, cause_data)
+ cell_explosion(target_turf, 60, 60, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, dir, cause_data)
+ qdel(src)
+
+/obj/item/explosive/plastic/breaching_charge/plasma
+ name = "plasma charge"
+ desc = "An alien explosive device. Who knows what it might do."
+ icon_state = "plasma-charge"
+ overlay_image = "plasma-active"
+ w_class = SIZE_SMALL
+ angle = 55
+ timer = 5
+ min_timer = 5
+ penetration = 0.60
+ deploying_time = 10
+ flags_item = NOBLUDGEON|ITEM_PREDATOR
+ shrapnel_volume = 10
+
+/obj/item/explosive/plastic/breaching_charge/plasma/can_place(mob/user, atom/target)
+ if(!HAS_TRAIT(user, TRAIT_YAUTJA_TECH))
+ to_chat(user, SPAN_WARNING("You don't quite understand how the device works..."))
+ return FALSE
+ . = ..()
+
+/obj/item/explosive/plastic/breaching_charge/plasma/handle_explosion(turf/target_turf, dir, cause_data)
+ var/explosion_target = get_step(target_turf, dir)
+ create_shrapnel(explosion_target, shrapnel_volume, dir, angle,/datum/ammo/bullet/shrapnel/plasma, cause_data)
+ addtimer(CALLBACK(src, PROC_REF(trigger_explosion), target_turf, dir, cause_data), 1)
+
+/obj/item/explosive/plastic/breaching_charge/plasma/trigger_explosion(turf/target_turf, dir, cause_data)
+ cell_explosion(target_turf, 90, 90, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, dir, cause_data)
+ qdel(src)
diff --git a/code/game/objects/items/reagent_containers/blood_pack.dm b/code/game/objects/items/reagent_containers/blood_pack.dm
index 8e29a26c2ecd..0879dcffdc68 100644
--- a/code/game/objects/items/reagent_containers/blood_pack.dm
+++ b/code/game/objects/items/reagent_containers/blood_pack.dm
@@ -2,10 +2,10 @@
#define BLOOD_BAG_TAKING 0
/obj/item/reagent_container/blood
- name = "BloodPack"
- desc = "Contains blood used for transfusion."
+ name = "blood pack"
+ desc = "A blood pack. Contains fluids, typically used for transfusions."
icon = 'icons/obj/items/bloodpack.dmi'
- icon_state = "empty"
+ icon_state = "bloodpack"
volume = 300
matter = list("plastic" = 500)
flags_atom = CAN_BE_SYRINGED
@@ -20,7 +20,7 @@
/obj/item/reagent_container/blood/Initialize()
. = ..()
if(blood_type != null)
- name = "BloodPack [blood_type]"
+ name = "[blood_type] blood pack"
reagents.add_reagent("blood", initial(volume), list("viruses" = null, "blood_type" = blood_type, "resistances" = null))
update_icon()
@@ -29,10 +29,26 @@
/obj/item/reagent_container/blood/update_icon()
var/percent = round((reagents.total_volume / volume) * 100)
- switch(percent)
- if(0 to 9) icon_state = "empty"
- if(10 to 50) icon_state = "half"
- if(51 to INFINITY) icon_state = "full"
+ overlays = null
+ underlays = null
+
+ if(blood_type)
+ overlays += image('icons/obj/items/bloodpack.dmi', src, blood_type)
+
+ if(reagents && reagents.total_volume)
+ var/image/filling = image('icons/obj/items/reagentfillings.dmi', src, "[icon_state]10")
+
+ switch(percent)
+ if(1 to 9) filling.icon_state = "[icon_state]5"
+ if(10 to 19) filling.icon_state = "[icon_state]10"
+ if(20 to 39) filling.icon_state = "[icon_state]25"
+ if(40 to 64) filling.icon_state = "[icon_state]50"
+ if(65 to 79) filling.icon_state = "[icon_state]75"
+ if(80 to 90) filling.icon_state = "[icon_state]80"
+ if(91 to INFINITY) filling.icon_state = "[icon_state]100"
+
+ filling.color = mix_color_from_reagents(reagents.reagent_list)
+ underlays += filling
/obj/item/reagent_container/blood/proc/update_beam()
if(current_beam)
@@ -65,7 +81,7 @@
if(user.action_busy)
return
- if(!do_after(user, 3 SECONDS * user.get_skill_duration_multiplier(SKILL_SURGERY), INTERRUPT_ALL, BUSY_ICON_FRIENDLY, attacked_mob, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
+ if(!do_after(user, (1 SECONDS) * user.get_skill_duration_multiplier(SKILL_SURGERY), INTERRUPT_ALL, BUSY_ICON_FRIENDLY, attacked_mob, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
to_chat(user, SPAN_WARNING("You were interrupted before you could finish!"))
return
@@ -132,7 +148,7 @@
connected_to.visible_message("[src] breaks free of [connected_to]!", "[src] is pulled out of you!")
connected_to.apply_damage(3, BRUTE, pick("r_arm", "l_arm"))
if(connected_to.pain.feels_pain)
- connected_to.emote("scream")
+ connected_to.emote("pain")
connected_to.active_transfusions -= src
connected_to.base_pixel_x = 0
connected_to = null
@@ -173,9 +189,8 @@
blood_type = "O-"
/obj/item/reagent_container/blood/empty
- name = "Empty BloodPack"
- desc = "Seems pretty useless... Maybe if there were a way to fill it?"
- icon_state = "empty"
+ name = "empty blood pack"
+ desc = "An empty blood pack. Sorry, vampires, no luck here."
#undef BLOOD_BAG_INJECTING
#undef BLOOD_BAG_TAKING
diff --git a/code/game/objects/items/reagent_containers/food.dm b/code/game/objects/items/reagent_containers/food.dm
index 13dfcac84ff8..ded1b90b106f 100644
--- a/code/game/objects/items/reagent_containers/food.dm
+++ b/code/game/objects/items/reagent_containers/food.dm
@@ -2,6 +2,10 @@
/// Food.
////////////////////////////////////////////////////////////////////////////////
/obj/item/reagent_container/food
+ item_icons = list(
+ WEAR_L_HAND = 'icons/mob/humans/onmob/items_lefthand_1.dmi',
+ WEAR_R_HAND = 'icons/mob/humans/onmob/items_righthand_1.dmi'
+ )
possible_transfer_amounts = null
volume = 50 //Sets the default container amount for all food items.
flags_atom = CAN_BE_SYRINGED
diff --git a/code/game/objects/items/reagent_containers/food/fish_snacks.dm b/code/game/objects/items/reagent_containers/food/fish_snacks.dm
index 9e9dcb1b8017..ba4a3c05fb0f 100644
--- a/code/game/objects/items/reagent_containers/food/fish_snacks.dm
+++ b/code/game/objects/items/reagent_containers/food/fish_snacks.dm
@@ -13,7 +13,7 @@
var/gut_icon_state = null
var/gut_time = 3
var/initial_desc = ""
- var/list/guttable_atoms = list(/obj/item/reagent_container/food/snacks/meat, /obj/item/reagent_container/food/snacks/meat/syntiflesh)//placeholders, for now
+ var/list/guttable_atoms = list(/obj/item/reagent_container/food/snacks/meat, /obj/item/reagent_container/food/snacks/meat/synthmeat)//placeholders, for now
var/base_gut_meat = /obj/item/reagent_container/food/snacks/meat
//slice_path = null//
//slices_num
diff --git a/code/game/objects/items/reagent_containers/food/snacks/meat.dm b/code/game/objects/items/reagent_containers/food/snacks/meat.dm
index 0174af8520d2..f459d1b169ae 100644
--- a/code/game/objects/items/reagent_containers/food/snacks/meat.dm
+++ b/code/game/objects/items/reagent_containers/food/snacks/meat.dm
@@ -24,10 +24,20 @@
else
..()
-/obj/item/reagent_container/food/snacks/meat/syntiflesh
+/obj/item/reagent_container/food/snacks/meat/synthmeat
name = "synthetic meat"
desc = "A synthetic slab of flesh."
+/obj/item/reagent_container/food/snacks/meat/synthmeat/synthflesh //meat made from synthetics. Slightly toxic
+ name = "synthetic flesh"
+ desc = "A slab of artificial, inorganic 'flesh' that resembles human meat. Probably came from a synth."
+ icon_state = "synthmeat"
+ filling_color = "#ffffff"
+
+/obj/item/reagent_container/food/snacks/meat/synthmeat/synthetic/Initialize()
+ . = ..()
+ reagents.add_reagent("pacid", 1.5)
+
/obj/item/reagent_container/food/snacks/meat/human
name = "human meat"
desc = "A slab of flesh for cannibals."
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 6e8b00fc48e8..290460da758f 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -3,6 +3,10 @@
desc = "Can hold various things."
icon = 'icons/obj/items/clothing/belts.dmi'
icon_state = "utilitybelt"
+ item_icons = list(
+ WEAR_L_HAND = 'icons/mob/humans/onmob/items_lefthand_1.dmi',
+ WEAR_R_HAND = 'icons/mob/humans/onmob/items_righthand_1.dmi'
+ )
item_state = "utility"
flags_equip_slot = SLOT_WAIST
attack_verb = list("whipped", "lashed", "disciplined")
diff --git a/code/game/objects/items/storage/pouch.dm b/code/game/objects/items/storage/pouch.dm
index 7a49f48cdc92..dc3ee0ba1506 100644
--- a/code/game/objects/items/storage/pouch.dm
+++ b/code/game/objects/items/storage/pouch.dm
@@ -58,29 +58,12 @@
max_w_class = SIZE_MEDIUM
cant_hold = list( //Prevent inventory bloat
/obj/item/storage/firstaid,
- /obj/item/storage/bible
+ /obj/item/storage/bible,
+ /obj/item/storage/box,
)
storage_slots = null
max_storage_space = 2
-/obj/item/storage/pouch/general/attackby(obj/item/W, mob/user)
- if(istype(W, /obj/item/ammo_magazine/shotgun))
- var/obj/item/ammo_magazine/shotgun/M = W
- dump_ammo_to(M,user)
- else if(istype(W, /obj/item/storage/box/nade_box) || istype(W, /obj/item/storage/box/m94))
- dump_into(W, user)
- else
- return ..()
-
-/obj/item/storage/pouch/general/can_be_inserted(obj/item/W, stop_messages)
- . = ..()
- if(. && W.w_class == SIZE_MEDIUM)
- for(var/obj/item/I in return_inv())
- if(I.w_class >= SIZE_MEDIUM)
- if(!stop_messages)
- to_chat(usr, SPAN_NOTICE("[src] is already too bulky with [I]."))
- return FALSE
-
/obj/item/storage/pouch/general/medium
name = "medium general pouch"
desc = "A general-purpose pouch used to carry a variety of differently sized items."
@@ -468,13 +451,6 @@
for(var/i = 1 to storage_slots)
new /obj/item/ammo_magazine/pistol/vp78(src)
-/obj/item/storage/pouch/magazine/shotgun/attackby(obj/item/W, mob/living/user)
- if(istype(W, /obj/item/ammo_magazine/shotgun))
- var/obj/item/ammo_magazine/shotgun/M = W
- dump_ammo_to(M, user, M.transfer_handful_amount)
- else
- return ..()
-
/obj/item/storage/pouch/magazine/pulse_rifle/fill_preset_inventory()
for(var/i = 1 to storage_slots)
new /obj/item/ammo_magazine/rifle(src)
diff --git a/code/game/objects/items/storage/surgical_tray.dm b/code/game/objects/items/storage/surgical_tray.dm
index d86918c697b2..16c0d1352961 100644
--- a/code/game/objects/items/storage/surgical_tray.dm
+++ b/code/game/objects/items/storage/surgical_tray.dm
@@ -4,7 +4,7 @@
icon_state = "surgical_tray"
flags_atom = FPRINT|CONDUCT
w_class = SIZE_LARGE //Should not fit in backpacks
- storage_slots = 13
+ storage_slots = 14
max_storage_space = 24
use_sound = "toolbox"
matter = list("plastic" = 3000)
@@ -31,6 +31,7 @@
new /obj/item/tool/surgery/FixOVein(src)
new /obj/item/stack/nanopaste(src)
new /obj/item/tool/surgery/surgical_line(src)
+ new /obj/item/tool/surgery/synthgraft(src)
/obj/item/storage/surgical_tray/update_icon()
if(!contents.len)
diff --git a/code/game/objects/items/tools/maintenance_tools.dm b/code/game/objects/items/tools/maintenance_tools.dm
index 2560c5ff91e8..b8affb0de616 100644
--- a/code/game/objects/items/tools/maintenance_tools.dm
+++ b/code/game/objects/items/tools/maintenance_tools.dm
@@ -219,41 +219,43 @@
toggle(TRUE)
-/obj/item/tool/weldingtool/attack(mob/M, mob/user)
+/obj/item/tool/weldingtool/attack(mob/target, mob/user)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- var/obj/limb/S = H.get_limb(user.zone_selected)
+ if(ishuman(target))
+ var/mob/living/carbon/human/human = target
+ var/obj/limb/limb = human.get_limb(user.zone_selected)
- if (!S) return
- if(!(S.status & (LIMB_ROBOT|LIMB_SYNTHSKIN)) || user.a_intent != INTENT_HELP)
+ if (!limb) return
+ if(!(limb.status & (LIMB_ROBOT|LIMB_SYNTHSKIN)) || user.a_intent != INTENT_HELP)
return ..()
if(user.action_busy)
return
var/self_fixing = FALSE
- if(H.species.flags & IS_SYNTHETIC && M == user)
+ if(human.species.flags & IS_SYNTHETIC && target == user)
self_fixing = TRUE
- if(S.brute_dam && welding)
+ if(limb.brute_dam && welding)
remove_fuel(1,user)
if(self_fixing)
- user.visible_message(SPAN_WARNING("\The [user] begins fixing some dents on their [S.display_name]."), \
- SPAN_WARNING("You begin to carefully patch some dents on your [S.display_name] so as not to void your warranty."))
+ user.visible_message(SPAN_WARNING("\The [user] begins fixing some dents on their [limb.display_name]."), \
+ SPAN_WARNING("You begin to carefully patch some dents on your [limb.display_name] so as not to void your warranty."))
if(!do_after(user, 30, INTERRUPT_ALL, BUSY_ICON_FRIENDLY))
return
- S.heal_damage(15, 0, TRUE)
- H.pain.recalculate_pain()
- H.UpdateDamageIcon()
- user.visible_message(SPAN_WARNING("\The [user] patches some dents on \the [H]'s [S.display_name] with \the [src]."), \
- SPAN_WARNING("You patch some dents on \the [H]'s [S.display_name] with \the [src]."))
+ limb.heal_damage(15, 0, TRUE)
+ human.pain.recalculate_pain()
+ human.UpdateDamageIcon()
+ user.visible_message(SPAN_WARNING("\The [user] patches some dents on \the [human]'s [limb.display_name] with \the [src]."), \
+ SPAN_WARNING("You patch some dents on \the [human]'s [limb.display_name] with \the [src]."))
return
else
to_chat(user, SPAN_WARNING("Nothing to fix!"))
else
+ if(ismob(target))
+ remove_fuel(1)
return ..()
/obj/item/tool/weldingtool/afterattack(obj/target, mob/user, proximity)
@@ -274,8 +276,6 @@
tank.explode()
return
if (welding)
- remove_fuel(1)
-
if(isliving(target))
var/mob/living/L = target
L.IgniteMob()
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/structures/pipes/vents/vents.dm b/code/game/objects/structures/pipes/vents/vents.dm
index fa3395d9e91d..2b3d5409dc8a 100644
--- a/code/game/objects/structures/pipes/vents/vents.dm
+++ b/code/game/objects/structures/pipes/vents/vents.dm
@@ -12,6 +12,7 @@
var/uid
var/vent_icon = "vent"
+ var/datum/effect_system/smoke_spread/gas_holder
/obj/structure/pipes/vents/Initialize()
. = ..()
@@ -123,7 +124,35 @@
qdel(src)
/obj/structure/pipes/vents/Destroy()
+ qdel(gas_holder)
if(initial_loc)
initial_loc.air_vent_info -= id_tag
initial_loc.air_vent_names -= id_tag
. = ..()
+
+/obj/structure/pipes/vents/proc/create_gas(gas_type = VENT_GAS_SMOKE, radius = 4, warning_time = 5 SECONDS)
+ if(welded)
+ to_chat(usr, SPAN_WARNING("You cannot release gas from a welded vent."))
+ return FALSE
+ var/datum/effect_system/smoke_spread/spreader
+ switch(gas_type)
+ if(VENT_GAS_SMOKE)
+ spreader = new /datum/effect_system/smoke_spread/bad
+ if(VENT_GAS_CN20)
+ spreader = new /datum/effect_system/smoke_spread/cn20
+ if(!spreader)
+ return FALSE
+ gas_holder = spreader
+ spreader.attach(src)
+
+ new /obj/effect/warning/explosive/gas(loc, warning_time)
+ visible_message(SPAN_HIGHDANGER("[src] begins to hiss as gas builds up within it."), SPAN_HIGHDANGER("You hear a hissing."), radius)
+ addtimer(CALLBACK(src, PROC_REF(release_gas), radius), warning_time)
+
+/obj/structure/pipes/vents/proc/release_gas(radius = 4)
+ radius = Clamp(radius, 1, 10)
+ if(!gas_holder || welded)
+ return FALSE
+ playsound(loc, 'sound/effects/smoke.ogg', 25, 1, 4)
+ gas_holder.set_up(radius, 0, get_turf(src), null, 10 SECONDS)
+ gas_holder.start()
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 37d46cbe6d5d..c8f5a7f82c0f 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
@@ -192,12 +192,12 @@
recently_nested = TRUE
addtimer(VARSET_CALLBACK(src, recently_nested, FALSE), 5 SECONDS)
-/obj/structure/bed/nest/buckle_mob(mob/M as mob, mob/user as mob)
+/obj/structure/bed/nest/buckle_mob(mob/mob, mob/user)
. = FALSE
- if(!isliving(M) || islarva(user) || (get_dist(src, user) > 1) || user.is_mob_restrained() || user.stat || user.lying || M.buckled || !iscarbon(user))
+ if(!isliving(mob) || islarva(user) || (get_dist(src, user) > 1) || user.is_mob_restrained() || user.stat || user.lying || mob.buckled || !iscarbon(user))
return
- if(isxeno(M))
+ if(isxeno(mob))
to_chat(user, SPAN_WARNING("You can't buckle your sisters."))
return
@@ -205,69 +205,64 @@
to_chat(user, SPAN_WARNING("There's already someone in [src]."))
return
- if(M.mob_size > MOB_SIZE_HUMAN)
- to_chat(user, SPAN_WARNING("\The [M] is too big to fit in [src]."))
+ if(mob.mob_size > MOB_SIZE_HUMAN)
+ to_chat(user, SPAN_WARNING("\The [mob] is too big to fit in [src]."))
return
- if(!isxeno(user) || issynth(M))
+ if(!isxeno(user) || issynth(mob))
to_chat(user, SPAN_WARNING("Gross! You're not touching that stuff."))
return
- if(isyautja(M) && !force_nest)
- to_chat(user, SPAN_WARNING("\The [M] seems to be wearing some kind of resin-resistant armor!"))
+ if(isyautja(mob) && !force_nest)
+ to_chat(user, SPAN_WARNING("\The [mob] seems to be wearing some kind of resin-resistant armor!"))
return
- if(M == user)
+ if(mob == user)
return
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(!H.lying) //Don't ask me why is has to be
- to_chat(user, SPAN_WARNING("[M] is resisting, ground them."))
+ var/mob/living/carbon/human/human = null
+ if(ishuman(mob))
+ human = mob
+ if(!human.lying) //Don't ask me why is has to be
+ to_chat(user, SPAN_WARNING("[mob] is resisting, ground them."))
return
var/securing_time = 15
// Don't increase the nesting time for monkeys and other species
- if(ishuman_strict(M))
+ if(ishuman_strict(mob))
securing_time = 75
- user.visible_message(SPAN_WARNING("[user] pins [M] into [src], preparing the securing resin."),
- SPAN_WARNING("[user] pins [M] into [src], preparing the securing resin."))
- var/M_loc = M.loc
+ user.visible_message(SPAN_WARNING("[user] pins [mob] into [src], preparing the securing resin."),
+ SPAN_WARNING("[user] pins [mob] into [src], preparing the securing resin."))
+ var/M_loc = mob.loc
if(!do_after(user, securing_time, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE))
return
- if(M.loc != M_loc)
+ if(mob.loc != M_loc)
return
if(buckled_mob) //Just in case
to_chat(user, SPAN_WARNING("There's already someone in [src]."))
return
- if(ishuman(M)) //Improperly stunned Marines won't be nested
- var/mob/living/carbon/human/H = M
- if(!H.lying) //Don't ask me why is has to be
- to_chat(user, SPAN_WARNING("[M] is resisting, ground them."))
+ if(human) //Improperly stunned Marines won't be nested
+ if(!human.lying) //Don't ask me why is has to be
+ to_chat(user, SPAN_WARNING("[mob] is resisting, ground them."))
return
- do_buckle(M, user)
- ADD_TRAIT(M, TRAIT_NESTED, TRAIT_SOURCE_BUCKLE)
+ do_buckle(mob, user)
+ ADD_TRAIT(mob, TRAIT_NESTED, TRAIT_SOURCE_BUCKLE)
- if(!ishuman(M))
+ if(!human)
return TRUE
//Disabling motion detectors and other stuff they might be carrying
- var/mob/living/carbon/human/H = M
- H.start_nesting_cooldown()
- H.disable_special_flags()
- H.disable_lights()
- H.disable_special_items()
-
- if(H.mind)
- var/choice = alert(M, "You have no possibility of escaping unless freed by your fellow marines, do you wish to Ghost? If you are freed while ghosted, you will be given the choice to return to your body.", ,"Ghost", "Remain")
- if(choice == "Ghost")
- // Ask to ghostize() so they can reenter, to leave mind and such intact
- ghost_of_buckled_mob = M.ghostize(can_reenter_corpse = TRUE)
- ghost_of_buckled_mob?.can_reenter_corpse = FALSE // Just don't for now
+ human.start_nesting_cooldown()
+ human.disable_special_flags()
+ human.disable_lights()
+ human.disable_special_items()
+
+ if(human.client)
+ human.do_ghost()
return TRUE
diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm
index b760340004f5..350d6047b2a4 100644
--- a/code/game/supplyshuttle.dm
+++ b/code/game/supplyshuttle.dm
@@ -191,11 +191,11 @@ var/datum/controller/supply/supply_controller = new()
var/list/data = list()
var/list/squad_list = list()
- for(var/datum/squad/S in RoleAuthority.squads)
- if(S.active && S.faction == faction && S.color)
+ for(var/datum/squad/current_squad in RoleAuthority.squads)
+ if(current_squad.active && current_squad.faction == faction && current_squad.equipment_color)
squad_list += list(list(
- "squad_name" = S.name,
- "squad_color" = squad_colors[S.color]
+ "squad_name" = current_squad.name,
+ "squad_color" = current_squad.equipment_color
))
data["can_pick_squad"] = can_pick_squad
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 3f7192b0090b..41ac80bfdc58 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -517,7 +517,7 @@
var/obj/item/clothing/gloves/yautja/hunter/Y = H.gloves
if(Y && istype(Y) && Y.cloaked)
to_chat(H, SPAN_WARNING(" Your bracers hiss and spark as they short out!"))
- Y.decloak(H, TRUE)
+ Y.decloak(H, TRUE, DECLOAK_SUBMERGED)
else if(isxeno(C))
river_slowdown -= 0.7
diff --git a/code/game/world.dm b/code/game/world.dm
index d6ddd1fa41f6..25cd609646da 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -39,7 +39,6 @@ var/list/reboot_sfx = file2list("config/reboot_sfx.txt")
GLOB.changelog_hash = fexists(latest_changelog) ? md5(latest_changelog) : 0 //for telling if the changelog has changed recently
initialize_tgs()
- initialize_marine_armor()
#ifdef UNIT_TESTS
GLOB.test_log = "data/logs/tests.log"
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 6154bb4f8c32..20ff65ed144e 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -191,6 +191,7 @@ var/list/admin_verbs_debug = list(
/client/proc/restart_controller,
/client/proc/debug_controller,
/client/proc/cmd_debug_toggle_should_check_for_win,
+ /client/proc/cmd_debug_mass_screenshot,
/client/proc/enable_debug_verbs,
/client/proc/toggledebuglogs,
/client/proc/togglenichelogs,
diff --git a/code/modules/admin/player_panel/actions/transform.dm b/code/modules/admin/player_panel/actions/transform.dm
index 11dd7525bb07..185165357e05 100644
--- a/code/modules/admin/player_panel/actions/transform.dm
+++ b/code/modules/admin/player_panel/actions/transform.dm
@@ -38,6 +38,11 @@ GLOBAL_LIST_INIT(pp_transformables, list(
name = "Facehugger",
key = /mob/living/carbon/xenomorph/facehugger,
color = "purple"
+ ),
+ list(
+ name = "Lesser Drone",
+ key = /mob/living/carbon/xenomorph/lesser_drone,
+ color = "purple"
)
),
diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm
index febc1550fca0..b9eb4fd47ea1 100644
--- a/code/modules/admin/tabs/event_tab.dm
+++ b/code/modules/admin/tabs/event_tab.dm
@@ -402,6 +402,29 @@
give_jelly_award(last_hive_checked, as_admin=TRUE)
+/client/proc/give_nuke()
+ if(!check_rights(R_ADMIN))
+ return
+ var/nuketype = "Decrypted Operational Nuke"
+ var/encrypt = tgui_alert(src, "Do you want the nuke to be already decrypted?", "Nuke Type", list("Encrypted", "Decrypted"), 20 SECONDS)
+ if(encrypt == "Encrypted")
+ nuketype = "Encrypted Operational Nuke"
+ var/prompt = tgui_alert(src, "THIS CAN BE USED TO END THE ROUND. Are you sure you want to spawn a nuke? The nuke will be put onto the ASRS Lift.", "DEFCON 1", list("No", "Yes"), 30 SECONDS)
+ if(prompt != "Yes")
+ return
+
+ 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 = MAIN_AI_SYSTEM
+ new_order.approvedby = MAIN_AI_SYSTEM
+ supply_controller.shoppinglist += new_order
+
+ marine_announcement("A nuclear device has been supplied and will be delivered to requisitions via ASRS.", "NUCLEAR ARSENAL ACQUIRED", 'sound/misc/notice2.ogg')
+ message_admins("[key_name_admin(usr)] admin-spawned a [encrypt] nuke.")
+ log_game("[key_name_admin(usr)] admin-spawned a [encrypt] nuke.")
+
/client/proc/turn_everyone_into_primitives()
var/random_names = FALSE
if (alert(src, "Do you want to give everyone random numbered names?", "Confirmation", "Yes", "No") == "Yes")
@@ -535,10 +558,11 @@
if(!input)
return FALSE
- var/datum/ares_link/link = GLOB.ares_link
- if(link.p_interface.inoperable())
- to_chat(usr, SPAN_WARNING("[MAIN_AI_SYSTEM] is not responding. It may be offline or destroyed."))
- return
+ if(!ares_can_interface())
+ var/prompt = tgui_alert(src, "ARES interface processor is offline or destroyed, send the message anyways?", "Choose.", list("Yes", "No"), 20 SECONDS)
+ if(prompt == "No")
+ to_chat(usr, SPAN_WARNING("[MAIN_AI_SYSTEM] is not responding. It's interface processor may be offline or destroyed."))
+ return
ai_announcement(input)
message_admins("[key_name_admin(src)] has created an AI comms report")
@@ -558,15 +582,12 @@
var/datum/ares_link/link = GLOB.ares_link
if(link.p_apollo.inoperable())
- to_chat(usr, SPAN_WARNING("[MAIN_AI_SYSTEM] is not responding. It may be offline or destroyed."))
- return FALSE
+ var/prompt = tgui_alert(src, "ARES APOLLO processor is offline or destroyed, send the message anyways?", "Choose.", list("Yes", "No"), 20 SECONDS)
+ if(prompt == "No")
+ to_chat(usr, SPAN_WARNING("[MAIN_AI_SYSTEM] is not responding. It's APOLLO processor may be offline or destroyed."))
+ return FALSE
- var/datum/language/apollo/apollo = GLOB.all_languages[LANGUAGE_APOLLO]
- for(var/mob/living/silicon/decoy/ship_ai/AI in ai_mob_list)
- apollo.broadcast(AI, input)
- for(var/mob/listener as anything in (GLOB.human_mob_list + GLOB.dead_mob_list))
- if(listener.hear_apollo())//Only plays sound to mobs and not observers, to reduce spam.
- playsound_client(listener.client, sound('sound/misc/interference.ogg'), listener, vol = 45)
+ ares_apollo_talk(input)
message_admins("[key_name_admin(src)] has created an AI APOLLO report")
log_admin("AI APOLLO report: [input]")
@@ -580,14 +601,15 @@
var/input = input(usr, "This is an announcement type message from the ship's AI. This will be announced to every conscious human on Almayer z-level. Be aware, this will work even if ARES unpowered/destroyed. Check with online staff before you send this.", "What?", "") as message|null
if(!input)
return FALSE
- for(var/obj/structure/machinery/ares/processor/interface/processor in machines)
- if(processor.inoperable())
- to_chat(usr, SPAN_WARNING("[MAIN_AI_SYSTEM] is not responding. It may be offline or destroyed."))
+ if(!ares_can_interface())
+ var/prompt = tgui_alert(src, "ARES interface processor is offline or destroyed, send the message anyways?", "Choose.", list("Yes", "No"), 20 SECONDS)
+ if(prompt == "No")
+ to_chat(usr, SPAN_WARNING("[MAIN_AI_SYSTEM] is not responding. It's interface processor may be offline or destroyed."))
return
- shipwide_ai_announcement(input)
- message_admins("[key_name_admin(src)] has created an AI shipwide report")
- log_admin("[key_name_admin(src)] AI shipwide report: [input]")
+ shipwide_ai_announcement(input)
+ message_admins("[key_name_admin(src)] has created an AI shipwide report")
+ log_admin("[key_name_admin(src)] AI shipwide report: [input]")
/client/proc/cmd_admin_create_predator_report()
set name = "Report: Yautja AI"
@@ -685,6 +707,7 @@
Misc
Award a medal
Award a royal jelly
+ Spawn a nuke
Toggle PMC gun restrictions
Turn everyone into monkies
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/topic/topic_events.dm b/code/modules/admin/topic/topic_events.dm
index 1055d5e47879..3240bf947938 100644
--- a/code/modules/admin/topic/topic_events.dm
+++ b/code/modules/admin/topic/topic_events.dm
@@ -22,6 +22,8 @@
owner.award_medal()
if("jelly")
owner.award_jelly()
+ if("nuke")
+ owner.give_nuke()
if("pmcguns")
owner.toggle_gun_restrictions()
if("monkify")
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 8a017446858d..6be6b14e0247 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -92,7 +92,74 @@
else
message_admins("[key_name(src)] disabled checking for round-end.")
+/client/proc/cmd_debug_mass_screenshot()
+ set category = "Debug"
+ set name = "Mass Screenshot"
+ set background = TRUE
+ set waitfor = FALSE
+
+ if(!check_rights(R_MOD))
+ return
+ if(tgui_alert(usr, "Are you sure you want to mass screenshot this z-level? Ensure your visual settings are correct first (other ghost visibility, zoom level, etc.) and you have emptied your BYOND/screenshots folder.", "Mass Screenshot", list("Yes", "No")) != "Yes")
+ return
+
+ var/sleep_duration = tgui_input_number(usr, "Enter a delay in deciseconds between screenshots to allow the client to render changes.", "Screenshot delay", 2, 10, 1, 0, TRUE)
+ if(!sleep_duration)
+ return
+
+ if(!mob)
+ return
+
+ if(!isobserver(mob))
+ admin_ghost()
+
+ mob.alpha = 0
+ if(mob.hud_used)
+ mob.hud_used.show_hud(HUD_STYLE_NOHUD)
+ mob.animate_movement = NO_STEPS
+
+ message_admins(WRAP_STAFF_LOG(usr, "started a mass screenshot operation."))
+
+ var/half_chunk_size = view + 1
+ var/chunk_size = half_chunk_size * 2 - 1
+ var/cur_x = half_chunk_size
+ var/cur_y = half_chunk_size
+ var/cur_z = mob.z
+ var/width
+ var/height
+ if(istype(SSmapping.z_list[cur_z], /datum/space_level))
+ var/datum/space_level/cur_level = SSmapping.z_list[cur_z]
+ width = cur_level.x_bounds - half_chunk_size + 2
+ height = cur_level.y_bounds - half_chunk_size + 2
+ else
+ width = world.maxx - half_chunk_size + 2
+ height = world.maxy - half_chunk_size + 2
+ var/width_inside = width - 1
+ var/height_inside = height - 1
+
+ while(cur_y < height)
+ while(cur_x < width)
+ mob.on_mob_jump()
+ mob.forceMove(locate(cur_x, cur_y, cur_z))
+ sleep(sleep_duration)
+ winset(src, null, "command='.screenshot auto'")
+ if(cur_x == width_inside)
+ break
+ cur_x += chunk_size
+ cur_x = min(cur_x, width_inside)
+ if(cur_y == height_inside)
+ break
+ cur_x = half_chunk_size
+ cur_y += chunk_size
+ cur_y = min(cur_y, height_inside)
+
+ mob.alpha = initial(mob.alpha)
+ if(mob.hud_used)
+ mob.hud_used.show_hud(HUD_STYLE_STANDARD)
+ mob.animate_movement = SLIDE_STEPS // Initial is incorrect
+
+ to_chat(usr, "Provide these values when asked for the MapTileImageTool: [width] [height] [half_chunk_size] [world.icon_size]")
//TODO: merge the vievars version into this or something maybe mayhaps
/client/proc/cmd_debug_del_all()
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 4ca05fe8a93c..e00f4a2d1263 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -271,7 +271,6 @@
/datum/asset/spritesheet/ranks/register()
var/icon_file = 'icons/mob/hud/marine_hud.dmi'
- var/list/squads = list("Alpha", "Bravo", "Charlie", "Delta", "Foxtrot", "Cryo")
var/list/icon_data = list(
list("Mar", null),
@@ -284,10 +283,8 @@
list("SL", "hudsquad_leader"),
)
- var/i
- for(i = 1; i < length(squads); i++)
- var/squad = squads[i]
- var/color = squad_colors[i]
+ for(var/datum/squad/marine/squad in RoleAuthority.squads)
+ var/color = squad.equipment_color
for(var/iref in icon_data)
var/list/iconref = iref
var/icon/background = icon('icons/mob/hud/marine_hud.dmi', "hudsquad", SOUTH)
diff --git a/code/modules/clans/client.dm b/code/modules/clans/client.dm
index 2d06725736ce..d1f403c08300 100644
--- a/code/modules/clans/client.dm
+++ b/code/modules/clans/client.dm
@@ -405,10 +405,10 @@
to_chat(src, SPAN_WARNING("This player doesn't belong to a clan!"))
return
- var/list/datum/rank/ranks = clan_ranks.Copy()
+ var/list/datum/yautja_rank/ranks = clan_ranks.Copy()
ranks -= CLAN_RANK_ADMIN // Admin rank should not and cannot be obtained from here
- var/datum/rank/chosen_rank
+ var/datum/yautja_rank/chosen_rank
if(has_clan_permission(CLAN_PERMISSION_ADMIN_MODIFY, warn = FALSE))
var/input = tgui_input_list(src, "Select the rank to change this user to.", "Select Rank", ranks)
diff --git a/code/modules/clans/rank.dm b/code/modules/clans/rank.dm
index b0748ce60662..a6b78a0d95e9 100644
--- a/code/modules/clans/rank.dm
+++ b/code/modules/clans/rank.dm
@@ -1,4 +1,4 @@
-/datum/rank
+/datum/yautja_rank
var/name
var/limit_type
@@ -7,29 +7,29 @@
var/permissions = CLAN_PERMISSION_USER_VIEW
var/permission_required = CLAN_PERMISSION_USER_MODIFY
-/datum/rank/unblooded
+/datum/yautja_rank/unblooded
name = CLAN_RANK_UNBLOODED
permission_required = CLAN_PERMISSION_ADMIN_MODIFY
-/datum/rank/young
+/datum/yautja_rank/young
name = CLAN_RANK_YOUNG
-/datum/rank/blooded
+/datum/yautja_rank/blooded
name = CLAN_RANK_BLOODED
-/datum/rank/elite
+/datum/yautja_rank/elite
name = CLAN_RANK_ELITE
limit_type = CLAN_LIMIT_SIZE
limit = 5
-/datum/rank/elder
+/datum/yautja_rank/elder
name = CLAN_RANK_ELDER
limit_type = CLAN_LIMIT_SIZE
limit = 12
-/datum/rank/leader
+/datum/yautja_rank/leader
name = CLAN_RANK_LEADER
permissions = CLAN_PERMISSION_USER_ALL
@@ -37,7 +37,7 @@
limit_type = CLAN_LIMIT_NUMBER
limit = 1
-/datum/rank/ancient
+/datum/yautja_rank/ancient
name = CLAN_RANK_ADMIN
permission_required = CLAN_PERMISSION_ADMIN_MANAGER
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 5698c30c0acf..16afa8d1b4f2 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -590,7 +590,7 @@ var/const/MAX_SAVE_SLOTS = 10
dat += "
"
dat += "
Gameplay Toggles:
"
dat += "
Toggle Being Able to Hurt Yourself: \
- [toggle_prefs & TOGGLE_IGNORE_SELF ? "On" : "Off"]"
+
[toggle_prefs & TOGGLE_IGNORE_SELF ? "Off" : "On"]"
dat += "
Toggle Help Intent Safety: \
[toggle_prefs & TOGGLE_HELP_INTENT_SAFETY ? "On" : "Off"]"
dat += "
Toggle Middle Mouse Ability Activation: \
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 0a1b54112f18..0f482fa7f894 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -199,22 +199,22 @@
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
UI_style = sanitize_inlist(UI_style, list("white", "dark", "midnight", "orange", "old"), initial(UI_style))
tgui_say = sanitize_integer(tgui_say, FALSE, TRUE, TRUE)
- be_special = sanitize_integer(be_special, 0, 65535, initial(be_special))
+ be_special = sanitize_integer(be_special, 0, SHORT_REAL_LIMIT, initial(be_special))
default_slot = sanitize_integer(default_slot, 1, MAX_SAVE_SLOTS, initial(default_slot))
- toggles_chat = sanitize_integer(toggles_chat, 0, 65535, initial(toggles_chat))
- chat_display_preferences = sanitize_integer(chat_display_preferences, 0, 65535, initial(chat_display_preferences))
- toggles_ghost = sanitize_integer(toggles_ghost, 0, 65535, initial(toggles_ghost))
- toggles_langchat = sanitize_integer(toggles_langchat, 0, 65535, initial(toggles_langchat))
- toggles_sound = sanitize_integer(toggles_sound, 0, 65535, initial(toggles_sound))
- toggle_prefs = sanitize_integer(toggle_prefs, 0, 65535, initial(toggle_prefs))
- toggles_flashing= sanitize_integer(toggles_flashing, 0, 65535, initial(toggles_flashing))
- toggles_ert = sanitize_integer(toggles_ert, 0, 65535, initial(toggles_ert))
- toggles_admin = sanitize_integer(toggles_admin, 0, 65535, initial(toggles_admin))
+ toggles_chat = sanitize_integer(toggles_chat, 0, SHORT_REAL_LIMIT, initial(toggles_chat))
+ chat_display_preferences = sanitize_integer(chat_display_preferences, 0, SHORT_REAL_LIMIT, initial(chat_display_preferences))
+ toggles_ghost = sanitize_integer(toggles_ghost, 0, SHORT_REAL_LIMIT, initial(toggles_ghost))
+ toggles_langchat = sanitize_integer(toggles_langchat, 0, SHORT_REAL_LIMIT, initial(toggles_langchat))
+ toggles_sound = sanitize_integer(toggles_sound, 0, SHORT_REAL_LIMIT, initial(toggles_sound))
+ toggle_prefs = sanitize_integer(toggle_prefs, 0, SHORT_REAL_LIMIT, initial(toggle_prefs))
+ toggles_flashing= sanitize_integer(toggles_flashing, 0, SHORT_REAL_LIMIT, initial(toggles_flashing))
+ toggles_ert = sanitize_integer(toggles_ert, 0, SHORT_REAL_LIMIT, initial(toggles_ert))
+ toggles_admin = sanitize_integer(toggles_admin, 0, SHORT_REAL_LIMIT, initial(toggles_admin))
UI_style_color = sanitize_hexcolor(UI_style_color, initial(UI_style_color))
UI_style_alpha = sanitize_integer(UI_style_alpha, 0, 255, initial(UI_style_alpha))
item_animation_pref_level = sanitize_integer(item_animation_pref_level, SHOW_ITEM_ANIMATIONS_NONE, SHOW_ITEM_ANIMATIONS_ALL, SHOW_ITEM_ANIMATIONS_ALL)
pain_overlay_pref_level = sanitize_integer(pain_overlay_pref_level, PAIN_OVERLAY_BLURRY, PAIN_OVERLAY_LEGACY, PAIN_OVERLAY_BLURRY)
- window_skin = sanitize_integer(window_skin, 0, 65535, initial(window_skin))
+ window_skin = sanitize_integer(window_skin, 0, SHORT_REAL_LIMIT, initial(window_skin))
ghost_vision_pref = sanitize_inlist(ghost_vision_pref, list(GHOST_VISION_LEVEL_NO_NVG, GHOST_VISION_LEVEL_MID_NVG, GHOST_VISION_LEVEL_FULL_NVG), GHOST_VISION_LEVEL_MID_NVG)
ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit))
playtime_perks = sanitize_integer(playtime_perks, 0, 1, 1)
diff --git a/code/modules/clothing/gloves/marine_gloves.dm b/code/modules/clothing/gloves/marine_gloves.dm
index bbddc1f10bb7..04d0b2f1c0cb 100644
--- a/code/modules/clothing/gloves/marine_gloves.dm
+++ b/code/modules/clothing/gloves/marine_gloves.dm
@@ -22,11 +22,17 @@
armor_rad = CLOTHING_ARMOR_NONE
armor_internaldamage = CLOTHING_ARMOR_LOW
var/adopts_squad_color = TRUE
+ /// The dmi where the grayscale squad overlays are contained
+ var/squad_overlay_icon = 'icons/mob/humans/onmob/hands_garb.dmi'
-/obj/item/clothing/gloves/marine/get_mob_overlay(mob/living/carbon/human/H, slot)
+/obj/item/clothing/gloves/marine/get_mob_overlay(mob/living/carbon/human/current_human, slot)
var/image/ret = ..()
- if(adopts_squad_color && slot == WEAR_HANDS && istype(H) && H.assigned_squad)
- ret.overlays += glovemarkings[H.assigned_squad.color]
+ if(!adopts_squad_color || !(current_human.assigned_squad && current_human.assigned_squad.equipment_color))
+ return ret
+ var/image/glove_overlay = image(squad_overlay_icon, icon_state = "std-gloves")
+ glove_overlay.alpha = current_human.assigned_squad.armor_alpha
+ glove_overlay.color = current_human.assigned_squad.equipment_color
+ ret.overlays += glove_overlay
return ret
/obj/item/clothing/gloves/marine/insulated
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 1b66d3e1bf5f..0181d239c574 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -360,7 +360,7 @@ GLOBAL_LIST_INIT(allowed_helmet_items, list(
var/flags_marine_helmet = HELMET_SQUAD_OVERLAY|HELMET_GARB_OVERLAY|HELMET_DAMAGE_OVERLAY
var/helmet_bash_cooldown = 0
- var/specialty = "M10 pattern marine" //Give them a specialty var so that they show up correctly in vendors.
+ var/specialty = "M10 pattern marine" //Give them a specialty var so that they show up correctly in vendors. speciality does NOTHING if you have NO_NAME_OVERRIDE.
valid_accessory_slots = list(ACCESSORY_SLOT_HELM_C)
restricted_accessory_slots = list(ACCESSORY_SLOT_HELM_C)
item_icons = list(
@@ -372,8 +372,8 @@ GLOBAL_LIST_INIT(allowed_helmet_items, list(
var/storage_slots_reserved_for_garb = 1
var/storage_max_w_class = SIZE_TINY // can hold tiny items only, EXCEPT for glasses & metal flask.
var/storage_max_storage_space = 4
-
- //speciality does NOTHING if you have NO_NAME_OVERRIDE
+ /// The dmi where the grayscale squad overlays are contained
+ var/helmet_overlay_icon = 'icons/mob/humans/onmob/head_1.dmi'
/obj/item/clothing/head/helmet/marine/New(loc,
new_protection[] = list(MAP_ICE_COLONY = ICE_PLANET_MIN_COLD_PROT))
diff --git a/code/modules/clothing/suits/marine_armor.dm b/code/modules/clothing/suits/marine_armor.dm
index c6a2f7fcc32b..c78e6782a833 100644
--- a/code/modules/clothing/suits/marine_armor.dm
+++ b/code/modules/clothing/suits/marine_armor.dm
@@ -28,44 +28,6 @@
#define SOF 7
#define NOSQUAD 8
-var/list/armormarkings = list()
-var/list/armormarkings_sql = list()
-var/list/helmetmarkings = list()
-var/list/helmetmarkings_sql = list()
-var/list/glovemarkings = list()
-var/list/squad_colors = list(rgb(230,25,25), rgb(255,195,45), rgb(200,100,200), rgb(65,72,200), rgb(103,214,146), rgb(196, 122, 80), rgb(64, 0, 0))
-var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150,255), rgb(130,140,255), rgb(103,214,146), rgb(196, 122, 80), rgb(64, 0, 0))
-
-/proc/initialize_marine_armor()
- var/i
- for(i=1, i<(length(squad_colors) + 1), i++)
- var/squad_color = squad_colors[i]
- var/armor_color = rgb(hex2num(copytext(squad_color, 2, 4)), hex2num(copytext(squad_color, 4, 6)), hex2num(copytext(squad_color, 6, 8)), 125)
-
- var/image/armor
- var/image/helmet
- var/image/glove
-
- armor = image('icons/mob/humans/onmob/suit_1.dmi',icon_state = "std-armor")
- armor.color = armor_color
- armormarkings += armor
- armor = image('icons/mob/humans/onmob/suit_1.dmi',icon_state = "sql-armor")
- armor.color = armor_color
- armormarkings_sql += armor
-
- helmet = image('icons/mob/humans/onmob/head_1.dmi',icon_state = "std-helmet")
- helmet.color = armor_color
- helmetmarkings += helmet
- helmet = image('icons/mob/humans/onmob/head_1.dmi',icon_state = "sql-helmet")
- helmet.color = armor_color
- helmetmarkings_sql += helmet
-
- glove = image('icons/mob/humans/onmob/hands_garb.dmi',icon_state = "std-gloves")
- glove.color = armor_color
- glovemarkings += glove
-
-
-
// MARINE STORAGE ARMOR
/obj/item/clothing/suit/storage/marine
@@ -122,7 +84,7 @@ var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150
var/armor_overlays[]
actions_types = list(/datum/action/item_action/toggle)
var/flags_marine_armor = ARMOR_SQUAD_OVERLAY|ARMOR_LAMP_OVERLAY
- var/specialty = "M3 pattern marine" //Same thing here. Give them a specialty so that they show up correctly in vendors.
+ var/specialty = "M3 pattern marine" //Same thing here. Give them a specialty so that they show up correctly in vendors. speciality does NOTHING if you have NO_NAME_OVERRIDE
w_class = SIZE_HUGE
uniform_restricted = list(/obj/item/clothing/under/marine)
sprite_sheets = list(SPECIES_MONKEY = 'icons/mob/humans/species/monkeys/onmob/suit_monkey_1.dmi')
@@ -132,7 +94,8 @@ var/list/squad_colors_chat = list(rgb(230,125,125), rgb(255,230,80), rgb(255,150
drop_sound = "armorequip"
equip_sounds = list('sound/handling/putting_on_armor1.ogg')
var/armor_variation = 0
- //speciality does NOTHING if you have NO_NAME_OVERRIDE
+ /// The dmi where the grayscale squad overlays are contained
+ var/squad_overlay_icon = 'icons/mob/humans/onmob/suit_1.dmi'
/obj/item/clothing/suit/storage/marine/Initialize(mapload)
. = ..()
diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm
index d0e3b77d70c5..d42e7d17bfd2 100644
--- a/code/modules/clothing/under/ties.dm
+++ b/code/modules/clothing/under/ties.dm
@@ -529,7 +529,7 @@
new /obj/item/device/multitool(src)
/obj/item/storage/internal/accessory/surg_vest
- storage_slots = 13
+ storage_slots = 14
can_hold = list(
/obj/item/tool/surgery,
/obj/item/stack/medical/advanced/bruise_pack,
@@ -569,6 +569,7 @@
new /obj/item/tool/surgery/FixOVein(src)
new /obj/item/stack/nanopaste(src)
new /obj/item/tool/surgery/surgical_line(src)
+ new /obj/item/tool/surgery/synthgraft(src)
/obj/item/clothing/accessory/storage/surg_vest
name = "surgical webbing vest"
diff --git a/code/modules/cm_aliens/structures/special/pylon_core.dm b/code/modules/cm_aliens/structures/special/pylon_core.dm
index a29b49b7745a..993d4f833fa6 100644
--- a/code/modules/cm_aliens/structures/special/pylon_core.dm
+++ b/code/modules/cm_aliens/structures/special/pylon_core.dm
@@ -327,5 +327,22 @@
// Tell admins that this condition is reached so they know what has happened if it fails somehow
return
+/obj/effect/alien/resin/special/pylon/core/proc/spawn_lesser_drone(mob/xeno_candidate)
+ if(!linked_hive.can_spawn_as_lesser_drone(xeno_candidate))
+ return FALSE
+
+ var/mob/living/carbon/xenomorph/lesser_drone/new_drone = new /mob/living/carbon/xenomorph/lesser_drone(loc, null, linked_hive.hivenumber)
+ xeno_candidate.mind.transfer_to(new_drone, TRUE)
+ new_drone.visible_message(SPAN_XENODANGER("A lesser drone emerges out of [src]!"), SPAN_XENODANGER("You emerge out of [src] and awaken from your slumber. For the Hive!"))
+ playsound(new_drone, 'sound/effects/xeno_newlarva.ogg', 25, TRUE)
+ new_drone.generate_name()
+
+ return TRUE
+
+/obj/effect/alien/resin/special/pylon/core/attack_ghost(mob/dead/observer/user)
+ . = ..()
+ if(SSticker.mode.check_xeno_late_join(user))
+ SSticker.mode.attempt_to_join_as_lesser_drone(user)
+
#undef PYLON_REPAIR_TIME
#undef PYLON_WEEDS_REGROWTH_TIME
diff --git a/code/modules/cm_marines/dropship_ammo.dm b/code/modules/cm_marines/dropship_ammo.dm
index 3c01688b70d7..d3d0266e5982 100644
--- a/code/modules/cm_marines/dropship_ammo.dm
+++ b/code/modules/cm_marines/dropship_ammo.dm
@@ -347,6 +347,21 @@
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 6, 60, 30, "#EE6515"), 0.5 SECONDS) //Color changed into napalm's color to better convey how intense the fire actually is.
QDEL_IN(src, 0.5 SECONDS)
+/obj/structure/ship_ammo/rocket/thermobaric
+ name = "\improper BLU-200 'Dragons Breath'"
+ desc = "The BLU-200 Dragons Breath a thermobaric fuel-air bomb. The aerosolized fuel mixture creates a vacuum when ignited causing serious damage to those in its way."
+ icon_state = "fatty"
+ ammo_id = "f"
+ travelling_time = 50
+ point_cost = 300
+ fire_mission_delay = 4
+
+/obj/structure/ship_ammo/rocket/thermobaric/detonate_on(turf/impact)
+ impact.ceiling_debris_check(3)
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 4, 25, 50, "#c96500"), 0.5 SECONDS) //Very intense but the fire doesn't last very long
+ for(var/mob/living/carbon/victim in orange(5, impact))
+ victim.throw_atom(impact, 3, 15, src, TRUE) // Implosion throws affected towards center of vacuum
+ QDEL_IN(src, 0.5 SECONDS)
//minirockets
diff --git a/code/modules/cm_marines/marines_consoles.dm b/code/modules/cm_marines/marines_consoles.dm
index 3539a43e6c15..36535a0b5141 100644
--- a/code/modules/cm_marines/marines_consoles.dm
+++ b/code/modules/cm_marines/marines_consoles.dm
@@ -594,11 +594,11 @@
/obj/structure/machinery/computer/squad_changer/ui_static_data(mob/user)
var/list/data = list()
var/list/squads = list()
- for(var/datum/squad/S in RoleAuthority.squads)
- if(S.name != "Root" && !S.locked && S.active && S.faction == faction)
+ for(var/datum/squad/current_squad in RoleAuthority.squads)
+ if(current_squad.name != "Root" && !current_squad.locked && current_squad.active && current_squad.faction == faction)
var/list/squad = list(list(
- "name" = S.name,
- "color" = S.color-1
+ "name" = current_squad.name,
+ "color" = current_squad.equipment_color
))
squads += squad
data["squads"] = squads
diff --git a/code/modules/cm_preds/falcon.dm b/code/modules/cm_preds/falcon.dm
index 7b369d6b1032..19977a7bd84a 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 ..()
diff --git a/code/modules/cm_preds/yaut_bracers.dm b/code/modules/cm_preds/yaut_bracers.dm
index 5c4079b2be23..f33d5f9a5554 100644
--- a/code/modules/cm_preds/yaut_bracers.dm
+++ b/code/modules/cm_preds/yaut_bracers.dm
@@ -31,6 +31,10 @@
var/notification_sound = TRUE // Whether the bracer pings when a message comes or not
var/charge = 1500
var/charge_max = 1500
+ /// The amount charged per process
+ var/charge_rate = 30
+ /// Cooldown on draining power from APC
+ var/charge_cooldown = COOLDOWN_BRACER_CHARGE
var/cloaked = 0
var/cloak_timer = 0
var/cloak_malfunction = 0
@@ -41,18 +45,15 @@
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)
/obj/item/clothing/gloves/yautja/equipped(mob/user, slot)
. = ..()
if(slot == WEAR_HANDS)
- flags_item |= NODROP
START_PROCESSING(SSobj, src)
- if(isyautja(user))
- to_chat(user, SPAN_WARNING("The bracer clamps securely around your forearm and beeps in a comfortable, familiar way."))
- else
- to_chat(user, SPAN_WARNING("The bracer clamps painfully around your forearm and beeps angrily. It won't come off!"))
if(!owner)
owner = user
+ toggle_lock_internal(user, TRUE)
/obj/item/clothing/gloves/yautja/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -75,11 +76,27 @@
if(!ishuman(loc))
STOP_PROCESSING(SSobj, src)
return
- var/mob/living/carbon/human/H = loc
+ var/mob/living/carbon/human/human_holder = loc
+
+ if(charge < charge_max)
+ var/charge_increase = charge_rate
+ if(is_ground_level(human_holder.z))
+ charge_increase = charge_rate / 6
+ else if(is_mainship_level(human_holder.z))
+ charge_increase = charge_rate / 3
+
+ charge = min(charge + charge_increase, charge_max)
+ var/perc_charge = (charge / charge_max * 100)
+ human_holder.update_power_display(perc_charge)
- charge = min(charge + 30, charge_max)
- var/perc_charge = (charge / charge_max * 100)
- H.update_power_display(perc_charge)
+ //Non-Yautja have a chance to get stunned with each power drain
+ if(!cloaked)
+ 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))
+ decloak(human_holder)
+ shock_user(human_holder)
/// handles decloaking only on HUNTER gloves
/obj/item/clothing/gloves/yautja/proc/decloak()
@@ -101,15 +118,6 @@
var/perc = (charge / charge_max * 100)
human.update_power_display(perc)
- //Non-Yautja have a chance to get stunned with each power drain
- if(!HAS_TRAIT(human, TRAIT_YAUTJA_TECH) && !human.hunter_data.thralled)
- if(prob(15))
- if(cloaked)
- decloak(human)
- cloak_timer = world.time + 5 SECONDS
- shock_user(human)
- return FALSE
-
return TRUE
/obj/item/clothing/gloves/yautja/proc/shock_user(mob/living/carbon/human/M)
@@ -215,7 +223,7 @@
var/caster_material = "ebony"
var/obj/item/card/id/bracer_chip/embedded_id
-
+ var/owner_rank = CLAN_RANK_UNBLOODED_INT
var/caster_deployed = FALSE
var/obj/item/weapon/gun/energy/yautja/plasma_caster/caster
@@ -224,8 +232,10 @@
var/obj/item/weapon/wristblades/left_wristblades
var/obj/item/weapon/wristblades/right_wristblades
-/obj/item/clothing/gloves/yautja/hunter/Initialize(mapload, new_translator_type, new_caster_material)
+/obj/item/clothing/gloves/yautja/hunter/Initialize(mapload, new_translator_type, new_caster_material, new_owner_rank)
. = ..()
+ if(new_owner_rank)
+ owner_rank = new_owner_rank
embedded_id = new(src)
if(new_translator_type)
translator_type = new_translator_type
@@ -242,7 +252,7 @@
if(wearer.gloves == src)
wearer.visible_message(SPAN_DANGER("You hear a hiss and crackle!"), SPAN_DANGER("Your bracers hiss and spark!"), SPAN_DANGER("You hear a hiss and crackle!"))
if(cloaked)
- decloak(wearer)
+ decloak(wearer, TRUE, DECLOAK_EMP)
else
var/turf/our_turf = get_turf(src)
our_turf.visible_message(SPAN_DANGER("You hear a hiss and crackle!"), SPAN_DANGER("You hear a hiss and crackle!"))
@@ -282,29 +292,25 @@
var/mob/living/carbon/human/human = loc
- if(cloaked)
- charge = max(charge - 10, 0)
- if(charge <= 0)
- decloak(loc)
- //Non-Yautja have a chance to get stunned with each power drain
- if(!isyautja(human))
- if(prob(15))
- decloak(human)
- shock_user(human)
- return
+ //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(cloaked)
+ decloak(human, TRUE, DECLOAK_SPECIES)
+ shock_user(human)
+
return ..()
/obj/item/clothing/gloves/yautja/hunter/dropped(mob/user)
move_chip_to_bracer()
if(cloaked)
- decloak(user)
+ decloak(user, TRUE)
..()
/obj/item/clothing/gloves/yautja/hunter/on_enter_storage(obj/item/storage/S)
if(ishuman(loc))
var/mob/living/carbon/human/human = loc
if(cloaked)
- decloak(human)
+ decloak(human, TRUE)
. = ..()
//We use this to activate random verbs for non-Yautja
@@ -421,15 +427,17 @@
var/gear_on_almayer = 0
var/gear_low_orbit = 0
var/closest = 10000
+ /// The item itself, to be referenced so Yautja know what to look for.
+ var/obj/closest_item
var/direction = -1
var/atom/areaLoc = null
- for(var/obj/item/I as anything in GLOB.loose_yautja_gear)
- var/atom/loc = get_true_location(I)
- if(I.anchored)
+ for(var/obj/item/tracked_item as anything in GLOB.loose_yautja_gear)
+ var/atom/loc = get_true_location(tracked_item)
+ if(tracked_item.anchored)
continue
- if(is_honorable_carrier(recursive_holder_check(I)))
+ if(is_honorable_carrier(recursive_holder_check(tracked_item)))
continue
- if(istype(get_area(I), /area/yautja))
+ if(istype(get_area(tracked_item), /area/yautja))
continue
if(is_reserved_level(loc.z))
gear_low_orbit++
@@ -441,6 +449,7 @@
var/dist = get_dist(M,loc)
if(dist < closest)
closest = dist
+ closest_item = tracked_item
direction = get_dir(M,loc)
areaLoc = loc
for(var/mob/living/carbon/human/Y as anything in GLOB.yautja_mob_list)
@@ -472,9 +481,9 @@
output = TRUE
var/areaName = get_area_name(areaLoc)
if(closest == 0)
- to_chat(M, SPAN_NOTICE("You are directly on top of the closest signature."))
+ to_chat(M, SPAN_NOTICE("You are directly on top of the[closest_item ? " [closest_item.name]'s" : ""] signature."))
else
- to_chat(M, SPAN_NOTICE("The closest signature is [closest > 10 ? "approximately [round(closest, 10)]" : "[closest]"] paces [dir2text(direction)] in [areaName]."))
+ to_chat(M, SPAN_NOTICE("The closest signature[closest_item ? ", a [closest_item.name]" : ""], is [closest > 10 ? "approximately [round(closest, 10)]" : "[closest]"] paces [dir2text(direction)] in [areaName]."))
if(!output)
to_chat(M, SPAN_NOTICE("There are no signatures that require your attention."))
return TRUE
@@ -528,7 +537,6 @@
if(true_cloak)
M.invisibility = INVISIBILITY_LEVEL_ONE
M.see_invisible = SEE_INVISIBLE_LEVEL_ONE
- new_alpha = 75
log_game("[key_name_admin(usr)] has enabled their cloaking device.")
M.visible_message(SPAN_WARNING("[M] vanishes into thin air!"), SPAN_NOTICE("You are now invisible to normal detection."))
@@ -553,17 +561,18 @@
sparks.set_up(5, 4, src)
sparks.start()
- decloak(wearer, TRUE)
+ decloak(wearer, TRUE, DECLOAK_EXTINGUISHER)
-/obj/item/clothing/gloves/yautja/hunter/decloak(mob/user, forced)
+/obj/item/clothing/gloves/yautja/hunter/decloak(mob/user, forced, force_multipler = DECLOAK_FORCED)
if(!user)
return
UnregisterSignal(user, COMSIG_HUMAN_EXTINGUISH)
UnregisterSignal(user, COMSIG_HUMAN_PRE_BULLET_ACT)
+ var/decloak_timer = (DECLOAK_STANDARD * force_multipler)
if(forced)
- cloak_malfunction = world.time + 10 SECONDS
+ cloak_malfunction = world.time + decloak_timer
cloaked = FALSE
log_game("[key_name_admin(usr)] has disabled their cloaking device.")
@@ -573,7 +582,7 @@
if(true_cloak)
user.invisibility = initial(user.invisibility)
user.see_invisible = initial(user.see_invisible)
- cloak_timer = world.time + 5 SECONDS
+ cloak_timer = world.time + (DECLOAK_STANDARD / 2)
var/datum/mob_hud/security/advanced/SA = huds[MOB_HUD_SECURITY_ADVANCED]
SA.add_to_hud(user)
@@ -731,12 +740,13 @@
return
exploding = FALSE
to_chat(M, SPAN_NOTICE("Your bracers stop beeping."))
- message_admins("[M] ([M.key]) has deactivated their Self-Destruct.")
+ message_all_yautja("[M.real_name] has cancelled their bracer's self-destruction sequence.")
+ message_admins("[key_name(M)] has deactivated their Self-Destruct.")
return
if(istype(M.wear_mask,/obj/item/clothing/mask/facehugger) || (M.status_flags & XENO_HOST))
to_chat(M, SPAN_WARNING("Strange...something seems to be interfering with your bracer functions..."))
return
- if(forced || alert("Detonate the bracers? Are you sure?","Explosive Bracers", "Yes", "No") == "Yes")
+ if(forced || alert("Detonate the bracers? Are you sure?\n\nNote: If you activate SD for any non-accidental reason during or after a fight, you commit to the SD. By initially activating the SD, you have accepted your impending death to preserve any lost honor.","Explosive Bracers", "Yes", "No") == "Yes")
if(M.gloves != src)
return
if(M.stat == DEAD)
@@ -1066,3 +1076,69 @@
M.u_equip(embedded_id, src, FALSE, TRUE)
else
embedded_id.forceMove(src)
+
+/// Verb to let Yautja attempt the unlocking.
+/obj/item/clothing/gloves/yautja/hunter/verb/toggle_lock()
+ set name = "Toggle Bracer Lock"
+ set desc = "Toggle the lock on your bracers, allowing them to be removed."
+ set category = "Yautja.Misc"
+ set src in usr
+
+ if(usr.stat)
+ to_chat(usr, SPAN_WARNING("You can't do that right now..."))
+ return FALSE
+ if(!HAS_TRAIT(usr, TRAIT_YAUTJA_TECH))
+ to_chat(usr, SPAN_WARNING("You have no idea how to use this..."))
+ return FALSE
+
+ attempt_toggle_lock(usr, FALSE)
+ return TRUE
+
+/// Handles all the locking and unlocking of bracers.
+/obj/item/clothing/gloves/yautja/proc/attempt_toggle_lock(mob/user, force_lock)
+ if(!user)
+ return FALSE
+
+ var/obj/item/grab/held_mob = user.get_active_hand()
+ if(!istype(held_mob))
+ log_attack("[key_name_admin(usr)] has unlocked their own bracer.")
+ toggle_lock_internal(user)
+ return TRUE
+
+ var/mob/living/carbon/human/victim = held_mob.grabbed_thing
+ var/obj/item/clothing/gloves/yautja/hunter/bracer = victim.gloves
+ if(isspeciesyautja(victim) && !(victim.stat == DEAD))
+ to_chat(user, SPAN_WARNING("You cannot unlock the bracer of a living hunter!"))
+ return FALSE
+
+ if(!istype(bracer))
+ to_chat(user, SPAN_WARNING("This [victim.species] does not have a bracer attached."))
+ return FALSE
+
+ if(alert("Are you sure you want to unlock this [victim.species]'s bracer?", "Unlock Bracers", "Yes", "No") != "Yes")
+ return FALSE
+
+ if(user.get_active_hand() == held_mob && victim && victim.gloves == bracer)
+ log_interact(user, victim, "[key_name(user)] unlocked the [bracer.name] of [key_name(victim)].")
+ user.visible_message(SPAN_WARNING("[user] presses a few buttons on [victim]'s wrist bracer."),SPAN_DANGER("You unlock the bracer."))
+ bracer.toggle_lock_internal(victim)
+ return TRUE
+
+/// The actual unlock/lock function.
+/obj/item/clothing/gloves/yautja/proc/toggle_lock_internal(mob/wearer, force_lock)
+ if(((flags_item & NODROP) || (flags_inventory & CANTSTRIP)) && !force_lock)
+ flags_item &= ~NODROP
+ flags_inventory &= ~CANTSTRIP
+ if(!isyautja(wearer))
+ to_chat(wearer, SPAN_WARNING("The bracer beeps pleasantly, releasing it's grip on your forearm."))
+ else
+ to_chat(wearer, SPAN_WARNING("With an angry blare the bracer releases your forearm."))
+ return TRUE
+
+ flags_item |= NODROP
+ flags_inventory |= CANTSTRIP
+ if(isyautja(wearer))
+ to_chat(wearer, SPAN_WARNING("The bracer clamps securely around your forearm and beeps in a comfortable, familiar way."))
+ else
+ to_chat(wearer, SPAN_WARNING("The bracer clamps painfully around your forearm and beeps angrily. It won't come off!"))
+ return TRUE
diff --git a/code/modules/cm_preds/yaut_items.dm b/code/modules/cm_preds/yaut_items.dm
index 8a3306817078..31526ae908f3 100644
--- a/code/modules/cm_preds/yaut_items.dm
+++ b/code/modules/cm_preds/yaut_items.dm
@@ -327,6 +327,7 @@
unacidable = TRUE
ignore_z = TRUE
black_market_value = 100
+ flags_item = ITEM_PREDATOR
/obj/item/device/radio/headset/yautja/talk_into(mob/living/M as mob, message, channel, verb = "commands", datum/language/speaking)
if(!isyautja(M)) //Nope.
@@ -338,9 +339,6 @@
to_chat(hellhound, "\[Radio\]: [M.real_name] [verb], '[message]'.")
..()
-/obj/item/device/radio/headset/yautja/attackby()
- return
-
/obj/item/device/radio/headset/yautja/elder //primarily for use in another MR
name = "\improper Elder Communicator"
volume_settings = list(RADIO_VOLUME_QUIET_STR, RADIO_VOLUME_RAISED_STR, RADIO_VOLUME_IMPORTANT_STR, RADIO_VOLUME_CRITICAL_STR)
@@ -697,6 +695,7 @@
var/tether_range = 5
var/mob/trapped_mob
layer = LOWER_ITEM_LAYER
+ flags_item = ITEM_PREDATOR
/obj/item/hunting_trap/Destroy()
cleanup_tether()
@@ -889,11 +888,31 @@
desc = "A complex cypher chip embedded within a set of clan bracers."
icon = 'icons/obj/items/radio.dmi'
icon_state = "upp_key"
+ access = list(ACCESS_YAUTJA_SECURE)
w_class = SIZE_TINY
flags_equip_slot = SLOT_ID
flags_item = ITEM_PREDATOR|DELONDROP|NODROP
paygrade = null
+/obj/item/card/id/bracer_chip/set_user_data(mob/living/carbon/human/human_user)
+ if(!istype(human_user))
+ return
+
+ registered_name = human_user.real_name
+ registered_ref = WEAKREF(human_user)
+ registered_gid = human_user.gid
+ blood_type = human_user.blood_type
+
+ var/list/new_access = list(ACCESS_YAUTJA_SECURE)
+ var/obj/item/clothing/gloves/yautja/hunter/bracer = loc
+ if(istype(bracer) && bracer.owner_rank)
+ switch(bracer.owner_rank)
+ if(CLAN_RANK_ELDER_INT, CLAN_RANK_LEADER_INT)
+ new_access = list(ACCESS_YAUTJA_SECURE, ACCESS_YAUTJA_ELDER)
+ if(CLAN_RANK_ADMIN_INT)
+ new_access = list(ACCESS_YAUTJA_SECURE, ACCESS_YAUTJA_ELDER, ACCESS_YAUTJA_ANCIENT)
+ access = new_access
+
/obj/item/storage/medicomp
name = "medicomp"
desc = "A complex kit of alien tools and medicines."
diff --git a/code/modules/cm_preds/yaut_machines.dm b/code/modules/cm_preds/yaut_machines.dm
index adba69043dcc..a1782ca22b85 100644
--- a/code/modules/cm_preds/yaut_machines.dm
+++ b/code/modules/cm_preds/yaut_machines.dm
@@ -5,7 +5,7 @@
icon_state = "globe"
breakable = FALSE
- minimap_type = MINIMAP_FLAG_XENO|MINIMAP_FLAG_USCM
+ minimap_type = MINIMAP_FLAG_ALL
/obj/structure/machinery/autolathe/yautja
name = "yautja autolathe"
diff --git a/code/modules/cm_preds/yaut_shield.dm b/code/modules/cm_preds/yaut_shield.dm
index 2e036b4a5357..7b84f935f4c5 100644
--- a/code/modules/cm_preds/yaut_shield.dm
+++ b/code/modules/cm_preds/yaut_shield.dm
@@ -45,10 +45,10 @@
M.apply_effect(3, DAZE)
M.apply_effect(5, SLOW)
-/obj/item/weapon/shield/riot/yautja/attackby(obj/item/I, mob/user)
+/obj/item/weapon/shield/riot/yautja/attackby(obj/item/attacking_item, mob/user)
if(cooldown < world.time - 25)
- if(istype(I, /obj/item/weapon) && (I.flags_item & ITEM_PREDATOR))
- user.visible_message(SPAN_WARNING("[user] bashes \the [src] with \the [I]!"))
+ if(istype(attacking_item, /obj/item/weapon) && (attacking_item.flags_item & ITEM_PREDATOR))
+ user.visible_message(SPAN_WARNING("[user] bashes [src] with [attacking_item]!"))
playsound(user.loc, 'sound/effects/shieldbash.ogg', 25, 1)
cooldown = world.time
else
diff --git a/code/modules/cm_preds/yaut_weapons.dm b/code/modules/cm_preds/yaut_weapons.dm
index 40006dafe60b..fbbe6c183aaa 100644
--- a/code/modules/cm_preds/yaut_weapons.dm
+++ b/code/modules/cm_preds/yaut_weapons.dm
@@ -757,7 +757,7 @@
/obj/item/weapon/gun/launcher/spike/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6
+ set_fire_delay(FIRE_DELAY_TIER_6)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_8
@@ -859,7 +859,7 @@
/obj/item/weapon/gun/energy/yautja/plasmarifle/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6*2
+ set_fire_delay(FIRE_DELAY_TIER_6*2)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
@@ -891,7 +891,7 @@
/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 = 0
+ charge_time -= 80
else
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/rifle/bolt]
charge_time -= 10
@@ -912,6 +912,8 @@
if(refund) charge_time *= 2
return TRUE
+#define FIRE_MODE_STANDARD "Standard"
+#define FIRE_MODE_INCENDIARY "Incendiary"
/obj/item/weapon/gun/energy/yautja/plasmapistol
name = "plasma pistol"
desc = "A plasma pistol capable of rapid fire. It has an integrated battery. Can be used to set fires, either to braziers or on people."
@@ -924,7 +926,12 @@
ammo = /datum/ammo/energy/yautja/pistol
muzzle_flash = null // TO DO, add a decent one.
w_class = SIZE_MEDIUM
+ /// Max amount of shots
var/charge_time = 40
+ /// Amount of charge_time drained per shot
+ var/shot_cost = 1
+ /// standard (sc = 1) or incendiary (sc = 5)
+ var/mode = FIRE_MODE_STANDARD
flags_gun_features = GUN_UNUSUAL_DESIGN
flags_item = ITEM_PREDATOR|IGNITING_ITEM|TWOHANDED
@@ -954,7 +961,7 @@
/obj/item/weapon/gun/energy/yautja/plasmapistol/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_7
+ set_fire_delay(FIRE_DELAY_TIER_7)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_8
@@ -965,9 +972,14 @@
if(isyautja(user))
. = ..()
. += SPAN_NOTICE("It currently has [charge_time]/40 charge.")
+
+ if(mode == FIRE_MODE_INCENDIARY)
+ . += SPAN_RED("It is set to fire incendiary plasma bolts.")
+ else
+ . += SPAN_ORANGE("It is set to fire plasma bolts.")
else
. = list()
- . += SPAN_NOTICE("This thing looks like an alien rifle of some kind. Strange.")
+ . += SPAN_NOTICE("This thing looks like an alien gun of some kind. Strange.")
/obj/item/weapon/gun/energy/yautja/plasmapistol/able_to_fire(mob/user)
@@ -983,7 +995,7 @@
var/obj/item/projectile/projectile = create_bullet(ammo, initial(name))
projectile.SetLuminosity(1)
in_chamber = projectile
- charge_time--
+ charge_time -= shot_cost
return in_chamber
/obj/item/weapon/gun/energy/yautja/plasmapistol/has_ammunition()
@@ -995,9 +1007,30 @@
/obj/item/weapon/gun/energy/yautja/plasmapistol/delete_bullet(obj/item/projectile/projectile_to_fire, refund = 0)
qdel(projectile_to_fire)
- if(refund) charge_time *= 2
+ if(refund)
+ charge_time += shot_cost
+ log_debug("Plasma Pistol refunded shot.")
return TRUE
+/obj/item/weapon/gun/energy/yautja/plasmapistol/use_unique_action()
+ switch(mode)
+ if(FIRE_MODE_STANDARD)
+ mode = FIRE_MODE_INCENDIARY
+ shot_cost = 5
+ fire_delay = FIRE_DELAY_TIER_5
+ to_chat(usr, SPAN_NOTICE("[src] will now fire incendiary plasma bolts."))
+ ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/pistol/incendiary]
+
+ if(FIRE_MODE_INCENDIARY)
+ mode = FIRE_MODE_STANDARD
+ shot_cost = 1
+ fire_delay = FIRE_DELAY_TIER_7
+ to_chat(usr, SPAN_NOTICE("[src] will now fire plasma bolts."))
+ ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/pistol]
+
+#undef FIRE_MODE_STANDARD
+#undef FIRE_MODE_INCENDIARY
+
/obj/item/weapon/gun/energy/yautja/plasma_caster
name = "plasma caster"
desc = "A powerful, shoulder-mounted energy weapon."
@@ -1051,7 +1084,7 @@
/obj/item/weapon/gun/energy/yautja/plasma_caster/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6
+ set_fire_delay(FIRE_DELAY_TIER_6)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT + FIRE_DELAY_TIER_6
scatter = SCATTER_AMOUNT_TIER_6
@@ -1067,21 +1100,21 @@
if("low power stun bolts")
strength = "high power stun bolts"
charge_cost = 100
- fire_delay = FIRE_DELAY_TIER_6 * 3
+ set_fire_delay(FIRE_DELAY_TIER_6 * 3)
fire_sound = 'sound/weapons/pred_lasercannon.ogg'
to_chat(user, SPAN_NOTICE("[src] will now fire [strength]."))
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/caster/bolt/stun]
if("high power stun bolts")
strength = "plasma immobilizers"
charge_cost = 300
- fire_delay = FIRE_DELAY_TIER_6 * 20
+ set_fire_delay(FIRE_DELAY_TIER_6 * 20)
fire_sound = 'sound/weapons/pulse.ogg'
to_chat(user, SPAN_NOTICE("[src] will now fire [strength]."))
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/caster/sphere/stun]
if("plasma immobilizers")
strength = "low power stun bolts"
charge_cost = 30
- fire_delay = FIRE_DELAY_TIER_6
+ set_fire_delay(FIRE_DELAY_TIER_6)
fire_sound = 'sound/weapons/pred_plasmacaster_fire.ogg'
to_chat(user, SPAN_NOTICE("[src] will now fire [strength]."))
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/caster/stun]
@@ -1090,14 +1123,14 @@
if("plasma bolts")
strength = "plasma spheres"
charge_cost = 1200
- fire_delay = FIRE_DELAY_TIER_6 * 20
+ set_fire_delay(FIRE_DELAY_TIER_6 * 20)
fire_sound = 'sound/weapons/pulse.ogg'
to_chat(user, SPAN_NOTICE("[src] will now fire [strength]."))
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/caster/sphere]
if("plasma spheres")
strength = "plasma bolts"
charge_cost = 100
- fire_delay = FIRE_DELAY_TIER_6 * 3
+ set_fire_delay(FIRE_DELAY_TIER_6 * 3)
fire_sound = 'sound/weapons/pred_lasercannon.ogg'
to_chat(user, SPAN_NOTICE("[src] will now fire [strength]."))
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/caster/bolt]
@@ -1109,7 +1142,7 @@
to_chat(usr, SPAN_YAUTJABOLD("[src.source] beeps: [src] is now set to [mode] mode"))
strength = "plasma bolts"
charge_cost = 100
- fire_delay = FIRE_DELAY_TIER_6 * 3
+ set_fire_delay(FIRE_DELAY_TIER_6 * 3)
fire_sound = 'sound/weapons/pred_lasercannon.ogg'
to_chat(usr, SPAN_NOTICE("[src] will now fire [strength]."))
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/caster/bolt]
@@ -1119,7 +1152,7 @@
to_chat(usr, SPAN_YAUTJABOLD("[src.source] beeps: [src] is now set to [mode] mode"))
strength = "low power stun bolts"
charge_cost = 30
- fire_delay = FIRE_DELAY_TIER_6
+ set_fire_delay(FIRE_DELAY_TIER_6)
fire_sound = 'sound/weapons/pred_plasmacaster_fire.ogg'
to_chat(usr, SPAN_NOTICE("[src] will now fire [strength]."))
ammo = GLOB.ammo_list[/datum/ammo/energy/yautja/caster/stun]
diff --git a/code/modules/cm_tech/droppod/lz_effect.dm b/code/modules/cm_tech/droppod/lz_effect.dm
index 32e0bed74388..6a73916c7b3f 100644
--- a/code/modules/cm_tech/droppod/lz_effect.dm
+++ b/code/modules/cm_tech/droppod/lz_effect.dm
@@ -24,8 +24,6 @@
name = "hoverpack warning"
color = "#D4AE1E"
- color = "#D4AE1E"
-
/obj/effect/warning/explosive
name = "explosive warning"
color = "#ff0000"
@@ -36,3 +34,6 @@
/obj/effect/warning/explosive/proc/disappear()
qdel(src)
+/obj/effect/warning/explosive/gas
+ name = "gas warning"
+ color = "#42acd6"
diff --git a/code/modules/cm_tech/implements/adv_weapon.dm b/code/modules/cm_tech/implements/adv_weapon.dm
index 58773f551306..3cc8f1ceb4d6 100644
--- a/code/modules/cm_tech/implements/adv_weapon.dm
+++ b/code/modules/cm_tech/implements/adv_weapon.dm
@@ -101,8 +101,8 @@
/obj/item/weapon/gun/rifle/techweb_railgun/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6*5
- burst_amount = BURST_AMOUNT_TIER_1
+ set_fire_delay(FIRE_DELAY_TIER_6*5)
+ set_burst_amount(BURST_AMOUNT_TIER_1)
accuracy_mult = BASE_ACCURACY_MULT * 3 //you HAVE to be able to hit
scatter = SCATTER_AMOUNT_TIER_8
damage_mult = BASE_BULLET_DAMAGE_MULT
diff --git a/code/modules/cm_tech/implements/medical_czsp.dm b/code/modules/cm_tech/implements/medical_czsp.dm
index ccfd59ce7cbc..e0b00ebf5afd 100644
--- a/code/modules/cm_tech/implements/medical_czsp.dm
+++ b/code/modules/cm_tech/implements/medical_czsp.dm
@@ -186,21 +186,21 @@
/obj/item/weapon/gun/pill/Fire(atom/target, mob/living/user, params, reflex, dual_wield)
if(!able_to_fire(user))
- return
+ return NONE
if(!current_mag.current_rounds)
click_empty(user)
- return
+ return NONE
if(!istype(current_mag, /obj/item/ammo_magazine/internal/pillgun))
- return
+ return NONE
var/obj/item/ammo_magazine/internal/pillgun/internal_mag = current_mag
var/obj/item/reagent_container/pill/pill_to_use = LAZYACCESS(internal_mag.pills, 1)
if(QDELETED(pill_to_use))
click_empty(user)
- return
+ return NONE
var/obj/item/projectile/pill/P = new /obj/item/projectile/pill(src, user, src)
P.generate_bullet(GLOB.ammo_list[/datum/ammo/pill], 0, 0)
@@ -211,6 +211,7 @@
playsound(user.loc, 'sound/items/syringeproj.ogg', 50, 1)
P.fire_at(target, user, src)
+ return AUTOFIRE_CONTINUE
/datum/ammo/pill
name = "syringe"
diff --git a/code/modules/cm_tech/techs/marine/tier4/nuke.dm b/code/modules/cm_tech/techs/marine/tier4/nuke.dm
index eb4e64b59951..441c9aba69fc 100644
--- a/code/modules/cm_tech/techs/marine/tier4/nuke.dm
+++ b/code/modules/cm_tech/techs/marine/tier4/nuke.dm
@@ -10,7 +10,7 @@
tier = /datum/tier/four
announce_name = "NUCLEAR ARSENAL ACQUIRED"
- announce_message = "A nuclear device has been purchased and will be delivered to requisitions via ASRS."
+ announce_message = "A nuclear device has been authorized and will be delivered to requisitions via ASRS."
flags = TREE_FLAG_MARINE
@@ -20,11 +20,12 @@
/datum/tech/nuke/on_unlock()
. = ..()
- var/datum/supply_order/new_order = new /datum/supply_order()
+ var/datum/supply_order/new_order = new()
new_order.ordernum = supply_controller.ordernum
supply_controller.ordernum++
- new_order.object = supply_controller.supply_packs["Intel Operational Nuke"]
+ new_order.object = supply_controller.supply_packs["Encrypted Operational Nuke"]
new_order.orderedby = MAIN_AI_SYSTEM
+ new_order.approvedby = MAIN_AI_SYSTEM
supply_controller.shoppinglist += new_order
diff --git a/code/modules/gear_presets/fun.dm b/code/modules/gear_presets/fun.dm
index 3930f6fb9999..20a7f18077dd 100644
--- a/code/modules/gear_presets/fun.dm
+++ b/code/modules/gear_presets/fun.dm
@@ -141,7 +141,7 @@
launcher.cylinder.storage_slots = launcher.internal_slots //need to adjust the internal storage as well.
for(var/i = 1 to launcher.internal_slots)
new /obj/item/explosive/grenade/high_explosive/frag(launcher.cylinder)
- launcher.fire_delay = FIRE_DELAY_TIER_4 //More HEFA per second, per second. Strictly speaking this is probably a nerf.
+ launcher.set_fire_delay(FIRE_DELAY_TIER_4) //More HEFA per second, per second. Strictly speaking this is probably a nerf.
// Satchel
if(satchel_success)
diff --git a/code/modules/gear_presets/pmc.dm b/code/modules/gear_presets/pmc.dm
index 59f1b9e09cdf..5fa5dd1374e8 100644
--- a/code/modules/gear_presets/pmc.dm
+++ b/code/modules/gear_presets/pmc.dm
@@ -1837,10 +1837,10 @@ list("POUCHES (CHOOSE 2)", 0, null, null, null),
languages = ALL_SYNTH_LANGUAGES
skills = /datum/skills/synthetic
- idtype = /obj/item/card/id/data
+ idtype = /obj/item/card/id/pmc
assignment = JOB_PMC_SYNTH
rank = JOB_PMC_SYNTH
- role_comm_title = "Syn"
+ role_comm_title = "WY Syn"
headset_type = /obj/item/device/radio/headset/distress/pmc/command
@@ -1879,7 +1879,7 @@ list("POUCHES (CHOOSE 2)", 0, null, null, null),
new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/surg_vest/equipped, WEAR_ACCESSORY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/veteran/pmc/light/synth, WEAR_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/weapon/telebaton, WEAR_IN_JACKET)
- new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/veteran/pmc, WEAR_HEAD)
@@ -1895,8 +1895,7 @@ list("POUCHES (CHOOSE 2)", 0, null, null, null),
new_human.equip_to_slot_or_del(new /obj/item/roller/surgical, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/tool/extinguisher/mini, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator/upgraded, WEAR_IN_BACK)
- new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_BACK)
- new /obj/item/clothing/suit/auto_cpr(new_human.back)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crew_monitor, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/lifesaver/full/dutch, WEAR_WAIST)
new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/smg/nailgun/compact, WEAR_J_STORE)
diff --git a/code/modules/gear_presets/synths.dm b/code/modules/gear_presets/synths.dm
index 3e732e93990a..eaf6299b88a8 100644
--- a/code/modules/gear_presets/synths.dm
+++ b/code/modules/gear_presets/synths.dm
@@ -372,6 +372,7 @@
/datum/equipment_preset/synth/survivor/security_synth
name = "Survivor - Synthetic - Corporate Security Synth"
idtype = /obj/item/card/id/silver/cl
+ role_comm_title = "WY Syn"
equipment_to_spawn = list(
WEAR_HEAD = /obj/item/clothing/head/soft/sec/corp,
WEAR_L_EAR = /obj/item/device/radio/headset/distress/WY,
@@ -392,6 +393,7 @@
/datum/equipment_preset/synth/survivor/protection_synth
name = "Survivor - Synthetic - Corporate Protection Synth"
idtype = /obj/item/card/id/pmc
+ role_comm_title = "WY Syn"
equipment_to_spawn = list(
WEAR_HEAD = /obj/item/clothing/head/helmet/marine/veteran/pmc,
WEAR_L_EAR = /obj/item/device/radio/headset/distress/pmc/hvh,
@@ -414,6 +416,8 @@
/datum/equipment_preset/synth/survivor/corporate_synth
name = "Survivor - Synthetic - Corporate Clerical Synth"
+ idtype = /obj/item/card/id/data
+ role_comm_title = "WY Syn"
equipment_to_spawn = list(
WEAR_L_EAR = /obj/item/device/radio/headset/distress/WY,
WEAR_BODY = /obj/item/clothing/under/suit_jacket/trainee,
@@ -471,7 +475,7 @@
new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/surg_vest/equipped, WEAR_ACCESSORY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/veteran/pmc/light/synth, WEAR_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/weapon/telebaton, WEAR_IN_JACKET)
- new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/marine/veteran/pmc, WEAR_HEAD)
@@ -487,8 +491,7 @@
new_human.equip_to_slot_or_del(new /obj/item/roller/surgical, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/tool/extinguisher/mini, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator/upgraded, WEAR_IN_BACK)
- new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/nailgun, WEAR_IN_BACK)
- new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/auto_cpr, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crew_monitor, WEAR_IN_BACK)
new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/lifesaver/full/dutch, WEAR_WAIST)
new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/smg/nailgun/compact, WEAR_J_STORE)
diff --git a/code/modules/gear_presets/uscm_medical.dm b/code/modules/gear_presets/uscm_medical.dm
index ac1e082f6655..080911951b54 100644
--- a/code/modules/gear_presets/uscm_medical.dm
+++ b/code/modules/gear_presets/uscm_medical.dm
@@ -168,7 +168,7 @@
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/doc(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/research(new_human), WEAR_L_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/researcher(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(new_human), WEAR_FEET)
diff --git a/code/modules/gear_presets/yautja.dm b/code/modules/gear_presets/yautja.dm
index 51d2178bd905..21656e62eb7c 100644
--- a/code/modules/gear_presets/yautja.dm
+++ b/code/modules/gear_presets/yautja.dm
@@ -8,6 +8,7 @@
skills = /datum/skills/yautja/warrior
var/default_cape_type = "None"
+ var/clan_rank
/datum/equipment_preset/yautja/load_race(mob/living/carbon/human/new_human, client/mob_client)
new_human.set_species(SPECIES_YAUTJA)
@@ -53,7 +54,7 @@
cape_color = mob_client.prefs.predator_cape_color
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/chainshirt/hunter(new_human), WEAR_BODY)
- new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/yautja/hunter(new_human, translator_type, caster_material), WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/yautja/hunter(new_human, translator_type, caster_material, clan_rank), WEAR_HANDS)
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/yautja(new_human), WEAR_L_EAR)
new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/lantern(new_human), WEAR_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/device/yautja_teleporter(new_human), WEAR_L_STORE)
@@ -89,6 +90,7 @@
/datum/equipment_preset/yautja/youngblood
name = "Yautja Young"
flags = EQUIPMENT_PRESET_START_OF_ROUND
+ clan_rank = CLAN_RANK_UNBLOODED_INT
/datum/equipment_preset/yautja/youngblood/load_name(mob/living/carbon/human/new_human, randomise)
. = ..()
@@ -100,12 +102,14 @@
name = "Yautja Blooded"
flags = EQUIPMENT_PRESET_START_OF_ROUND
default_cape_type = PRED_YAUTJA_QUARTER_CAPE
+ clan_rank = CLAN_RANK_BLOODED_INT
// ELITE
/datum/equipment_preset/yautja/elite
name = "Yautja Elite"
flags = EQUIPMENT_PRESET_START_OF_ROUND
default_cape_type = PRED_YAUTJA_HALF_CAPE
+ clan_rank = CLAN_RANK_ELITE_INT
/datum/equipment_preset/yautja/elite/load_name(mob/living/carbon/human/new_human, randomise)
. = ..()
@@ -117,6 +121,7 @@
name = "Yautja Elder"
flags = EQUIPMENT_PRESET_START_OF_ROUND
default_cape_type = PRED_YAUTJA_THIRD_CAPE
+ clan_rank = CLAN_RANK_ELDER_INT
/datum/equipment_preset/yautja/elder/load_name(mob/living/carbon/human/new_human, randomise)
. = ..()
@@ -132,6 +137,7 @@
name = "Yautja Leader"
flags = EQUIPMENT_PRESET_START_OF_ROUND
default_cape_type = PRED_YAUTJA_CAPE
+ clan_rank = CLAN_RANK_LEADER_INT
/datum/equipment_preset/yautja/leader/load_name(mob/living/carbon/human/new_human, randomise)
. = ..()
@@ -147,6 +153,7 @@
name = "Yautja Ancient"
flags = EQUIPMENT_PRESET_START_OF_ROUND
default_cape_type = PRED_YAUTJA_PONCHO
+ clan_rank = CLAN_RANK_ADMIN_INT
/datum/equipment_preset/yautja/ancient/load_name(mob/living/carbon/human/new_human, randomise)
. = ..()
diff --git a/code/modules/mapping/space_management/space_level.dm b/code/modules/mapping/space_management/space_level.dm
index 86958c5b41b4..861258aa20a2 100644
--- a/code/modules/mapping/space_management/space_level.dm
+++ b/code/modules/mapping/space_management/space_level.dm
@@ -4,11 +4,13 @@
var/list/traits
var/z_value = 1 //actual z placement
var/linkage = SELFLOOPING
- var/xi
- var/yi //imaginary placements on the grid
+ var/x_bounds
+ var/y_bounds
/datum/space_level/New(new_z, new_name, list/new_traits = list())
z_value = new_z
name = new_name
traits = new_traits
//set_linkage(new_traits[ZTRAIT_LINKAGE])
+ x_bounds = world.maxx
+ y_bounds = world.maxy
diff --git a/code/modules/mob/dead/observer/actions.dm b/code/modules/mob/dead/observer/actions.dm
index 49a3890088f6..ff897db4a3f6 100644
--- a/code/modules/mob/dead/observer/actions.dm
+++ b/code/modules/mob/dead/observer/actions.dm
@@ -81,6 +81,22 @@
if(SSticker.mode.check_xeno_late_join(owner))
SSticker.mode.attempt_to_join_as_xeno(owner)
+/datum/action/observer_action/join_lesser_drone
+ name = "Join as Lesser Drone"
+ action_icon_state = "join_lesser_drone"
+ listen_signal = COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE
+
+/datum/action/observer_action/join_lesser_drone/action_activate()
+ if(!owner.client)
+ return
+
+ if(SSticker.current_state < GAME_STATE_PLAYING || !SSticker.mode)
+ owner.balloon_alert(owner, "game must start!")
+ return
+
+ if(SSticker.mode.check_xeno_late_join(owner))
+ SSticker.mode.attempt_to_join_as_lesser_drone(owner)
+
/datum/keybinding/observer
category = CATEGORY_OBSERVER
weight = WEIGHT_DEAD
@@ -108,3 +124,10 @@
name = "join_pred"
full_name = "Join the Hunt"
keybind_signal = COMSIG_KB_OBSERVER_JOIN_PREDATOR
+
+/datum/keybinding/observer/join_lesser_drone
+ hotkey_keys = list("Unbound")
+ classic_keys = list("Unbound")
+ name = "join_lesser_drone"
+ full_name = "Join as Lesser Drone"
+ keybind_signal = COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index ac67471ce30f..9e8fa264af1d 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -54,7 +54,7 @@
var/datum/health_scan/last_health_display
var/ghost_orbit = GHOST_ORBIT_CIRCLE
var/own_orbit_size = 0
- var/observer_actions = list(/datum/action/observer_action/join_xeno)
+ var/observer_actions = list(/datum/action/observer_action/join_xeno, /datum/action/observer_action/join_lesser_drone)
var/datum/action/minimap/observer/minimap
var/larva_queue_cached_message
///Used to bypass time of death checks such as when being selected for larva.
@@ -368,12 +368,13 @@ Works together with spawning an observer, noted above.
if(ghost.client.player_data)
ghost.client.player_data.load_timestat_data()
- // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
- // We don't change facehugger timeofdeath because they are still on cooldown if they died as a hugger
- var/new_tod = isfacehugger(src) ? 1 : ghost.timeofdeath
- // if they died as facehugger, bypass typical TOD checks
- ghost.bypass_time_of_death_checks = isfacehugger(src)
- ghost.client.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
+ // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers or lesser drone
+ var/new_tod = (isfacehugger(src) || islesserdrone(src)) ? 1 : ghost.timeofdeath
+
+ // if they died as facehugger or lesser drone, bypass typical TOD checks
+ ghost.bypass_time_of_death_checks = (isfacehugger(src) || islesserdrone(src))
+
+ ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
ghost.set_huds_from_prefs()
@@ -396,7 +397,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
ghostize(TRUE)
else
var/list/options = list("Ghost", "Stay in body")
- if(check_rights(R_MOD))
+ if(check_other_rights(client, R_MOD, FALSE))
options = list("Aghost") + options
var/text_prompt = "Are you -sure- you want to ghost?\n(You are alive. If you ghost, you won't be able to return to your body. You can't change your mind so choose wisely!)"
var/is_nested = (buckled && istype(buckled, /obj/structure/bed/nest)) ? TRUE : FALSE
@@ -412,15 +413,18 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
AdjustSleeping(2) // Sleep so you will be properly recognized as ghosted
var/turf/location = get_turf(src)
if(location) //to avoid runtime when a mob ends up in nullspace
- msg_admin_niche("[key_name_admin(usr)] has ghosted. [ADMIN_JMP(location)]")
- log_game("[key_name_admin(usr)] has ghosted.")
+ msg_admin_niche("[key_name_admin(client)] has ghosted. [ADMIN_JMP(location)]")
+ log_game("[key_name_admin(client)] has ghosted.")
var/mob/dead/observer/ghost = ghostize((is_nested && nest && !QDELETED(nest))) //FALSE parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
if(ghost && !is_admin_level(z))
ghost.timeofdeath = world.time
- // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
- var/new_tod = isfacehugger(src) ? 1 : world.time
- ghost.bypass_time_of_death_checks = isfacehugger(src)
+ // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers or lesser drone
+ var/new_tod = (isfacehugger(src) || islesserdrone(src)) ? 1 : ghost.timeofdeath
+
+ // if they died as facehugger or lesser drone, bypass typical TOD checks
+ ghost.bypass_time_of_death_checks = (isfacehugger(src) || islesserdrone(src))
+
ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
if(is_nested && nest && !QDELETED(nest))
ghost.can_reenter_corpse = FALSE
@@ -779,6 +783,21 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(SSticker.mode.check_xeno_late_join(src))
SSticker.mode.attempt_to_join_as_facehugger(src)
+/mob/dead/verb/join_as_lesser_drone()
+ set category = "Ghost.Join"
+ set name = "Join as a Lesser Drone"
+ set desc = "Try joining as a Lesser Drone to support the hive."
+
+ if (!client)
+ return
+
+ if(SSticker.current_state < GAME_STATE_PLAYING || !SSticker.mode)
+ to_chat(src, SPAN_WARNING("The game hasn't started yet!"))
+ return
+
+ if(SSticker.mode.check_xeno_late_join(src))
+ SSticker.mode.attempt_to_join_as_lesser_drone(src)
+
/mob/dead/verb/join_as_zombie() //Adapted from join as hellhoud
set category = "Ghost.Join"
set name = "Join as Zombie"
diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm
index 94d1203493da..a77138668d72 100644
--- a/code/modules/mob/dead/observer/orbit.dm
+++ b/code/modules/mob/dead/observer/orbit.dm
@@ -134,7 +134,7 @@
serialized["icon"] = icon ? icon : "private"
if(human.assigned_squad)
- serialized["background_color"] = human.assigned_squad.color ? squad_colors[human.assigned_squad.color] : human.assigned_squad.minimap_color
+ serialized["background_color"] = human.assigned_squad.equipment_color ? human.assigned_squad.equipment_color : human.assigned_squad.minimap_color
else
serialized["background_color"] = human.assigned_equipment_preset?.minimap_background
diff --git a/code/modules/mob/language/languages.dm b/code/modules/mob/language/languages.dm
index e5b693e02b80..2844b5841781 100644
--- a/code/modules/mob/language/languages.dm
+++ b/code/modules/mob/language/languages.dm
@@ -162,24 +162,31 @@
if (!message)
return
+ ///Font size
+ var/scale = "message"
+ if(isARES(speaker))
+ scale = "large"
+
var/message_start = "[name], [speaker.name]"
var/message_body = "broadcasts, \"[message]\""
+ var/full_message = "[message_start] [message_body]"
+
+
GLOB.STUI.game.Add("\[[time_stamp()]]APOLLO: [key_name(speaker)] : [message]
")
GLOB.STUI.processing |= STUI_LOG_GAME_CHAT
log_say("[speaker.name != "Unknown" ? speaker.name : "([speaker.real_name])"] \[APOLLO\]: [message] (CKEY: [speaker.key]) (JOB: [speaker.job])")
log_ares_apollo(speaker.real_name, message)
for (var/mob/dead in GLOB.dead_mob_list)
if(!istype(dead,/mob/new_player) && !istype(dead,/mob/living/brain)) //No meta-evesdropping
- dead.show_message("[message_start] [message_body]", SHOW_MESSAGE_VISIBLE)
+ var/dead_message = "[message_start](F) [message_body]"
+ dead.show_message(dead_message, SHOW_MESSAGE_VISIBLE)
for (var/mob/living/listener in GLOB.alive_mob_list)
if (!listener.hear_apollo())
continue
- else if(isAI(listener))
- message_start = "[name], [speaker.name]"
- listener.show_message("[message_start] [message_body]", SHOW_MESSAGE_VISIBLE)
+ listener.show_message(full_message, SHOW_MESSAGE_VISIBLE)
var/list/listening = hearers(1, src)
listening -= src
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index 181f1c8a2814..8a1b7203c1ca 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -1,72 +1,72 @@
/mob/living/carbon/human/var/cpr_cooldown
/mob/living/carbon/human/var/cpr_attempt_timer
-/mob/living/carbon/human/attack_hand(mob/living/carbon/human/M)
+/mob/living/carbon/human/attack_hand(mob/living/carbon/human/attacking_mob)
if(..())
return TRUE
- if((M != src) && check_shields(0, M.name))
- visible_message(SPAN_DANGER("[M] attempted to touch [src]!"), null, null, 5)
+ if((attacking_mob != src) && check_shields(0, attacking_mob.name))
+ visible_message(SPAN_DANGER("[attacking_mob] attempted to touch [src]!"), null, null, 5)
return 0
- switch(M.a_intent)
+ switch(attacking_mob.a_intent)
if(INTENT_HELP)
- if(on_fire && M != src)
+ if(on_fire && attacking_mob != src)
adjust_fire_stacks(-10, min_stacks = 0)
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
- M.visible_message(SPAN_DANGER("[M] tries to put out the fire on [src]!"), \
+ attacking_mob.visible_message(SPAN_DANGER("[attacking_mob] tries to put out the fire on [src]!"), \
SPAN_WARNING("You try to put out the fire on [src]!"), null, 5)
if(fire_stacks <= 0)
- M.visible_message(SPAN_DANGER("[M] has successfully extinguished the fire on [src]!"), \
+ attacking_mob.visible_message(SPAN_DANGER("[attacking_mob] has successfully extinguished the fire on [src]!"), \
SPAN_NOTICE("You extinguished the fire on [src]."), null, 5)
return 1
// If unconcious with oxygen damage, do CPR. If dead, we do CPR
if(!(stat == UNCONSCIOUS && getOxyLoss() > 0) && !(stat == DEAD))
- help_shake_act(M)
+ help_shake_act(attacking_mob)
return 1
- if(M.head && (M.head.flags_inventory & COVERMOUTH) || M.wear_mask && (M.wear_mask.flags_inventory & COVERMOUTH) && !(M.wear_mask.flags_inventory & ALLOWCPR))
- to_chat(M, SPAN_NOTICE("Remove your mask!"))
+ if(attacking_mob.head && (attacking_mob.head.flags_inventory & COVERMOUTH) || attacking_mob.wear_mask && (attacking_mob.wear_mask.flags_inventory & COVERMOUTH) && !(attacking_mob.wear_mask.flags_inventory & ALLOWCPR))
+ to_chat(attacking_mob, SPAN_NOTICE("Remove your mask!"))
return 0
if(head && (head.flags_inventory & COVERMOUTH) || wear_mask && (wear_mask.flags_inventory & COVERMOUTH) && !(wear_mask.flags_inventory & ALLOWCPR))
- to_chat(M, SPAN_NOTICE("Remove [src.gender==MALE?"his":"her"] mask!"))
+ to_chat(attacking_mob, SPAN_NOTICE("Remove [src.gender==MALE?"his":"her"] mask!"))
return 0
if(cpr_attempt_timer >= world.time)
- to_chat(M, SPAN_NOTICE("CPR is already being performed on [src]!"))
+ to_chat(attacking_mob, SPAN_NOTICE("CPR is already being performed on [src]!"))
return 0
//CPR
- if(M.action_busy)
+ if(attacking_mob.action_busy)
return 1
- M.visible_message(SPAN_NOTICE("[M] starts performing CPR on [src]."),
+ attacking_mob.visible_message(SPAN_NOTICE("[attacking_mob] starts performing CPR on [src]."),
SPAN_HELPFUL("You start performing CPR on [src]."))
- cpr_attempt_timer = world.time + HUMAN_STRIP_DELAY * M.get_skill_duration_multiplier(SKILL_MEDICAL)
- if(do_after(M, HUMAN_STRIP_DELAY * M.get_skill_duration_multiplier(SKILL_MEDICAL), INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
+ cpr_attempt_timer = world.time + HUMAN_STRIP_DELAY * attacking_mob.get_skill_duration_multiplier(SKILL_MEDICAL)
+ if(do_after(attacking_mob, HUMAN_STRIP_DELAY * attacking_mob.get_skill_duration_multiplier(SKILL_MEDICAL), INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
if(stat != DEAD)
var/suff = min(getOxyLoss(), 10) //Pre-merge level, less healing, more prevention of dieing.
apply_damage(-suff, OXY)
updatehealth()
- src.affected_message(M,
+ src.affected_message(attacking_mob,
SPAN_HELPFUL("You feel a breath of fresh air enter your lungs. It feels good."),
SPAN_HELPFUL("You perform CPR on [src]. Repeat at least every 7 seconds."),
- SPAN_NOTICE("[M] performs CPR on [src]."))
+ SPAN_NOTICE("[attacking_mob] performs CPR on [src]."))
if(is_revivable() && stat == DEAD)
if(cpr_cooldown < world.time)
revive_grace_period += 7 SECONDS
- M.visible_message(SPAN_NOTICE("[M] performs CPR on [src]."),
+ attacking_mob.visible_message(SPAN_NOTICE("[attacking_mob] performs CPR on [src]."),
SPAN_HELPFUL("You perform CPR on [src]."))
else
- M.visible_message(SPAN_NOTICE("[M] fails to perform CPR on [src]."),
+ attacking_mob.visible_message(SPAN_NOTICE("[attacking_mob] fails to perform CPR on [src]."),
SPAN_HELPFUL("You fail to perform CPR on [src]. Incorrect rhythm. Do it slower."))
cpr_cooldown = world.time + 7 SECONDS
cpr_attempt_timer = 0
return 1
if(INTENT_GRAB)
- if(M == src)
+ if(attacking_mob == src)
check_for_injuries()
return 1
@@ -74,116 +74,112 @@
return 0
if(w_uniform)
- w_uniform.add_fingerprint(M)
-
- M.start_pulling(src)
+ w_uniform.add_fingerprint(attacking_mob)
+ attacking_mob.start_pulling(src)
return 1
if(INTENT_HARM)
// See if they can attack, and which attacks to use.
- var/datum/unarmed_attack/attack = M.species.unarmed
- if(!attack.is_usable(M)) attack = M.species.secondary_unarmed
- if(!attack.is_usable(M)) return
+ var/datum/unarmed_attack/attack = attacking_mob.species.unarmed
+ if(!attack.is_usable(attacking_mob))
+ attack = attacking_mob.species.secondary_unarmed
+ return
last_damage_data = create_cause_data("fisticuffs", src)
- M.attack_log += text("\[[time_stamp()]\] [pick(attack.attack_verb)]ed [key_name(src)]")
- attack_log += text("\[[time_stamp()]\] Has been [pick(attack.attack_verb)]ed by [key_name(M)]")
- msg_admin_attack("[key_name(M)] [pick(attack.attack_verb)]ed [key_name(src)] in [get_area(src)] ([src.loc.x],[src.loc.y],[src.loc.z]).", src.loc.x, src.loc.y, src.loc.z)
+ attacking_mob.attack_log += text("\[[time_stamp()]\] [pick(attack.attack_verb)]ed [key_name(src)]")
+ attack_log += text("\[[time_stamp()]\] Has been [pick(attack.attack_verb)]ed by [key_name(attacking_mob)]")
+ msg_admin_attack("[key_name(attacking_mob)] [pick(attack.attack_verb)]ed [key_name(src)] in [get_area(src)] ([src.loc.x],[src.loc.y],[src.loc.z]).", src.loc.x, src.loc.y, src.loc.z)
- M.animation_attack_on(src)
- M.flick_attack_overlay(src, "punch")
+ attacking_mob.animation_attack_on(src)
+ attacking_mob.flick_attack_overlay(src, "punch")
var/extra_cqc_dmg = 0 //soft maximum of 5, this damage is added onto the final value depending on how much cqc skill you have
- if(M.skills)
- extra_cqc_dmg = M.skills?.get_skill_level(SKILL_CQC)
+ if(attacking_mob.skills)
+ extra_cqc_dmg = attacking_mob.skills?.get_skill_level(SKILL_CQC)
var/raw_damage = 0 //final value, gets absorbed by the armor and then deals the leftover to the mob
- var/obj/limb/affecting = get_limb(rand_zone(M.zone_selected, 70))
+ var/obj/limb/affecting = get_limb(rand_zone(attacking_mob.zone_selected, 70))
var/armor = getarmor(affecting, ARMOR_MELEE)
playsound(loc, attack.attack_sound, 25, 1)
- visible_message(SPAN_DANGER("[M] [pick(attack.attack_verb)]ed [src]!"), null, null, 5)
+ visible_message(SPAN_DANGER("[attacking_mob] [pick(attack.attack_verb)]ed [src]!"), null, null, 5)
raw_damage = attack.damage + extra_cqc_dmg
var/final_damage = armor_damage_reduction(GLOB.marine_melee, raw_damage, armor, FALSE) // no penetration from punches
apply_damage(final_damage, BRUTE, affecting, sharp=attack.sharp, edge = attack.edge)
if(INTENT_DISARM)
- if(M == src)
+ if(attacking_mob == src)
check_for_injuries()
return 1
- M.attack_log += text("\[[time_stamp()]\] Disarmed [key_name(src)]")
- src.attack_log += text("\[[time_stamp()]\] Has been disarmed by [key_name(M)]")
+ attacking_mob.attack_log += text("\[[time_stamp()]\] Disarmed [key_name(src)]")
+ src.attack_log += text("\[[time_stamp()]\] Has been disarmed by [key_name(attacking_mob)]")
- M.animation_attack_on(src)
- M.flick_attack_overlay(src, "disarm")
+ attacking_mob.animation_attack_on(src)
+ attacking_mob.flick_attack_overlay(src, "disarm")
- msg_admin_attack("[key_name(M)] disarmed [key_name(src)] in [get_area(src)] ([src.loc.x],[src.loc.y],[src.loc.z]).", src.loc.x, src.loc.y, src.loc.z)
+ msg_admin_attack("[key_name(attacking_mob)] disarmed [key_name(src)] in [get_area(src)] ([src.loc.x],[src.loc.y],[src.loc.z]).", src.loc.x, src.loc.y, src.loc.z)
if(w_uniform)
- w_uniform.add_fingerprint(M)
+ w_uniform.add_fingerprint(attacking_mob)
//Accidental gun discharge
- if(!skillcheck(M, SKILL_CQC, SKILL_CQC_SKILLED))
+ if(!skillcheck(attacking_mob, SKILL_CQC, SKILL_CQC_SKILLED))
if (isgun(r_hand) || isgun(l_hand))
- var/obj/item/weapon/gun/W = null
+ var/obj/item/weapon/gun/held_weapon = null
var/chance = 0
if (isgun(l_hand))
- W = l_hand
+ held_weapon = l_hand
chance = hand ? 40 : 20
if (isgun(r_hand))
- W = r_hand
+ held_weapon = r_hand
chance = !hand ? 40 : 20
if (prob(chance))
- visible_message(SPAN_DANGER("[M] accidentally makes [src]'s [W.name] go off during the struggle!"), SPAN_DANGER("You accidentally make [src]'s [W.name] go off during the struggle!"), null, 5)
+ visible_message(SPAN_DANGER("[attacking_mob] accidentally makes [src]'s [held_weapon.name] go off during the struggle!"), SPAN_DANGER("You accidentally make [src]'s [held_weapon.name] go off during the struggle!"), null, 5)
var/list/turfs = list()
for(var/turf/T in view())
turfs += T
var/turf/target = pick(turfs)
count_niche_stat(STATISTICS_NICHE_DISCHARGE)
- attack_log += "\[[time_stamp()]\] [key_name(src)] accidentally fired [W.name] in [get_area(src)] triggered by [key_name(M)]."
- M.attack_log += "\[[time_stamp()]\] [key_name(src)] accidentally fired [W.name] in [get_area(src)] triggered by [key_name(M)]."
- msg_admin_attack("[key_name(src)] accidentally fired [W.name] in [get_area(M)] ([M.loc.x],[M.loc.y],[M.loc.z]) triggered by [key_name(M)].", M.loc.x, M.loc.y, M.loc.z)
-
- return W.afterattack(target,src)
+ attack_log += "\[[time_stamp()]\] [key_name(src)] accidentally fired [held_weapon.name] in [get_area(src)] triggered by [key_name(attacking_mob)]."
+ attacking_mob.attack_log += "\[[time_stamp()]\] [key_name(src)] accidentally fired [held_weapon.name] in [get_area(src)] triggered by [key_name(attacking_mob)]."
+ msg_admin_attack("[key_name(src)] accidentally fired [held_weapon.name] in [get_area(attacking_mob)] ([attacking_mob.loc.x],[attacking_mob.loc.y],[attacking_mob.loc.z]) triggered by [key_name(attacking_mob)].", attacking_mob.loc.x, attacking_mob.loc.y, attacking_mob.loc.z)
- var/randn = rand(1, 100)
- var/skill_level = M.skills.get_skill_level(SKILL_CQC)
- if(M.skills)
- randn -= 5 * skill_level //attacker's martial arts training
+ return held_weapon.afterattack(target,src)
- if(skills)
- randn += 5 * skill_level //defender's martial arts training
+ var/disarm_chance = rand(1, 100)
+ var/attacker_skill_level = attacking_mob.skills ? skills.get_skill_level(SKILL_CQC) : SKILL_CQC_MAX // No skills, so assume max
+ var/defender_skill_level = skills ? skills.get_skill_level(SKILL_CQC) : SKILL_CQC_MAX // No skills, so assume max
+ disarm_chance -= 5 * attacker_skill_level
+ disarm_chance += 5 * defender_skill_level
- if (randn <= 25)
- apply_effect(2 + skill_level, WEAKEN)
+ if(disarm_chance <= 25)
+ apply_effect(2 + max((attacker_skill_level - defender_skill_level), 0), WEAKEN)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
- var/shovetext = skill_level > 1 ? "tackled" : pick("pushed", "shoved")
- visible_message(SPAN_DANGER("[M] has [shovetext] [src]!"), null, null, 5)
+ var/shove_text = attacker_skill_level > 1 ? "tackled" : pick("pushed", "shoved")
+ visible_message(SPAN_DANGER("[attacking_mob] has [shove_text] [src]!"), null, null, 5)
return
- if(randn <= 60)
+ if(disarm_chance <= 60)
//BubbleWrap: Disarming breaks a pull
if(pulling)
- visible_message(SPAN_DANGER("[M] has broken [src]'s grip on [pulling]!"), null, null, 5)
+ visible_message(SPAN_DANGER("[attacking_mob] has broken [src]'s grip on [pulling]!"), null, null, 5)
stop_pulling()
else
drop_held_item()
- visible_message(SPAN_DANGER("[M] has disarmed [src]!"), null, null, 5)
+ visible_message(SPAN_DANGER("[attacking_mob] has disarmed [src]!"), null, null, 5)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7)
return
-
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, 7)
- visible_message(SPAN_DANGER("[M] attempted to disarm [src]!"), null, null, 5)
- return
+ visible_message(SPAN_DANGER("[attacking_mob] attempted to disarm [src]!"), null, null, 5)
/mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params)
return
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..b6eeb68e95d3 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -123,6 +123,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..139b339fbfd7 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
@@ -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..8e8849f5ad4c 100644
--- a/code/modules/mob/living/carbon/human/species/yautja.dm
+++ b/code/modules/mob/living/carbon/human/species/yautja/_species.dm
@@ -180,16 +180,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 +208,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 +221,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..f29a990b6254
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/yautja/fake_sounds.dm
@@ -0,0 +1,22 @@
+/datum/emote/living/carbon/human/yautja/fake_sound
+ category = YAUTJA_EMOTE_CATEGORY_FAKESOUND
+
+/datum/emote/living/carbon/human/yautja/fake_sound/aliengrowl
+ 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
+ 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
+ key = "malescream"
+ sound = "male_scream"
+
+/datum/emote/living/carbon/human/yautja/fake_sound/femalescream
+ 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..409fa83e7f1b
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/yautja/fake_voice.dm
@@ -0,0 +1,50 @@
+/datum/emote/living/carbon/human/yautja/voice
+ category = YAUTJA_EMOTE_CATEGORY_VOICE
+
+/datum/emote/living/carbon/human/yautja/voice/anytime
+ key = "anytime"
+ sound = 'sound/voice/pred_anytime.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/helpme
+ key = "helpme"
+ sound = 'sound/voice/pred_helpme.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/iseeyou
+ key = "iseeyou"
+ sound = 'sound/hallucinations/i_see_you2.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/itsatrap
+ key = "itsatrap"
+ sound = 'sound/voice/pred_itsatrap.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/overhere
+ key = "overhere"
+ sound = 'sound/voice/pred_overhere.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/turnaround
+ key = "turnaround"
+ sound = 'sound/voice/pred_turnaround.ogg'
+ volume = 25
+
+
+/datum/emote/living/carbon/human/yautja/voice/comeonout
+ key = "comeonout"
+ sound = 'sound/voice/pred_come_on_out.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/overthere
+ key = "overthere"
+ sound = 'sound/voice/pred_over_there.ogg'
+
+
+/datum/emote/living/carbon/human/yautja/voice/uglyfreak
+ 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/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index f74b65c2606d..6ee249a6333e 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -367,7 +367,6 @@ Applied by gun suicide and high impact bullet executions, removed by rejuvenate,
overlays_standing[GLOVES_LAYER] = I
apply_overlay(GLOVES_LAYER)
-
/mob/living/carbon/human/update_inv_glasses()
remove_overlay(GLASSES_LAYER)
if(glasses)
@@ -458,12 +457,15 @@ Applied by gun suicide and high impact bullet executions, removed by rejuvenate,
if(istype(head, /obj/item/clothing/head/helmet/marine))
var/obj/item/clothing/head/helmet/marine/marine_helmet = head
if(assigned_squad && marine_helmet.flags_marine_helmet & HELMET_SQUAD_OVERLAY)
- var/datum/squad/S = assigned_squad
- var/leader = S.squad_leader == src
- if(S.color <= helmetmarkings.len)
- var/image/J = leader? helmetmarkings_sql[S.color] : helmetmarkings[S.color]
- J.layer = -HEAD_SQUAD_LAYER
- overlays_standing[HEAD_SQUAD_LAYER] = J
+ if(assigned_squad && assigned_squad.equipment_color)
+ var/leader = assigned_squad.squad_leader
+ var/image/helmet_overlay = image(marine_helmet.helmet_overlay_icon, icon_state = "std-helmet")
+ if(leader == src)
+ helmet_overlay = image(marine_helmet.helmet_overlay_icon, icon_state = "sql-helmet")
+ helmet_overlay.layer = -HEAD_SQUAD_LAYER
+ helmet_overlay.alpha = assigned_squad.armor_alpha
+ helmet_overlay.color = assigned_squad.equipment_color
+ overlays_standing[HEAD_SQUAD_LAYER] = helmet_overlay
apply_overlay(HEAD_SQUAD_LAYER)
var/num_helmet_overlays = 0
@@ -517,14 +519,16 @@ Applied by gun suicide and high impact bullet executions, removed by rejuvenate,
if(istype(wear_suit, /obj/item/clothing/suit/storage/marine))
var/obj/item/clothing/suit/storage/marine/marine_armor = wear_suit
if(marine_armor.flags_marine_armor & ARMOR_SQUAD_OVERLAY)
- if(assigned_squad)
- var/datum/squad/S = assigned_squad
- var/leader = S.squad_leader == src
- if(S.color <= helmetmarkings.len)
- var/image/J = leader? armormarkings_sql[S.color] : armormarkings[S.color]
- J.layer = -SUIT_SQUAD_LAYER
- overlays_standing[SUIT_SQUAD_LAYER] = J
- apply_overlay(SUIT_SQUAD_LAYER)
+ if(assigned_squad && assigned_squad.equipment_color)
+ var/leader = assigned_squad.squad_leader
+ var/image/squad_overlay = image(marine_armor.squad_overlay_icon, icon_state = "std-armor")
+ if(leader == src)
+ squad_overlay = image(marine_armor.squad_overlay_icon, icon_state = "sql-armor")
+ squad_overlay.layer = -SUIT_SQUAD_LAYER
+ squad_overlay.alpha = assigned_squad.armor_alpha
+ squad_overlay.color = assigned_squad.equipment_color
+ overlays_standing[SUIT_SQUAD_LAYER] = squad_overlay
+ apply_overlay(SUIT_SQUAD_LAYER)
if(marine_armor.armor_overlays.len)
var/image/K
diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
index e6fc734e4e5a..e390cd15dca2 100644
--- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm
@@ -190,7 +190,8 @@
if(isyautja(affected_mob) || (flags_embryo & FLAG_EMBRYO_PREDATOR))
new_xeno = new /mob/living/carbon/xenomorph/larva/predalien(affected_mob)
- yautja_announcement(SPAN_YAUTJABOLDBIG("WARNING!\n\nAn abomination has been detected at [get_area_name(new_xeno)]. It is a stain upon our purity and is unfit for life. Exterminate it immediately"))
+ yautja_announcement(SPAN_YAUTJABOLDBIG("WARNING!\n\nAn abomination has been detected at [get_area_name(new_xeno)]. It is a stain upon our purity and is unfit for life. Exterminate it immediately.\n\nHeavy Armory unlocked."))
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_YAUTJA_ARMORY_OPENED)
else
new_xeno = new(affected_mob)
@@ -218,6 +219,18 @@
to_chat(new_xeno, "Talk in Hivemind using ; (e.g. ';My life for the queen!')")
playsound_client(new_xeno.client, 'sound/effects/xeno_newlarva.ogg', 25, 1)
+ // Inform observers to grab some popcorn if it isnt nested
+ if(!HAS_TRAIT(affected_mob, TRAIT_NESTED))
+ var/area/burst_area = get_area(src)
+ if(burst_area)
+ for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
+ to_chat(observer, SPAN_DEADSAY("A [new_xeno.hive.prefix]Larva is about to chestburst out of [affected_mob] at \the [burst_area]! [OBSERVER_JMP(observer, affected_mob)]"))
+ to_chat(src, SPAN_DEADSAY("A [new_xeno.hive.prefix]Larva is about to chestburst out of [affected_mob] at \the [burst_area]!"))
+ else
+ for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
+ 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
/mob/living/carbon/xenomorph/larva/proc/cause_unbearable_pain(mob/living/carbon/victim)
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
index b12ff5d6c3bb..6361ff595b10 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
@@ -185,7 +185,7 @@
var/is_shover_queen = isqueen(M)
var/can_resist_shove = M.hivenumber != src.hivenumber || ((isqueen(src) || IS_XENO_LEADER(src)) && !is_shover_queen)
var/can_mega_shove = is_shover_queen || IS_XENO_LEADER(M)
- if(can_mega_shove && !can_resist_shove)
+ if(can_mega_shove && !can_resist_shove || (mob_size < MOB_SIZE_XENO_SMALL && M.mob_size >= MOB_SIZE_XENO_SMALL))
playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1)
M.visible_message(SPAN_WARNING("\The [M] shoves \the [src] out of her way!"), \
SPAN_WARNING("You shove \the [src] out of your way!"), null, 5, CHAT_TYPE_XENO_COMBAT)
diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
index 5586da3765dc..aea1ce9a5906 100644
--- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm
@@ -271,27 +271,40 @@
//
//////////////////////////////////////////////////////////////////
var/tunnel = FALSE
- var/stealth = FALSE // for check on lurker invisibility
+ /// for check on lurker invisibility
+ var/stealth = FALSE
var/burrow = FALSE
var/fortify = FALSE
var/crest_defense = FALSE
- var/agility = FALSE // 0 - upright, 1 - all fours
+ /// 0/FALSE - upright, 1/TRUE - all fours
+ var/agility = FALSE
var/ripping_limb = FALSE
var/steelcrest = FALSE
- // Related to zooming out (primarily queen and boiler)
- var/devour_timer = 0 // The world.time at which we will regurgitate our currently-vored victim
- var/extra_build_dist = 0 // For drones/hivelords. Extends the maximum build range they have
+ /// The world.time at which we will regurgitate our currently-vored victim
+ var/devour_timer = 0
+ /// For drones/hivelords. Extends the maximum build range they have
+ var/extra_build_dist = 0
+ /// tiles from self you can plant eggs.
+ var/egg_planting_range = 1
var/can_stack_builds = FALSE
var/list/resin_build_order
- var/selected_resin // Which resin structure to build when we secrete resin, defaults to null.
- var/selected_construction = XENO_STRUCTURE_CORE //which special structure to build when we place constructions
- var/selected_mark // If leader what mark you will place when you make one
- var/datum/ammo/xeno/ammo = null //The ammo datum for our spit projectiles. We're born with this, it changes sometimes.
+ /// Which resin structure to build when we secrete resin, defaults to null.
+ var/selected_resin
+ /// which special structure to build when we place constructions
+ var/selected_construction = XENO_STRUCTURE_CORE
+ /// If leader what mark you will place when you make one
+ var/selected_mark
+ /// The ammo datum for our spit projectiles. We're born with this, it changes sometimes.
+ var/datum/ammo/xeno/ammo = null
var/tunnel_delay = 0
- var/list/available_fruits = list() // List of placeable the xenomorph has access to.
- var/list/current_fruits = list() // If we have current_fruits that are limited, e.g. fruits
- var/max_placeable = 0 // Limit to that amount
- var/obj/effect/alien/resin/fruit/selected_fruit = null // the typepath of the placeable we wanna put down
+ /// List of placeable the xenomorph has access to.
+ var/list/available_fruits = list()
+ /// If we have current_fruits that are limited, e.g. fruits
+ var/list/current_fruits = list()
+ /// Limit to that amount
+ var/max_placeable = 0
+ /// the typepath of the placeable we wanna put down
+ var/obj/effect/alien/resin/fruit/selected_fruit = null
var/list/built_structures = list()
var/icon_xeno
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
index d95fbc304397..c1b1ee5f8902 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
@@ -382,6 +382,7 @@
name = "Hide"
action_icon_state = "xenohide"
plasma_cost = 0
+ xeno_cooldown = 0.5 SECONDS
macro_path = /datum/action/xeno_action/verb/verb_hide
action_type = XENO_ACTION_CLICK
listen_signal = COMSIG_KB_XENO_HIDE
@@ -391,6 +392,15 @@
if(X && !X.buckled && !X.is_mob_incapacitated())
return TRUE
+/// remove hide and apply modified attack cooldown
+/datum/action/xeno_action/onclick/xenohide/proc/post_attack()
+ var/mob/living/carbon/xenomorph/xeno = owner
+ if(xeno.layer == XENO_HIDING_LAYER)
+ xeno.layer = initial(xeno.layer)
+ button.icon_state = "template"
+ xeno.update_wounds()
+ apply_cooldown(4) //2 second cooldown after attacking
+
/datum/action/xeno_action/onclick/xenohide/give_to(mob/living/living_mob)
. = ..()
var/mob/living/carbon/xenomorph/xeno = owner
@@ -510,16 +520,15 @@
SIGNAL_HANDLER
if(tracked_queen)
- UnregisterSignal(tracked_queen, list(COMSIG_QUEEN_MOUNT_OVIPOSITOR, COMSIG_QUEEN_DISMOUNT_OVIPOSITOR, COMSIG_PARENT_QDELETING))
+ UnregisterSignal(tracked_queen, list(COMSIG_QUEEN_MOUNT_OVIPOSITOR, COMSIG_QUEEN_DISMOUNT_OVIPOSITOR))
tracked_queen = new_queen
- if(!tracked_queen.ovipositor)
+ if(!tracked_queen?.ovipositor)
hide_from(owner)
RegisterSignal(tracked_queen, COMSIG_QUEEN_MOUNT_OVIPOSITOR, PROC_REF(handle_mount_ovipositor))
RegisterSignal(tracked_queen, COMSIG_QUEEN_DISMOUNT_OVIPOSITOR, PROC_REF(handle_dismount_ovipositor))
- RegisterSignal(tracked_queen, COMSIG_PARENT_QDELETING, PROC_REF(handle_queen_qdel))
/// deals with the queen mounting the ovipositor, unhiding the action from the user
/datum/action/xeno_action/onclick/tacmap/proc/handle_mount_ovipositor()
@@ -533,13 +542,6 @@
hide_from(owner)
-/// cleans up references to the queen when the queen is being qdel'd, hides the action from the user
-/datum/action/xeno_action/onclick/tacmap/proc/handle_queen_qdel()
- SIGNAL_HANDLER
-
- tracked_queen = null
- hide_from(owner)
-
/datum/action/xeno_action/onclick/tacmap/use_ability(atom/target)
var/mob/living/carbon/xenomorph/xeno = owner
xeno.xeno_tacmap()
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 4a57c0729b91..a9cffb196cb3 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
@@ -396,11 +396,9 @@
return
if(X.layer == XENO_HIDING_LAYER) //Xeno is currently hiding, unhide him
- X.layer = MOB_LAYER
- X.update_wounds()
- var/datum/action/hide_ability = get_xeno_action_by_type(X, /datum/action/xeno_action/onclick/xenohide)
- if(hide_ability)
- hide_ability.button.icon_state = "template"
+ var/datum/action/xeno_action/onclick/xenohide/hide = get_xeno_action_by_type(X, /datum/action/xeno_action/onclick/xenohide)
+ if(hide)
+ hide.post_attack()
if(isravager(X))
X.emote("roar")
@@ -508,6 +506,10 @@
var/mob/living/carbon/xenomorph/xeno = owner
if(!xeno.check_state(TRUE))
return
+ if(!action_cooldown_check())
+ return
+ if(xeno.action_busy)
+ return
if(xeno.layer != XENO_HIDING_LAYER)
xeno.layer = XENO_HIDING_LAYER
to_chat(xeno, SPAN_NOTICE("You are now hiding."))
@@ -517,6 +519,7 @@
to_chat(xeno, SPAN_NOTICE("You have stopped hiding."))
button.icon_state = "template"
xeno.update_wounds()
+ apply_cooldown()
return ..()
/datum/action/xeno_action/onclick/place_trap/use_ability(atom/A)
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_abilities.dm
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_abilities.dm
@@ -0,0 +1 @@
+
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_macros.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_macros.dm
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_macros.dm
@@ -0,0 +1 @@
+
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_powers.dm
new file mode 100644
index 000000000000..515efea23d53
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/lesser_drone/lesser_drone_powers.dm
@@ -0,0 +1,6 @@
+/datum/action/xeno_action/onclick/plant_weeds/lesser/use_ability(atom/A)
+ if(!(locate(/obj/effect/alien/weeds/node) in orange(4, owner)))
+ to_chat(owner, SPAN_XENONOTICE("You can only plant resin nodes near other resin nodes!"))
+ return
+
+ . = ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
index 058e643f5c64..5c1584c565c6 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
@@ -25,7 +25,7 @@
var/obj/item/clothing/gloves/yautja/hunter/YG = locate(/obj/item/clothing/gloves/yautja/hunter) in human
if(isyautja(human) && YG)
if(YG.cloaked)
- YG.decloak(human)
+ YG.decloak(human, TRUE, DECLOAK_PREDALIEN)
YG.cloak_timer = xeno_cooldown * 0.1
else if(isxeno(carbon) && xeno.can_not_harm(carbon))
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
index ec1697f30081..6b847a6a4fec 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm
@@ -82,15 +82,20 @@
var/list/hugger_image_index = list()
var/mutable_appearance/hugger_overlays_icon
+ var/mutable_appearance/eggsac_overlays_icon
/mob/living/carbon/xenomorph/carrier/update_icons()
. = ..()
-
- update_hugger_overlays()
+ if (mutation_type == CARRIER_NORMAL)
+ update_hugger_overlays()
+ if (mutation_type == CARRIER_EGGSAC)
+ update_eggsac_overlays()
/mob/living/carbon/xenomorph/carrier/proc/update_hugger_overlays()
if(!hugger_overlays_icon)
return
+ if(mutation_type != CARRIER_NORMAL)
+ return
overlays -= hugger_overlays_icon
hugger_overlays_icon.overlays.Cut()
@@ -99,7 +104,7 @@
hugger_image_index.Cut()
return
- update_icon_maths(round(( huggers_cur / huggers_max ) * 3.999) + 1)
+ update_clinger_maths(round(( huggers_cur / huggers_max ) * 3.999) + 1)
for(var/i in hugger_image_index)
if(stat == DEAD)
@@ -114,8 +119,8 @@
overlays += hugger_overlays_icon
-/mob/living/carbon/xenomorph/carrier/proc/update_icon_maths(number)
- var/funny_list = list(1,2,3,4)
+/mob/living/carbon/xenomorph/carrier/proc/update_clinger_maths(number)
+ var/clinger_list = list(1,2,3,4)
if(length(hugger_image_index) != number)
if(length(hugger_image_index) > number)
while(length(hugger_image_index) != number)
@@ -123,20 +128,56 @@
else
while(length(hugger_image_index) != number)
for(var/i in hugger_image_index)
- if(locate(i) in funny_list)
- funny_list -= i
- hugger_image_index += funny_list[rand(1,length(funny_list))]
+ if(locate(i) in clinger_list)
+ clinger_list -= i
+ hugger_image_index += clinger_list[rand(1,length(clinger_list))]
+
+/mob/living/carbon/xenomorph/carrier/proc/update_eggsac_overlays()
+ if(!eggsac_overlays_icon)
+ return
+ if(mutation_type != CARRIER_EGGSAC)
+ return
+
+ overlays -= eggsac_overlays_icon
+ eggsac_overlays_icon.overlays.Cut()
+
+ if(!eggs_cur)
+ return
+
+ ///Simplified image index change.
+ var/i = 0
+ if(eggs_cur > 8)
+ i = 3
+ else if (eggs_cur > 4)
+ i = 2
+ else if (eggs_cur > 0)
+ i = 1
+
+ if(stat != DEAD)
+ if(lying)
+ if((resting || sleeping) && (!knocked_down && !knocked_out && health > 0))
+ eggsac_overlays_icon.overlays += icon(icon, "eggsac_[i] Sleeping")
+ else
+ eggsac_overlays_icon.overlays +=icon(icon, "eggsac_[i] Knocked Down")
+ else
+ eggsac_overlays_icon.overlays +=icon(icon, "eggsac_[i]")
+
+ overlays += eggsac_overlays_icon
/mob/living/carbon/xenomorph/carrier/Initialize(mapload, mob/living/carbon/xenomorph/oldxeno, h_number)
. = ..()
hugger_overlays_icon = mutable_appearance('icons/mob/xenos/overlay_effects64x64.dmi',"empty")
+ eggsac_overlays_icon = mutable_appearance('icons/mob/xenos/overlay_effects64x64.dmi',"empty")
/mob/living/carbon/xenomorph/carrier/death(cause, gibbed)
. = ..(cause, gibbed)
if(.)
- var/chance = 75
+ var/chance = 75 //75% to drop an egg or hugger.
+ if(mutation_type == CARRIER_EGGSAC)
+ visible_message(SPAN_XENOWARNING("[src] throes as its eggsac bursts into a mess of acid!"))
+ playsound(src.loc, 'sound/effects/alien_egg_burst.ogg', 25, 1)
- if (huggers_cur)
+ if(huggers_cur)
//Hugger explosion, like an egg morpher
var/obj/item/clothing/mask/facehugger/hugger
visible_message(SPAN_XENOWARNING("The chittering mass of tiny aliens is trying to escape [src]!"))
@@ -145,10 +186,15 @@
hugger = new(loc, hivenumber)
step_away(hugger, src, 1)
- while (eggs_cur > 0)
+ var/eggs_dropped = FALSE
+ for(var/i in 1 to eggs_cur)
if(prob(chance))
new /obj/item/xeno_egg(loc, hivenumber)
- eggs_cur--
+ eggs_dropped = TRUE
+ eggs_cur = 0
+
+ if(eggs_dropped) //Checks whether or not to announce egg drop.
+ xeno_message(SPAN_XENOANNOUNCE("[src] has dropped some precious eggs!"), 2, hive.hivenumber)
/mob/living/carbon/xenomorph/carrier/get_status_tab_items()
. = ..()
@@ -272,6 +318,7 @@
if(eggs_cur < eggs_max)
if(stat == CONSCIOUS)
eggs_cur++
+ update_icons()
to_chat(src, SPAN_NOTICE("You store the egg and carry it for safekeeping. Now sheltering: [eggs_cur] / [eggs_max]."))
qdel(E)
else
@@ -306,6 +353,7 @@
return
E = new(src, hivenumber)
eggs_cur--
+ update_icons()
put_in_active_hand(E)
to_chat(src, SPAN_XENONOTICE("You grab one of the eggs in your storage. Now sheltering: [eggs_cur] / [eggs_max]."))
return
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
index 271fe3182210..a44fee5645ac 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Hellhound.dm
@@ -12,6 +12,7 @@
evasion = XENO_EVASION_LOW
speed = XENO_SPEED_HELLHOUND
attack_delay = -2
+ behavior_delegate_type = /datum/behavior_delegate/hellhound_base
minimum_evolve_time = 0
@@ -125,3 +126,13 @@
/mob/living/carbon/xenomorph/hellhound/handle_blood_splatter(splatter_dir)
new /obj/effect/temp_visual/dir_setting/bloodsplatter/hellhound(loc, splatter_dir)
+
+/datum/behavior_delegate/hellhound_base
+ name = "Base Hellhound Behavior Delegate"
+
+/datum/behavior_delegate/hellhound_base/melee_attack_additional_effects_self()
+ ..()
+
+ var/datum/action/xeno_action/onclick/xenohide/hide = get_xeno_action_by_type(bound_xeno, /datum/action/xeno_action/onclick/xenohide)
+ if(hide)
+ hide.post_attack()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
index c7970e017c4b..14aa31b1f1e8 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm
@@ -136,12 +136,18 @@ Your health meter will not regenerate normally, so kill and die for the hive!= WEED_LEVEL_HIVE && W.linked_hive.hivenumber == hivenumber)
- hive_weeds = W
+ var/obj/effect/alien/weeds/hive_weeds
+ var/obj/effect/alien/weeds/any_weeds
+ for(var/obj/effect/alien/weeds/weed in T)
+ if(weed.weed_strength >= WEED_LEVEL_HIVE && weed.linked_hive.hivenumber == hivenumber)
+ hive_weeds = weed
break
+ if(weed.weed_strength >= WEED_LEVEL_WEAK && weed.linked_hive.hivenumber == hivenumber) //check for ANY weeds
+ any_weeds = weed
- if(!hive_weeds)
- var/datum/hive_status/hive = GLOB.hive_datum[hivenumber]
+ var/datum/hive_status/hive = GLOB.hive_datum[hivenumber]
+ if(!any_weeds && !hive_weeds) //you need at least some weeds to plant on.
+ to_chat(user, SPAN_XENOWARNING("[src] must be planted on [lowertext(hive.prefix)]weeds."))
+ return
+
+ if(!hive_weeds && user.mutation_type != CARRIER_EGGSAC)
to_chat(user, SPAN_XENOWARNING("[src] can only be planted on [lowertext(hive.prefix)]hive weeds."))
return
@@ -106,8 +116,8 @@
if(!user.check_plasma(30))
return
- for(var/obj/effect/alien/weeds/W in T)
- if(W.weed_strength >= WEED_LEVEL_HIVE)
+ for(var/obj/effect/alien/weeds/weed in T)
+ if(weed.weed_strength >= WEED_LEVEL_HIVE || user.mutation_type == CARRIER_EGGSAC)
user.use_plasma(30)
var/obj/effect/alien/egg/newegg = new /obj/effect/alien/egg(T, hivenumber)
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm
index c8e6b768f2bc..7e5773e5aec5 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/carrier/eggsac.dm
@@ -1,6 +1,6 @@
/datum/xeno_mutator/eggsac
name = "STRAIN: Carrier - Eggsac"
- description = "In exchange for your ability to store huggers and place traps, you gain larger plasma stores, strong pheromones, and the ability to lay eggs by using your plasma stores. In addition, you can now carry twelve eggs at once and can place eggs one pace further than normal."
+ description = "In exchange for your ability to store huggers and place traps, you gain larger plasma stores, strong pheromones, and the ability to lay eggs by using your plasma stores. In addition, you can now carry twelve eggs at once, can plant eggs of non-hive weeds, and can place eggs one pace further than normal"
flavor_description = "An egg is always an adventure; the next one may be different."
cost = MUTATOR_COST_EXPENSIVE
individual_only = TRUE
@@ -36,8 +36,10 @@
playsound(carrier.loc, 'sound/voice/alien_facehugger_dies.ogg', 25, 1)
carrier.huggers_cur = 0
carrier.huggers_max = 0
+ carrier.update_hugger_overlays()
+ carrier.update_eggsac_overlays()
carrier.eggs_max = 12
- carrier.extra_build_dist = 1
+ carrier.egg_planting_range = 2
return TRUE
/datum/action/xeno_action/active_toggle/generate_egg
@@ -73,3 +75,5 @@
if(egg_generation_progress >= 15)
egg_generation_progress = 0
xeno.eggs_cur++
+ to_chat(xeno, SPAN_XENONOTICE("You generate a egg. Now sheltering: [xeno.eggs_cur] / [xeno.eggs_max]."))
+ xeno.update_icons()
diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
index 93ee952acbab..05d81d92daa4 100644
--- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
+++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm
@@ -1,5 +1,3 @@
-#define JOIN_AS_FACEHUGGER_DELAY 3 MINUTES
-
// Actual caste datum basedef
/datum/caste_datum
var/caste_type = ""
@@ -352,7 +350,10 @@
/// How many huggers can the hive support
var/playable_hugger_limit = 0
- var/datum/tacmap/tacmap
+ /// How many lesser drones the hive can support
+ var/lesser_drone_limit = 0
+
+ var/datum/tacmap/xeno/tacmap
var/minimap_type = MINIMAP_FLAG_XENO
/datum/hive_status/New()
@@ -811,16 +812,6 @@
#undef OPEN_SLOTS
#undef GUARANTEED_SLOTS
-// returns if that location can be used to plant eggs
-/datum/hive_status/proc/in_egg_plant_range(turf/T)
- if(!istype(living_xeno_queen))
- return TRUE // xenos already dicked without queen. Let them plant whereever
-
- if(!living_xeno_queen.ovipositor)
- return FALSE // ovid queen only
-
- return get_dist(living_xeno_queen, T) <= egg_planting_range
-
/datum/hive_status/proc/can_build_structure(structure_name)
if(!structure_name || !hive_structures_limit[structure_name])
return FALSE
@@ -893,8 +884,11 @@
qdel(S)
for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos)
if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z))
- if(isfacehugger(xeno))
+ if(isfacehugger(xeno) || islesserdrone(xeno))
to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind."))
+ if(xeno.stomach_contents.len)
+ xeno.devour_timer = 0
+ xeno.handle_stomach_contents()
qdel(xeno)
continue
if(xeno.hunter_data.hunted && !isqueen(xeno))
@@ -908,7 +902,7 @@
qdel(xeno)
stored_larva++
continue
- if(!isfacehugger(xeno))
+ if(xeno.tier >= 1)
xenos_count++
for(var/i in GLOB.alive_mob_list)
var/mob/living/potential_host = i
@@ -1036,8 +1030,12 @@
to_chat(user, SPAN_WARNING("\The [GLOB.hive_datum[hivenumber]] cannot support more facehuggers! Limit: [current_hugger_count]/[playable_hugger_limit]"))
return FALSE
- if(alert(user, "Are you sure you want to become a facehugger?", "Confirmation", "Yes", "No") != "Yes")
+ if(tgui_alert(user, "Are you sure you want to become a facehugger?", "Confirmation", list("Yes", "No")) != "Yes")
return FALSE
+
+ if(!user.client)
+ return FALSE
+
return TRUE
/datum/hive_status/proc/spawn_as_hugger(mob/dead/observer/user, atom/A)
@@ -1046,6 +1044,54 @@
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)
+
+/datum/hive_status/proc/can_spawn_as_lesser_drone(mob/dead/observer/user)
+ if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber])
+ return FALSE
+
+ if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned
+ to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph."))
+ return FALSE
+
+ if(world.time - user.timeofdeath < JOIN_AS_LESSER_DRONE_DELAY)
+ var/time_left = round((user.timeofdeath + JOIN_AS_LESSER_DRONE_DELAY - world.time) / 10)
+ to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a lesser drone until 30 seconds have passed ([time_left] seconds remaining)."))
+ return FALSE
+
+ if(totalXenos.len <= 0)
+ to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!"))
+ return FALSE
+
+ if(!living_xeno_queen)
+ to_chat(user, SPAN_WARNING("The selected hive does not have a Queen!"))
+ return FALSE
+
+ if(!living_xeno_queen.ovipositor && !SSticker.mode.is_in_endgame)
+ to_chat(user, SPAN_WARNING("The selected hive does not have a Queen on Ovipositor!"))
+ return FALSE
+
+ update_lesser_drone_limit()
+
+ var/current_lesser_drone_count = 0
+ for(var/mob/mob as anything in totalXenos)
+ 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]"))
+ return FALSE
+
+ if(tgui_alert(user, "Are you sure you want to become a lesser drone?", "Confirmation", list("Yes", "No")) != "Yes")
+ return FALSE
+
+ if(!user.client)
+ return FALSE
+
+ return TRUE
///Called by /obj/item/alien_embryo when a host is bursting to determine extra larva per burst
/datum/hive_status/proc/increase_larva_after_burst()
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/power/apc.dm b/code/modules/power/apc.dm
index 7247e9b87b16..e7a160095705 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -676,9 +676,9 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
var/turf/T = get_turf(src)
var/obj/structure/cable/N = T.get_cable_node()
if(prob(50) && electrocute_mob(usr, N, N))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(5, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread
+ spark.set_up(5, 1, src)
+ spark.start()
return
if(C.use(10))
user.visible_message(SPAN_NOTICE("[user] wires [src]'s frame."),
@@ -700,9 +700,9 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
to_chat(user, SPAN_WARNING("\The [src] lacks a terminal to remove."))
return
if (prob(50) && electrocute_mob(user, terminal.powernet, terminal))
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(5, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread
+ spark.set_up(5, 1, src)
+ spark.start()
return
new /obj/item/stack/cable_coil(loc,10)
user.visible_message(SPAN_NOTICE("[user] removes [src]'s wiring and terminal."),
@@ -798,41 +798,87 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
//Human mob special interaction goes here.
if(ishuman(user))
- var/mob/living/carbon/human/H = user
+ var/mob/living/carbon/human/grabber = user
- if(H.species.flags & IS_SYNTHETIC && H.a_intent == INTENT_GRAB)
- if(H.action_busy)
- return
-
- if(!do_after(H, 20, INTERRUPT_ALL, BUSY_ICON_GENERIC))
- return
+ if(grabber.a_intent == INTENT_GRAB)
- playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+ //Synthpack recharge
+ if((grabber.species.flags & IS_SYNTHETIC) && istype(grabber.back, /obj/item/storage/backpack/marine/smartpack))
+ var/obj/item/storage/backpack/marine/smartpack/s_pack = grabber.back
+ if(grabber.action_busy)
+ return
- if(stat & BROKEN)
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- to_chat(H, SPAN_DANGER("The APC's power currents surge eratically, damaging your chassis!"))
- H.apply_damage(10,0, BURN)
- else if(cell && cell.charge > 0)
- if(!istype(H.back, /obj/item/storage/backpack/marine/smartpack))
+ if(!do_after(grabber, 20, INTERRUPT_ALL, BUSY_ICON_GENERIC))
return
- var/obj/item/storage/backpack/marine/smartpack/S = H.back
- if(S.battery_charge < SMARTPACK_MAX_POWER_STORED)
- var/charge_to_use = min(cell.charge, SMARTPACK_MAX_POWER_STORED - S.battery_charge)
- if(!(cell.use(charge_to_use)))
+ playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+
+ if(stat & BROKEN)
+ var/datum/effect_system/spark_spread/spark = new()
+ spark.set_up(3, 1, src)
+ spark.start()
+ to_chat(grabber, SPAN_DANGER("The APC's power currents surge eratically, damaging your chassis!"))
+ grabber.apply_damage(10,0, BURN)
+ else if(cell && cell.charge > 0)
+ if(!istype(s_pack))
return
- S.battery_charge += charge_to_use
- to_chat(user, SPAN_NOTICE("You slot your fingers into the APC interface and siphon off some of the stored charge. [S.name] now has [S.battery_charge]/[SMARTPACK_MAX_POWER_STORED]"))
- charging = APC_CHARGING
+
+ if(s_pack.battery_charge < SMARTPACK_MAX_POWER_STORED)
+ var/charge_to_use = min(cell.charge, SMARTPACK_MAX_POWER_STORED - s_pack.battery_charge)
+ if(!(cell.use(charge_to_use)))
+ return
+ s_pack.battery_charge += charge_to_use
+ to_chat(user, SPAN_NOTICE("You slot your fingers into the APC interface and siphon off some of the stored charge. [s_pack.name] now has [s_pack.battery_charge]/[SMARTPACK_MAX_POWER_STORED]"))
+ charging = APC_CHARGING
+ else
+ to_chat(user, SPAN_WARNING("[s_pack.name] is already fully charged."))
else
- to_chat(user, SPAN_WARNING("[S.name] is already fully charged."))
- else
- to_chat(user, SPAN_WARNING("There is no charge to draw from that APC."))
- return
- else if(H.species.can_shred(H))
+ to_chat(user, SPAN_WARNING("There is no charge to draw from that APC."))
+ return
+
+ // Yautja Bracer Recharge
+ var/obj/item/clothing/gloves/yautja/bracer = grabber.gloves
+ if(istype(bracer))
+ if(grabber.action_busy)
+ return FALSE
+ if(!COOLDOWN_FINISHED(bracer, bracer_recharge))
+ to_chat(user, SPAN_WARNING("It is too soon for [bracer.name] to siphon power again. Wait [COOLDOWN_SECONDSLEFT(bracer, bracer_recharge)] seconds."))
+ return FALSE
+ to_chat(user, SPAN_NOTICE("You rest your bracer against the APC interface and begin to siphon off some of the stored energy."))
+ if(!do_after(grabber, 20, INTERRUPT_ALL, BUSY_ICON_HOSTILE))
+ return FALSE
+
+ if(stat & BROKEN)
+ var/datum/effect_system/spark_spread/spark = new()
+ spark.set_up(3, 1, src)
+ spark.start()
+ to_chat(grabber, SPAN_DANGER("The APC's power currents surge eratically, super-heating your bracer!"))
+ playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+ grabber.apply_damage(10,0, BURN)
+ return FALSE
+ if(!cell || cell.charge <= 0)
+ to_chat(user, SPAN_WARNING("There is no charge to draw from that APC."))
+ return FALSE
+
+ if(bracer.charge_max <= bracer.charge)
+ to_chat(user, SPAN_WARNING("[bracer.name] is already fully charged."))
+ return FALSE
+
+ var/charge_to_use = min(cell.charge, bracer.charge_max - bracer.charge)
+ if(!(cell.use(charge_to_use)))
+ return FALSE
+ playsound(src.loc, 'sound/effects/sparks2.ogg', 25, 1)
+ bracer.charge += charge_to_use
+ COOLDOWN_START(bracer, bracer_recharge, bracer.charge_cooldown)
+ to_chat(grabber, SPAN_YAUTJABOLD("[icon2html(bracer)] \The [bracer] beep: Power siphon complete. Charge at [bracer.charge]/[bracer.charge_max]."))
+ if(bracer.notification_sound)
+ playsound(bracer.loc, 'sound/items/pred_bracer.ogg', 75, 1)
+ charging = APC_CHARGING
+ set_broken() // Breaks the APC
+
+ return TRUE
+
+ else if(grabber.species.can_shred(grabber))
var/allcut = TRUE
for(var/wire = 1; wire < length(get_wire_descriptions()); wire++)
if(!isWireCut(wire))
@@ -1011,9 +1057,9 @@ GLOBAL_LIST_INIT(apc_wire_descriptions, list(
smoke.set_up(1, 0, loc)
smoke.attach(src)
smoke.start()
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(1, 1, src)
- s.start()
+ var/datum/effect_system/spark_spread/spark = new()
+ spark.set_up(1, 1, src)
+ spark.start()
visible_message(SPAN_WARNING("[src] suddenly lets out a blast of smoke and some sparks!"))
/obj/structure/machinery/power/apc/surplus()
diff --git a/code/modules/projectiles/ammo_datums.dm b/code/modules/projectiles/ammo_datums.dm
index 7114c924fe61..f97195d5ac32 100644
--- a/code/modules/projectiles/ammo_datums.dm
+++ b/code/modules/projectiles/ammo_datums.dm
@@ -2217,12 +2217,27 @@
damage = 40
shell_speed = AMMO_SPEED_TIER_2
-/datum/ammo/energy/yautja/pistol/set_bullet_traits()
+/datum/ammo/energy/yautja/pistol/incendiary
+ damage = 10
+
+/datum/ammo/energy/yautja/pistol/incendiary/set_bullet_traits()
. = ..()
LAZYADD(traits_to_give, list(
BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_incendiary)
))
+/datum/ammo/bullet/shrapnel/plasma
+ name = "plasma wave"
+ shrapnel_chance = 0
+ penetration = ARMOR_PENETRATION_TIER_10
+ accuracy = HIT_ACCURACY_TIER_MAX
+ damage = 15
+ icon_state = "shrapnel_plasma"
+ damage_type = BURN
+
+/datum/ammo/bullet/shrapnel/plasma/on_hit_mob(mob/hit_mob, obj/item/projectile/hit_projectile)
+ hit_mob.apply_effect(2, WEAKEN)
+
/datum/ammo/energy/yautja/caster
name = "root caster bolt"
icon_state = "ion"
diff --git a/code/modules/projectiles/full_auto.dm b/code/modules/projectiles/full_auto.dm
deleted file mode 100644
index ad3e037b68e6..000000000000
--- a/code/modules/projectiles/full_auto.dm
+++ /dev/null
@@ -1,129 +0,0 @@
-/obj/item/weapon/gun/proc/full_auto_start(client/source, atom/A, params)
- SIGNAL_HANDLER
- if(!ismob(loc) || !A)
- return
- var/mob/user = loc
-
- // No FA with mods
- if(params["shift"] || params["ctrl"] || params["alt"])
- return
-
- // No shooting the 4th wall
- if(istype(A, /atom/movable/screen))
- return
-
- // No FA on attachables
- if(active_attachable)
- return
-
- if(user.get_active_hand() != src)
- return
-
- if(user.throw_mode)
- return
-
- // Don't open fire on adjacent targets. Let normal attack code handle this
- if(A.Adjacent(user) && user.a_intent != INTENT_HARM)
- return
-
- if(user.client?.prefs?.toggle_prefs & TOGGLE_HELP_INTENT_SAFETY && (user.a_intent == INTENT_HELP))
- if(world.time % 3) // Limits how often this message pops up, saw this somewhere else and thought it was clever
- to_chat(user, SPAN_NOTICE("You consider shooting at [A], but do not follow through."))
- return
-
- fa_firing = TRUE
- fa_shots = 0
- fa_target = A
- fa_params = params
-
- // Ward off spamclickin fellas
- if(world.time < last_fired + fa_delay)
- if(world.time % 3) // Limits how often this message pops up, saw this somewhere else and it's fucking stupid
- to_chat(user, SPAN_WARNING("[src] is not ready to fire again!"))
- return
-
- // Kick off the full-auto
- INVOKE_ASYNC(src, PROC_REF(repeat_fire), user)
-
-/obj/item/weapon/gun/proc/full_auto_stop(client/source, atom/A, params)
- SIGNAL_HANDLER
- fa_target = null
- fa_params = null
-
-/obj/item/weapon/gun/proc/full_auto_new_target(client/source, atom/start, atom/hovered, params)
- SIGNAL_HANDLER
- if(!ismob(loc))
- return
- var/mob/user = loc
-
- if(istype(hovered, /atom/movable/screen))
- return
-
- if(get_turf(hovered) == get_turf(user))
- return
-
- fa_target = hovered
- fa_params = params2list(params)
-
-/obj/item/weapon/gun/proc/repeat_fire(mob/user)
- if(!fa_target)
- return
-
- // fun is ended
- if(active_attachable)
- return
-
- // fun's over
- if(!in_chamber && !current_mag)
- click_empty(user)
- return
-
- // fun's also over
- if(!user.canmove || user.stat || user.is_mob_restrained() || !user.loc || !isturf(user.loc))
- return
-
- // you abandoned the f u n
- if(user.get_active_hand() != src || loc != user)
- return
-
- if(user.throw_mode)
- return
-
- user.face_atom(fa_target)
- Fire(fa_target, user, fa_params)
-
- addtimer(CALLBACK(src, PROC_REF(repeat_fire), user), fa_delay)
-
-// Make sure we're not trying to start full auto when the gun isn't even held by anyone
-/obj/item/weapon/gun/dropped(mob/user)
- ..()
-
- if(!user.client)
- return
-
- UnregisterSignal(user.client, list(
- COMSIG_CLIENT_LMB_DOWN,
- COMSIG_CLIENT_LMB_UP,
- COMSIG_CLIENT_LMB_DRAG,
- ))
-
-// Also make sure it's registered when held in any hand and full-auto is on
-/obj/item/weapon/gun/equipped(mob/user, slot)
- ..()
-
- if(!user.client)
- return
-
- // If it was equipped to anything but the hands, make sure we're not registered
- if(slot != WEAR_R_HAND && slot != WEAR_L_HAND)
- UnregisterSignal(user.client, list(
- COMSIG_CLIENT_LMB_DOWN,
- COMSIG_CLIENT_LMB_UP,
- COMSIG_CLIENT_LMB_DRAG,
- ))
- return
-
- if(flags_gun_features & GUN_FULL_AUTO_ON)
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_DOWN, PROC_REF(full_auto_start))
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_UP, PROC_REF(full_auto_stop))
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_DRAG, PROC_REF(full_auto_new_target))
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 263f5b07cca4..54e066e05544 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -92,8 +92,8 @@
///Multiplier. Increased and decreased through attachments. Multiplies the accuracy/scatter penalty of the projectile when firing onehanded while moving.
var/movement_onehanded_acc_penalty_mult = 5 //Multiplier. Increased and decreased through attachments. Multiplies the accuracy/scatter penalty of the projectile when firing onehanded while moving.
- ///For regular shots, how long to wait before firing again.
- var/fire_delay = 0
+ ///For regular shots, how long to wait before firing again. Use modify_fire_delay and set_fire_delay instead of modifying this on the fly
+ VAR_PROTECTED/fire_delay = 0
///When it was last fired, related to world.time.
var/last_fired = 0
@@ -112,10 +112,10 @@
var/delay_style = WEAPON_DELAY_SCATTER_AND_ACCURACY
//Burst fire.
- ///How many shots can the weapon shoot in burst? Anything less than 2 and you cannot toggle burst.
- var/burst_amount = 1
- ///The delay in between shots. Lower = less delay = faster.
- var/burst_delay = 1
+ ///How many shots can the weapon shoot in burst? Anything less than 2 and you cannot toggle burst. Use modify_burst_amount and set_burst_amount instead of modifying this
+ VAR_PROTECTED/burst_amount = 1
+ ///The delay in between shots. Lower = less delay = faster. Use modify_burst_delay and set_burst_delay instead of modifying this
+ VAR_PROTECTED/burst_delay = 1
///When burst-firing, this number is extra time before the weapon can fire again. Depends on number of rounds fired.
var/extra_delay = 0
///When PB burst firing and handing off to /fire after a target moves out of range, this is how many bullets have been fired.
@@ -124,22 +124,13 @@
// Full auto
///Whether or not the gun is firing full-auto
var/fa_firing = FALSE
- ///How many shots have been fired using full-auto. Used to figure out scatter
- var/fa_shots = 0
///How many full-auto shots to get to max scatter?
- var/fa_scatter_peak = 8
+ var/fa_scatter_peak = 4
///How bad does the scatter get on full auto?
- var/fa_max_scatter = 5
- ///The delay when firing full-auto
- var/fa_delay = 2.5
- ///The atom we're shooting at while full-autoing
- var/atom/fa_target = null
+ var/fa_max_scatter = 8.5
///Click parameters to use when firing full-auto
var/fa_params = null
- //Targeting.
- ///List of who yer targeting.
- var/tmp/list/mob/living/target
///Used to fire faster at more than one person.
var/tmp/mob/living/last_moved_mob
var/tmp/lock_time = -100
@@ -223,6 +214,21 @@
// Set to TRUE or FALSE, it overrides the is_civilian_usable check with its value. Does nothing if null.
var/civilian_usable_override = null
+ ///Current selected firemode of the gun.
+ var/gun_firemode = GUN_FIREMODE_SEMIAUTO
+ ///List of allowed firemodes.
+ var/list/gun_firemode_list = list()
+ ///How many bullets the gun fired while bursting/auto firing
+ var/shots_fired = 0
+ /// Currently selected target to fire at. Set with set_target()
+ VAR_PRIVATE/atom/target
+ /// Current user (holding) of the gun. Set with set_gun_user()
+ VAR_PRIVATE/mob/gun_user
+ /// If this gun should spawn with semi-automatic fire. Protected due to it never needing to be edited.
+ VAR_PROTECTED/start_semiauto = TRUE
+ /// If this gun should spawn with automatic fire. Protected due to it never needing to be edited.
+ VAR_PROTECTED/start_automatic = FALSE
+
/**
* An assoc list where the keys are fire delay group string defines
@@ -253,11 +259,13 @@
else
current_mag = new current_mag(src, spawn_empty? 1:0)
replace_ammo(null, current_mag)
- else ammo = GLOB.ammo_list[ammo] //If they don't have a mag, they fire off their own thing.
+ else
+ ammo = GLOB.ammo_list[ammo] //If they don't have a mag, they fire off their own thing.
set_gun_attachment_offsets()
set_gun_config_values()
set_bullet_traits()
+ setup_firemodes()
update_force_list() //This gives the gun some unique attack verbs for attacking.
handle_starting_attachment()
handle_random_attachments()
@@ -265,6 +273,8 @@
if(auto_retrieval_slot)
AddElement(/datum/element/drop_retrieval/gun, auto_retrieval_slot)
update_icon() //for things like magazine overlays
+ gun_firemode = gun_firemode_list[1] || GUN_FIREMODE_SEMIAUTO
+ AddComponent(/datum/component/automatedfire/autofire, fire_delay, burst_delay, burst_amount, gun_firemode, CALLBACK(src, PROC_REF(set_bursting)), CALLBACK(src, PROC_REF(reset_fire)), CALLBACK(src, PROC_REF(fire_wrapper)), CALLBACK(src, PROC_REF(display_ammo)), CALLBACK(src, PROC_REF(set_auto_firing))) //This should go after handle_starting_attachment() and setup_firemodes() to get the proper values set.
/obj/item/weapon/gun/proc/set_gun_attachment_offsets()
attachable_offset = null
@@ -288,8 +298,8 @@
attachments = null
attachable_overlays = null
QDEL_NULL(active_attachable)
- fa_target = null
GLOB.gun_list -= src
+ set_gun_user(null)
. = ..()
/*
@@ -299,12 +309,12 @@
* This makes reading each gun's values MUCH easier.
*/
/obj/item/weapon/gun/proc/set_gun_config_values()
- fire_delay = FIRE_DELAY_TIER_5
+ set_fire_delay(FIRE_DELAY_TIER_5)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
burst_scatter_mult = SCATTER_AMOUNT_TIER_7
- burst_amount = BURST_AMOUNT_TIER_1
+ set_burst_amount(BURST_AMOUNT_TIER_5)
scatter_unwielded = SCATTER_AMOUNT_TIER_6
damage_mult = BASE_BULLET_DAMAGE_MULT
damage_falloff_mult = DAMAGE_FALLOFF_TIER_10
@@ -378,8 +388,9 @@
//Add attachment bonuses
for(var/slot in attachments)
var/obj/item/attachable/R = attachments[slot]
- if(!R) continue
- fire_delay += R.delay_mod
+ if(!R)
+ continue
+ modify_fire_delay(R.delay_mod)
accuracy_mult += R.accuracy_mod
accuracy_mult_unwielded += R.accuracy_unwielded_mod
scatter += R.scatter_mod
@@ -392,7 +403,7 @@
effective_range_max += R.range_max_mod
recoil += R.recoil_mod
burst_scatter_mult += R.burst_scatter_mod
- burst_amount += R.burst_mod
+ modify_burst_amount(R.burst_mod)
recoil_unwielded += R.recoil_unwielded_mod
aim_slowdown += R.aim_speed_mod
wield_delay += R.wield_delay_mod
@@ -471,6 +482,10 @@
for(var/obj/O in contents)
O.emp_act(severity)
+/*
+Note: pickup and dropped on weapons must have both the ..() to update zoom AND twohanded,
+As sniper rifles have both and weapon mods can change them as well. ..() deals with zoom only.
+*/
/obj/item/weapon/gun/equipped(mob/user, slot)
if(flags_item & NODROP) return
@@ -480,8 +495,31 @@
pull_time += 3
guaranteed_delay_time = world.time + WEAPON_GUARANTEED_DELAY
+ var/delay_left = (last_fired + fire_delay + additional_fire_group_delay) - world.time
+ if(fire_delay_group && delay_left > 0)
+ for(var/group in fire_delay_group)
+ LAZYSET(user.fire_delay_next_fire, group, world.time + delay_left)
+
+ if(slot in list(WEAR_L_HAND, WEAR_R_HAND))
+ set_gun_user(user)
+ else
+ set_gun_user(null)
+
return ..()
+/obj/item/weapon/gun/dropped(mob/user)
+ . = ..()
+
+ disconnect_light_from_mob(user)
+
+ var/delay_left = (last_fired + fire_delay + additional_fire_group_delay) - world.time
+ if(fire_delay_group && delay_left > 0)
+ for(var/group in fire_delay_group)
+ LAZYSET(user.fire_delay_next_fire, group, world.time + delay_left)
+
+ unwield(user)
+ set_gun_user(null)
+
/obj/item/weapon/gun/update_icon()
if(overlays)
overlays.Cut()
@@ -675,8 +713,8 @@
data["penetration_max"] = ARMOR_PENETRATION_TIER_10
data["punch_max"] = 5
data["glob_armourbreak"] = GLOB.xeno_general.armor_ignore_integrity
- data["automatic"] = flags_gun_features & GUN_HAS_FULL_AUTO
- data["auto_only"] = flags_gun_features & GUN_FULL_AUTO_ONLY
+ data["automatic"] = (GUN_FIREMODE_AUTOMATIC in gun_firemode_list)
+ data["auto_only"] = ((length(gun_firemode_list) == 1) && (GUN_FIREMODE_AUTOMATIC in gun_firemode_list))
return data
@@ -724,14 +762,10 @@
else
wield_time -= 2*user.skills.get_skill_level(SKILL_FIREARMS)
- if(flags_gun_features & GUN_FULL_AUTO_ON)
- ADD_TRAIT(user, TRAIT_OVERRIDE_CLICKDRAG, TRAIT_SOURCE_WEAPON)
-
return 1
/obj/item/weapon/gun/unwield(mob/user)
. = ..()
- REMOVE_TRAIT(user, TRAIT_OVERRIDE_CLICKDRAG, TRAIT_SOURCE_WEAPON)
if(.)
slowdown = initial(slowdown)
@@ -892,33 +926,6 @@ User can be passed as null, (a gun reloading itself for instance), so we need to
// \\
//----------------------------------------------------------
-/obj/item/weapon/gun/afterattack(atom/A, mob/living/user, flag, params)
- if(active_attachable && (active_attachable.flags_attach_features & ATTACH_MELEE)) //this is expected to do something in melee.
- active_attachable.fire_attachment(A, src, user)
- return TRUE
- if(flag)
- return FALSE //It's adjacent, is the user, or is on the user's person
- if(!istype(A))
- return FALSE
- // If firing full-auto, the firing starts when the mouse is clicked, not when it's released
- // so the gun should already be shooting
- if(fa_firing)
- fa_firing = FALSE
- return TRUE
- if(flags_gun_features & GUN_BURST_FIRING)
- return FALSE
- if(!user || !user.client || !user.client.prefs)
- return FALSE
- else if(user.client?.prefs?.toggle_prefs & TOGGLE_HELP_INTENT_SAFETY && (user.a_intent == INTENT_HELP))
- if (world.time % 3) // Limits how often this message pops up, saw this somewhere else and thought it was clever
- //Absolutely SCREAM this at people so they don't get killed by it
- to_chat(user, SPAN_HIGHDANGER("Help intent safety is on! Switch to another intent to fire your weapon."))
- click_empty(user)
- return FALSE
- else
- Fire(A,user,params) //Otherwise, fire normally.
- return TRUE
-
/**
load_into_chamber(), reload_into_chamber(), and clear_jam() do all of the heavy lifting.
If you need to change up how a gun fires, just change these procs for that subtype
@@ -1056,17 +1063,11 @@ and you're good to go.
// \\
//----------------------------------------------------------
-/obj/item/weapon/gun/proc/Fire(atom/target, mob/living/user, params, reflex = 0, dual_wield)
- set waitfor = 0
-
- if(!able_to_fire(user) || !target)
- return
+/obj/item/weapon/gun/proc/Fire(atom/target, mob/living/user, params, reflex = FALSE, dual_wield)
+ set waitfor = FALSE
- var/turf/curloc = get_turf(user) //In case the target or we are expired.
- var/turf/targloc = get_turf(target)
- if(!targloc || !curloc)
- return //Something has gone wrong...
- var/atom/original_target = target //This is for burst mode, in case the target changes per scatter chance in between fired bullets.
+ if(!able_to_fire(user) || !target || !get_turf(user) || !get_turf(target))
+ return NONE
/*
This is where the grenade launcher and flame thrower function as attachments.
@@ -1074,12 +1075,10 @@ and you're good to go.
*/
var/check_for_attachment_fire = 0
- //Number of bullets based on burst. If an active attachable is shooting, bursting is always zero.
- var/bullets_to_fire = 1
if(active_attachable?.flags_attach_features & ATTACH_WEAPON) //Attachment activated and is a weapon.
check_for_attachment_fire = 1
if(!(active_attachable.flags_attach_features & ATTACH_PROJECTILE)) //If it's unique projectile, this is where we fire it.
- if(active_attachable.current_rounds <= 0)
+ if((active_attachable.current_rounds <= 0) && !(active_attachable.flags_attach_features & ATTACH_IGNORE_EMPTY))
click_empty(user) //If it's empty, let them know.
to_chat(user, SPAN_WARNING("[active_attachable] is empty!"))
to_chat(user, SPAN_NOTICE("You disable [active_attachable]."))
@@ -1087,18 +1086,16 @@ and you're good to go.
else
active_attachable.fire_attachment(target, src, user) //Fire it.
active_attachable.last_fired = world.time
- return
+ return NONE
//If there's more to the attachment, it will be processed farther down, through in_chamber and regular bullet act.
/*
This is where burst is established for the proceeding section. Which just means the proc loops around that many times.
If burst = 1, you must null it if you ever RETURN during the for() cycle. If for whatever reason burst is left on while
the gun is not firing, it will break a lot of stuff. BREAK is fine, as it will null it.
*/
- else if((flags_gun_features & GUN_BURST_ON) && burst_amount > 1)
- bullets_to_fire = burst_amount
+ else if((gun_firemode == GUN_FIREMODE_BURSTFIRE) && burst_amount > BURST_AMOUNT_TIER_1)
flags_gun_features |= GUN_BURST_FIRING
if(PB_burst_bullets_fired) //Has a burst been carried over from a PB?
- bullets_to_fire -= PB_burst_bullets_fired
PB_burst_bullets_fired = 0 //Don't need this anymore. The torch is passed.
//Dual wielding. Do we have a gun in the other hand, is it loaded, and is it the same category?
@@ -1114,109 +1111,110 @@ and you're good to go.
else
akimbo.Fire(target,user,params, 0, TRUE)
- var/bullets_fired
- for(bullets_fired = 1 to bullets_to_fire)
- if(loc != user || (flags_gun_features & GUN_WIELDED_FIRING_ONLY && !(flags_item & WIELDED)))
- break //If you drop it while bursting, for example.
+ var/fire_return = handle_fire(target, user, params, reflex, dual_wield, check_for_attachment_fire)
+ if(!fire_return)
+ return fire_return
- if (bullets_fired > 1 && !(flags_gun_features & GUN_BURST_FIRING)) // No longer burst firing somehow
- break
-
- //The gun should return the bullet that it already loaded from the end cycle of the last Fire().
- var/obj/item/projectile/projectile_to_fire = load_into_chamber(user) //Load a bullet in or check for existing one.
- if(!projectile_to_fire) //If there is nothing to fire, click.
- click_empty(user)
- flags_gun_features &= ~GUN_BURST_FIRING
- return
+ flags_gun_features &= ~GUN_BURST_FIRING // We always want to turn off bursting when we're done, mainly for when we break early mid-burstfire.
+ return AUTOFIRE_CONTINUE
- apply_bullet_effects(projectile_to_fire, user, bullets_fired, reflex, dual_wield) //User can be passed as null.
- SEND_SIGNAL(projectile_to_fire, COMSIG_BULLET_USER_EFFECTS, user)
+/obj/item/weapon/gun/proc/handle_fire(atom/target, mob/living/user, params, reflex = FALSE, dual_wield, check_for_attachment_fire)
+ var/turf/curloc = get_turf(user) //In case the target or we are expired.
+ var/turf/targloc = get_turf(target)
- curloc = get_turf(user)
- if(QDELETED(original_target)) //If the target's destroyed, shoot at where it was last.
- target = targloc
- else
- target = original_target
- targloc = get_turf(target)
-
- projectile_to_fire.original = target
-
- // turf-targeted projectiles are fired without scatter, because proc would raytrace them further away
- var/ammo_flags = projectile_to_fire.ammo.flags_ammo_behavior | projectile_to_fire.projectile_override_flags
- if(!(ammo_flags & AMMO_HITS_TARGET_TURF))
- target = simulate_scatter(projectile_to_fire, target, curloc, targloc, user, bullets_fired)
-
- var/bullet_velocity = projectile_to_fire?.ammo?.shell_speed + velocity_add
-
- if(params) // Apply relative clicked position from the mouse info to offset projectile
- if(!params["click_catcher"])
- if(params["vis-x"])
- projectile_to_fire.p_x = text2num(params["vis-x"])
- else if(params["icon-x"])
- projectile_to_fire.p_x = text2num(params["icon-x"])
- if(params["vis-y"])
- projectile_to_fire.p_y = text2num(params["vis-y"])
- else if(params["icon-y"])
- projectile_to_fire.p_y = text2num(params["icon-y"])
- var/atom/movable/clicked_target = original_target
- if(istype(clicked_target))
- projectile_to_fire.p_x -= clicked_target.bound_width / 2
- projectile_to_fire.p_y -= clicked_target.bound_height / 2
- else
- projectile_to_fire.p_x -= world.icon_size / 2
- projectile_to_fire.p_y -= world.icon_size / 2
- else
- projectile_to_fire.p_x -= world.icon_size / 2
- projectile_to_fire.p_y -= world.icon_size / 2
+ var/atom/original_target = target //This is for burst mode, in case the target changes per scatter chance in between fired bullets.
- //Finally, make with the pew pew!
- if(QDELETED(projectile_to_fire) || !isobj(projectile_to_fire))
- to_chat(user, "ERROR CODE I1: Gun malfunctioned due to invalid chambered projectile, clearing it. AHELP if this persists.")
- log_debug("ERROR CODE I1: projectile malfunctioned while firing. User: [user] Weapon: [src] Magazine: [current_mag]")
- flags_gun_features &= ~GUN_BURST_FIRING
- in_chamber = null
- click_empty(user)
- return
+ if(loc != user || (flags_gun_features & GUN_WIELDED_FIRING_ONLY && !(flags_item & WIELDED)))
+ return TRUE
- if(targloc != curloc)
- simulate_recoil(dual_wield, user, target)
+ //The gun should return the bullet that it already loaded from the end cycle of the last Fire().
+ var/obj/item/projectile/projectile_to_fire = load_into_chamber(user) //Load a bullet in or check for existing one.
+ if(!projectile_to_fire) //If there is nothing to fire, click.
+ click_empty(user)
+ flags_gun_features &= ~GUN_BURST_FIRING
+ return NONE
- //This is where the projectile leaves the barrel and deals with projectile code only.
- //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- in_chamber = null // It's not in the gun anymore
- INVOKE_ASYNC(projectile_to_fire, TYPE_PROC_REF(/obj/item/projectile, fire_at), target, user, src, projectile_to_fire?.ammo?.max_range, bullet_velocity, original_target)
- projectile_to_fire = null // Important: firing might have made projectile collide early and ALREADY have deleted it. We clear it too.
- //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ apply_bullet_effects(projectile_to_fire, user, reflex, dual_wield) //User can be passed as null.
+ SEND_SIGNAL(projectile_to_fire, COMSIG_BULLET_USER_EFFECTS, user)
- if(check_for_attachment_fire)
- active_attachable.last_fired = world.time
+ curloc = get_turf(user)
+ if(QDELETED(original_target)) //If the target's destroyed, shoot at where it was last.
+ target = targloc
+ else
+ target = original_target
+ targloc = get_turf(target)
+
+ projectile_to_fire.original = target
+
+ // turf-targeted projectiles are fired without scatter, because proc would raytrace them further away
+ var/ammo_flags = projectile_to_fire.ammo.flags_ammo_behavior | projectile_to_fire.projectile_override_flags
+ if(!(ammo_flags & AMMO_HITS_TARGET_TURF))
+ target = simulate_scatter(projectile_to_fire, target, curloc, targloc, user)
+
+ var/bullet_velocity = projectile_to_fire?.ammo?.shell_speed + velocity_add
+
+ if(params) // Apply relative clicked position from the mouse info to offset projectile
+ if(!params["click_catcher"])
+ if(params["vis-x"])
+ projectile_to_fire.p_x = text2num(params["vis-x"])
+ else if(params["icon-x"])
+ projectile_to_fire.p_x = text2num(params["icon-x"])
+ if(params["vis-y"])
+ projectile_to_fire.p_y = text2num(params["vis-y"])
+ else if(params["icon-y"])
+ projectile_to_fire.p_y = text2num(params["icon-y"])
+ var/atom/movable/clicked_target = original_target
+ if(istype(clicked_target))
+ projectile_to_fire.p_x -= clicked_target.bound_width / 2
+ projectile_to_fire.p_y -= clicked_target.bound_height / 2
else
- last_fired = world.time
- SEND_SIGNAL(user, COMSIG_MOB_FIRED_GUN, src)
- . = TRUE
+ projectile_to_fire.p_x -= world.icon_size / 2
+ projectile_to_fire.p_y -= world.icon_size / 2
+ else
+ projectile_to_fire.p_x -= world.icon_size / 2
+ projectile_to_fire.p_y -= world.icon_size / 2
+
+ //Finally, make with the pew pew!
+ if(QDELETED(projectile_to_fire) || !isobj(projectile_to_fire))
+ to_chat(user, "ERROR CODE I1: Gun malfunctioned due to invalid chambered projectile, clearing it. AHELP if this persists.")
+ log_debug("ERROR CODE I1: projectile malfunctioned while firing. User: [user] Weapon: [src] Magazine: [current_mag]")
+ flags_gun_features &= ~GUN_BURST_FIRING
+ in_chamber = null
+ click_empty(user)
+ return NONE
- if(flags_gun_features & GUN_FULL_AUTO_ON)
- fa_shots++
+ if(targloc != curloc)
+ simulate_recoil(dual_wield, user, target)
- else // This happens in very rare circumstances when you're moving a lot while burst firing, so I'm going to toss it up to guns jamming.
- clear_jam(projectile_to_fire,user)
- break
+ //This is where the projectile leaves the barrel and deals with projectile code only.
+ //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ in_chamber = null // It's not in the gun anymore
+ INVOKE_ASYNC(projectile_to_fire, TYPE_PROC_REF(/obj/item/projectile, fire_at), target, user, src, projectile_to_fire?.ammo?.max_range, bullet_velocity, original_target)
+ projectile_to_fire = null // Important: firing might have made projectile collide early and ALREADY have deleted it. We clear it too.
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- //>>POST PROCESSING AND CLEANUP BEGIN HERE.<<
- var/angle = round(Get_Angle(user,target)) //Let's do a muzzle flash.
- muzzle_flash(angle,user)
+ if(check_for_attachment_fire)
+ active_attachable.last_fired = world.time
+ else
+ last_fired = world.time
+ SEND_SIGNAL(user, COMSIG_MOB_FIRED_GUN, src)
+ . = TRUE
- //This is where we load the next bullet in the chamber. We check for attachments too, since we don't want to load anything if an attachment is active.
- if(!check_for_attachment_fire && !reload_into_chamber(user)) // It has to return a bullet, otherwise it's empty. Unless it's an undershotgun.
- click_empty(user)
- break //Nothing else to do here, time to cancel out.
+ shots_fired++
- if(bullets_fired < bullets_to_fire) // We still have some bullets to fire.
- extra_delay = fire_delay * 0.5
- sleep(burst_delay)
+ else // This happens in very rare circumstances when you're moving a lot while burst firing, so I'm going to toss it up to guns jamming.
+ clear_jam(projectile_to_fire,user)
+ return TRUE
- flags_gun_features &= ~GUN_BURST_FIRING // We always want to turn off bursting when we're done, mainly for when we break early mid-burstfire.
- display_ammo(user)
+ //>>POST PROCESSING AND CLEANUP BEGIN HERE.<<
+ var/angle = round(Get_Angle(user,target)) //Let's do a muzzle flash.
+ muzzle_flash(angle,user)
+
+ //This is where we load the next bullet in the chamber. We check for attachments too, since we don't want to load anything if an attachment is active.
+ if(!check_for_attachment_fire && !reload_into_chamber(user)) // It has to return a bullet, otherwise it's empty. Unless it's an undershotgun.
+ click_empty(user)
+ return TRUE //Nothing else to do here, time to cancel out.
+ return TRUE
#define EXECUTION_CHECK (attacked_mob.stat == UNCONSCIOUS || attacked_mob.is_mob_restrained()) && ((user.a_intent == INTENT_GRAB)||(user.a_intent == INTENT_DISARM))
@@ -1335,7 +1333,7 @@ and you're good to go.
var/bullets_to_fire = 1
- if(!check_for_attachment_fire && (flags_gun_features & GUN_BURST_ON) && burst_amount > 1)
+ if(!check_for_attachment_fire && (gun_firemode == GUN_FIREMODE_BURSTFIRE) && burst_amount > BURST_AMOUNT_TIER_1)
bullets_to_fire = burst_amount
flags_gun_features |= GUN_BURST_FIRING
@@ -1470,6 +1468,8 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
*/
if(flags_gun_features & GUN_BURST_FIRING)
+ return TRUE
+ if(user.is_mob_incapacitated())
return
if(world.time < guaranteed_delay_time)
return
@@ -1540,12 +1540,15 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
if(active_attachable)
return
- if(flags_gun_features & GUN_AMMO_COUNTER && !(flags_gun_features & GUN_BURST_FIRING) && current_mag)
+ if(!user)
+ user = gun_user
+
+ if(flags_gun_features & GUN_AMMO_COUNTER && current_mag)
var/chambered = in_chamber ? TRUE : FALSE
to_chat(user, SPAN_DANGER("[current_mag.current_rounds][chambered ? "+1" : ""] / [current_mag.max_rounds] ROUNDS REMAINING"))
//This proc applies some bonus effects to the shot/makes the message when a bullet is actually fired.
-/obj/item/weapon/gun/proc/apply_bullet_effects(obj/item/projectile/projectile_to_fire, mob/user, bullets_fired = 1, reflex = 0, dual_wield = 0)
+/obj/item/weapon/gun/proc/apply_bullet_effects(obj/item/projectile/projectile_to_fire, mob/user, reflex = 0, dual_wield = 0)
var/actual_sound = fire_sound
if(isnull(fire_sound))
actual_sound = pick(fire_sounds)
@@ -1626,8 +1629,8 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
var/fire_angle = Get_Angle(curloc, targloc)
var/total_scatter_angle = projectile_to_fire.scatter
- if(flags_gun_features & GUN_BURST_ON && bullets_fired > 1)//Much higher scatter on burst. Each additional bullet adds scatter
- var/bullet_amt_scat = min(bullets_fired-1, SCATTER_AMOUNT_TIER_6)//capped so we don't penalize large bursts too much.
+ if((gun_firemode == GUN_FIREMODE_BURSTFIRE))//Much higher scatter on burst. Each additional bullet adds scatter
+ var/bullet_amt_scat = min(burst_amount - 1, SCATTER_AMOUNT_TIER_6)//capped so we don't penalize large bursts too much.
if(flags_item & WIELDED)
total_scatter_angle += max(0, bullet_amt_scat * burst_scatter_mult)
else
@@ -1635,9 +1638,9 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
// Full auto fucks your scatter up big time
// Note that full auto uses burst scatter multipliers
- if(flags_gun_features & GUN_FULL_AUTO_ON)
+ if(gun_firemode == GUN_FIREMODE_AUTOMATIC)
// The longer you fire full-auto, the worse the scatter gets
- var/bullet_amt_scat = min((fa_shots/fa_scatter_peak) * fa_max_scatter, fa_max_scatter)
+ var/bullet_amt_scat = min((shots_fired / fa_scatter_peak) * fa_max_scatter, fa_max_scatter)
if(flags_item & WIELDED)
total_scatter_angle += max(0, bullet_amt_scat * burst_scatter_mult)
else
@@ -1730,3 +1733,152 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
xeno.animation_attack_on(src)
xeno.visible_message(SPAN_XENOWARNING("\The [xeno] slashes the lights on \the [src]!"), SPAN_XENONOTICE("You slash the lights on \the [src]!"))
return XENO_ATTACK_ACTION
+
+/// Setter proc to toggle burst firing
+/obj/item/weapon/gun/proc/set_bursting(bursting = FALSE)
+ if(bursting)
+ flags_gun_features |= GUN_BURST_FIRING
+ else
+ flags_gun_features &= ~GUN_BURST_FIRING
+
+///Clean all references
+/obj/item/weapon/gun/proc/reset_fire()
+ shots_fired = 0//Let's clean everything
+ set_target(null)
+ set_auto_firing(FALSE)
+
+/// adder for fire_delay
+/obj/item/weapon/gun/proc/modify_fire_delay(value)
+ fire_delay += value
+ SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay)
+
+/// setter for fire_delay
+/obj/item/weapon/gun/proc/set_fire_delay(value)
+ fire_delay = value
+ SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay)
+
+/// getter for fire_delay
+/obj/item/weapon/gun/proc/get_fire_delay(value)
+ return fire_delay
+
+/// setter for burst_amount
+/obj/item/weapon/gun/proc/set_burst_amount(value, mob/user)
+ burst_amount = value
+ SEND_SIGNAL(src, COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED, burst_amount)
+ setup_firemodes()
+
+/// adder for burst_amount
+/obj/item/weapon/gun/proc/modify_burst_amount(value, mob/user)
+ burst_amount += value
+ SEND_SIGNAL(src, COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED, burst_amount)
+ setup_firemodes()
+
+/// Adder for burst_delay
+/obj/item/weapon/gun/proc/modify_burst_delay(value, mob/user)
+ burst_delay += value
+ SEND_SIGNAL(src, COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED, burst_delay)
+
+/// Setter for burst_delay
+/obj/item/weapon/gun/proc/set_burst_delay(value, mob/user)
+ burst_delay = value
+ SEND_SIGNAL(src, COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED, burst_delay)
+
+///Set the target and take care of hard delete
+/obj/item/weapon/gun/proc/set_target(atom/object)
+ active_attachable?.set_target(object)
+ if(object == target || object == loc)
+ return
+ if(target)
+ UnregisterSignal(target, COMSIG_PARENT_QDELETING)
+ target = object
+ if(target)
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(clean_target))
+
+///Set the target to its turf, so we keep shooting even when it was qdeled
+/obj/item/weapon/gun/proc/clean_target()
+ SIGNAL_HANDLER
+ active_attachable?.clean_target()
+ target = get_turf(target)
+
+/obj/item/weapon/gun/proc/stop_fire()
+ SIGNAL_HANDLER
+ if(!target || (gun_user.get_active_hand() != src))
+ return
+
+ if(gun_firemode == GUN_FIREMODE_AUTOMATIC)
+ reset_fire()
+ display_ammo()
+ SEND_SIGNAL(src, COMSIG_GUN_STOP_FIRE)
+
+/obj/item/weapon/gun/proc/set_gun_user(mob/to_set)
+ if(to_set == gun_user)
+ return
+ if(gun_user)
+ UnregisterSignal(gun_user, list(COMSIG_MOB_MOUSEUP, COMSIG_MOB_MOUSEDOWN, COMSIG_MOB_MOUSEDRAG))
+
+ gun_user = to_set
+ RegisterSignal(gun_user, COMSIG_MOB_MOUSEDOWN, PROC_REF(start_fire))
+ RegisterSignal(gun_user, COMSIG_MOB_MOUSEDRAG, PROC_REF(change_target))
+ RegisterSignal(gun_user, COMSIG_MOB_MOUSEUP, PROC_REF(stop_fire))
+
+///Update the target if you draged your mouse
+/obj/item/weapon/gun/proc/change_target(datum/source, atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params)
+ SIGNAL_HANDLER
+ set_target(get_turf_on_clickcatcher(over_object, gun_user, params))
+ gun_user?.face_atom(target)
+
+///Check if the gun can fire and add it to bucket auto_fire system if needed, or just fire the gun if not
+/obj/item/weapon/gun/proc/start_fire(datum/source, atom/object, turf/location, control, params, bypass_checks = FALSE)
+ SIGNAL_HANDLER
+
+ var/list/modifiers = params2list(params)
+ if(modifiers["shift"] || modifiers["middle"] || modifiers["right"])
+ return
+
+ // Don't allow doing anything else if inside a container of some sort, like a locker.
+ if(!isturf(gun_user.loc))
+ return
+
+ if(istype(object, /atom/movable/screen))
+ return
+
+ if(!bypass_checks)
+ if(gun_user.hand && !isgun(gun_user.l_hand) || !gun_user.hand && !isgun(gun_user.r_hand)) // If the object in our active hand is not a gun, abort
+ return
+
+ if(gun_user.throw_mode)
+ return
+
+ if(gun_user.Adjacent(object)) //Dealt with by attack code
+ return
+
+ if(QDELETED(object))
+ return
+
+ if(gun_user.client?.prefs?.toggle_prefs & TOGGLE_HELP_INTENT_SAFETY && (gun_user.a_intent == INTENT_HELP))
+ if(world.time % 3) // Limits how often this message pops up, saw this somewhere else and thought it was clever
+ //Absolutely SCREAM this at people so they don't get killed by it
+ to_chat(gun_user, SPAN_HIGHDANGER("Help intent safety is on! Switch to another intent to fire your weapon."))
+ click_empty(gun_user)
+ return FALSE
+
+ set_target(get_turf_on_clickcatcher(object, gun_user, params))
+ if((gun_firemode == GUN_FIREMODE_SEMIAUTO) || active_attachable)
+ Fire(object, gun_user, modifiers)
+ reset_fire()
+ display_ammo()
+ return
+ SEND_SIGNAL(src, COMSIG_GUN_FIRE)
+
+/// Wrapper proc for the autofire subsystem to ensure the important args aren't null
+/obj/item/weapon/gun/proc/fire_wrapper(atom/target, mob/living/user, params, reflex = FALSE, dual_wield)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ if(!target)
+ target = src.target
+ if(!user)
+ user = src.gun_user
+ return Fire(target, user, params, reflex, dual_wield)
+
+/// Setter proc for fa_firing
+/obj/item/weapon/gun/proc/set_auto_firing(auto = FALSE)
+ fa_firing = auto
diff --git a/code/modules/projectiles/gun_attachables.dm b/code/modules/projectiles/gun_attachables.dm
index 17dc435210c9..001cd9fbe10c 100644
--- a/code/modules/projectiles/gun_attachables.dm
+++ b/code/modules/projectiles/gun_attachables.dm
@@ -131,8 +131,7 @@ Defined in conflicts.dm of the #defines folder.
G.attachments[slot] = src
G.recalculate_attachment_bonuses()
- if(G.burst_amount <= 1)
- G.flags_gun_features &= ~GUN_BURST_ON //Remove burst if they can no longer use it.
+ G.setup_firemodes()
G.update_force_list() //This updates the gun to use proper force verbs.
var/mob/living/living
@@ -855,7 +854,7 @@ Defined in conflicts.dm of the #defines folder.
/obj/item/attachable/scope/proc/apply_scoped_buff(obj/item/weapon/gun/G, mob/living/carbon/user)
if(G.zoom)
G.accuracy_mult += accuracy_scoped_buff
- G.fire_delay += delay_scoped_nerf
+ G.modify_fire_delay(delay_scoped_nerf)
G.damage_falloff_mult += damage_falloff_scoped_buff
using_scope = TRUE
RegisterSignal(user, COMSIG_LIVING_ZOOM_OUT, PROC_REF(remove_scoped_buff))
@@ -865,7 +864,7 @@ Defined in conflicts.dm of the #defines folder.
UnregisterSignal(user, COMSIG_LIVING_ZOOM_OUT)
using_scope = FALSE
G.accuracy_mult -= accuracy_scoped_buff
- G.fire_delay -= delay_scoped_nerf
+ G.modify_fire_delay(-delay_scoped_nerf)
G.damage_falloff_mult -= damage_falloff_scoped_buff
/obj/item/attachable/scope/activate_attachment(obj/item/weapon/gun/G, mob/living/carbon/user, turn_off)
@@ -1926,18 +1925,34 @@ Defined in conflicts.dm of the #defines folder.
/// An assoc list in the format list(/datum/element/bullet_trait_to_give = list(...args))
/// that will be given to the projectiles of the attached gun
var/list/list/traits_to_give_attached
+ /// Current target we're firing at
+ var/mob/target
-/obj/item/attachable/attached_gun/New() //Let's make sure if something needs an ammo type, it spawns with one.
- ..()
+/obj/item/attachable/attached_gun/Initialize(mapload, ...) //Let's make sure if something needs an ammo type, it spawns with one.
+ . = ..()
if(ammo)
ammo = GLOB.ammo_list[ammo]
/obj/item/attachable/attached_gun/Destroy()
ammo = null
- . = ..()
-
+ target = null
+ return ..()
+/// setter for target
+/obj/item/attachable/attached_gun/proc/set_target(atom/object)
+ if(object == target)
+ return
+ if(target)
+ UnregisterSignal(target, COMSIG_PARENT_QDELETING)
+ target = object
+ if(target)
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(clean_target))
+
+///Set the target to its turf, so we keep shooting even when it was qdeled
+/obj/item/attachable/attached_gun/proc/clean_target()
+ SIGNAL_HANDLER
+ target = get_turf(target)
/obj/item/attachable/attached_gun/activate_attachment(obj/item/weapon/gun/G, mob/living/user, turn_off)
if(G.active_attachable == src)
@@ -2414,7 +2429,7 @@ Defined in conflicts.dm of the #defines folder.
attach_icon = "flamer_nozzle_a_1"
w_class = SIZE_MEDIUM
slot = "under"
- flags_attach_features = ATTACH_REMOVABLE|ATTACH_ACTIVATION|ATTACH_WEAPON|ATTACH_MELEE
+ flags_attach_features = ATTACH_REMOVABLE|ATTACH_ACTIVATION|ATTACH_WEAPON|ATTACH_MELEE|ATTACH_IGNORE_EMPTY
pixel_shift_x = 4
pixel_shift_y = 14
@@ -2442,7 +2457,7 @@ Defined in conflicts.dm of the #defines folder.
/obj/item/attachable/attached_gun/flamer_nozzle/fire_attachment(atom/target, obj/item/weapon/gun/gun, mob/living/user)
. = ..()
- if(world.time < gun.last_fired + gun.fire_delay)
+ if(world.time < gun.last_fired + gun.get_fire_delay())
return
if((gun.flags_gun_features & GUN_WIELDED_FIRING_ONLY) && !(gun.flags_item & WIELDED))
diff --git a/code/modules/projectiles/gun_helpers.dm b/code/modules/projectiles/gun_helpers.dm
index 62f7378191a5..a60773c88be7 100644
--- a/code/modules/projectiles/gun_helpers.dm
+++ b/code/modules/projectiles/gun_helpers.dm
@@ -136,29 +136,6 @@ DEFINES in setup.dm, referenced here.
else
..()
-/*
-Note: pickup and dropped on weapons must have both the ..() to update zoom AND twohanded,
-As sniper rifles have both and weapon mods can change them as well. ..() deals with zoom only.
-*/
-/obj/item/weapon/gun/dropped(mob/user)
- . = ..()
-
- disconnect_light_from_mob(user)
-
- var/delay_left = (last_fired + fire_delay + additional_fire_group_delay) - world.time
- if(fire_delay_group && delay_left > 0)
- for(var/group in fire_delay_group)
- LAZYSET(user.fire_delay_next_fire, group, world.time + delay_left)
-
- unwield(user)
-
-/obj/item/weapon/gun/equipped(mob/user, slot)
- . = ..()
-
- var/delay_left = (last_fired + fire_delay + additional_fire_group_delay) - world.time
- if(fire_delay_group && delay_left > 0)
- for(var/group in fire_delay_group)
- LAZYSET(user.fire_delay_next_fire, group, world.time + delay_left)
/// This function disconnects the luminosity from the mob and back to the gun
/obj/item/weapon/gun/proc/disconnect_light_from_mob(mob/bearer)
@@ -687,72 +664,71 @@ As sniper rifles have both and weapon mods can change them as well. ..() deals w
playsound(src, 'sound/handling/attachment_remove.ogg', 15, 1, 4)
update_icon()
-/obj/item/weapon/gun/proc/toggle_burst(mob/user)
- //Burst of 1 doesn't mean anything. The weapon will only fire once regardless.
- //Just a good safety to have all weapons that can equip a scope with 1 burst_amount.
- if(burst_amount < 2 && !(flags_gun_features & GUN_HAS_FULL_AUTO))
- to_chat(user, SPAN_WARNING("This weapon does not have a burst fire mode!"))
- return
-
+/obj/item/weapon/gun/proc/do_toggle_firemode(datum/source, datum/keybinding, new_firemode)
+ SIGNAL_HANDLER
if(flags_gun_features & GUN_BURST_FIRING)//can't toggle mid burst
return
- if(flags_gun_features & GUN_BURST_ONLY)
- if(!(flags_gun_features & GUN_BURST_ON))
- stack_trace("[src] has GUN_BURST_ONLY flag but not GUN_BURST_ON.")
- flags_gun_features |= GUN_BURST_ON
- return
+ if(!length(gun_firemode_list))
+ CRASH("[src] called do_toggle_firemode() with an empty gun_firemode_list")
- to_chat(user, SPAN_NOTICE("\The [src] can only be fired in bursts!"))
+ if(length(gun_firemode_list) == 1)
+ to_chat(source, SPAN_NOTICE("[icon2html(src, source)] This gun only has one firemode."))
return
- if(flags_gun_features & GUN_FULL_AUTO_ONLY)
- if(!(flags_gun_features & GUN_FULL_AUTO_ON))
- stack_trace("[src] has GUN_FULL_AUTO_ONLY flag but not GUN_FULL_AUTO_ON.")
- flags_gun_features |= GUN_FULL_AUTO_ON
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_DOWN, PROC_REF(full_auto_start))
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_UP, PROC_REF(full_auto_stop))
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_DRAG, PROC_REF(full_auto_new_target))
- return
+ if(new_firemode)
+ if(!(new_firemode in gun_firemode_list))
+ CRASH("[src] called do_toggle_firemode() with [new_firemode] new_firemode, not on gun_firemode_list")
+ gun_firemode = new_firemode
+ else
+ var/mode_index = gun_firemode_list.Find(gun_firemode)
+ if(++mode_index <= length(gun_firemode_list))
+ gun_firemode = gun_firemode_list[mode_index]
+ else
+ gun_firemode = gun_firemode_list[1]
- to_chat(user, SPAN_NOTICE("\The [src] can only be fired in full auto mode!"))
- return
+ playsound(source, 'sound/weapons/handling/gun_burst_toggle.ogg', 15, 1)
- playsound(user, 'sound/weapons/handling/gun_burst_toggle.ogg', 15, 1)
+ if(ishuman(source))
+ to_chat(source, SPAN_NOTICE("[icon2html(src, source)] You switch to [gun_firemode]."))
+ SEND_SIGNAL(src, COMSIG_GUN_FIRE_MODE_TOGGLE, gun_firemode)
- if(flags_gun_features & GUN_HAS_FULL_AUTO)
- if((flags_gun_features & GUN_BURST_ON) || (burst_amount < 2 && !(flags_gun_features & GUN_FULL_AUTO_ON)))
- flags_gun_features &= ~GUN_BURST_ON
- flags_gun_features |= GUN_FULL_AUTO_ON
+/obj/item/weapon/gun/proc/add_firemode(added_firemode, mob/user)
+ gun_firemode_list |= added_firemode
- // Register the full auto click listeners
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_DOWN, PROC_REF(full_auto_start))
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_UP, PROC_REF(full_auto_stop))
- RegisterSignal(user.client, COMSIG_CLIENT_LMB_DRAG, PROC_REF(full_auto_new_target))
+ if(!length(gun_firemode_list))
+ CRASH("add_firemode called with a resulting gun_firemode_list length of [length(gun_firemode_list)].")
- to_chat(user, SPAN_NOTICE("[icon2html(src, user)] You set [src] to full auto mode."))
- return
- else if(flags_gun_features & GUN_FULL_AUTO_ON)
- flags_gun_features &= ~GUN_FULL_AUTO_ON
- REMOVE_TRAIT(user, TRAIT_OVERRIDE_CLICKDRAG, TRAIT_SOURCE_WEAPON)
- full_auto_stop() // If the LMBUP hasn't been called for any reason.
- UnregisterSignal(user.client, list(
- COMSIG_CLIENT_LMB_DOWN,
- COMSIG_CLIENT_LMB_UP,
- COMSIG_CLIENT_LMB_DRAG,
- ))
-
- to_chat(user, SPAN_NOTICE("[icon2html(src, user)] You set [src] to single fire mode."))
- return
+/obj/item/weapon/gun/proc/remove_firemode(removed_firemode, mob/user)
+ if(!length(gun_firemode_list) || (length(gun_firemode_list) == 1))
+ CRASH("remove_firemode called with gun_firemode_list length [length(gun_firemode_list)].")
+
+ gun_firemode_list -= removed_firemode
+ if(gun_firemode == removed_firemode)
+ gun_firemode = gun_firemode_list[1]
+ do_toggle_firemode(user, gun_firemode)
- flags_gun_features ^= GUN_BURST_ON
- to_chat(user, SPAN_NOTICE("[icon2html(src, user)] You [flags_gun_features & GUN_BURST_ON ? "enable" : "disable"] [src]'s burst fire mode."))
+/obj/item/weapon/gun/proc/setup_firemodes()
+ gun_firemode_list.len = 0
+ if(start_semiauto)
+ gun_firemode_list |= GUN_FIREMODE_SEMIAUTO
+
+ if(burst_amount > BURST_AMOUNT_TIER_1)
+ gun_firemode_list |= GUN_FIREMODE_BURSTFIRE
+
+ if(start_automatic)
+ gun_firemode_list |= GUN_FIREMODE_AUTOMATIC
+
+ if(!length(gun_firemode_list))
+ CRASH("[src] called setup_firemodes() with an empty gun_firemode_list")
+ else
+ gun_firemode = gun_firemode_list[1]
/obj/item/weapon/gun/verb/use_toggle_burst()
set category = "Weapons"
- set name = "Toggle Burst Fire Mode"
- set desc = "Toggle on or off your weapon burst mode, if it has one. Greatly reduces accuracy."
+ set name = "Toggle Firemode"
+ set desc = "Cycles through your gun's firemodes. Automatic modes greatly reduce accuracy."
set src = usr.contents
var/obj/item/weapon/gun/active_firearm = get_active_firearm(usr)
@@ -760,7 +736,7 @@ As sniper rifles have both and weapon mods can change them as well. ..() deals w
return
src = active_firearm
- toggle_burst(usr)
+ do_toggle_firemode(usr)
/obj/item/weapon/gun/verb/empty_mag()
set category = "Weapons"
@@ -966,3 +942,12 @@ As sniper rifles have both and weapon mods can change them as well. ..() deals w
return TRUE
return FALSE
+
+///Helper proc that processes a clicked target, if the target is not black tiles, it will not change it. If they are it will return the turf of the black tiles. It will return null if the object is a screen object other than black tiles.
+/proc/get_turf_on_clickcatcher(atom/target, mob/user, params)
+ var/list/modifiers = params2list(params)
+ if(!istype(target, /atom/movable/screen))
+ return target
+ if(!istype(target, /atom/movable/screen/click_catcher))
+ return null
+ return params2turf(modifiers["screen-loc"], get_turf(user), user.client)
diff --git a/code/modules/projectiles/guns/boltaction.dm b/code/modules/projectiles/guns/boltaction.dm
index c34e9d44a626..a558d3dd7969 100644
--- a/code/modules/projectiles/guns/boltaction.dm
+++ b/code/modules/projectiles/guns/boltaction.dm
@@ -56,8 +56,8 @@
/obj/item/weapon/gun/boltaction/set_gun_config_values()
..()
- burst_amount = 0
- fire_delay = FIRE_DELAY_TIER_4
+ set_burst_amount(0)
+ set_fire_delay(FIRE_DELAY_TIER_4)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index 12f1976e186e..25efbd420ec6 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -90,6 +90,7 @@
to_firer = "[round((cell.charge / charge_cost), 1)] / [max_shots] SHOTS REMAINING"
user.visible_message(SPAN_DANGER("[user] fires \the [src]!"),
SPAN_DANGER("[to_firer]"), message_flags = CHAT_TYPE_WEAPON_USE)
+ return AUTOFIRE_CONTINUE
/obj/item/weapon/gun/energy/reload_into_chamber()
update_icon()
@@ -132,7 +133,7 @@
/obj/item/weapon/gun/energy/rxfm5_eva/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
+ set_fire_delay(FIRE_DELAY_TIER_8)
accuracy_mult = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_3
scatter = SCATTER_AMOUNT_TIER_7
damage_mult = BASE_BULLET_DAMAGE_MULT
@@ -183,9 +184,9 @@
/obj/item/weapon/gun/energy/laz_uzi/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
- burst_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_2
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
accuracy_mult = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_5
@@ -217,7 +218,7 @@
/obj/item/weapon/gun/energy/taser/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_7
+ set_fire_delay(FIRE_DELAY_TIER_7)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
damage_mult = BASE_BULLET_DAMAGE_MULT
diff --git a/code/modules/projectiles/guns/flamer/flamer.dm b/code/modules/projectiles/guns/flamer/flamer.dm
index c5a0c8ee2f8c..f327a92ffc9c 100644
--- a/code/modules/projectiles/guns/flamer/flamer.dm
+++ b/code/modules/projectiles/guns/flamer/flamer.dm
@@ -51,7 +51,7 @@
/obj/item/weapon/gun/flamer/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_4 * 5
+ set_fire_delay(FIRE_DELAY_TIER_4 * 5)
/obj/item/weapon/gun/flamer/unique_action(mob/user)
toggle_gun_safety()
@@ -92,7 +92,7 @@
. = ..()
if(.)
if(!current_mag || !current_mag.current_rounds)
- return
+ return NONE
/obj/item/weapon/gun/flamer/proc/get_fire_sound()
var/list/fire_sounds = list(
@@ -102,20 +102,20 @@
return pick(fire_sounds)
/obj/item/weapon/gun/flamer/Fire(atom/target, mob/living/user, params, reflex)
- set waitfor = 0
+ set waitfor = FALSE
if(!able_to_fire(user))
- return
+ return NONE
var/turf/curloc = get_turf(user) //In case the target or we are expired.
var/turf/targloc = get_turf(target)
if (!targloc || !curloc)
- return //Something has gone wrong...
+ return NONE //Something has gone wrong...
if(active_attachable && active_attachable.flags_attach_features & ATTACH_WEAPON) //Attachment activated and is a weapon.
if(active_attachable.flags_attach_features & ATTACH_PROJECTILE)
return
- if(active_attachable.current_rounds <= 0)
+ if((active_attachable.current_rounds <= 0) && !(active_attachable.flags_attach_features & ATTACH_IGNORE_EMPTY))
click_empty(user) //If it's empty, let them know.
to_chat(user, SPAN_WARNING("[active_attachable] is empty!"))
to_chat(user, SPAN_NOTICE("You disable [active_attachable]."))
@@ -123,20 +123,22 @@
else
active_attachable.fire_attachment(target, src, user) //Fire it.
active_attachable.last_fired = world.time
- return
+ return NONE
if(flags_gun_features & GUN_TRIGGER_SAFETY)
to_chat(user, SPAN_WARNING("\The [src] isn't lit!"))
- return
+ return NONE
if(!current_mag)
- return
+ return NONE
if(current_mag.current_rounds <= 0)
click_empty(user)
else
user.track_shot(initial(name))
unleash_flame(target, user)
+ return AUTOFIRE_CONTINUE
+ return NONE
/obj/item/weapon/gun/flamer/reload(mob/user, obj/item/ammo_magazine/magazine)
if(!magazine || !istype(magazine))
@@ -321,7 +323,7 @@
unload(user, drop_override = TRUE)
current_mag = fuelpack.active_fuel
update_icon()
- ..()
+ return ..()
/obj/item/weapon/gun/flamer/M240T/reload(mob/user, obj/item/ammo_magazine/magazine)
@@ -360,6 +362,16 @@
return TRUE
return FALSE
+/obj/item/weapon/gun/flamer/M240T/auto // With NEW advances in science, we've learned how to drain a pyro's tank in 6 seconds, or your money back!
+ name = "\improper M240-T2 incinerator unit"
+ desc = "A prototyped model of the M240-T incinerator unit, it was discontinued after its automatic mode was deemed too expensive to deploy in the field."
+ start_semiauto = FALSE
+ start_automatic = TRUE
+
+/obj/item/weapon/gun/flamer/M240T/auto/set_gun_config_values()
+ . = ..()
+ set_fire_delay(FIRE_DELAY_TIER_3)
+
GLOBAL_LIST_EMPTY(flamer_particles)
/particles/flamer_fire
icon = 'icons/effects/particles/fire.dmi'
diff --git a/code/modules/projectiles/guns/lever_action.dm b/code/modules/projectiles/guns/lever_action.dm
index 259c6170ae48..986f42716e64 100644
--- a/code/modules/projectiles/guns/lever_action.dm
+++ b/code/modules/projectiles/guns/lever_action.dm
@@ -45,7 +45,7 @@ their unique feature is that a direct hit will buff your damage and firerate
/obj/item/weapon/gun/lever_action/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_1 + FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_1 + FIRE_DELAY_TIER_10)
lever_delay = FIRE_DELAY_TIER_3
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
@@ -118,12 +118,12 @@ their unique feature is that a direct hit will buff your damage and firerate
last_fired = world.time - buff_fire_reduc //to shoot the next round faster
lever_delay = FIRE_DELAY_TIER_10
damage_mult = initial(damage_mult) + BULLET_DAMAGE_MULT_TIER_10
- fire_delay = FIRE_DELAY_TIER_5
+ set_fire_delay(FIRE_DELAY_TIER_5)
for(var/slot in attachments)
var/obj/item/attachable/AM = attachments[slot]
if(AM.damage_mod || AM.delay_mod)
damage_mult += AM.damage_mod
- fire_delay += AM.delay_mod
+ modify_fire_delay(AM.delay_mod)
wield_delay = 0 //for one-handed levering
/obj/item/weapon/gun/lever_action/proc/reset_hit_buff(mob/user, one_hand_lever)
@@ -137,7 +137,7 @@ their unique feature is that a direct hit will buff your damage and firerate
cur_onehand_chance = initial(cur_onehand_chance)
//these are init configs and so cannot be initial()
lever_delay = FIRE_DELAY_TIER_3
- fire_delay = FIRE_DELAY_TIER_1
+ set_fire_delay(FIRE_DELAY_TIER_1)
damage_mult = BASE_BULLET_DAMAGE_MULT
recalculate_attachment_bonuses() //stock wield delay
if(one_hand_lever)
@@ -392,7 +392,7 @@ their unique feature is that a direct hit will buff your damage and firerate
/obj/item/weapon/gun/lever_action/xm88/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_1
+ set_fire_delay(FIRE_DELAY_TIER_1)
lever_delay = FIRE_DELAY_TIER_3
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
@@ -478,7 +478,7 @@ their unique feature is that a direct hit will buff your damage and firerate
lever_sound = lever_super_sound
lever_message = "You quickly press the [lever_name]!"
last_fired = world.time - buff_fire_reduc //to shoot the next round faster
- fire_delay = FIRE_DELAY_TIER_3
+ set_fire_delay(FIRE_DELAY_TIER_3)
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_4
if(floating_penetration < floating_penetration_upper_limit)
@@ -488,12 +488,12 @@ their unique feature is that a direct hit will buff your damage and firerate
var/obj/item/attachable/AM = attachments[slot]
if(AM && (AM.damage_mod || AM.delay_mod))
damage_mult += AM.damage_mod
- fire_delay += AM.delay_mod
+ modify_fire_delay(AM.delay_mod)
wield_delay = 0 //for one-handed levering
/obj/item/weapon/gun/lever_action/xm88/Fire(atom/target, mob/living/user, params, reflex, dual_wield)
if(!able_to_fire(user) || !target) //checks here since we don't want to fuck up applying the increase
- return
+ return NONE
if(floating_penetration && in_chamber) //has to go before actual firing
var/obj/item/projectile/P = in_chamber
switch(floating_penetration)
@@ -529,7 +529,7 @@ their unique feature is that a direct hit will buff your damage and firerate
P.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88]
floating_penetration = FLOATING_PENETRATION_TIER_0
//these are init configs and so cannot be initial()
- fire_delay = FIRE_DELAY_TIER_1 + FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_1 + FIRE_DELAY_TIER_10)
lever_delay = FIRE_DELAY_TIER_3
damage_mult = BASE_BULLET_DAMAGE_MULT
recalculate_attachment_bonuses() //stock wield delay
diff --git a/code/modules/projectiles/guns/misc.dm b/code/modules/projectiles/guns/misc.dm
index 4a84f99d7c62..2aa6b2dfc4d3 100644
--- a/code/modules/projectiles/guns/misc.dm
+++ b/code/modules/projectiles/guns/misc.dm
@@ -13,8 +13,10 @@
current_mag = /obj/item/ammo_magazine/minigun
w_class = SIZE_HUGE
force = 20
- flags_gun_features = GUN_AUTO_EJECTOR|GUN_WIELDED_FIRING_ONLY|GUN_AMMO_COUNTER|GUN_RECOIL_BUILDUP|GUN_HAS_FULL_AUTO|GUN_CAN_POINTBLANK
+ flags_gun_features = GUN_AUTO_EJECTOR|GUN_WIELDED_FIRING_ONLY|GUN_AMMO_COUNTER|GUN_RECOIL_BUILDUP|GUN_CAN_POINTBLANK
gun_category = GUN_CATEGORY_HEAVY
+ start_semiauto = FALSE
+ start_automatic = TRUE
/obj/item/weapon/gun/minigun/Initialize(mapload, spawn_empty)
. = ..()
@@ -22,7 +24,7 @@
/obj/item/weapon/gun/minigun/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
@@ -45,7 +47,7 @@
/obj/item/weapon/gun/minigun/upp
name = "\improper GSh-7.62 rotary machine gun"
desc = "A gas-operated rotary machine gun used by UPP heavies. Its enormous volume of fire and ammunition capacity allows the suppression of large concentrations of enemy forces. Heavy weapons training is required control its recoil."
- flags_gun_features = GUN_AUTO_EJECTOR|GUN_SPECIALIST|GUN_WIELDED_FIRING_ONLY|GUN_AMMO_COUNTER|GUN_RECOIL_BUILDUP|GUN_HAS_FULL_AUTO|GUN_CAN_POINTBLANK
+ flags_gun_features = GUN_AUTO_EJECTOR|GUN_SPECIALIST|GUN_WIELDED_FIRING_ONLY|GUN_AMMO_COUNTER|GUN_RECOIL_BUILDUP|GUN_CAN_POINTBLANK
/obj/item/weapon/gun/minigun/upp/able_to_fire(mob/living/user)
. = ..()
@@ -72,7 +74,7 @@
current_mag = /obj/item/ammo_magazine/m60
w_class = SIZE_LARGE
force = 25
- flags_gun_features = GUN_WIELDED_FIRING_ONLY|GUN_HAS_FULL_AUTO|GUN_FULL_AUTO_ON|GUN_FULL_AUTO_ONLY|GUN_CAN_POINTBLANK
+ flags_gun_features = GUN_WIELDED_FIRING_ONLY|GUN_CAN_POINTBLANK
gun_category = GUN_CATEGORY_HEAVY
attachable_allowed = list(
/obj/item/attachable/m60barrel,
@@ -82,6 +84,8 @@
/obj/item/attachable/m60barrel,
/obj/item/attachable/bipod/m60,
)
+ start_semiauto = FALSE
+ start_automatic = TRUE
var/cover_open = FALSE //if the gun's feed-cover is open or not.
@@ -96,9 +100,9 @@
/obj/item/weapon/gun/m60/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
- burst_amount = BURST_AMOUNT_TIER_5
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
+ set_burst_amount(BURST_AMOUNT_TIER_5)
+ set_burst_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_10
diff --git a/code/modules/projectiles/guns/pistols.dm b/code/modules/projectiles/guns/pistols.dm
index cc4207724b60..47b80f7b9218 100644
--- a/code/modules/projectiles/guns/pistols.dm
+++ b/code/modules/projectiles/guns/pistols.dm
@@ -72,7 +72,7 @@
/obj/item/weapon/gun/pistol/m4a3/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -93,7 +93,7 @@
/obj/item/weapon/gun/pistol/m4a3/custom/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -122,7 +122,7 @@
/obj/item/weapon/gun/pistol/m1911/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
+ set_fire_delay(FIRE_DELAY_TIER_8)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -140,7 +140,7 @@
/obj/item/weapon/gun/pistol/m1911/socom/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
+ set_fire_delay(FIRE_DELAY_TIER_8)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_2
scatter = SCATTER_AMOUNT_TIER_8
@@ -171,14 +171,14 @@
BFA.flags_attach_features &= ~ATTACH_REMOVABLE
BFA.Attach(src)
update_attachable(BFA.slot)
- flags_gun_features |= GUN_BURST_ON
+ add_firemode(GUN_FIREMODE_BURSTFIRE)
/obj/item/weapon/gun/pistol/b92fs/set_gun_attachment_offsets()
attachable_offset = list("muzzle_x" = 28, "muzzle_y" = 20,"rail_x" = 10, "rail_y" = 22, "under_x" = 21, "under_y" = 17, "stock_x" = 21, "stock_y" = 17)
/obj/item/weapon/gun/pistol/b92fs/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_7
@@ -218,9 +218,9 @@
/obj/item/weapon/gun/pistol/heavy/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_4
- burst_amount = BURST_AMOUNT_TIER_2
- burst_delay = FIRE_DELAY_TIER_7
+ set_fire_delay(FIRE_DELAY_TIER_4)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
+ set_burst_delay(FIRE_DELAY_TIER_7)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_5
scatter = SCATTER_AMOUNT_TIER_6
@@ -242,7 +242,7 @@
/obj/item/weapon/gun/pistol/heavy/co/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5
+ set_fire_delay(FIRE_DELAY_TIER_5)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_6
@@ -285,7 +285,7 @@
/obj/item/weapon/gun/pistol/c99/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
scatter = SCATTER_AMOUNT_TIER_6
@@ -337,7 +337,7 @@
/obj/item/weapon/gun/pistol/kt42/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_1
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_2
scatter = SCATTER_AMOUNT_TIER_6
@@ -373,7 +373,7 @@
/obj/item/weapon/gun/pistol/holdout/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -410,7 +410,7 @@
/obj/item/weapon/gun/pistol/clfpistol/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -462,8 +462,8 @@
if(!manually_slided)
click_empty()
to_chat(user, SPAN_DANGER("\The [src] makes a clicking noise! You need to manually rack the slide after loading in a new magazine!"))
- return
- ..()
+ return NONE
+ return ..()
/obj/item/weapon/gun/pistol/highpower/unique_action(mob/user)
if(!manually_slided)
@@ -488,7 +488,7 @@
/obj/item/weapon/gun/pistol/highpower/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5
+ set_fire_delay(FIRE_DELAY_TIER_5)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_3
scatter = SCATTER_AMOUNT_TIER_6
@@ -552,9 +552,9 @@
/obj/item/weapon/gun/pistol/mod88/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_7
@@ -651,9 +651,9 @@
/obj/item/weapon/gun/pistol/vp78/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_4
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_4)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -683,9 +683,9 @@ It is a modified Beretta 93R, and can fire three-round burst or single fire. Whe
/obj/item/weapon/gun/pistol/auto9/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_7
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_7)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -715,9 +715,9 @@ It is a modified Beretta 93R, and can fire three-round burst or single fire. Whe
/obj/item/weapon/gun/pistol/chimp/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
- burst_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_2
+ set_fire_delay(FIRE_DELAY_TIER_8)
+ set_burst_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -747,9 +747,9 @@ It is a modified Beretta 93R, and can fire three-round burst or single fire. Whe
/obj/item/weapon/gun/pistol/smart/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_10)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
@@ -776,7 +776,7 @@ It is a modified Beretta 93R, and can fire three-round burst or single fire. Whe
fire_sound = 'sound/weapons/gun_skorpion.ogg'
current_mag = /obj/item/ammo_magazine/pistol/skorpion
- flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_ONE_HAND_WIELDED|GUN_HAS_FULL_AUTO|GUN_FULL_AUTO_ON|GUN_FULL_AUTO_ONLY
+ flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_ONE_HAND_WIELDED
attachable_allowed = list(
/obj/item/attachable/reddot, //Rail
/obj/item/attachable/reflex,
@@ -787,14 +787,16 @@ It is a modified Beretta 93R, and can fire three-round burst or single fire. Whe
/obj/item/attachable/heavy_barrel,
/obj/item/attachable/lasersight, //Underbarrel
/obj/item/attachable/burstfire_assembly,
- )
+ )
+ start_semiauto = FALSE
+ start_automatic = TRUE
/obj/item/weapon/gun/pistol/skorpion/set_gun_attachment_offsets()
attachable_offset = list("muzzle_x" = 29, "muzzle_y" = 18,"rail_x" = 16, "rail_y" = 21, "under_x" = 23, "under_y" = 15, "stock_x" = 23, "stock_y" = 15)
/obj/item/weapon/gun/pistol/skorpion/set_gun_config_values()
..()
- fa_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
fa_scatter_peak = 15 //shots
fa_max_scatter = SCATTER_AMOUNT_TIER_4
diff --git a/code/modules/projectiles/guns/revolvers.dm b/code/modules/projectiles/guns/revolvers.dm
index 3553ae880d7d..022a8cabd349 100644
--- a/code/modules/projectiles/guns/revolvers.dm
+++ b/code/modules/projectiles/guns/revolvers.dm
@@ -33,7 +33,7 @@
/obj/item/weapon/gun/revolver/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5
+ set_fire_delay(FIRE_DELAY_TIER_5)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_3
scatter = SCATTER_AMOUNT_TIER_8
@@ -318,7 +318,7 @@
/obj/item/weapon/gun/revolver/m44/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_7
+ set_fire_delay(FIRE_DELAY_TIER_7)
accuracy_mult = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_8
damage_mult = BASE_BULLET_DAMAGE_MULT
@@ -375,7 +375,7 @@
..()
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_2
- fire_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
/obj/item/weapon/gun/revolver/m44/custom/pkd_special/k2049
name = "\improper M2049 Blaster"
@@ -414,9 +414,9 @@
..()
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_2
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_2
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
+ set_burst_delay(FIRE_DELAY_TIER_10)
/obj/item/weapon/gun/revolver/m44/custom/webley //Van Bandolier's Webley.
@@ -469,7 +469,7 @@
/obj/item/weapon/gun/revolver/nagant/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
+ set_fire_delay(FIRE_DELAY_TIER_8)
accuracy_mult = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_1
@@ -504,7 +504,7 @@
/obj/item/weapon/gun/revolver/small/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6
+ set_fire_delay(FIRE_DELAY_TIER_6)
accuracy_mult = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_5
@@ -596,9 +596,9 @@
/obj/item/weapon/gun/revolver/mateba/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_2
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_7
+ set_fire_delay(FIRE_DELAY_TIER_2)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_7)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_2
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_5
scatter = SCATTER_AMOUNT_TIER_7
@@ -699,14 +699,14 @@
/obj/item/weapon/gun/revolver/cmb/Fire(atom/target, mob/living/user, params, reflex = 0, dual_wield)
playsound('sound/weapons/gun_cmb_bass.ogg') // badass shooting bass
- . = ..()
+ return ..()
/obj/item/weapon/gun/revolver/cmb/set_gun_attachment_offsets()
attachable_offset = list("muzzle_x" = 29, "muzzle_y" = 22,"rail_x" = 11, "rail_y" = 25, "under_x" = 20, "under_y" = 18, "stock_x" = 20, "stock_y" = 18)
/obj/item/weapon/gun/revolver/cmb/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6
+ set_fire_delay(FIRE_DELAY_TIER_6)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
scatter = SCATTER_AMOUNT_TIER_7
diff --git a/code/modules/projectiles/guns/rifles.dm b/code/modules/projectiles/guns/rifles.dm
index ab2602204f72..09a0e2b683cf 100644
--- a/code/modules/projectiles/guns/rifles.dm
+++ b/code/modules/projectiles/guns/rifles.dm
@@ -18,9 +18,9 @@
/obj/item/weapon/gun/rifle/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_5)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_6
@@ -82,12 +82,13 @@
/obj/item/weapon/gun/rifle/m41a/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4 + 2*HIT_ACCURACY_MULT_TIER_1
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_8
+ //fa_scatter_peak = FULL_AUTO_SCATTER_PEAK_TIER_8 //Zonenote
burst_scatter_mult = SCATTER_AMOUNT_TIER_10
scatter_unwielded = SCATTER_AMOUNT_TIER_2
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_2
@@ -137,7 +138,7 @@
/obj/item/attachable/scope/mini/nsg23,
)
- flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER|GUN_BURST_ON|GUN_BURST_ONLY|GUN_WY_RESTRICTED
+ flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER|GUN_WY_RESTRICTED
random_spawn_muzzle = list(
/obj/item/attachable/suppressor,
@@ -148,6 +149,7 @@
/obj/item/attachable/scope/mini/nsg23,
/obj/item/attachable/attached_gun/flamer/advanced,
)
+ start_semiauto = FALSE
/obj/item/weapon/gun/rifle/nsg23/Initialize(mapload, spawn_empty)
. = ..()
@@ -158,9 +160,9 @@
/obj/item/weapon/gun/rifle/nsg23/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_7
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_8
+ set_fire_delay(FIRE_DELAY_TIER_7)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_8)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_9
@@ -182,7 +184,7 @@
starting_attachment_types = list() //starts with the stock anyways due to handle_starting_attachment()
/obj/item/weapon/gun/rifle/nsg23/no_lock
- flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER|GUN_BURST_ON|GUN_BURST_ONLY
+ flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER
starting_attachment_types = list(
/obj/item/attachable/scope/mini/nsg23,
/obj/item/attachable/attached_gun/flamer,//non-op flamer for normal spawns
@@ -230,9 +232,9 @@
/obj/item/weapon/gun/rifle/m41a/elite/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_2
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
+ set_burst_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
scatter = SCATTER_AMOUNT_TIER_10
@@ -291,7 +293,7 @@
indestructible = TRUE
current_mag = /obj/item/ammo_magazine/rifle/xm40/heap
- flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER|GUN_BURST_ON
+ flags_gun_features = GUN_AUTO_EJECTOR|GUN_CAN_POINTBLANK|GUN_AMMO_COUNTER
aim_slowdown = SLOWDOWN_ADS_QUICK
wield_delay = WIELD_DELAY_FAST
map_specific_decoration = FALSE
@@ -344,9 +346,9 @@
/obj/item/weapon/gun/rifle/m41a/elite/xm40/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
scatter = SCATTER_AMOUNT_TIER_10
@@ -388,9 +390,9 @@
/obj/item/weapon/gun/rifle/m41aMK1/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_4
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_4)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_9
@@ -496,6 +498,7 @@
LAZYADD(traits_to_give, list(
BULLET_TRAIT_ENTRY_ID("iff", /datum/element/bullet_trait_iff)
))
+ recalculate_attachment_bonuses()
/obj/item/weapon/gun/rifle/m46c/Destroy()
linked_human = null
@@ -515,9 +518,9 @@
/obj/item/weapon/gun/rifle/m46c/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
- burst_amount = BURST_AMOUNT_TIER_4
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_8)
+ set_burst_amount(BURST_AMOUNT_TIER_4)
+ set_burst_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_8
scatter = SCATTER_AMOUNT_TIER_8
@@ -614,6 +617,7 @@
to_chat(usr, SPAN_WARNING("[icon2html(src, usr)] Action denied by [src]. Unauthorized user."))
return
+ gun_firemode = GUN_FIREMODE_SEMIAUTO
iff_enabled = !iff_enabled
to_chat(usr, SPAN_NOTICE("[icon2html(src, usr)] You [iff_enabled? "enable": "disable"] the IFF on [src]."))
playsound(loc,'sound/machines/click.ogg', 25, 1)
@@ -627,10 +631,11 @@
/obj/item/weapon/gun/rifle/m46c/recalculate_attachment_bonuses()
. = ..()
if(iff_enabled)
- fire_delay += FIRE_DELAY_TIER_10
- burst_amount -= BURST_AMOUNT_TIER_6
+ modify_fire_delay(FIRE_DELAY_TIER_10)
+ remove_firemode(GUN_FIREMODE_BURSTFIRE)
- flags_gun_features &= ~GUN_BURST_ON //Gun loses some combat ability in return for IFF, as well as burst fire mode
+ else
+ add_firemode(GUN_FIREMODE_BURSTFIRE)
/obj/item/weapon/gun/rifle/m46c/proc/name_after_co(mob/living/carbon/human/H)
@@ -723,9 +728,9 @@
/obj/item/weapon/gun/rifle/mar40/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_7
- burst_amount = BURST_AMOUNT_TIER_4
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_7)
+ set_burst_amount(BURST_AMOUNT_TIER_4)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_6
@@ -796,7 +801,7 @@
/obj/item/weapon/gun/rifle/mar40/carbine/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
+ set_fire_delay(FIRE_DELAY_TIER_8)
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
damage_mult = BASE_BULLET_DAMAGE_MULT - BULLET_DAMAGE_MULT_TIER_2
scatter_unwielded = SCATTER_AMOUNT_TIER_4
@@ -842,9 +847,9 @@
/obj/item/weapon/gun/rifle/mar40/lmg/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
- burst_amount = BURST_AMOUNT_TIER_5
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_8)
+ set_burst_amount(BURST_AMOUNT_TIER_5)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_6
@@ -935,9 +940,9 @@
/obj/item/weapon/gun/rifle/m16/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_7
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_10
@@ -1046,9 +1051,9 @@
/obj/item/weapon/gun/rifle/xm177/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_SMG
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_6
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_3
scatter = SCATTER_AMOUNT_TIER_8
@@ -1123,9 +1128,9 @@
/obj/item/weapon/gun/rifle/ar10/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_7
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_7
+ set_fire_delay(FIRE_DELAY_TIER_7)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_7)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_8
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_9
@@ -1185,9 +1190,9 @@
/obj/item/weapon/gun/rifle/xm177/dutch/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_SMG
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_6
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_3
scatter = SCATTER_AMOUNT_TIER_8
@@ -1236,10 +1241,9 @@
/obj/item/weapon/gun/rifle/lmg/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_LMG
- burst_amount = BURST_AMOUNT_TIER_5
- burst_delay = FIRE_DELAY_TIER_LMG
- fa_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_LMG)
+ set_burst_amount(BURST_AMOUNT_TIER_5)
+ set_burst_delay(FIRE_DELAY_TIER_LMG)
fa_scatter_peak = FULL_AUTO_SCATTER_PEAK_TIER_3
fa_max_scatter = SCATTER_AMOUNT_TIER_4
accuracy_mult = BASE_ACCURACY_MULT
@@ -1299,9 +1303,9 @@
/obj/item/weapon/gun/rifle/type71/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_amount = BURST_AMOUNT_TIER_4
- burst_delay = FIRE_DELAY_TIER_8
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_amount(BURST_AMOUNT_TIER_4)
+ set_burst_delay(FIRE_DELAY_TIER_8)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_7
scatter = SCATTER_AMOUNT_TIER_6
@@ -1414,7 +1418,7 @@
/obj/item/weapon/gun/rifle/type71/carbine/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
damage_mult = BASE_BULLET_DAMAGE_MULT - BULLET_DAMAGE_MULT_TIER_2
scatter_unwielded = SCATTER_AMOUNT_TIER_5
recoil_unwielded = RECOIL_AMOUNT_TIER_4
@@ -1479,8 +1483,8 @@
..()
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_7
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
- fire_delay = FIRE_DELAY_TIER_9
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_delay(FIRE_DELAY_TIER_10)
scatter = SCATTER_AMOUNT_TIER_8
//-------------------------------------------------------
@@ -1526,8 +1530,8 @@
/obj/item/weapon/gun/rifle/m4ra/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
- burst_amount = 0
+ set_fire_delay(FIRE_DELAY_TIER_8)
+ set_burst_amount(0)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_6
@@ -1590,8 +1594,8 @@
/obj/item/weapon/gun/rifle/l42a/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_8
- burst_amount = 0
+ set_fire_delay(FIRE_DELAY_TIER_8)
+ set_burst_amount(0)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_6
diff --git a/code/modules/projectiles/guns/shotguns.dm b/code/modules/projectiles/guns/shotguns.dm
index d844498c2c02..8903be95db95 100644
--- a/code/modules/projectiles/guns/shotguns.dm
+++ b/code/modules/projectiles/guns/shotguns.dm
@@ -35,7 +35,7 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5
+ set_fire_delay(FIRE_DELAY_TIER_5)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
@@ -199,9 +199,9 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/merc/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6*2
- burst_amount = BURST_AMOUNT_TIER_2
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_6*2)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
@@ -222,8 +222,8 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/merc/damaged/set_gun_config_values()
..()
- fire_delay = 1.5 SECONDS
- burst_amount = BURST_AMOUNT_TIER_1
+ set_fire_delay(1.5 SECONDS)
+ set_burst_amount(BURST_AMOUNT_TIER_1)
accuracy_mult = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_6
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_5
@@ -279,7 +279,7 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/combat/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5*2
+ set_fire_delay(FIRE_DELAY_TIER_5*2)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
@@ -324,9 +324,9 @@ can cause issues with ammo types getting mixed up during the burst.
current_mag = /obj/item/ammo_magazine/internal/shotgun/buckshot
flags_equip_slot = SLOT_WAIST|SLOT_BACK
- flags_gun_features = GUN_CAN_POINTBLANK|GUN_INTERNAL_MAG|GUN_HAS_FULL_AUTO|GUN_FULL_AUTO_ON
- fa_delay = FIRE_DELAY_TIER_6
+ flags_gun_features = GUN_CAN_POINTBLANK|GUN_INTERNAL_MAG
auto_retrieval_slot = WEAR_J_STORE
+ start_automatic = TRUE
/obj/item/weapon/gun/shotgun/combat/marsoc/Initialize(mapload, spawn_empty)
. = ..()
@@ -347,7 +347,7 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/combat/marsoc/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5*2
+ set_fire_delay(FIRE_DELAY_TIER_6)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3 - HIT_ACCURACY_MULT_TIER_5
scatter = SCATTER_AMOUNT_TIER_6
@@ -395,7 +395,7 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/type23/set_gun_config_values()
..()
- fire_delay = 2.5 SECONDS
+ set_fire_delay(2.5 SECONDS)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_4
@@ -521,8 +521,8 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/double/set_gun_config_values()
..()
- burst_amount = BURST_AMOUNT_TIER_2
- fire_delay = FIRE_DELAY_TIER_9
+ set_burst_amount(BURST_AMOUNT_TIER_2)
+ set_fire_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
@@ -632,8 +632,8 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/double/damaged/set_gun_config_values()
..()
- burst_amount = BURST_AMOUNT_TIER_1
- fire_delay = 0.9 SECONDS
+ set_burst_amount(BURST_AMOUNT_TIER_1)
+ set_fire_delay(0.9 SECONDS)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_7
@@ -655,7 +655,7 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/double/sawn/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3 - HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
@@ -701,8 +701,8 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/double/cane/set_gun_config_values()
..()
- burst_amount = BURST_AMOUNT_TIER_1
- fire_delay = FIRE_DELAY_TIER_7
+ set_burst_amount(BURST_AMOUNT_TIER_1)
+ set_fire_delay(FIRE_DELAY_TIER_7)
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10
scatter_unwielded = SCATTER_AMOUNT_TIER_7
damage_mult = BASE_BULLET_DAMAGE_MULT + BULLET_DAMAGE_MULT_TIER_5
@@ -788,8 +788,8 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/double/mou53/set_gun_config_values()
..()
- burst_amount = BURST_AMOUNT_TIER_1
- fire_delay = FIRE_DELAY_TIER_8
+ set_burst_amount(BURST_AMOUNT_TIER_1)
+ set_fire_delay(FIRE_DELAY_TIER_8)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_10
@@ -879,8 +879,8 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/double/twobore/set_gun_config_values()
..()
- burst_amount = BURST_AMOUNT_TIER_1
- fire_delay = 2 SECONDS //Less than the stun time, but you still have to brace to fire safely.
+ set_burst_amount(BURST_AMOUNT_TIER_1)
+ set_fire_delay(2 SECONDS )//Less than the stun time, but you still have to brace to fire safely.
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_8
@@ -935,9 +935,9 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/double/twobore/Fire(atom/target, mob/living/carbon/human/user, params, reflex = 0, dual_wield) //Using this instead of apply_bullet_effects() as RPG does so I get more granular angles than just user direction.
var/prefire_rounds = current_mag.current_rounds //How many rounds do we have before we fire?
- ..()
+ . = ..()
if(current_mag.current_rounds == prefire_rounds) //We didn't fire a shot.
- return
+ return NONE
var/target_angle = Get_Compass_Dir(user, target) //More precise than get_dir().
fired_shots++
twobore_recoil(user, target_angle)
@@ -1111,8 +1111,8 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/pump/set_gun_config_values()
..()
- burst_amount = BURST_AMOUNT_TIER_1
- fire_delay = FIRE_DELAY_TIER_7 * 5
+ set_burst_amount(BURST_AMOUNT_TIER_1)
+ set_fire_delay(FIRE_DELAY_TIER_7 * 5)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
@@ -1220,7 +1220,7 @@ can cause issues with ammo types getting mixed up during the burst.
playsound(src, 'sound/machines/switch.ogg', 15, TRUE)
return TRUE
-/obj/item/weapon/gun/shotgun/pump/dual_tube/toggle_burst()
+/obj/item/weapon/gun/shotgun/pump/dual_tube/set_bursting()
var/obj/item/weapon/gun/shotgun/pump/dual_tube/shotgun = get_active_firearm(usr)
if(shotgun == src)
swap_tube(usr)
@@ -1257,7 +1257,7 @@ can cause issues with ammo types getting mixed up during the burst.
/obj/item/weapon/gun/shotgun/pump/dual_tube/cmb/set_gun_config_values()
..()
- fire_delay = 1.6 SECONDS
+ set_fire_delay(1.6 SECONDS)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_3
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = SCATTER_AMOUNT_TIER_6
diff --git a/code/modules/projectiles/guns/smartgun.dm b/code/modules/projectiles/guns/smartgun.dm
index c73d9c0f6423..859f99b17908 100644
--- a/code/modules/projectiles/guns/smartgun.dm
+++ b/code/modules/projectiles/guns/smartgun.dm
@@ -59,10 +59,12 @@
/obj/item/attachable/flashlight,
)
- flags_gun_features = GUN_SPECIALIST|GUN_WIELDED_FIRING_ONLY|GUN_HAS_FULL_AUTO|GUN_FULL_AUTO_ON|GUN_FULL_AUTO_ONLY
+ flags_gun_features = GUN_SPECIALIST|GUN_WIELDED_FIRING_ONLY
gun_category = GUN_CATEGORY_HEAVY
starting_attachment_types = list(/obj/item/attachable/smartbarrel)
auto_retrieval_slot = WEAR_J_STORE
+ start_semiauto = FALSE
+ start_automatic = TRUE
/obj/item/weapon/gun/smartgun/Initialize(mapload, ...)
@@ -85,10 +87,7 @@
/obj/item/weapon/gun/smartgun/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
- burst_amount = BURST_AMOUNT_TIER_3
- burst_delay = FIRE_DELAY_TIER_9
- fa_delay = FIRE_DELAY_TIER_SG
+ set_fire_delay(FIRE_DELAY_TIER_SG)
fa_scatter_peak = FULL_AUTO_SCATTER_PEAK_TIER_8
fa_max_scatter = SCATTER_AMOUNT_TIER_9
if(accuracy_improvement)
@@ -101,7 +100,6 @@
else
scatter = SCATTER_AMOUNT_TIER_6
recoil = RECOIL_AMOUNT_TIER_3
- burst_scatter_mult = SCATTER_AMOUNT_TIER_8
damage_mult = BASE_BULLET_DAMAGE_MULT
/obj/item/weapon/gun/smartgun/set_bullet_traits()
@@ -364,15 +362,13 @@
/obj/item/weapon/gun/smartgun/Fire(atom/target, mob/living/user, params, reflex = 0, dual_wield)
if(!requires_battery)
- ..()
- return
+ return ..()
if(battery)
if(!requires_power)
- ..()
- return
+ return ..()
if(drain_battery())
- ..()
+ return ..()
/obj/item/weapon/gun/smartgun/proc/drain_battery(override_drain)
@@ -672,7 +668,7 @@
ammo = /obj/item/ammo_magazine/smartgun/dirty
ammo_primary = /datum/ammo/bullet/smartgun/dirty//Toggled ammo type
ammo_secondary = /datum/ammo/bullet/smartgun/dirty/armor_piercing///Toggled ammo type
- flags_gun_features = GUN_WY_RESTRICTED|GUN_SPECIALIST|GUN_WIELDED_FIRING_ONLY|GUN_HAS_FULL_AUTO|GUN_FULL_AUTO_ON|GUN_FULL_AUTO_ONLY
+ flags_gun_features = GUN_WY_RESTRICTED|GUN_SPECIALIST|GUN_WIELDED_FIRING_ONLY
/obj/item/weapon/gun/smartgun/dirty/Initialize(mapload, ...)
. = ..()
@@ -690,12 +686,12 @@
/obj/item/weapon/gun/smartgun/dirty/elite/set_gun_config_values()
..()
- burst_amount = BURST_AMOUNT_TIER_5
- burst_delay = FIRE_DELAY_TIER_10
+ set_burst_amount(BURST_AMOUNT_TIER_5)
+ set_burst_delay(FIRE_DELAY_TIER_10)
if(!recoil_compensation)
scatter = SCATTER_AMOUNT_TIER_8
burst_scatter_mult = SCATTER_AMOUNT_TIER_10
- fa_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
fa_scatter_peak = FULL_AUTO_SCATTER_PEAK_TIER_10
fa_max_scatter = SCATTER_AMOUNT_NONE
diff --git a/code/modules/projectiles/guns/smgs.dm b/code/modules/projectiles/guns/smgs.dm
index 0c3e7f42fe72..699f05ed318c 100644
--- a/code/modules/projectiles/guns/smgs.dm
+++ b/code/modules/projectiles/guns/smgs.dm
@@ -75,9 +75,9 @@
/obj/item/weapon/gun/smg/m39/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
- burst_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_3
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_5
scatter = SCATTER_AMOUNT_TIER_4
@@ -124,7 +124,7 @@
/obj/item/weapon/gun/smg/m39/elite/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_7
accuracy_mult_unwielded = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_9
@@ -176,9 +176,9 @@
/obj/item/weapon/gun/smg/mp5/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
- burst_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_3
+ set_fire_delay(FIRE_DELAY_TIER_9)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
@@ -227,9 +227,9 @@
/obj/item/weapon/gun/smg/mp27/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
- burst_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_2
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_2
scatter = SCATTER_AMOUNT_TIER_4 + (SCATTER_AMOUNT_TIER_10 * 0.5)
@@ -260,9 +260,9 @@
/obj/item/weapon/gun/smg/ppsh/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
- burst_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_3
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_5
scatter = SCATTER_AMOUNT_TIER_4
@@ -283,15 +283,15 @@
playsound(src, 'sound/weapons/handling/gun_jam_click.ogg', 35, TRUE)
to_chat(user, SPAN_WARNING("Your gun is jammed! Mash Unique-Action to unjam it!"))
balloon_alert(user, "*jammed*")
- return
+ return NONE
else if(prob(ppsh_mag?.jam_chance))
jammed = TRUE
playsound(src, 'sound/weapons/handling/gun_jam_initial_click.ogg', 50, FALSE)
user.visible_message(SPAN_DANGER("[src] makes a noticeable clicking noise!"), SPAN_HIGHDANGER("\The [src] suddenly jams and refuses to fire! Mash Unique-Action to unjam it."))
balloon_alert(user, "*jammed*")
- return
+ return NONE
else
- . = ..()
+ return ..()
/obj/item/weapon/gun/smg/ppsh/unique_action(mob/user)
if(jammed)
@@ -361,6 +361,7 @@
)
wield_delay = WIELD_DELAY_NONE
aim_slowdown = SLOWDOWN_ADS_NONE
+ start_automatic = TRUE
/obj/item/weapon/gun/smg/mac15/set_gun_attachment_offsets()
attachable_offset = list("muzzle_x" = 32, "muzzle_y" = 20,"rail_x" = 16, "rail_y" = 22, "under_x" = 22, "under_y" = 16, "stock_x" = 22, "stock_y" = 16)
@@ -368,13 +369,9 @@
/obj/item/weapon/gun/smg/mac15/set_gun_config_values()
..()
- /* commented out until better fullauto code
- fa_delay = FIRE_DELAY_TIER_10
fa_scatter_peak = FULL_AUTO_SCATTER_PEAK_TIER_7
fa_max_scatter = SCATTER_AMOUNT_TIER_3
- */
-
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_5
burst_scatter_mult = SCATTER_AMOUNT_TIER_8
@@ -413,6 +410,7 @@
)
wield_delay = WIELD_DELAY_MIN
aim_slowdown = SLOWDOWN_ADS_QUICK
+ start_automatic = TRUE
var/jammed = FALSE
/obj/item/weapon/gun/smg/uzi/set_gun_attachment_offsets()
@@ -421,13 +419,9 @@
/obj/item/weapon/gun/smg/uzi/set_gun_config_values()
..()
- /* commented out until better fullauto code
- fa_delay = FIRE_DELAY_TIER_9
fa_scatter_peak = FULL_AUTO_SCATTER_PEAK_TIER_5
fa_max_scatter = SCATTER_AMOUNT_TIER_5
- */
-
- fire_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_2
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_2
scatter = SCATTER_AMOUNT_TIER_6
@@ -444,15 +438,15 @@
playsound(src, 'sound/weapons/handling/gun_jam_click.ogg', 35, TRUE)
to_chat(user, SPAN_WARNING("Your gun is jammed! Mash Unique-Action to unjam it!"))
balloon_alert(user, "*jammed*")
- return
+ return NONE
else if(prob(uzi_mag.jam_chance))
jammed = TRUE
playsound(src, 'sound/weapons/handling/gun_jam_initial_click.ogg', 35, TRUE)
user.visible_message(SPAN_DANGER("[src] makes a noticeable clicking noise!"), SPAN_HIGHDANGER("\The [src] suddenly jams and refuses to fire! Mash Unique-Action to unjam it."))
balloon_alert(user, "*jammed*")
- return
+ return NONE
else
- . = ..()
+ return ..()
/obj/item/weapon/gun/smg/uzi/unique_action(mob/user)
if(jammed)
@@ -515,9 +509,9 @@
/obj/item/weapon/gun/smg/fp9000/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_SMG
- burst_delay = FIRE_DELAY_TIER_SMG
- burst_amount = BURST_AMOUNT_TIER_3
+ set_fire_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_delay(FIRE_DELAY_TIER_SMG)
+ set_burst_amount(BURST_AMOUNT_TIER_3)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_5
scatter = SCATTER_AMOUNT_TIER_6
@@ -578,7 +572,7 @@
/obj/item/weapon/gun/smg/nailgun/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_5
accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_4
diff --git a/code/modules/projectiles/guns/souto.dm b/code/modules/projectiles/guns/souto.dm
index fbc1a39124b8..8d7a1b2550a4 100644
--- a/code/modules/projectiles/guns/souto.dm
+++ b/code/modules/projectiles/guns/souto.dm
@@ -27,14 +27,14 @@
to_chat(user, "You must equip the specialized Backpack Souto Vending Machine to use the Souto Slinger Supremo!")
click_empty(user)
unlink_soutopack()
- return
+ return NONE
if(soutopack)
if(!current_mag)
current_mag = soutopack.internal_mag
// Check we're actually firing the right fuel tank
if(current_mag != soutopack.internal_mag)
current_mag = soutopack.internal_mag
- ..()
+ return ..()
/obj/item/weapon/gun/souto/reload(mob/user, obj/item/ammo_magazine/magazine)
to_chat(user, SPAN_WARNING("The [src] feed system cannot be reloaded manually."))
diff --git a/code/modules/projectiles/guns/specialist.dm b/code/modules/projectiles/guns/specialist.dm
index be1bf5068a54..22fb290878e9 100644
--- a/code/modules/projectiles/guns/specialist.dm
+++ b/code/modules/projectiles/guns/specialist.dm
@@ -267,7 +267,7 @@
if(toggling_action)
toggling_action.update_button_icon()
-/obj/item/weapon/gun/rifle/sniper/toggle_burst(mob/user)
+/obj/item/weapon/gun/rifle/sniper/set_bursting(mob/user)
if(has_aimed_shot)
toggle_laser(user)
else
@@ -321,8 +321,8 @@
/obj/item/weapon/gun/rifle/sniper/M42A/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6*3
- burst_amount = BURST_AMOUNT_TIER_1
+ set_fire_delay(FIRE_DELAY_TIER_6*3)
+ set_burst_amount(BURST_AMOUNT_TIER_1)
accuracy_mult = BASE_ACCURACY_MULT * 3 //you HAVE to be able to hit
scatter = SCATTER_AMOUNT_TIER_8
damage_mult = BASE_BULLET_DAMAGE_MULT
@@ -365,9 +365,9 @@
/obj/item/weapon/gun/rifle/sniper/XM42B/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6 * 6 //Big boy damage, but it takes a lot of time to fire a shot.
+ set_fire_delay(FIRE_DELAY_TIER_6 * 6 )//Big boy damage, but it takes a lot of time to fire a shot.
//Kaga: Adjusted from 56 (Tier 4, 7*8) -> 30 (Tier 6, 5*6) ticks. 95 really wasn't big-boy damage anymore, although I updated it to 125 to remain consistent with the other 10x99mm caliber weapon (M42C). Now takes only twice as long as the M42A.
- burst_amount = BURST_AMOUNT_TIER_1
+ set_burst_amount(BURST_AMOUNT_TIER_1)
accuracy_mult = BASE_ACCURACY_MULT + 2*HIT_ACCURACY_MULT_TIER_10 //Who coded this like this, and why? It just calculates out to 1+1=2. Leaving a note here to check back later.
scatter = SCATTER_AMOUNT_TIER_10
damage_mult = BASE_BULLET_DAMAGE_MULT
@@ -430,8 +430,8 @@
/obj/item/weapon/gun/rifle/sniper/elite/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6*5
- burst_amount = BURST_AMOUNT_TIER_1
+ set_fire_delay(FIRE_DELAY_TIER_6*5)
+ set_burst_amount(BURST_AMOUNT_TIER_1)
accuracy_mult = BASE_ACCURACY_MULT * 3 //Was previously BAM + HAMT10, similar to the XM42B, and coming out to 1.5? Changed to be consistent with M42A. -Kaga
scatter = SCATTER_AMOUNT_TIER_10 //Was previously 8, changed to be consistent with the XM42B.
damage_mult = BASE_BULLET_DAMAGE_MULT
@@ -507,9 +507,9 @@
/obj/item/weapon/gun/rifle/sniper/svd/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6
- burst_amount = BURST_AMOUNT_TIER_2
- burst_delay = FIRE_DELAY_TIER_9
+ set_fire_delay(FIRE_DELAY_TIER_6)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
+ set_burst_delay(FIRE_DELAY_TIER_9)
accuracy_mult = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_8
burst_scatter_mult = SCATTER_AMOUNT_TIER_6
@@ -584,9 +584,9 @@
/obj/item/weapon/gun/rifle/m4ra_custom/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6
- burst_amount = BURST_AMOUNT_TIER_2
- burst_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_6)
+ set_burst_amount(BURST_AMOUNT_TIER_2)
+ set_burst_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_2
scatter = SCATTER_AMOUNT_TIER_8
burst_scatter_mult = SCATTER_AMOUNT_TIER_8
@@ -922,7 +922,7 @@
/obj/item/weapon/gun/launcher/grenade/m92/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_4*4
+ set_fire_delay(FIRE_DELAY_TIER_4*4)
/obj/item/weapon/gun/launcher/grenade/m92/able_to_fire(mob/living/user)
. = ..()
@@ -949,7 +949,7 @@
/obj/item/weapon/gun/launcher/grenade/m81/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_4 * 1.5
+ set_fire_delay(FIRE_DELAY_TIER_4 * 1.5)
/obj/item/weapon/gun/launcher/grenade/m81/on_pocket_removal()
..()
@@ -1058,7 +1058,7 @@
/obj/item/weapon/gun/launcher/rocket/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_6*2
+ set_fire_delay(FIRE_DELAY_TIER_6*2)
accuracy_mult = BASE_ACCURACY_MULT
scatter = SCATTER_AMOUNT_TIER_6
damage_mult = BASE_BULLET_DAMAGE_MULT
@@ -1225,9 +1225,9 @@
/obj/item/weapon/gun/launcher/rocket/m57a4/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_5
- burst_delay = FIRE_DELAY_TIER_7
- burst_amount = BURST_AMOUNT_TIER_4
+ set_fire_delay(FIRE_DELAY_TIER_5)
+ set_burst_delay(FIRE_DELAY_TIER_7)
+ set_burst_amount(BURST_AMOUNT_TIER_4)
accuracy_mult = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_4
scatter = SCATTER_AMOUNT_TIER_6
damage_mult = BASE_BULLET_DAMAGE_MULT
@@ -1372,7 +1372,7 @@
/obj/item/weapon/gun/flare/set_gun_config_values()
..()
- fire_delay = FIRE_DELAY_TIER_10
+ set_fire_delay(FIRE_DELAY_TIER_10)
accuracy_mult = BASE_ACCURACY_MULT
accuracy_mult_unwielded = BASE_ACCURACY_MULT - HIT_ACCURACY_MULT_TIER_10
scatter = 0
diff --git a/code/modules/reagents/chemistry_reactions/food_drink.dm b/code/modules/reagents/chemistry_reactions/food_drink.dm
index 9864af675cee..7e3fdac324f7 100644
--- a/code/modules/reagents/chemistry_reactions/food_drink.dm
+++ b/code/modules/reagents/chemistry_reactions/food_drink.dm
@@ -160,16 +160,16 @@
new /obj/item/reagent_container/food/snacks/sliceable/cheesewheel/immature(location)
-/datum/chemical_reaction/syntiflesh
- name = "Syntiflesh"
- id = "syntiflesh"
+/datum/chemical_reaction/synthmeat
+ name = "synthmeat"
+ id = "synthmeat"
result = null
required_reagents = list("blood" = 5, "clonexadone" = 1)
result_amount = 1
-/datum/chemical_reaction/syntiflesh/on_reaction(datum/reagents/holder, created_volume)
+/datum/chemical_reaction/synthmeat/on_reaction(datum/reagents/holder, created_volume)
var/location = get_turf(holder.my_atom)
- new /obj/item/reagent_container/food/snacks/meat/syntiflesh(location)
+ new /obj/item/reagent_container/food/snacks/meat/synthmeat(location)
/datum/chemical_reaction/hot_ramen
diff --git a/code/modules/shuttle/computers/escape_pod_computer.dm b/code/modules/shuttle/computers/escape_pod_computer.dm
index a28f2bfd2d60..99a989ab2e9f 100644
--- a/code/modules/shuttle/computers/escape_pod_computer.dm
+++ b/code/modules/shuttle/computers/escape_pod_computer.dm
@@ -204,11 +204,18 @@
heat_proof = 1
unslashable = TRUE
unacidable = TRUE
+ var/obj/docking_port/mobile/escape_shuttle/linked_shuttle
/obj/structure/machinery/door/airlock/evacuation/Initialize()
. = ..()
INVOKE_ASYNC(src, PROC_REF(lock))
+/obj/structure/machinery/door/airlock/evacuation/Destroy()
+ if(linked_shuttle)
+ linked_shuttle.mode = SHUTTLE_CRASHED
+ linked_shuttle.door_handler.doors -= list(src)
+ . = ..()
+
//Can't interact with them, mostly to prevent grief and meta.
/obj/structure/machinery/door/airlock/evacuation/Collided()
return FALSE
@@ -219,8 +226,24 @@
/obj/structure/machinery/door/airlock/evacuation/attack_hand()
return FALSE
-/obj/structure/machinery/door/airlock/evacuation/attack_alien()
- return FALSE //Probably a better idea that these cannot be forced open.
+/obj/structure/machinery/door/airlock/evacuation/attack_alien(mob/living/carbon/xenomorph/xeno)
+ if(!density || unslashable) //doors become slashable after evac is called
+ return FALSE
+
+ if(xeno.claw_type < CLAW_TYPE_SHARP)
+ to_chat(xeno, SPAN_WARNING("[src] is bolted down tight."))
+ return XENO_NO_DELAY_ACTION
+
+ xeno.animation_attack_on(src)
+ playsound(src, 'sound/effects/metalhit.ogg', 25, 1)
+ take_damage(HEALTH_DOOR / XENO_HITS_TO_DESTROY_BOLTED_DOOR)
+ return XENO_ATTACK_ACTION
+
/obj/structure/machinery/door/airlock/evacuation/attack_remote()
return FALSE
+
+/obj/structure/machinery/door/airlock/evacuation/get_applying_acid_time() //you can melt evacuation doors only when they are manually locked
+ if(!density)
+ return -1
+ return ..()
diff --git a/code/modules/shuttle/shuttles/escape_shuttle.dm b/code/modules/shuttle/shuttles/escape_shuttle.dm
index 6c580f9f163b..859aa45aceb3 100644
--- a/code/modules/shuttle/shuttles/escape_shuttle.dm
+++ b/code/modules/shuttle/shuttles/escape_shuttle.dm
@@ -9,7 +9,8 @@
ignitionTime = 8 SECONDS
ignition_sound = 'sound/effects/escape_pod_warmup.ogg'
/// The % chance of the escape pod crashing into the groundmap
- var/crash_land_chance = 33
+ var/early_crash_land_chance = 75
+ var/crash_land_chance = 25
var/datum/door_controller/single/door_handler = new()
var/launched = FALSE
@@ -19,11 +20,12 @@
/obj/docking_port/mobile/escape_shuttle/Initialize(mapload)
. = ..(mapload)
for(var/place in shuttle_areas)
- for(var/obj/structure/machinery/door/air in place)
+ for(var/obj/structure/machinery/door/airlock/evacuation/air in place)
door_handler.doors += list(air)
air.breakable = FALSE
air.indestructible = TRUE
air.unacidable = TRUE
+ air.linked_shuttle = src
/obj/docking_port/mobile/escape_shuttle/proc/cancel_evac()
door_handler.control_doors("force-unlock")
@@ -43,6 +45,12 @@
for(var/area/interior_area in shuttle_areas)
for(var/obj/structure/machinery/cryopod/evacuation/cryotube in interior_area)
cryotube.dock_state = STATE_READY
+ for(var/obj/structure/machinery/door/air in door_handler.doors)
+ air.breakable = TRUE
+ air.indestructible = FALSE
+ air.unslashable = FALSE
+ air.unacidable = FALSE
+
/obj/docking_port/mobile/escape_shuttle/proc/evac_launch()
if(mode == SHUTTLE_CRASHED)
@@ -76,7 +84,7 @@
return
destination = null
- if(prob(crash_land_chance))
+ if(prob((EvacuationAuthority.evac_status >= EVACUATION_STATUS_IN_PROGRESS ? crash_land_chance : early_crash_land_chance)))
create_crash_point()
set_mode(SHUTTLE_IGNITING)
@@ -84,6 +92,16 @@
setTimer(ignitionTime)
launched = TRUE
+ if(!crash_land) // so doors won't break in space
+ for(var/obj/structure/machinery/door/air in door_handler.doors)
+ for(var/obj/effect/xenomorph/acid/acid in air.loc)
+ if(acid.acid_t == air)
+ qdel(acid)
+ air.breakable = FALSE
+ air.indestructible = TRUE
+ air.unacidable = TRUE
+
+
/obj/docking_port/mobile/escape_shuttle/proc/create_crash_point()
for(var/i = 1 to 10)
var/list/all_ground_levels = SSmapping.levels_by_trait(ZTRAIT_GROUND)
@@ -191,6 +209,8 @@
id = ESCAPE_SHUTTLE_EAST_CL
width = 4
height = 5
+ early_crash_land_chance = 25
+ crash_land_chance = 5
/obj/docking_port/mobile/escape_shuttle/w
id = ESCAPE_SHUTTLE_WEST
diff --git a/code/modules/tents/blockers.dm b/code/modules/tents/blockers.dm
index 3301768d9147..24765ca2a284 100644
--- a/code/modules/tents/blockers.dm
+++ b/code/modules/tents/blockers.dm
@@ -7,6 +7,7 @@
invisibility = INVISIBILITY_MAXIMUM
density = TRUE
opacity = FALSE // Unfortunately this doesn't behave as we'd want with ON_BORDER so we can't make tent opaque
+ throwpass = TRUE // Needs this so xenos can attack through the blocker and hit the tents or people inside
/// The tent this blocker relates to, will be destroyed along with it
var/obj/structure/tent/linked_tent
@@ -36,3 +37,9 @@
/obj/structure/blocker/tent/get_projectile_hit_boolean(obj/item/projectile/P)
. = ..()
return FALSE // Always fly through the tent
+
+//Blocks all direction, basically an invisible wall
+/obj/structure/blocker/tent/full_tile
+ flags_atom = NO_FLAGS
+ icon = 'icons/landmarks.dmi'
+ icon_state = "invisible_wall"
diff --git a/code/modules/tents/deployed_tents.dm b/code/modules/tents/deployed_tents.dm
index c81beb318d5a..28bf11a38a6a 100644
--- a/code/modules/tents/deployed_tents.dm
+++ b/code/modules/tents/deployed_tents.dm
@@ -3,6 +3,7 @@
/obj/structure/tent
name = "tent"
icon = 'icons/obj/structures/tents_deployed_classic.dmi'
+ desc = "Can be torn down with an entrenching tool."
opacity = FALSE // Seems only the initial turf blocks light, not all of the multitile. Therefore, useless.
layer = INTERIOR_WALL_SOUTH_LAYER // This should be below FLY_LAYER but just thank chairs and other bs
health = 200
@@ -11,7 +12,7 @@
/// Turf dimensions along the X axis, beginning from left, at ground level
var/x_dim = 2
/// Turf dimensions along the Y axis, beginning from bottom, at ground level
- var/y_dim = 3
+ var/y_dim = 4
/// How much cold protection to add to entering humans - Full body clothing means complete (1) protection
var/cold_protection_factor = 0.4
@@ -55,6 +56,7 @@
return
var/mob/subject_mob = subject
RegisterSignal(subject_mob, list(COMSIG_MOVABLE_TURF_ENTERED, COMSIG_GHOST_MOVED), PROC_REF(mob_moved), override = TRUE) // Must override because we can't know if mob was already inside tent without keeping an awful ref list
+ subject_mob.RegisterSignal(src, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/mob, tent_deletion_clean_up), override = TRUE)
var/atom/movable/screen/plane_master/roof/roof_plane = subject_mob.hud_used.plane_masters["[ROOF_PLANE]"]
roof_plane?.invisibility = INVISIBILITY_MAXIMUM
if(ishuman(subject))
@@ -71,28 +73,60 @@
/obj/structure/tent/proc/mob_exited_tent(mob/subject)
UnregisterSignal(subject, list(COMSIG_MOVABLE_TURF_ENTERED, COMSIG_GHOST_MOVED, COMSIG_HUMAN_COLD_PROTECTION_APPLY_MODIFIERS))
+ subject.UnregisterSignal(src, COMSIG_PARENT_QDELETING)
var/atom/movable/screen/plane_master/roof/roof_plane = subject.hud_used.plane_masters["[ROOF_PLANE]"]
roof_plane?.invisibility = 0
+/mob/proc/tent_deletion_clean_up(obj/structure/tent/deleting_tent)
+ SIGNAL_HANDLER
+ deleting_tent.mob_exited_tent(src)
+
/obj/structure/tent/attack_alien(mob/living/carbon/xenomorph/M)
if(unslashable)
return
- health -= 20
+
+ M.animation_attack_on(src)
+ health -= rand(M.melee_damage_lower, M.melee_damage_upper)
+ playsound(src, 'sound/items/paper_ripped.ogg', 25, 1)
+
+ M.visible_message(SPAN_DANGER("[M] [M.slashes_verb] [src]!"), \
+ SPAN_DANGER("You [M.slash_verb] [src]!"), null, 5, CHAT_TYPE_XENO_COMBAT)
+
if(health <= 0)
visible_message(SPAN_BOLDWARNING("The [src] collapses!"))
qdel(src)
+ return XENO_ATTACK_ACTION
+
+/obj/structure/tent/attackby(obj/item/item, mob/user)
+ var/obj/item/tool/shovel/shovel = item
+ if(!istype(shovel) || shovel.folded)
+ return
+ visible_message(SPAN_HIGHDANGER("[user] is trying to tear down the [src]"))
+ playsound(src, 'sound/items/paper_ripped.ogg', 25, 1)
+
+ if(user.action_busy || !do_after(user, 150, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE, src) || QDELETED(src))
+ return
+
+ visible_message(SPAN_HIGHDANGER("[user] tears down the [src]"))
+ playsound(src, 'sound/items/paper_ripped.ogg', 25, 1)
+ qdel(src)
+
+/obj/structure/tent/get_projectile_hit_boolean(obj/item/projectile/P)
+ . = ..()
+ return FALSE // Always fly through the tent
+
/// Command tent, providing basics for field command: a phone, and an overwatch console
/obj/structure/tent/cmd
icon_state = "cmd_interior"
roof_state = "cmd_top"
- desc = "A standard USCM Command Tent. This one comes equipped with a self-powered Overwatch Console and a Telephone. It is very frail, do not burn, expose to sharp objects, or explosives."
+ desc = "A standard USCM Command Tent. This one comes equipped with a self-powered Overwatch Console and a Telephone. It is very frail, do not burn, expose to sharp objects, or explosives. Can be torn down with an entrenching tool."
/// Medical tent, procures a buff to surgery speed
/obj/structure/tent/med
icon_state = "med_interior"
roof_state = "med_top"
- desc = "A standard USCM Medical Tent. This one comes equipped with advanced field surgery facilities. It is very fragile however and won't withstand the rigors of war."
+ desc = "A standard USCM Medical Tent. This one comes equipped with advanced field surgery facilities. It is very fragile however and won't withstand the rigors of war. Can be torn down with an entrenching tool."
var/surgery_speed_mult = 0.9
var/surgery_pain_reduction = 5
diff --git a/code/modules/tents/folded_tents.dm b/code/modules/tents/folded_tents.dm
index d1f922703500..08b8142ab358 100644
--- a/code/modules/tents/folded_tents.dm
+++ b/code/modules/tents/folded_tents.dm
@@ -12,6 +12,8 @@
var/off_y = 0
/// Map Template to use for the tent
var/template
+ /// If this tent can be deployed anywhere
+ var/unrestricted_deployment = FALSE
/// Check an area is clear for deployment of the tent
/obj/item/folded_tent/proc/check_area(turf/ref_turf, mob/message_receiver, display_error = FALSE)
@@ -20,7 +22,7 @@
var/list/turf_block = get_deployment_area(ref_turf)
for(var/turf/turf as anything in turf_block)
var/area/area = get_area(turf)
- if(!area.can_build_special)
+ if(!area.can_build_special && !unrestricted_deployment)
if(message_receiver)
to_chat(message_receiver, SPAN_WARNING("You cannot deploy tents on restricted areas."))
if(display_error)
@@ -73,7 +75,7 @@
if(!istype(deploy_turf) || (deploy_turf.x + dim_x > world.maxx) || (deploy_turf.y + dim_y > world.maxy)) // Map border basically
return
- if(!is_ground_level(deploy_turf.z))
+ if(!is_ground_level(deploy_turf.z) && !unrestricted_deployment)
to_chat(user, SPAN_WARNING("USCM Operational Tents are intended for operations, not ship or space recreation."))
return
@@ -112,7 +114,7 @@
icon_state = "cmd"
desc = "A standard USCM Command Tent. This one comes equipped with a self-powered Overwatch Console and a Telephone. Unfold in a suitable location to maximize usefulness. Staff Officer not included. ENTRANCE TO THE SOUTH."
dim_x = 2
- dim_y = 3
+ dim_y = 4
off_x = -1
template = /datum/map_template/tent/cmd
@@ -121,7 +123,7 @@
icon_state = "med"
desc = "A standard USCM Medical Tent. This one comes equipped with advanced field surgery facilities. Unfold in a suitable location to maximize health gains. Surgical Tray not included. ENTRANCE TO THE SOUTH."
dim_x = 2
- dim_y = 3
+ dim_y = 4
template = /datum/map_template/tent/med
/obj/item/folded_tent/reqs
@@ -129,7 +131,7 @@
icon_state = "req"
desc = "A standard USCM Requisitions Tent. Now, you can enjoy req line anywhere you go! Unfold in a suitable location to maximize resource distribution. ASRS not included. ENTRANCE TO THE SOUTH."
dim_x = 4
- dim_y = 3
+ dim_y = 4
off_x = -2
template = /datum/map_template/tent/reqs
@@ -138,7 +140,7 @@
icon_state = "big"
desc = "A standard USCM Tent. This one is just a bigger, general purpose version. Unfold in a suitable location for maximum FOB vibes. Mess Tech not included. ENTRANCE TO THE SOUTH."
dim_x = 3
- dim_y = 3
+ dim_y = 4
off_x = -2
template = /datum/map_template/tent/big
diff --git a/code/modules/tgui/tgui_number_input.dm b/code/modules/tgui/tgui_number_input.dm
index 9c447ecd5a03..aa189b1d2039 100644
--- a/code/modules/tgui/tgui_number_input.dm
+++ b/code/modules/tgui/tgui_number_input.dm
@@ -31,7 +31,7 @@
qdel(number_input)
///A clone of tgui_input_number that defaults to accepting negative inputs too.
-/proc/tgui_input_real_number(mob/user, message, title = "Number Input", default = 0, max_value = 16777216, min_value = -16777216, timeout = 0, integer_only = FALSE)
+/proc/tgui_input_real_number(mob/user, message, title = "Number Input", default = 0, max_value = SHORT_REAL_LIMIT, min_value = -SHORT_REAL_LIMIT, timeout = 0, integer_only = FALSE)
return tgui_input_number(user, message, title, default, max_value, min_value, timeout, integer_only)
/**
* Creates an asynchronous TGUI number input window with an associated callback.
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 010cba770ce2..7e620b6bc1fa 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -85,7 +85,7 @@
#include "unit_test.dm"
#include "spawn_humans.dm"
#include "check_runtimes.dm"
-#include "wj_emotes.dm"
+#include "emote_panels.dm"
#undef TEST_ASSERT
#undef TEST_ASSERT_EQUAL
diff --git a/code/modules/unit_tests/emote_panels.dm b/code/modules/unit_tests/emote_panels.dm
new file mode 100644
index 000000000000..87bbd5e15620
--- /dev/null
+++ b/code/modules/unit_tests/emote_panels.dm
@@ -0,0 +1,11 @@
+/// Test that all emotes for Working Joes & Yautja have a category
+/datum/unit_test/emote_panels
+
+/datum/unit_test/emote_panels/Run()
+ for(var/datum/emote/living/carbon/human/synthetic/working_joe/wj_emote as anything in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe))
+ if(!initial(wj_emote.category))
+ TEST_FAIL("Emote [wj_emote] did not have a category!")
+
+ for(var/datum/emote/living/carbon/human/yautja/yautja_emote as anything in subtypesof(/datum/emote/living/carbon/human/yautja))
+ if(!initial(yautja_emote.category))
+ TEST_FAIL("Emote [yautja_emote] did not have a category!")
diff --git a/code/modules/unit_tests/missing_icons.dm b/code/modules/unit_tests/missing_icons.dm
index 53abbed70d6a..44f135ae34fb 100644
--- a/code/modules/unit_tests/missing_icons.dm
+++ b/code/modules/unit_tests/missing_icons.dm
@@ -19,6 +19,7 @@
/datum/unit_test/missing_icons/Run()
generate_possible_icon_states_list()
generate_possible_icon_states_list("icons/effects/")
+ generate_possible_icon_states_list("icons/mobs/")
if(additional_icon_location)
generate_possible_icon_states_list(additional_icon_location)
diff --git a/code/modules/unit_tests/wj_emotes.dm b/code/modules/unit_tests/wj_emotes.dm
deleted file mode 100644
index f89757665011..000000000000
--- a/code/modules/unit_tests/wj_emotes.dm
+++ /dev/null
@@ -1,7 +0,0 @@
-/// Test that all working joe emotes have a category
-/datum/unit_test/wj_emotes
-
-/datum/unit_test/wj_emotes/Run()
- for(var/datum/emote/living/carbon/human/synthetic/working_joe/emote as anything in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe))
- if(!initial(emote.category))
- TEST_FAIL("Emote [emote] did not have a category!")
diff --git a/code/span_macros.dm b/code/span_macros.dm
index d5e9cdcb9c36..1eca82ea563f 100644
--- a/code/span_macros.dm
+++ b/code/span_macros.dm
@@ -55,6 +55,7 @@
// Misc
#define SPAN_BOLD(X) "[X]"
#define SPAN_UNDERLINE(X) "[X]"
+#define SPAN_LARGE(X) "[X]"
#define SPAN_BOLDANNOUNCE(X) "[X]"
#define SPAN_BOLDNOTICE(X) "[X]"
diff --git a/colonialmarines.dme b/colonialmarines.dme
index b241992cf172..2b3b08984dfa 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -33,6 +33,7 @@
#include "code\__DEFINES\admin.dm"
#include "code\__DEFINES\ARES.dm"
#include "code\__DEFINES\atmospherics.dm"
+#include "code\__DEFINES\autofire.dm"
#include "code\__DEFINES\autolathe.dm"
#include "code\__DEFINES\blood.dm"
#include "code\__DEFINES\bsql.config.dm"
@@ -51,6 +52,7 @@
#include "code\__DEFINES\db_defs.dm"
#include "code\__DEFINES\defenses.dm"
#include "code\__DEFINES\dropships.dm"
+#include "code\__DEFINES\emote_panels.dm"
#include "code\__DEFINES\equipment.dm"
#include "code\__DEFINES\events.dm"
#include "code\__DEFINES\fire.dm"
@@ -111,7 +113,6 @@
#include "code\__DEFINES\vv.dm"
#include "code\__DEFINES\weapon_stats.dm"
#include "code\__DEFINES\weather.dm"
-#include "code\__DEFINES\wj_emotes.dm"
#include "code\__DEFINES\xeno.dm"
#include "code\__DEFINES\dcs\flags.dm"
#include "code\__DEFINES\dcs\helpers.dm"
@@ -228,6 +229,7 @@
#include "code\controllers\subsystem\admin.dm"
#include "code\controllers\subsystem\assets.dm"
#include "code\controllers\subsystem\atoms.dm"
+#include "code\controllers\subsystem\autofire.dm"
#include "code\controllers\subsystem\cellauto.dm"
#include "code\controllers\subsystem\chat.dm"
#include "code\controllers\subsystem\communications.dm"
@@ -372,6 +374,8 @@
#include "code\datums\components\toxin_buildup.dm"
#include "code\datums\components\weed_damage_reduction.dm"
#include "code\datums\components\weed_food.dm"
+#include "code\datums\components\autofire\_automated_fire.dm"
+#include "code\datums\components\autofire\autofire.dm"
#include "code\datums\components\xeno\shield_slash.dm"
#include "code\datums\construction\construction_template.dm"
#include "code\datums\construction\xenomorph\construction_template_xenomorph.dm"
@@ -1785,12 +1789,10 @@
#include "code\modules\mob\living\carbon\human\powers\human_powers.dm"
#include "code\modules\mob\living\carbon\human\powers\issue_order.dm"
#include "code\modules\mob\living\carbon\human\species\emote-monkey.dm"
-#include "code\modules\mob\living\carbon\human\species\emote-yautja.dm"
#include "code\modules\mob\living\carbon\human\species\human.dm"
#include "code\modules\mob\living\carbon\human\species\monkey.dm"
#include "code\modules\mob\living\carbon\human\species\species.dm"
#include "code\modules\mob\living\carbon\human\species\synthetic.dm"
-#include "code\modules\mob\living\carbon\human\species\yautja.dm"
#include "code\modules\mob\living\carbon\human\species\zombie.dm"
#include "code\modules\mob\living\carbon\human\species\working_joe\_emote.dm"
#include "code\modules\mob\living\carbon\human\species\working_joe\_species.dm"
@@ -1802,6 +1804,11 @@
#include "code\modules\mob\living\carbon\human\species\working_joe\restricted_area.dm"
#include "code\modules\mob\living\carbon\human\species\working_joe\task_update.dm"
#include "code\modules\mob\living\carbon\human\species\working_joe\warning.dm"
+#include "code\modules\mob\living\carbon\human\species\yautja\_emote.dm"
+#include "code\modules\mob\living\carbon\human\species\yautja\_species.dm"
+#include "code\modules\mob\living\carbon\human\species\yautja\fake_sounds.dm"
+#include "code\modules\mob\living\carbon\human\species\yautja\fake_voice.dm"
+#include "code\modules\mob\living\carbon\human\species\yautja\yautja_sound.dm"
#include "code\modules\mob\living\carbon\xenomorph\Abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\attack_alien.dm"
#include "code\modules\mob\living\carbon\xenomorph\damage_procs.dm"
@@ -1855,6 +1862,9 @@
#include "code\modules\mob\living\carbon\xenomorph\abilities\facehugger\facehugger_powers.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\hivelord\hivelord_abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\hivelord\hivelord_powers.dm"
+#include "code\modules\mob\living\carbon\xenomorph\abilities\lesser_drone\lesser_drone_abilities.dm"
+#include "code\modules\mob\living\carbon\xenomorph\abilities\lesser_drone\lesser_drone_macros.dm"
+#include "code\modules\mob\living\carbon\xenomorph\abilities\lesser_drone\lesser_drone_powers.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\lurker\lurker_abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\lurker\lurker_macros.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\lurker\lurker_powers.dm"
@@ -1891,6 +1901,7 @@
#include "code\modules\mob\living\carbon\xenomorph\castes\Hellhound.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Hivelord.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Larva.dm"
+#include "code\modules\mob\living\carbon\xenomorph\castes\lesser_drone.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Lurker.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Praetorian.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Predalien.dm"
@@ -2061,7 +2072,6 @@
#include "code\modules\power\turbine.dm"
#include "code\modules\projectiles\ammo_datums.dm"
#include "code\modules\projectiles\ammunition.dm"
-#include "code\modules\projectiles\full_auto.dm"
#include "code\modules\projectiles\gun.dm"
#include "code\modules\projectiles\gun_attachables.dm"
#include "code\modules\projectiles\gun_helpers.dm"
diff --git a/dependencies.sh b/dependencies.sh
index ef00662eaa4d..2889751d36e6 100644
--- a/dependencies.sh
+++ b/dependencies.sh
@@ -8,7 +8,7 @@ export BYOND_MAJOR=514
export BYOND_MINOR=1588
#rust_g git tag
-export RUST_G_VERSION=1.2.0
+export RUST_G_VERSION=2.1.0
#node version
export NODE_VERSION=14
diff --git a/html/changelogs/AutoChangeLog-pr-3455.yml b/html/changelogs/AutoChangeLog-pr-3455.yml
new file mode 100644
index 000000000000..c3189ad233bd
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3455.yml
@@ -0,0 +1,7 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - rscadd: "Adds a plasma breaching charge that detonates a plasma wave stunning those opposite it."
+ - rscadd: "Adds the name of the tracked item to the Yautja gear tracker."
+ - rscadd: "Added an alternate mode for the Plasma Pistol and moved the incendiary property to it."
+ - rscadd: "Added MINIMAP_FLAG_ALL to Yautja globe map."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3757.yml b/html/changelogs/AutoChangeLog-pr-3757.yml
new file mode 100644
index 000000000000..4cc6cc928423
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3757.yml
@@ -0,0 +1,4 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - rscadd: "Made Yautja cloak cost no power to operate, and created multipliers for disabled duration based upon what caused the Yautja to decloak."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3764.yml b/html/changelogs/AutoChangeLog-pr-3764.yml
new file mode 100644
index 000000000000..ecb789daaf91
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3764.yml
@@ -0,0 +1,7 @@
+author: "ghostsheet"
+delete-after: True
+changes:
+ - rscadd: "Large General Pouch has stricter restriction against internal boxes."
+ - rscadd: "Large General Pouch no longer restricted to 1 medium item."
+ - rscadd: "Large General Pouch added to REQ."
+ - rscadd: "Shuffled REQ pouch order into Meds, Engi, Misc, Ammo."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3783.yml b/html/changelogs/AutoChangeLog-pr-3783.yml
new file mode 100644
index 000000000000..1d995da56001
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3783.yml
@@ -0,0 +1,16 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - rscadd: "Adds functional maintenance ticket control to the Apollo Console"
+ - rscadd: "Adds a unique ID system for each ticket. Credit to Ben10083 for the idea."
+ - rscadd: "Adds procs for vents to spew gas within a radius. Not currently usable without proccall."
+ - code_imp: "Starts work on Access Tickets."
+ - code_imp: "START of contribs from Ben10083"
+ - rscadd: "ARES now announces when Bioscan fails."
+ - code_imp: "new procs to see if ARES can talk, or log something. Bioscan proc renamed for consistency."
+ - admin: "Admins can now force an ARES announcement or communication if subsystem is offline."
+ - rscadd: "new emergency protocol from ARES; call General Quarters, which sets the ship to immediate Red Alert."
+ - rscadd: "New APOLLO maintenance ticket categories; Janitorial and Support. Fire now a priority ticket"
+ - qol: "Claimed APOLLO tickets can be unclaimed"
+ - qol: "APOLLO tickets can be rejected/completed if unclaimed by any Working Joe"
+ - code_imp: "END of contribs from Ben10083"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3796.yml b/html/changelogs/AutoChangeLog-pr-3796.yml
new file mode 100644
index 000000000000..0cc5e31fb217
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3796.yml
@@ -0,0 +1,5 @@
+author: "Hopek"
+delete-after: True
+changes:
+ - rscadd: "Changed the description flavortext of the fountain pen to match the new lore. There is now a laser engraving of the owner on the pen itself."
+ - rscadd: "Added a detailed lore description for the fountain pen."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3850.yml b/html/changelogs/AutoChangeLog-pr-3850.yml
deleted file mode 100644
index dc31ab865e69..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3850.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "realforest2001"
-delete-after: True
-changes:
- - rscadd: "Added donator kit boxes that hold a donator's special gear. They can be destroyed with a right-click while on the ground, and are locked to the donator."
- - code_imp: "Tidied up typepaths for many donator items to make it clearer what or who they are for."
- - code_imp: "Removed the requirement for donators to match their character name to our config file."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3861.yml b/html/changelogs/AutoChangeLog-pr-3861.yml
new file mode 100644
index 000000000000..4d81b7dab6e3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3861.yml
@@ -0,0 +1,4 @@
+author: "blackdragonTOW"
+delete-after: True
+changes:
+ - rscadd: "Added unique, faction dependent music to Round End in the event that Xenos lose post hijack."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3867.yml b/html/changelogs/AutoChangeLog-pr-3867.yml
new file mode 100644
index 000000000000..3cb52494ecde
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3867.yml
@@ -0,0 +1,5 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - code_imp: "Added additional lints to dmi/test.py to test for duplicate state names and excessive quantity and added another type path to the missing_icons unit_test."
+ - imageadd: "Renamed and moved some icons around to comply with new testing."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3868.yml b/html/changelogs/AutoChangeLog-pr-3868.yml
new file mode 100644
index 000000000000..4bd11e9a6b0e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3868.yml
@@ -0,0 +1,6 @@
+author: "MarpleJones, ihatethisengine2"
+delete-after: True
+changes:
+ - rscadd: "Added new sprites for the Eggsac Carrier. Includes an additional death sound for the eggsac bursting."
+ - rscadd: "Added a hive announcement for when a Carrier dies with eggs."
+ - bugfix: "Carrier egg drop chance upon death now works as intended."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3877.yml b/html/changelogs/AutoChangeLog-pr-3877.yml
new file mode 100644
index 000000000000..ba7f0a62e282
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3877.yml
@@ -0,0 +1,12 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - code_imp: "Changes /datum/rank to /datum/yautja_rank to be clearer on what it is used for."
+ - rscadd: "Added access defines for Yautja ship."
+ - rscadd: "Added rank identifiers on Yautja equipment presets to help set access levels on their bracer chips."
+ - maptweak: "Changed the doors on the Yautja ship to be their own subtypes, with certain ones being locked to Yautja or certain Yautja Ranks."
+ - code_imp: "Slightly modernised obj/proc/allowed, and also made it check for access on bracer chips if present."
+ - maptweak: "Replaced the unworthy prey section of the Yautja Ship with a secondary armory for plasma rifles, however the door is inaccessible to most Yautja. Intended to make it a bit less obvious admins are spawning them in for the rare times they're used/necessary."
+ - maptweak: "Above armory will automatically open if an Abomination is detected."
+ - maptweak: "Reduces amount of herbs on the Yautja Ship."
+ - maptweak: "Fixes the \"Consoles\" orientation in the Yautja flight deck."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3880.yml b/html/changelogs/AutoChangeLog-pr-3880.yml
new file mode 100644
index 000000000000..4e90dcf5622a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3880.yml
@@ -0,0 +1,7 @@
+author: "Ben10083"
+delete-after: True
+changes:
+ - bugfix: "Non-techweb nuke now works properly."
+ - ui: "Nuke timers now use minutes instead of seconds"
+ - spellcheck: "Nukes renamed to 'Encrypted' and 'Decrypted' nuke respectively."
+ - admin: "Ability to spawn nuke added to event panel"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3891.yml b/html/changelogs/AutoChangeLog-pr-3891.yml
new file mode 100644
index 000000000000..b2331552771b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3891.yml
@@ -0,0 +1,7 @@
+author: "Ben10083"
+delete-after: True
+changes:
+ - balance: "Carriers with Eggsac strain can now place eggs on regular weeds."
+ - bugfix: "Extra egg planting range now properly applied for Eggsac strain and Queen on ovi"
+ - qol: "Eggsac Carriers now notified when they generate a egg."
+ - code_imp: "new variable to xenomorphs to dictate egg planting distance, and removal of unused procs"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3893.yml b/html/changelogs/AutoChangeLog-pr-3893.yml
deleted file mode 100644
index 75983d786455..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3893.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Morrow"
-delete-after: True
-changes:
- - bugfix: "Colonial Marshal ERT now uses their own faction"
- - bugfix: "Anchorpoint marines now use the proper define for USCM"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3899.yml b/html/changelogs/AutoChangeLog-pr-3899.yml
deleted file mode 100644
index 215aaa3eb4f3..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3899.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-author: "Morrow"
-delete-after: True
-changes:
- - rscadd: "Added sounds for inserting and removing knifes to the knife webbing"
- - rscadd: "Added draw delay for knives to the knife webbing"
- - rscadd: "Allowed using quickdraw with the knife webbing"
- - rscadd: "Allowed knife webbing to be alt-clicked to draw from it"
- - rscadd: "Knife webbing is now spawned full"
- - code_imp: "Standardized knife draw delay in a define"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3902.yml b/html/changelogs/AutoChangeLog-pr-3902.yml
new file mode 100644
index 000000000000..2c487af71f24
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3902.yml
@@ -0,0 +1,4 @@
+author: "Ben10083"
+delete-after: True
+changes:
+ - balance: "Xenomorphs lose their hiding ability for 2 seconds after a melee attack. 0.5 second cooldown applied to hide ability."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3906.yml b/html/changelogs/AutoChangeLog-pr-3906.yml
new file mode 100644
index 000000000000..60acb1a378d8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3906.yml
@@ -0,0 +1,4 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - ui: "Adds a UI panel for Yautja emotes, all credit to Zonespace."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3907.yml b/html/changelogs/AutoChangeLog-pr-3907.yml
new file mode 100644
index 000000000000..7c62f716ff1b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3907.yml
@@ -0,0 +1,5 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - rscadd: "Added a toggle for Yautja bracer lock. This also works with the same mechanic of SDing another Yautja."
+ - rscadd: "Added a message to all Yautja if an SD is cancelled."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3908.yml b/html/changelogs/AutoChangeLog-pr-3908.yml
new file mode 100644
index 000000000000..6390d0e2da16
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3908.yml
@@ -0,0 +1,4 @@
+author: "BeagleGaming1"
+delete-after: True
+changes:
+ - code_imp: "Changed squad color code from a list to a variable"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3909.yml b/html/changelogs/AutoChangeLog-pr-3909.yml
new file mode 100644
index 000000000000..18da06c1cb45
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3909.yml
@@ -0,0 +1,5 @@
+author: "BraveMole, Zonespace"
+delete-after: True
+changes:
+ - bugfix: "Smartgunners can no longer shoot their smartgun while aghosted"
+ - balance: "The Uzi and Mac-15 now have full-auto"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3910.yml b/html/changelogs/AutoChangeLog-pr-3910.yml
new file mode 100644
index 000000000000..72c4e9be4c46
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3910.yml
@@ -0,0 +1,7 @@
+author: "Ben10083"
+delete-after: True
+changes:
+ - bugfix: "Working Joes no longer spawn meat when butchered by Abominations"
+ - bugfix: "Synthetics now are butchered into synthflesh when butchered by Abominations"
+ - rscadd: "special meat subtype 'synthflesh' if Synthetics get butchered. Uses same recipes as synthmeat"
+ - imageadd: "synthflesh sprite"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3911.yml b/html/changelogs/AutoChangeLog-pr-3911.yml
deleted file mode 100644
index 10b29d57f05f..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3911.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Awantje"
-delete-after: True
-changes:
- - code_imp: "Medicomp surgery tools no longer care about what zone you target."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3918.yml b/html/changelogs/AutoChangeLog-pr-3918.yml
new file mode 100644
index 000000000000..521e80ec2ad5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3918.yml
@@ -0,0 +1,9 @@
+author: "ghostsheet"
+delete-after: True
+changes:
+ - rscadd: "Tents can be destroyed with an E-tool."
+ - rscadd: "Tents are now in their respective department, Big tent is now in Bravo Bunks."
+ - rscadd: "You can no longer stand behind a tent."
+ - bugfix: "Xenos can now slash tents from the side."
+ - bugfix: "Tents no longer randomly block bullets."
+ - bugfix: "Staying inside a tent when it is destroyed no longer give your permanent see-through roof vision."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3927.yml b/html/changelogs/AutoChangeLog-pr-3927.yml
deleted file mode 100644
index 0fd5837fb3fc..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3927.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "QuickLode"
-delete-after: True
-changes:
- - bugfix: "limits the Survivor CMB Synthetic comms"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3930.yml b/html/changelogs/AutoChangeLog-pr-3930.yml
new file mode 100644
index 000000000000..9ef0ae996150
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3930.yml
@@ -0,0 +1,6 @@
+author: "QuickLode"
+delete-after: True
+changes:
+ - rscadd: "Radio titles for W-Y prepositioned Synthetics"
+ - qol: "Swaps PMC Synthetic autocompressor for a crew monitor to facilitate search and rescue."
+ - bugfix: "fixes PMC Synthetic ID & removes redundant graft"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3932.yml b/html/changelogs/AutoChangeLog-pr-3932.yml
deleted file mode 100644
index df7c88fa0e2a..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3932.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "harryob"
-delete-after: True
-changes:
- - admin: "you can no longer touch bad vars"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3934.yml b/html/changelogs/AutoChangeLog-pr-3934.yml
deleted file mode 100644
index dffcae96ead9..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3934.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Morrow"
-delete-after: True
-changes:
- - balance: "Removes large pouches as buyable from all squad roles other than SL"
- - balance: "Removes medkit, medical (the base ones, not first aid), and syringe pouches from squad prep room vendors"
- - balance: "Replaces the EZ-injector first aid pouch with the alternate tricord/bandage/splint/ointment version in the squad prep room vendors"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3935.yml b/html/changelogs/AutoChangeLog-pr-3935.yml
deleted file mode 100644
index 14b051b42c00..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3935.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Morrow"
-delete-after: True
-changes:
- - balance: "Modifies INTERRUPT_NO_NEEDHAND flag to require you hold the item at least in one hand."
- - balance: "Changes splint back to using INTERRUPT_NO_NEEDHAND"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3936.yml b/html/changelogs/AutoChangeLog-pr-3936.yml
new file mode 100644
index 000000000000..b8b29e5efc20
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3936.yml
@@ -0,0 +1,4 @@
+author: "Katskan"
+delete-after: True
+changes:
+ - bugfix: "Fixed synthetics getting an extra 45 points more than any previous who had used the gadget vendor"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3938.yml b/html/changelogs/AutoChangeLog-pr-3938.yml
deleted file mode 100644
index 30042f4a6a2f..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3938.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "realforest2001"
-delete-after: True
-changes:
- - bugfix: "Fixes PMC survivor synth spawning with marine comms, again."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3939.yml b/html/changelogs/AutoChangeLog-pr-3939.yml
new file mode 100644
index 000000000000..97bac6e085de
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3939.yml
@@ -0,0 +1,4 @@
+author: "Morrow"
+delete-after: True
+changes:
+ - rscadd: "Added lesser drones"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3940.yml b/html/changelogs/AutoChangeLog-pr-3940.yml
deleted file mode 100644
index 6561d2a12c0b..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3940.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Ben10083"
-delete-after: True
-changes:
- - rscadd: "Facehuggers now bypass time of death checks when being considered for larva."
- - code_imp: "new variable for observers to handle bypass of time of death checks"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3941.yml b/html/changelogs/AutoChangeLog-pr-3941.yml
deleted file mode 100644
index a9dd3647263f..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3941.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "realforest2001"
-delete-after: True
-changes:
- - bugfix: "Reduces yautja armor bullet resistance by 5, and puts that 5 extra onto the heavy armor."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3945.yml b/html/changelogs/AutoChangeLog-pr-3945.yml
new file mode 100644
index 000000000000..def51189c3d8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3945.yml
@@ -0,0 +1,6 @@
+author: "ihatethisengine"
+delete-after: True
+changes:
+ - balance: "early pod launch now has a 75% chance of crashing, launch after the timer has a 25% chance."
+ - rscadd: "CL's pod has a 25% chance of crashing on early launch and only 5% otherwise."
+ - rscadd: "xenos can slash and melt manually locked pod's doors."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3946.yml b/html/changelogs/AutoChangeLog-pr-3946.yml
deleted file mode 100644
index c0076176b75e..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3946.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Drathek"
-delete-after: True
-changes:
- - code_imp: "Removed extra CI testing for override maps"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3949.yml b/html/changelogs/AutoChangeLog-pr-3949.yml
new file mode 100644
index 000000000000..eca0cae6dc4a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3949.yml
@@ -0,0 +1,4 @@
+author: "Morrow"
+delete-after: True
+changes:
+ - bugfix: "Stops multiple facehuggers spawning from one client"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3964.yml b/html/changelogs/AutoChangeLog-pr-3964.yml
new file mode 100644
index 000000000000..ccd54d9eda5f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3964.yml
@@ -0,0 +1,4 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - bugfix: "Fix bodybags not accepting warm (recent) dead bodies even if unreviveable"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3969.yml b/html/changelogs/AutoChangeLog-pr-3969.yml
new file mode 100644
index 000000000000..b8aa0aaf31d2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3969.yml
@@ -0,0 +1,4 @@
+author: "Ben10083"
+delete-after: True
+changes:
+ - balance: "LVL-624 Robotics Dome operating table replaced with bioprinter."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3970.yml b/html/changelogs/AutoChangeLog-pr-3970.yml
new file mode 100644
index 000000000000..e4cd11253c33
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3970.yml
@@ -0,0 +1,5 @@
+author: "BeagleGaming1"
+delete-after: True
+changes:
+ - rscadd: "How long disarm stuns for depends on the difference in CQC skill between the participants"
+ - bugfix: "CQC properly affects disarm chance again"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3972.yml b/html/changelogs/AutoChangeLog-pr-3972.yml
new file mode 100644
index 000000000000..6afc923e23b7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3972.yml
@@ -0,0 +1,4 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - rscadd: "Added the debug verb Mass-Screenshot and a python script MapTileImageTool to combine those images into a single full image map."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3974.yml b/html/changelogs/AutoChangeLog-pr-3974.yml
new file mode 100644
index 000000000000..b74b0992d5f3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3974.yml
@@ -0,0 +1,4 @@
+author: "ihatethisengine"
+delete-after: True
+changes:
+ - bugfix: "fixed behavior_immobile flag using wrong number"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3975.yml b/html/changelogs/AutoChangeLog-pr-3975.yml
new file mode 100644
index 000000000000..6f871b20bbb6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3975.yml
@@ -0,0 +1,5 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - bugfix: "Fixed ability deactivation and late join current slot toggles not persisting."
+ - bugfix: "Toggle the Ability to Hurt Yourself now says On when you can hurt yourself."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3981.yml b/html/changelogs/AutoChangeLog-pr-3981.yml
new file mode 100644
index 000000000000..d68aca0cd5de
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3981.yml
@@ -0,0 +1,4 @@
+author: "realforest2001"
+delete-after: True
+changes:
+ - rscadd: "Allowed Falcon Drones to relay speech back to their operators."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3982.yml b/html/changelogs/AutoChangeLog-pr-3982.yml
new file mode 100644
index 000000000000..7c1740a90d24
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3982.yml
@@ -0,0 +1,4 @@
+author: "Zonespace27"
+delete-after: True
+changes:
+ - bugfix: "Welding goggles are no longer marked as \"mandatory\" in comtech vendors"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3986.yml b/html/changelogs/AutoChangeLog-pr-3986.yml
new file mode 100644
index 000000000000..73b88de3cf27
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3986.yml
@@ -0,0 +1,5 @@
+author: "Ben10083"
+delete-after: True
+changes:
+ - rscadd: "Infection gamemode announcement fixed and adjusts for each map, like Distress Signal"
+ - code_imp: "elements of distress signal post_setup code moved to parent to be called by all gamemodes"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3987.yml b/html/changelogs/AutoChangeLog-pr-3987.yml
new file mode 100644
index 000000000000..6b747e79133d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3987.yml
@@ -0,0 +1,4 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - bugfix: "Fixed xeno hide ability not checking for busy status."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3988.yml b/html/changelogs/AutoChangeLog-pr-3988.yml
new file mode 100644
index 000000000000..36388809d9a9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3988.yml
@@ -0,0 +1,5 @@
+author: "Ben10083"
+delete-after: True
+changes:
+ - rscadd: "You can now petition High Command via ARES for a nuclear device, bypassing techweb (If you can convince them, that is.)"
+ - admin: "Implemented Admin side of ARES Nuke request to allow for granting of different nuke types, or denying the request."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3991.yml b/html/changelogs/AutoChangeLog-pr-3991.yml
new file mode 100644
index 000000000000..7c60e24982c2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3991.yml
@@ -0,0 +1,4 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - bugfix: "Fix huggers not retaining at least their old death value."
\ No newline at end of file
diff --git a/html/changelogs/archive/2023-07.yml b/html/changelogs/archive/2023-07.yml
index 0edee30b9f8e..99120b3a13c4 100644
--- a/html/changelogs/archive/2023-07.yml
+++ b/html/changelogs/archive/2023-07.yml
@@ -327,3 +327,85 @@
silencer_pl:
- qol: Default paper and desc_lore viewing windows are now larger to match the expected
content in them better.
+2023-07-21:
+ Awantje:
+ - code_imp: Medicomp surgery tools no longer care about what zone you target.
+ Ben10083:
+ - rscadd: Facehuggers now bypass time of death checks when being considered for
+ larva.
+ - code_imp: new variable for observers to handle bypass of time of death checks
+ Drathek:
+ - code_imp: Removed extra CI testing for override maps
+ Morrow:
+ - bugfix: Colonial Marshal ERT now uses their own faction
+ - bugfix: Anchorpoint marines now use the proper define for USCM
+ - rscadd: Added sounds for inserting and removing knifes to the knife webbing
+ - rscadd: Added draw delay for knives to the knife webbing
+ - rscadd: Allowed using quickdraw with the knife webbing
+ - rscadd: Allowed knife webbing to be alt-clicked to draw from it
+ - rscadd: Knife webbing is now spawned full
+ - code_imp: Standardized knife draw delay in a define
+ - balance: Modifies INTERRUPT_NO_NEEDHAND flag to require you hold the item at least
+ in one hand.
+ - balance: Changes splint back to using INTERRUPT_NO_NEEDHAND
+ - balance: Removes large pouches as buyable from all squad roles other than SL
+ - balance: Removes medkit, medical (the base ones, not first aid), and syringe pouches
+ from squad prep room vendors
+ - balance: Replaces the EZ-injector first aid pouch with the alternate tricord/bandage/splint/ointment
+ version in the squad prep room vendors
+ QuickLode:
+ - bugfix: limits the Survivor CMB Synthetic comms
+ harryob:
+ - admin: you can no longer touch bad vars
+ realforest2001:
+ - bugfix: Fixes PMC survivor synth spawning with marine comms, again.
+ - bugfix: Reduces yautja armor bullet resistance by 5, and puts that 5 extra onto
+ the heavy armor.
+ - rscadd: Added donator kit boxes that hold a donator's special gear. They can be
+ destroyed with a right-click while on the ground, and are locked to the donator.
+ - code_imp: Tidied up typepaths for many donator items to make it clearer what or
+ who they are for.
+ - code_imp: Removed the requirement for donators to match their character name to
+ our config file.
+2023-07-22:
+ Ben10083:
+ - qol: Ghosts now informed when a non-nested host is about to chestburst
+ ClairionCM:
+ - rscadd: Chem ERT objectives now mention that they cant deploy without explicit
+ permission.
+ Drathek:
+ - bugfix: 'Fixed the prompt when nested to ghost: Now uses a TGUI prompt and sets
+ larva queue time.'
+ Morrow:
+ - balance: Decreased blood bag IV insertion time from 3 to 1 second
+ Waseemq1235:
+ - code_imp: Adds unrestricted_deployment var to tents. Bypasses groundside checks.
+2023-07-23:
+ Anuv:
+ - rscadd: Added a headset to Researchers with the intel channel included
+ Ben10083:
+ - maptweak: AI Core APC now uses a hyper capacity cell
+ Drathek:
+ - bugfix: Fix xeno tacmap staying open when it should be unavailable
+ - code_imp: Bump rust_g to 2.1.0 and bump all github testing ubuntu versions to
+ latest.
+ Khadd:
+ - bugfix: blowtorch no longer loses fuel on clicks that produce no action
+ - code_imp: replaced 1-letter vars
+ QuickLode:
+ - bugfix: Surgical graft now fits in surgical trays and vests.
+ blackdragonTOW:
+ - rscadd: 'Added new CAS Missile: BLU-200 Dragons Breath'
+ ihatethisengine:
+ - bugfix: vendors icon_state resets after vending as intended.
+ nauticall:
+ - imageadd: Resprited blood bags to look nicer and use proper a proper overlay/underlay
+ system. Their types are also now distinguishable at a glance.
+ - code_imp: Reworked the way blood bag sprites work behind the scenes to use the
+ overlay/underlay system.
+2023-07-24:
+ JackieEstegado:
+ - maptweak: Made LV-624's "Fully-locked" Research nightmare insert no longer have
+ indestructible blast doors.
+ Morrow:
+ - bugfix: Fixes wood window numbering
diff --git a/icons/mob/hud/actions.dmi b/icons/mob/hud/actions.dmi
index 4d0697733207..9f885c44f50f 100644
Binary files a/icons/mob/hud/actions.dmi and b/icons/mob/hud/actions.dmi differ
diff --git a/icons/mob/hud/human_old.dmi b/icons/mob/hud/human_old.dmi
index 194206d4de16..e8b682c7c831 100644
Binary files a/icons/mob/hud/human_old.dmi and b/icons/mob/hud/human_old.dmi differ
diff --git a/icons/mob/humans/onmob/items_lefthand_0.dmi b/icons/mob/humans/onmob/items_lefthand_0.dmi
index 676920e0bac0..1f154ef92396 100644
Binary files a/icons/mob/humans/onmob/items_lefthand_0.dmi and b/icons/mob/humans/onmob/items_lefthand_0.dmi differ
diff --git a/icons/mob/humans/onmob/items_lefthand_1.dmi b/icons/mob/humans/onmob/items_lefthand_1.dmi
index b149c4ea872b..d65bcccc02af 100644
Binary files a/icons/mob/humans/onmob/items_lefthand_1.dmi and b/icons/mob/humans/onmob/items_lefthand_1.dmi differ
diff --git a/icons/mob/humans/onmob/items_righthand_0.dmi b/icons/mob/humans/onmob/items_righthand_0.dmi
index a380e5eb73f4..858da88c29ed 100644
Binary files a/icons/mob/humans/onmob/items_righthand_0.dmi and b/icons/mob/humans/onmob/items_righthand_0.dmi differ
diff --git a/icons/mob/humans/onmob/items_righthand_1.dmi b/icons/mob/humans/onmob/items_righthand_1.dmi
index 70fa3a73fbc5..f9b5350d6a7b 100644
Binary files a/icons/mob/humans/onmob/items_righthand_1.dmi and b/icons/mob/humans/onmob/items_righthand_1.dmi differ
diff --git a/icons/mob/xenonids/lesser_drone.dmi b/icons/mob/xenonids/lesser_drone.dmi
new file mode 100644
index 000000000000..bff44e659162
Binary files /dev/null and b/icons/mob/xenonids/lesser_drone.dmi differ
diff --git a/icons/mob/xenos/carrier.dmi b/icons/mob/xenos/carrier.dmi
index 6832bbba8716..4b69cffd5313 100644
Binary files a/icons/mob/xenos/carrier.dmi and b/icons/mob/xenos/carrier.dmi differ
diff --git a/icons/mob/xenos/lesser_drone.dmi b/icons/mob/xenos/lesser_drone.dmi
new file mode 100644
index 000000000000..134ec0c4ae7c
Binary files /dev/null and b/icons/mob/xenos/lesser_drone.dmi differ
diff --git a/icons/obj/items/assemblies.dmi b/icons/obj/items/assemblies.dmi
index 668d62d23d4e..522e0fb5e55d 100644
Binary files a/icons/obj/items/assemblies.dmi and b/icons/obj/items/assemblies.dmi differ
diff --git a/icons/obj/items/bloodpack.dmi b/icons/obj/items/bloodpack.dmi
index b46b6eb638c0..d2bee4892dcf 100644
Binary files a/icons/obj/items/bloodpack.dmi and b/icons/obj/items/bloodpack.dmi differ
diff --git a/icons/obj/items/food.dmi b/icons/obj/items/food.dmi
index 6e2c86aa4999..3c2e963e3804 100644
Binary files a/icons/obj/items/food.dmi and b/icons/obj/items/food.dmi differ
diff --git a/icons/obj/items/reagentfillings.dmi b/icons/obj/items/reagentfillings.dmi
index 3c6420fa8839..1514db495e13 100644
Binary files a/icons/obj/items/reagentfillings.dmi and b/icons/obj/items/reagentfillings.dmi differ
diff --git a/icons/obj/structures/props/rocks.dmi b/icons/obj/structures/props/rocks.dmi
index e494bdcba2f8..71ea709c771f 100644
Binary files a/icons/obj/structures/props/rocks.dmi and b/icons/obj/structures/props/rocks.dmi differ
diff --git a/icons/rebase_icons.dmi b/icons/rebase_icons.dmi
index 2d394cef793d..6b0a25a1f7e6 100644
Binary files a/icons/rebase_icons.dmi and b/icons/rebase_icons.dmi differ
diff --git a/icons/turf/floors/desert_dirt.dmi b/icons/turf/floors/desert_dirt.dmi
index e153917e708a..ed6c57cc1809 100644
Binary files a/icons/turf/floors/desert_dirt.dmi and b/icons/turf/floors/desert_dirt.dmi differ
diff --git a/icons/turf/floors/desert_rock.dmi b/icons/turf/floors/desert_rock.dmi
index 8cbd01e3cec1..79a33d83257f 100644
Binary files a/icons/turf/floors/desert_rock.dmi and b/icons/turf/floors/desert_rock.dmi differ
diff --git a/icons/turf/walls/windows.dmi b/icons/turf/walls/windows.dmi
index 85f822873e68..a3f2fd1d4198 100644
Binary files a/icons/turf/walls/windows.dmi and b/icons/turf/walls/windows.dmi differ
diff --git a/icons/ui_icons/map_blips.dmi b/icons/ui_icons/map_blips.dmi
index 85ef9959027f..829d9b8a43b0 100644
Binary files a/icons/ui_icons/map_blips.dmi and b/icons/ui_icons/map_blips.dmi differ
diff --git a/librust_g.so b/librust_g.so
index 49a86aad89b7..56625573d004 100644
Binary files a/librust_g.so and b/librust_g.so differ
diff --git a/maps/map_files/LV624/LV624.dmm b/maps/map_files/LV624/LV624.dmm
index b84ed33ef0e4..a8aac22505c8 100644
--- a/maps/map_files/LV624/LV624.dmm
+++ b/maps/map_files/LV624/LV624.dmm
@@ -4877,7 +4877,9 @@
/turf/open/gm/coast/beachcorner/north_west,
/area/lv624/ground/jungle/west_jungle)
"ayj" = (
-/obj/structure/machinery/optable,
+/obj/structure/machinery/bioprinter{
+ stored_metal = 1000
+ },
/obj/structure/machinery/light/small{
dir = 4
},
diff --git a/maps/map_files/LV624/science/40.fullylocked.dmm b/maps/map_files/LV624/science/40.fullylocked.dmm
index 63a5d17558f3..3ba311757b73 100644
--- a/maps/map_files/LV624/science/40.fullylocked.dmm
+++ b/maps/map_files/LV624/science/40.fullylocked.dmm
@@ -7,7 +7,7 @@
/area/lv624/lazarus/research)
"ac" = (
/obj/structure/window/framed/colony/reinforced,
-/obj/structure/machinery/door/poddoor/almayer/locked{
+/obj/structure/machinery/door/poddoor/almayer/planet_side_blastdoor{
id = "science_blast";
layer = 3.3;
name = "\improper Science Wing Blast Door"
@@ -15,7 +15,7 @@
/turf/open/floor/plating,
/area/lv624/lazarus/research)
"ad" = (
-/obj/structure/machinery/door/poddoor/almayer/locked{
+/obj/structure/machinery/door/poddoor/almayer/planet_side_blastdoor{
id = "science_blast";
layer = 3.3;
name = "\improper Science Wing Blast Door"
@@ -41,7 +41,7 @@
/area/lv624/ground/jungle/north_west_jungle)
"ai" = (
/obj/structure/window_frame/colony/reinforced,
-/obj/structure/machinery/door/poddoor/almayer/locked{
+/obj/structure/machinery/door/poddoor/almayer/planet_side_blastdoor{
id = "science_blast";
layer = 3.3;
name = "\improper Science Wing Blast Door"
@@ -73,7 +73,7 @@
/area/lv624/ground/colony/south_medbay_road)
"ao" = (
/obj/structure/window/framed/colony/reinforced,
-/obj/structure/machinery/door/poddoor/almayer/locked{
+/obj/structure/machinery/door/poddoor/almayer/planet_side_blastdoor{
dir = 4;
id = "science_blast";
layer = 3.3;
@@ -218,7 +218,7 @@
},
/area/lv624/lazarus/research)
"aI" = (
-/obj/structure/machinery/door/poddoor/almayer/locked{
+/obj/structure/machinery/door/poddoor/almayer/planet_side_blastdoor{
dir = 4;
id = "science_blast";
layer = 3.3;
@@ -336,7 +336,7 @@
},
/area/lv624/lazarus/research)
"aU" = (
-/obj/structure/machinery/door/poddoor/almayer/locked{
+/obj/structure/machinery/door/poddoor/almayer/planet_side_blastdoor{
dir = 4;
id = "science_blast";
layer = 3.3;
diff --git a/maps/map_files/USS_Almayer/USS_Almayer.dmm b/maps/map_files/USS_Almayer/USS_Almayer.dmm
index 8ccc43dbc64d..80262ec61937 100644
--- a/maps/map_files/USS_Almayer/USS_Almayer.dmm
+++ b/maps/map_files/USS_Almayer/USS_Almayer.dmm
@@ -5938,6 +5938,7 @@
/obj/item/device/radio/marine,
/obj/item/device/radio/marine,
/obj/item/device/radio/marine,
+/obj/item/folded_tent/cmd,
/turf/open/floor/almayer{
icon_state = "redfull"
},
@@ -15896,6 +15897,10 @@
desc = "Someone has crossed out the Space from Space Cleaner and written in Surgery. 'Do not remove under punishment of death!!!' is scrawled on the back.";
name = "Surgery Cleaner"
},
+/obj/item/folded_tent/med{
+ pixel_x = -8;
+ pixel_y = 14
+ },
/turf/open/floor/almayer{
dir = 8;
icon_state = "sterile_green_corner"
@@ -29660,11 +29665,6 @@
pixel_x = 9
},
/obj/item/reagent_container/pill/happy,
-/obj/item/stack/tile/plasteel{
- layer = 2.5;
- pixel_x = -8;
- pixel_y = 4
- },
/obj/item/clothing/glasses/disco_fever{
pixel_x = -3;
pixel_y = -2
@@ -37958,18 +37958,6 @@
icon_state = "plate"
},
/area/almayer/hallways/vehiclehangar)
-"gCu" = (
-/obj/structure/disposalpipe/segment{
- dir = 8
- },
-/obj/structure/pipes/standard/simple/hidden/supply{
- dir = 4
- },
-/obj/item/folded_tent/cmd,
-/turf/open/floor/almayer{
- icon_state = "plating_striped"
- },
-/area/almayer/squads/req)
"gCw" = (
/obj/item/reagent_container/food/drinks/cans/beer{
pixel_x = 10
@@ -40122,18 +40110,6 @@
/obj/structure/pipes/standard/manifold/hidden/supply,
/turf/open/floor/plating/plating_catwalk,
/area/almayer/hallways/hangar)
-"hBP" = (
-/obj/structure/disposalpipe/segment{
- dir = 8
- },
-/obj/structure/pipes/standard/simple/hidden/supply{
- dir = 4
- },
-/obj/item/folded_tent/med,
-/turf/open/floor/almayer{
- icon_state = "plating_striped"
- },
-/area/almayer/squads/req)
"hBU" = (
/obj/structure/largecrate/random/secure,
/obj/effect/decal/warning_stripes{
@@ -42135,8 +42111,9 @@
/area/almayer/squads/delta)
"ixN" = (
/obj/structure/largecrate,
-/obj/item/prop/almayer/handheld1{
- pixel_y = 12
+/obj/item/folded_tent/reqs{
+ pixel_x = -3;
+ pixel_y = 10
},
/turf/open/floor/almayer{
icon_state = "test_floor4"
@@ -57166,18 +57143,6 @@
icon_state = "green"
},
/area/almayer/hallways/aft_hallway)
-"pvF" = (
-/obj/structure/disposalpipe/segment{
- dir = 8
- },
-/obj/structure/pipes/standard/simple/hidden/supply{
- dir = 4
- },
-/obj/item/folded_tent/reqs,
-/turf/open/floor/almayer{
- icon_state = "plating_striped"
- },
-/area/almayer/squads/req)
"pvJ" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -64804,7 +64769,8 @@
/area/almayer/living/briefing)
"sTV" = (
/obj/structure/machinery/power/apc/almayer/hardened{
- dir = 1
+ dir = 1;
+ cell_type = /obj/item/cell/hyper
},
/turf/open/floor/plating,
/area/almayer/command/airoom)
@@ -71714,18 +71680,6 @@
icon_state = "plate"
},
/area/almayer/living/grunt_rnr)
-"vSr" = (
-/obj/structure/disposalpipe/segment{
- dir = 8
- },
-/obj/structure/pipes/standard/simple/hidden/supply{
- dir = 4
- },
-/obj/item/folded_tent/big,
-/turf/open/floor/almayer{
- icon_state = "plating_striped"
- },
-/area/almayer/squads/req)
"vSE" = (
/obj/structure/closet/secure_closet/personal,
/turf/open/floor/almayer{
@@ -74845,12 +74799,17 @@
desc = "A supply crate containing everything you need to stop a CLF uprising.";
name = "\improper USCM crate 'FOB supplies'"
},
-/obj/item/storage/box/mousetraps{
- pixel_y = 12
- },
/obj/structure/sign/arcturianstopsign{
pixel_y = 32
},
+/obj/item/folded_tent/big{
+ pixel_y = 10;
+ pixel_x = -6
+ },
+/obj/item/storage/box/mousetraps{
+ pixel_y = 12;
+ pixel_x = 3
+ },
/turf/open/floor/almayer{
icon_state = "plate"
},
@@ -119396,7 +119355,7 @@ bEi
bZr
bmD
ngw
-gCu
+pjG
boz
bpR
bpR
@@ -119599,7 +119558,7 @@ brp
bZr
bmD
xId
-hBP
+pjG
boz
bpR
bpR
@@ -120005,7 +119964,7 @@ buz
bZr
bmD
dmE
-pvF
+pjG
boz
bpR
bpR
@@ -120208,7 +120167,7 @@ bEm
bZr
bmD
hAc
-vSr
+pjG
boz
bpR
bpR
diff --git a/maps/predship/huntership.dmm b/maps/predship/huntership.dmm
index 6a129b6f8cfe..5680271c4df2 100644
--- a/maps/predship/huntership.dmm
+++ b/maps/predship/huntership.dmm
@@ -195,7 +195,7 @@
},
/area/yautja)
"ar" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
dir = 1;
name = "\improper Wargear Storage"
},
@@ -407,7 +407,7 @@
},
/area/yautja)
"aP" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
dir = 1;
name = "\improper Trophy Room"
},
@@ -427,7 +427,7 @@
},
/area/yautja)
"aT" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
name = "\improper Operation Room"
},
/turf/open/shuttle/predship,
@@ -486,6 +486,10 @@
/obj/structure/pipes/standard/simple/hidden{
dir = 4
},
+/obj/structure/machinery/computer/crew/alt{
+ faction = "Yautja";
+ pixel_y = 24
+ },
/turf/open/floor/corsat{
dir = 1;
icon_state = "squareswood"
@@ -505,7 +509,7 @@
/turf/open/floor/light,
/area/yautja)
"be" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
name = "\improper Blooded Teleporter"
},
/turf/open/floor/strata{
@@ -531,13 +535,13 @@
/turf/closed/wall/huntership,
/area/yautja)
"bl" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
name = "\improper Starboard Wing"
},
/turf/open/shuttle/predship,
/area/yautja)
"bm" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
name = "\improper Port Wing"
},
/turf/open/shuttle/predship,
@@ -566,7 +570,7 @@
},
/area/yautja)
"bp" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
dir = 1;
name = "\improper Research Chamber"
},
@@ -600,7 +604,7 @@
},
/area/yautja)
"bu" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
dir = 1;
name = "\improper Burial Room"
},
@@ -618,7 +622,7 @@
/turf/open/shuttle/predship,
/area/yautja)
"bx" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
dir = 1;
name = "\improper Storage Chamber"
},
@@ -680,10 +684,11 @@
},
/area/yautja)
"bD" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
dir = 1;
- name = "\improper Unworthy Prey"
+ name = "\improper Heavy Armory"
},
+/obj/structure/machinery/door/poddoor/shutters/almayer/yautja,
/turf/open/shuttle/predship,
/area/yautja)
"bE" = (
@@ -697,7 +702,7 @@
},
/area/yautja)
"bG" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
name = "\improper Cooler Room"
},
/turf/open/shuttle/predship,
@@ -749,13 +754,9 @@
/turf/open/shuttle/predship,
/area/yautja)
"bN" = (
-/obj/structure/kitchenspike{
- icon_state = "spikebloodygreen"
- },
-/obj/effect/decal/cleanable/blood/gibs/xeno/body,
-/turf/open/floor/strata{
- color = "#5e5d5d";
- icon_state = "multi_tiles"
+/turf/closed/wall/cult{
+ hull = 1;
+ name = "runed hull"
},
/area/yautja)
"bO" = (
@@ -781,16 +782,6 @@
icon_state = "squareswood"
},
/area/yautja)
-"bR" = (
-/obj/structure/kitchenspike{
- icon_state = "spikebloody"
- },
-/obj/effect/decal/cleanable/blood/gibs,
-/turf/open/floor/strata{
- color = "#5e5d5d";
- icon_state = "multi_tiles"
- },
-/area/yautja)
"bS" = (
/obj/structure/pipes/standard/simple/hidden{
dir = 9
@@ -802,7 +793,8 @@
id = "Cell Lockdown 1";
name = "Cell Lockdown 1";
pixel_x = -25;
- pixel_y = 9
+ pixel_y = 9;
+ req_one_access_txt = "250;251;252"
},
/turf/open/shuttle/predship,
/area/yautja)
@@ -811,12 +803,13 @@
id = "Cell Lockdown 4";
name = "Cell Lockdown 4";
pixel_x = -7;
- pixel_y = 9
+ pixel_y = 9;
+ req_one_access_txt = "250;251;252"
},
/turf/closed/wall/huntership,
/area/yautja)
"bW" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Prisoner Cell 1";
opacity = 0
},
@@ -828,7 +821,7 @@
/turf/open/shuttle/predship,
/area/yautja)
"bX" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Prisoner Cell 4";
opacity = 0
},
@@ -865,7 +858,8 @@
id = "Cell Lockdown 2";
name = "Cell Lockdown 2";
pixel_x = -25;
- pixel_y = 9
+ pixel_y = 9;
+ req_one_access_txt = "250;251;252"
},
/turf/open/shuttle/predship,
/area/yautja)
@@ -889,7 +883,7 @@
},
/area/yautja)
"ce" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Prisoner Cell 2";
opacity = 0
},
@@ -901,7 +895,7 @@
/turf/open/shuttle/predship,
/area/yautja)
"cf" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Prisoner Cell 5";
opacity = 0
},
@@ -917,7 +911,8 @@
id = "Cell Lockdown 3";
name = "Cell Lockdown 3";
pixel_x = -25;
- pixel_y = 9
+ pixel_y = 9;
+ req_one_access_txt = "250;251;252"
},
/turf/open/shuttle/predship,
/area/yautja)
@@ -926,7 +921,8 @@
id = "Cell Lockdown 6";
name = "Cell Lockdown 6";
pixel_x = -7;
- pixel_y = 9
+ pixel_y = 9;
+ req_one_access_txt = "250;251;252"
},
/turf/closed/wall/huntership,
/area/yautja)
@@ -948,7 +944,7 @@
},
/area/yautja)
"cm" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Prisoner Cell 3";
opacity = 0
},
@@ -960,7 +956,7 @@
/turf/open/shuttle/predship,
/area/yautja)
"cn" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Prisoner Cell 6";
opacity = 0
},
@@ -1002,7 +998,7 @@
/turf/open/shuttle/predship,
/area/yautja)
"ct" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
dir = 1;
name = "\improper Fore Rooms"
},
@@ -1106,7 +1102,7 @@
},
/area/yautja)
"cA" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
name = "\improper Elder Teleporter"
},
/turf/open/floor/corsat{
@@ -1115,9 +1111,9 @@
},
/area/yautja)
"cB" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
dir = 1;
- name = "\improper Navigation Chamber"
+ name = "\improper Control Center"
},
/turf/open/shuttle/predship,
/area/yautja)
@@ -1136,7 +1132,7 @@
},
/area/yautja)
"cF" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
dir = 1;
name = "\improper Engine Room"
},
@@ -1499,7 +1495,7 @@
},
/area/yautja)
"dc" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
dir = 1;
name = "\improper Research Containment"
},
@@ -1591,7 +1587,8 @@
id = "Cell Lockdown 5";
name = "Cell Lockdown 5";
pixel_x = -7;
- pixel_y = 9
+ pixel_y = 9;
+ req_one_access_txt = "250;251;252"
},
/turf/closed/wall/huntership,
/area/yautja)
@@ -1669,6 +1666,24 @@
/obj/structure/machinery/cryopod,
/turf/open/shuttle/predship,
/area/yautja)
+"fo" = (
+/obj/structure/surface/table/reinforced/prison{
+ color = "#6b675e"
+ },
+/obj/structure/window/reinforced{
+ dir = 4;
+ health = 80
+ },
+/obj/structure/window/reinforced{
+ dir = 8;
+ health = 80
+ },
+/obj/item/storage/box/bracer,
+/turf/open/floor/corsat{
+ dir = 1;
+ icon_state = "squareswood"
+ },
+/area/yautja)
"fq" = (
/obj/structure/window/framed/colony/reinforced/hull{
color = "#aba9a9"
@@ -1727,19 +1742,39 @@
},
/area/yautja)
"gb" = (
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
-/obj/item/reagent_container/blood/OMinus,
/obj/structure/closet/crate{
color = "#6b675e"
},
+/obj/item/stack/medical/advanced/bruise_pack/predator{
+ pixel_x = 6
+ },
+/obj/item/stack/medical/advanced/bruise_pack/predator{
+ pixel_x = 6
+ },
+/obj/item/stack/medical/advanced/bruise_pack/predator{
+ pixel_x = 6
+ },
+/obj/item/stack/medical/advanced/bruise_pack/predator{
+ pixel_x = 6
+ },
+/obj/item/stack/medical/advanced/bruise_pack/predator{
+ pixel_x = 6
+ },
+/obj/item/stack/medical/advanced/ointment/predator{
+ pixel_x = -6
+ },
+/obj/item/stack/medical/advanced/ointment/predator{
+ pixel_x = -6
+ },
+/obj/item/stack/medical/advanced/ointment/predator{
+ pixel_x = -6
+ },
+/obj/item/stack/medical/advanced/ointment/predator{
+ pixel_x = -6
+ },
+/obj/item/stack/medical/advanced/ointment/predator{
+ pixel_x = -6
+ },
/turf/open/floor/strata{
color = "#5e5d5d";
icon_state = "multi_tiles"
@@ -1753,30 +1788,31 @@
},
/area/yautja)
"gr" = (
-/obj/structure/surface/table/reinforced/prison{
- color = "#6b675e"
- },
-/obj/item/weapon/unathiknife,
-/obj/item/weapon/unathiknife{
- attack_speed = 12;
- force = 25
+/obj/structure/closet/crate/secure{
+ req_one_access_txt = "252";
+ color = "#6b675e";
+ name = "Secure Yautja crate"
},
+/obj/item/explosive/grenade/spawnergrenade/hellhound,
+/obj/item/explosive/grenade/spawnergrenade/hellhound,
+/obj/item/explosive/grenade/spawnergrenade/hellhound,
+/obj/item/explosive/grenade/spawnergrenade/hellhound,
/turf/open/floor/strata{
color = "#5e5d5d";
icon_state = "multi_tiles"
},
/area/yautja)
"gG" = (
-/obj/structure/surface/table/reinforced/prison{
- color = "#6b675e"
+/obj/structure/machinery/door_control{
+ id = "Yautja Armory";
+ name = "Armory Shutters";
+ pixel_x = 24;
+ req_one_access_txt = "252";
+ needs_power = 0
},
-/obj/item/tool/surgery/FixOVein/predatorFixOVein,
-/obj/item/tool/surgery/bonegel/predatorbonegel,
-/obj/item/tool/surgery/bonesetter/predatorbonesetter,
-/obj/item/tool/surgery/surgicaldrill/predatorsurgicaldrill,
-/turf/open/floor/strata{
- color = "#5e5d5d";
- icon_state = "multi_tiles"
+/turf/open/floor/corsat{
+ dir = 1;
+ icon_state = "squareswood"
},
/area/yautja)
"ha" = (
@@ -1931,13 +1967,6 @@
},
/area/yautja)
"nh" = (
-/obj/item/storage/box/bracer{
- pixel_y = 16
- },
-/obj/item/storage/box/bracer{
- pixel_y = 8
- },
-/obj/item/storage/box/bracer,
/obj/structure/surface/rack{
color = "#6b675e";
layer = 2.79
@@ -2123,7 +2152,7 @@
},
/area/yautja)
"sV" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
dir = 1;
name = "\improper Feed Hall"
},
@@ -2180,6 +2209,24 @@
icon_state = "squareswood"
},
/area/yautja)
+"tR" = (
+/obj/structure/surface/table/reinforced/prison{
+ color = "#6b675e"
+ },
+/obj/structure/window/reinforced{
+ dir = 8;
+ health = 80
+ },
+/obj/structure/window/reinforced{
+ dir = 4;
+ health = 80
+ },
+/obj/item/storage/box/bracer,
+/turf/open/floor/corsat{
+ dir = 1;
+ icon_state = "squareswood"
+ },
+/area/yautja)
"uf" = (
/obj/structure/barricade/handrail/strata{
dir = 8
@@ -2375,27 +2422,36 @@
/obj/structure/machinery/cryopod/right,
/turf/open/shuttle/predship,
/area/yautja)
-"Br" = (
-/turf/open/gm/dirtgrassborder{
- icon_state = "desert2"
+"Bg" = (
+/obj/structure/closet/crate/secure{
+ req_one_access_txt = "252";
+ color = "#6b675e";
+ name = "Secure Yautja crate"
},
-/area/yautja)
-"BB" = (
-/obj/structure/machinery/iv_drip,
+/obj/item/weapon/yautja/combistick,
+/obj/item/weapon/yautja/combistick{
+ pixel_x = -4;
+ pixel_y = 4
+ },
+/obj/item/weapon/twohanded/yautja/glaive/alt,
+/obj/item/weapon/yautja/chain,
+/obj/item/weapon/yautja/sword,
+/obj/item/weapon/yautja/scythe,
/turf/open/floor/strata{
color = "#5e5d5d";
icon_state = "multi_tiles"
},
/area/yautja)
+"Br" = (
+/turf/open/gm/dirtgrassborder{
+ icon_state = "desert2"
+ },
+/area/yautja)
"BK" = (
/obj/structure/surface/table/reinforced/prison{
color = "#6b675e"
},
-/obj/item/tool/surgery/cautery/predatorcautery,
-/obj/item/tool/surgery/circular_saw/predatorbonesaw,
-/obj/item/tool/surgery/hemostat/predatorhemostat,
-/obj/item/tool/surgery/retractor/predatorretractor,
-/obj/item/tool/surgery/scalpel/predatorscalpel,
+/obj/item/storage/medicomp/full,
/turf/open/floor/strata{
color = "#5e5d5d";
icon_state = "multi_tiles"
@@ -2460,7 +2516,7 @@
},
/area/yautja)
"Dr" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
indestructible = 1;
locked = 1;
name = "\improper External Airlock"
@@ -2488,7 +2544,13 @@
},
/area/yautja)
"DJ" = (
-/obj/structure/machinery/computer/crew,
+/obj/structure/machinery/computer/crew/alt{
+ dir = 1;
+ name = "human monitoring computer"
+ },
+/obj/structure/surface/table/reinforced/prison{
+ color = "#6b675e"
+ },
/turf/open/floor/strata{
color = "#5e5d5d";
icon_state = "multi_tiles"
@@ -2528,13 +2590,12 @@
/obj/structure/surface/table/reinforced/prison{
color = "#6b675e"
},
-/obj/item/bedsheet{
- anchored = 1;
+/obj/structure/showcase{
desc = "A console used by the Hunters for navigation purposes.";
dir = 8;
icon = 'icons/obj/structures/machinery/computer.dmi';
icon_state = "security_cam";
- name = "Hunter Nav Console"
+ name = "Radar Console"
},
/turf/open/floor/strata{
color = "#5e5d5d";
@@ -2555,7 +2616,7 @@
/turf/open/shuttle/predship,
/area/yautja)
"Gd" = (
-/obj/structure/machinery/door/airlock/strata/autoname,
+/obj/structure/machinery/door/airlock/yautja/secure,
/turf/open/shuttle/predship,
/area/yautja)
"Gk" = (
@@ -2624,18 +2685,16 @@
color = "#6b675e";
layer = 2.79
},
-/obj/item/stack/medical/advanced/ointment/predator,
-/obj/item/stack/medical/advanced/ointment/predator,
-/obj/item/stack/medical/advanced/ointment/predator,
-/obj/item/stack/medical/advanced/ointment/predator,
-/obj/item/stack/medical/advanced/ointment/predator,
-/obj/item/stack/medical/advanced/ointment/predator,
-/obj/item/stack/medical/advanced/bruise_pack/predator,
-/obj/item/stack/medical/advanced/bruise_pack/predator,
-/obj/item/stack/medical/advanced/bruise_pack/predator,
-/obj/item/stack/medical/advanced/bruise_pack/predator,
-/obj/item/stack/medical/advanced/bruise_pack/predator,
-/obj/item/stack/medical/advanced/bruise_pack/predator,
+/obj/item/stack/medical/advanced/ointment/predator{
+ pixel_x = 6;
+ desc = "A poultice made of cold, blue petals that is rubbed on burns. Not to be removed from the ship.";
+ name = "arena soothing herbs"
+ },
+/obj/item/stack/medical/advanced/bruise_pack/predator{
+ pixel_x = -6;
+ desc = "A poultice made of soft leaves that is rubbed on bruises. Not to be removed from the ship.";
+ name = "arena mending herbs"
+ },
/turf/open/shuttle/predship,
/area/yautja)
"Hp" = (
@@ -2659,6 +2718,25 @@
icon_state = "squareswood"
},
/area/yautja)
+"HD" = (
+/obj/structure/surface/rack{
+ color = "#6b675e";
+ layer = 2.79;
+ pixel_y = 24;
+ density = 0
+ },
+/obj/item/weapon/gun/energy/yautja/plasmarifle{
+ pixel_y = -8
+ },
+/obj/item/weapon/gun/energy/yautja/plasmarifle,
+/obj/item/weapon/gun/energy/yautja/plasmarifle{
+ pixel_y = 8
+ },
+/turf/open/floor/strata{
+ color = "#5e5d5d";
+ icon_state = "multi_tiles"
+ },
+/area/yautja)
"HN" = (
/obj/structure/surface/table/reinforced/prison{
color = "#6b675e"
@@ -2674,8 +2752,17 @@
},
/area/yautja)
"If" = (
-/obj/structure/machinery/optable,
-/obj/effect/decal/cleanable/blood,
+/obj/structure/surface/rack{
+ color = "#6b675e";
+ layer = 2.79
+ },
+/obj/item/weapon/gun/energy/yautja/plasmapistol{
+ pixel_y = -8
+ },
+/obj/item/weapon/gun/energy/yautja/plasmapistol,
+/obj/item/weapon/gun/energy/yautja/plasmapistol{
+ pixel_y = 8
+ },
/turf/open/floor/strata{
color = "#5e5d5d";
icon_state = "multi_tiles"
@@ -2721,7 +2808,7 @@
},
/area/yautja)
"Iw" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Hellhound Quarters"
},
/turf/open/floor/corsat{
@@ -2730,7 +2817,7 @@
},
/area/yautja)
"IC" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure/elder{
name = "\improper Elder Quarters"
},
/turf/open/shuttle/predship,
@@ -2876,7 +2963,7 @@
},
/area/yautja)
"MI" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
name = "\improper Blooded Teleporter"
},
/turf/open/floor/strata{
@@ -2930,7 +3017,7 @@
},
/area/yautja)
"Pr" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja/secure{
name = "\improper Research Containment"
},
/turf/open/floor/strata{
@@ -3074,13 +3161,12 @@
/obj/structure/surface/table/reinforced/prison{
color = "#6b675e"
},
-/obj/item/bedsheet{
- anchored = 1;
+/obj/structure/showcase{
desc = "A console used by the Hunters for navigation purposes.";
dir = 4;
icon = 'icons/obj/structures/machinery/computer.dmi';
icon_state = "security_cam";
- name = "Hunter Nav Console"
+ name = "Radar Console"
},
/turf/open/floor/strata{
color = "#5e5d5d";
@@ -3110,7 +3196,7 @@
/turf/open/shuttle/predship,
/area/yautja)
"Wg" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
dir = 1;
name = "\improper Gladiator Hall"
},
@@ -3198,7 +3284,7 @@
},
/area/yautja)
"YL" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
+/obj/structure/machinery/door/airlock/yautja{
dir = 1;
name = "\improper Cryo Room"
},
@@ -3214,8 +3300,11 @@
/turf/open/shuttle/predship,
/area/yautja)
"Zs" = (
-/obj/structure/machinery/door/airlock/strata/autoname{
- name = "\improper Containment"
+/obj/structure/machinery/door/airlock/yautja/secure{
+ name = "\improper Heavy Armory"
+ },
+/obj/structure/machinery/door/poddoor/shutters/almayer/yautja{
+ dir = 4
},
/turf/open/floor/strata{
color = "#5e5d5d";
@@ -4816,7 +4905,7 @@ bj
aa
aa
aa
-aa
+dI
aa
bj
bj
@@ -5028,7 +5117,7 @@ cP
cP
cP
cP
-wQ
+fo
bj
bj
aa
@@ -5100,7 +5189,7 @@ ZM
au
cP
cP
-wQ
+fo
bj
bj
bj
@@ -5149,7 +5238,7 @@ aa
aa
ac
bj
-af
+bN
ZR
aV
bn
@@ -5172,7 +5261,7 @@ bj
wQ
cP
cP
-wQ
+fo
bj
bj
bj
@@ -5221,7 +5310,7 @@ aa
aa
ae
bj
-af
+bN
aC
cP
bL
@@ -5244,7 +5333,7 @@ rH
wQ
bL
cP
-wQ
+fo
bj
bj
bj
@@ -5310,9 +5399,9 @@ KD
bL
JH
JH
-af
+bN
be
-af
+bN
JH
JH
JH
@@ -6318,9 +6407,9 @@ bL
bL
JH
JH
-af
+bN
MI
-af
+bN
JH
JH
JH
@@ -6373,7 +6462,7 @@ aa
aa
ae
bj
-af
+bN
aC
cP
bL
@@ -6396,7 +6485,7 @@ ti
Ut
bL
cP
-Ut
+tR
bj
bj
bj
@@ -6445,7 +6534,7 @@ aa
aa
ad
bj
-af
+bN
ZR
aV
bn
@@ -6468,7 +6557,7 @@ bj
Ut
cP
cP
-Ut
+tR
bj
bj
bj
@@ -6540,7 +6629,7 @@ ZM
zZ
cP
cP
-Ut
+tR
bj
bj
bj
@@ -6612,7 +6701,7 @@ cP
cP
cP
cP
-Ut
+tR
bj
bj
aa
@@ -6765,9 +6854,9 @@ aa
aa
bj
bj
-bR
+HD
+JH
If
-bN
bj
bj
bj
@@ -6837,9 +6926,9 @@ aa
bj
bj
bj
-BB
-JH
+HD
JH
+If
bj
bj
bj
@@ -6983,7 +7072,7 @@ gb
JH
JH
JH
-JH
+Bg
bj
bj
bj
@@ -7126,7 +7215,7 @@ bD
df
JH
BK
-gG
+BK
bj
bj
bj
@@ -7192,7 +7281,7 @@ bj
bL
bL
bL
-bL
+gG
bL
Fy
dl
diff --git a/maps/tents/tent_big.dmm b/maps/tents/tent_big.dmm
index f341e67ce2aa..acf19e26a98e 100644
--- a/maps/tents/tent_big.dmm
+++ b/maps/tents/tent_big.dmm
@@ -7,6 +7,10 @@
/obj/structure/tent_curtain,
/turf/template_noop,
/area/template_noop)
+"k" = (
+/obj/structure/blocker/tent/full_tile,
+/turf/template_noop,
+/area/template_noop)
"n" = (
/obj/structure/blocker/tent{
dir = 4
@@ -59,19 +63,19 @@
/area/template_noop)
(1,1,1) = {"
-O
+k
J
v
a
"}
(2,1,1) = {"
-O
+k
S
O
x
"}
(3,1,1) = {"
-O
+k
n
s
G
diff --git a/maps/tents/tent_cmd.dmm b/maps/tents/tent_cmd.dmm
index e7260ca9bccf..0dbd6a6ef330 100644
--- a/maps/tents/tent_cmd.dmm
+++ b/maps/tents/tent_cmd.dmm
@@ -30,6 +30,7 @@
/turf/template_noop,
/area/template_noop)
"p" = (
+/obj/structure/blocker/tent/full_tile,
/turf/template_noop,
/area/template_noop)
"v" = (
diff --git a/maps/tents/tent_med.dmm b/maps/tents/tent_med.dmm
index fcb446a68e25..527f3dc7bc05 100644
--- a/maps/tents/tent_med.dmm
+++ b/maps/tents/tent_med.dmm
@@ -16,6 +16,7 @@
/turf/template_noop,
/area/template_noop)
"p" = (
+/obj/structure/blocker/tent/full_tile,
/turf/template_noop,
/area/template_noop)
"v" = (
diff --git a/maps/tents/tent_reqs.dmm b/maps/tents/tent_reqs.dmm
index 7d9cf926e698..2dc47531ed37 100644
--- a/maps/tents/tent_reqs.dmm
+++ b/maps/tents/tent_reqs.dmm
@@ -70,6 +70,10 @@
},
/turf/template_noop,
/area/template_noop)
+"R" = (
+/obj/structure/blocker/tent/full_tile,
+/turf/template_noop,
+/area/template_noop)
"S" = (
/obj/structure/blocker/tent{
dir = 8
@@ -83,25 +87,25 @@
/area/template_noop)
(1,1,1) = {"
-a
+R
B
S
m
"}
(2,1,1) = {"
-a
+R
J
a
c
"}
(3,1,1) = {"
-a
+R
w
U
k
"}
(4,1,1) = {"
-a
+R
n
d
k
diff --git a/rust_g.dll b/rust_g.dll
index 52a74ce745ea..72a27df14403 100644
Binary files a/rust_g.dll and b/rust_g.dll differ
diff --git a/sound/theme/lastmanstanding_clf.ogg b/sound/theme/lastmanstanding_clf.ogg
new file mode 100644
index 000000000000..365af09116bb
Binary files /dev/null and b/sound/theme/lastmanstanding_clf.ogg differ
diff --git a/sound/theme/lastmanstanding_upp.ogg b/sound/theme/lastmanstanding_upp.ogg
new file mode 100644
index 000000000000..d74812f73df1
Binary files /dev/null and b/sound/theme/lastmanstanding_upp.ogg differ
diff --git a/sound/theme/lastmanstanding_wy.ogg b/sound/theme/lastmanstanding_wy.ogg
new file mode 100644
index 000000000000..225914b416d1
Binary files /dev/null and b/sound/theme/lastmanstanding_wy.ogg differ
diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js
index 727973378649..97640d062a86 100644
--- a/tgui/packages/tgui/index.js
+++ b/tgui/packages/tgui/index.js
@@ -10,6 +10,7 @@ import './styles/themes/abductor.scss';
import './styles/themes/cardtable.scss';
import './styles/themes/crt/crt-blue.scss';
import './styles/themes/crt/crt-green.scss';
+import './styles/themes/crt/crt-red.scss';
import './styles/themes/crt/crt-yellow.scss';
import './styles/themes/spookyconsole.scss';
import './styles/themes/hackerman.scss';
diff --git a/tgui/packages/tgui/interfaces/AresInterface.js b/tgui/packages/tgui/interfaces/AresInterface.js
index 6bf85e96522f..fc7901795046 100644
--- a/tgui/packages/tgui/interfaces/AresInterface.js
+++ b/tgui/packages/tgui/interfaces/AresInterface.js
@@ -22,11 +22,18 @@ const PAGES = {
export const AresInterface = (props, context) => {
const { data } = useBackend(context);
- const { current_menu } = data;
+ const { current_menu, sudo } = data;
const PageComponent = PAGES[current_menu]();
+ let themecolor = 'crtblue';
+ if (sudo >= 1) {
+ themecolor = 'crtred';
+ } else if (current_menu === 'emergency') {
+ themecolor = 'crtred';
+ }
+
return (
-
+
@@ -1364,6 +1371,8 @@ const Emergency = (props, context) => {
nuketimelock,
nuke_available,
} = data;
+ const canQuarters = alert_level < 2;
+ let quarters_reason = 'Call for General Quarters.';
const minimumEvacTime = worldtime > distresstimelock;
const distressCooldown = worldtime < distresstime;
const canDistress = alert_level === 2 && !distressCooldown && minimumEvacTime;
@@ -1396,7 +1405,8 @@ const Emergency = (props, context) => {
let nuke_reason =
'Request a nuclear device to be authorized by USCM High Command.';
if (!nuke_available) {
- nuke_reason = 'No nuclear ordnance is available during this operation.';
+ nuke_reason =
+ 'No nuclear ordnance is available during this operation, or one has already been provided.';
} else if (mission_failed) {
nuke_reason =
'You have already lost the objective, you cannot use a nuclear device aboard the ship!';
@@ -1445,6 +1455,20 @@ const Emergency = (props, context) => {
Emergency Protocols
+ act('general_quarters')}
+ disabled={!canQuarters}
+ />
{
{data.decryption_complete
? 'Decryption complete.'
: `Decryption time left :
- ${data.decryption_time} seconds`}
+ ${data.decryption_time} minutes`}
diff --git a/tgui/packages/tgui/interfaces/SquadMod.js b/tgui/packages/tgui/interfaces/SquadMod.js
index 85ff237d3aac..15ce727a5341 100644
--- a/tgui/packages/tgui/interfaces/SquadMod.js
+++ b/tgui/packages/tgui/interfaces/SquadMod.js
@@ -5,7 +5,6 @@ import { Window } from '../layouts';
export const SquadMod = (props, context) => {
const { act, data } = useBackend(context);
const { squads = [], human, id_name, has_id } = data;
- const COLORS_SPECTRUM = ['red', 'yellow', 'purple', 'teal', 'brown', 'grey'];
return (
@@ -47,7 +46,7 @@ export const SquadMod = (props, context) => {
key={entry.name}
fluid
content={entry.name}
- color={COLORS_SPECTRUM[entry.color]}
+ backgroundColor={entry.color}
onClick={() =>
act('PRG_squad', {
name: entry.name,
diff --git a/tgui/packages/tgui/interfaces/WorkingJoe.js b/tgui/packages/tgui/interfaces/WorkingJoe.js
index 492446a7a882..ab8e4783ef41 100644
--- a/tgui/packages/tgui/interfaces/WorkingJoe.js
+++ b/tgui/packages/tgui/interfaces/WorkingJoe.js
@@ -19,7 +19,7 @@ export const WorkingJoe = (props, context) => {
const PageComponent = PAGES[current_menu]();
return (
-
+
@@ -44,7 +44,7 @@ const Login = (props, context) => {
WY-DOS Executive
- Version 12.7.1
+ Version 12.8.3
Copyright © 2182, Weyland Yutani Corp.