Compare commits

..
166 Commits
Author SHA1 Message Date
Vanessa 17e2dffdb2 AAAAAA 2026-06-25 11:42:43 -05:00
Vanessa 22e287ec45 AAA 2026-06-25 11:40:46 -05:00
Vanessa 9a0a4abf5d minus one newline 2026-06-25 11:39:43 -05:00
Vanessa 0b685a52b6 Fix pop up too 2026-06-25 11:38:42 -05:00
Vanessa 52d3b54d2c Fix admin fax notifications. 2026-06-21 08:53:15 -05:00
github-actions[bot]andDeltaV-Bot 8ba993d1a9 Update Credits (#6072)
Co-authored-by: DeltaV-Bot <github@deltav.gay>
2026-06-21 05:30:08 +02:00
Delta-V bot edd1500bf3 Automatic changelog update 2026-06-20 19:14:31 +02:00
einknusprigestoastandpre-commit-ci[bot] 2ec64360b7 ERT Security Loadout overhaul (#6049)
* ERT Selectors Amber Sec

* Added SRT Loadouts

* Added SRT Loadouts

* Added ERT selectors

* ERT Loadout tweaks

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Changed Omnitool to be ToolDebug + Multitool

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-20 19:14:11 +02:00
Delta-V bot e0540c6205 Automatic changelog update 2026-06-20 17:02:34 +02:00
7953b0469a I SCREAM, YOU SCREAM, WE ALL SCREAM FOR ICE CREAM (#5943)
* Initial commit.

* Minor tweaks to names, added caramel bottle.

* Fixing open state of caramel bottle.

* JSON cleanup.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Minor tweak to cookie dough ice cream recipe. Removed unused sprites.

* Swapping out updated sprites for rainbow sherbet, tropical sundae, neapolitan.

* Removing plushies.zip.

* Moved frozen treats to their own guidebook section under drinks to reduce clutter.

* Fixing flavor references, adding icecreamflavors.yml to contain definitions, corrected loadout ID references in loadoutgroups.

* Committing loadout changes that slipped through the cracks.

* Corrected casing in reference to RSI.

* Changed recipe for caramel to be just sugar to prevent conflict with Sigynate's recipe.

* So I'm kinda dumb and somehow it didn't occur to me that making sugar alone yield caramel would actually not only not fix the issue with Syginate but also wreck it for so many other chems, oopsie daisy, we're swapping it for Saline for a more shelf-stable salted caramel sauce.

* Tweaking ratio for Caramel, also, what are the odds that the tempermental build just works after this?

* At this point I'm just curious if a different build will fail this time.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update Resources/Prototypes/Catalog/VendingMachines/Inventories/bardrobe.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

* Update Resources/Prototypes/Catalog/VendingMachines/Inventories/bardrobe.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

* Update Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

* Update Resources/Prototypes/Catalog/VendingMachines/Inventories/boozeomat.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Clothing/Uniforms/jumpsuits.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Objects/Consumable/Drinks/frozen_treats.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

* Update Resources/Prototypes/Guidebook/references.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

* Review changes

* Pascal-ify-ing flavor IDs.

* Update Resources/Prototypes/_DV/Entities/Objects/Consumable/Drinks/frozen_treats.yml

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>

---------

Signed-off-by: robobozu <240598948+robobozu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
2026-06-20 15:02:13 +00:00
Delta-V bot a3d3d23600 Automatic changelog update 2026-06-20 16:24:55 +02:00
pathetic meowmeow 5ccfce1e7f NanoChat? Outside the same grid? Perish the thought. (#6057) 2026-06-20 09:24:35 -05:00
Delta-V bot 879cbf43f5 Automatic changelog update 2026-06-20 16:20:24 +02:00
einknusprigestoastandpre-commit-ci[bot] 9c84627db5 Added CentComm Turrets (#6069)
* Added CentComm Turrets

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-20 09:20:04 -05:00
Delta-V bot 3134e91fa5 Automatic changelog update 2026-06-20 12:46:22 +02:00
3ac30b7b7b [new item] Hitman card (#5896)
* Created Hitman_Case_catalog.yml

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

credit:stormyatt. thanks for the help those on discord and the host briefcase

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

credit: stormyatt with help from the discord sever and the hos briefcase pr

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* 16/24 items filled

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* more added

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* top three filled

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman, full disguise kit

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* 27/30

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* 28/30

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* found the doorjack!

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* spyCrewMonitor is cool

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* found you!

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* hitman: icons

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* Created Hitman_Case.yml

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* Create Hitman_Briefcase.rsi

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* Delete Resources/Textures/_DV/Objects/Tools/Hitman_Briefcase.rsi

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* almost made hitman card

* many modifactions to make it work.

* i need to get around to fixing my repo after this

* hope this works. reverting by hand

Signed-off-by: storm! <253825839+stormyatt@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* as requested

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* less dependies, pretty sure only needed ones now

* review 2. card space addition

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>
Signed-off-by: storm! <253825839+stormyatt@users.noreply.github.com>
Co-authored-by: stormyatt <stormyatt@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-20 12:46:02 +02:00
Delta-V bot 4daea082bc Automatic changelog update 2026-06-20 00:07:10 +02:00
Vanessa 6a37e1268e SRN Fixes (#6045)
* Fixed SRNs getting borg AA on their borgs

* SRNs can print batteries
2026-06-19 18:06:50 -04:00
Delta-V bot 331a25c7d3 Automatic changelog update 2026-06-19 23:54:07 +02:00
Vanessaandpre-commit-ci[bot] b5121e10e9 Increase Syndicate Hardsuit Aura by 15% (#6060)
* Various syndicate hardsuit sprite upgrades for overlay.

* Added (missing) overlays for Feroxi and Reptiles.

* added base bluekie light

* Added stealthsuit variant.

* Fixed duplicate yaml entry

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix missing entry in meta.json for syndie medic

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-19 17:53:47 -04:00
Delta-V bot 9584a7b0ae Automatic changelog update 2026-06-19 23:43:04 +02:00
Vanessaandpre-commit-ci[bot] f9880c148b Cloned zombie players no longer get mauled by cats (#6053)
* Moved DeltaV modifications of ZombieSystem to a partial class. Fixed factions and pacifist trait on zombie cloning. Added survival objective to cloned II.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-19 17:42:45 -04:00
Delta-V bot 25ef15f162 Automatic changelog update 2026-06-19 21:26:41 +02:00
Theo 997a6e31e6 Felinid SinkClaws action (#5996)
action and sprites
2026-06-19 14:26:22 -05:00
Delta-V bot 5a342570a4 Automatic changelog update 2026-06-19 20:35:52 +02:00
Proxy also works :3 97bb68b2a6 kill cult metashield (#6026) 2026-06-19 13:35:33 -05:00
Delta-V bot ac9f8852b3 Automatic changelog update 2026-06-19 20:12:19 +02:00
DisposableCrewmember42andneomoth 8c8f4b4f2c tweak: better screenshake (#6059)
* tweak: explosion shake directional, distance fall-off

partial port of ss14Starlight/space-station-14#3707 by neomoth:
0b6282eb267ef1efc0b2bcd78a39e53a776f1c79

Co-authored-by: neomoth <69372252+neomoth@users.noreply.github.com>

* tweak: melee shake depends on damage/wielding

partial port of ss14Starlight/space-station-14#3707 by neomoth:
0b6282eb267ef1efc0b2bcd78a39e53a776f1c79

Co-authored-by: neomoth <69372252+neomoth@users.noreply.github.com>

* tweak: explosion kick falloff

* chore: remove debug log

* feat: melee shake has heavy/light variants instead of only showing for high blunt/wielded

* refactor: use MathF instead of Math

* tweak: more noticeable camera kick falloff

* tweak: generally dampen explosion kick a little more, fall off slower

* docs: explain camera kick falloff

* docs: remove commented upstream code per review

* docs: document screenshake falloff too, clarify distance

---------

Co-authored-by: neomoth <69372252+neomoth@users.noreply.github.com>
2026-06-19 13:11:59 -05:00
Delta-V bot 8eeb5f6523 Automatic changelog update 2026-06-19 20:00:05 +02:00
Theo 45432aa3bb Kobolds exist again (#5968)
copy paste ops
2026-06-19 17:59:45 +00:00
Delta-V bot b9153f0f13 Automatic changelog update 2026-06-19 19:53:03 +02:00
Theo e3d3fe72f0 remove damage examine from bomb pen (#6010)
remove hit damage
2026-06-19 17:52:43 +00:00
Delta-V bot 0a65236363 Automatic changelog update 2026-06-19 19:49:19 +02:00
Halo3moth afe6021455 remove chameleon gears metashield (#6000)
Update 2_Metagaming.xml
2026-06-19 13:48:59 -04:00
Delta-V bot e5db02c736 Automatic changelog update 2026-06-19 18:16:18 +02:00
Theo cbc5f819c4 Security radio recolor (#6016)
* change

* less saturated
2026-06-19 11:15:58 -05:00
Delta-V bot 339b3fb32c Automatic changelog update 2026-06-19 18:06:08 +02:00
AstraandVanessa e96faec07d Allow purified salts in belts/medkits (#6062)
* Allow purified salts in medical belts

* Medkits too why not

* Update Resources/Prototypes/Entities/Objects/Specific/Medical/medkits.yml

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>

---------

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
2026-06-19 16:05:48 +00:00
Delta-V bot a1bbf142a4 Automatic changelog update 2026-06-19 17:52:56 +02:00
DisposableCrewmember42andPieter-Jan Briers e47a0bf455 feat: chat follow links for ghosts (upstream port) (#6063)
* Ghosts have follow buttons in chat (#44284)

* Fix ghost follow buttons for station entity (#44289)

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2026-06-19 10:52:36 -05:00
Delta-V bot 72f3edd378 Automatic changelog update 2026-06-19 17:49:17 +02:00
freezer dog 030deb6f27 SV-213 QoL stuff (and a tiny bit more) (#6065)
wwoof
2026-06-19 10:48:57 -05:00
Vanessaandpathetic meowmeow 4397017c5a Added ESEffectSparks to excluded list on integration tests (#6061)
* Added ESEffectSparks to excluded list on integration tests

* Update Content.IntegrationTests/Tests/EntityTest.cs

Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>

---------

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
2026-06-18 20:32:39 -04:00
Delta-V bot 0f19340d41 Automatic changelog update 2026-06-18 18:37:52 +02:00
Stxcking 3c28ee8d20 Tweak: Broken Station Maps can be Deconstructed (#6005)
mhm
2026-06-18 11:37:32 -05:00
Delta-V bot 11cc04dd87 Automatic changelog update 2026-06-18 18:35:12 +02:00
Sugarcane f87ed28744 Bluesec in Guidebook (#6013) 2026-06-18 11:34:52 -05:00
Delta-V bot 62c76332f5 Automatic changelog update 2026-06-18 18:22:35 +02:00
pathetic meowmeow 34c212e4fb Fax machines? That are ready roundstart? Perish the thought. (#6058) 2026-06-18 11:22:15 -05:00
Radezolid 353290e55b Mostly locker entityTables cleanup (#5982)
* Remove our changes to dressers.yml

* Engineering

* Heads

* Science

* Security

* Append our tables with upstream's, apply suggestion and small comment cleanup
2026-06-17 15:55:26 -04:00
Delta-V bot 0cbe9c01fb Automatic changelog update 2026-06-17 17:32:53 +02:00
Vanessa d88938887b Fixed biomasser dividing by zero (#6042)
Fixed biomasser dividing by 0.
2026-06-17 17:32:34 +02:00
Delta-V bot 1656a91c5e Automatic changelog update 2026-06-17 16:08:07 +02:00
Proxy also works :3 c70cbcb9f5 bne (#6050) 2026-06-17 09:07:47 -05:00
Delta-V bot 3e22d89d1e Automatic changelog update 2026-06-17 15:56:46 +02:00
TNE a790954f06 Prevent zombies/non-humanoids/non-borgs from using jetpacks (#5776)
Prevent zombies from using jetpacks

Introduces CanComplexInteract check from ActionBlockerSystem alongside a translation string
When Zombifying: Removes AutomaticJetpackUserComponent, calls SharedJetpackSystem.SetEnable(..., false, ...)
2026-06-17 15:56:26 +02:00
Delta-V bot 0676186fe5 Automatic changelog update 2026-06-17 15:36:21 +02:00
Theo db6d1e3970 Uplink pt 1 touchup (#5991)
* buffs

* damage change on knuckledusters
2026-06-17 08:36:01 -05:00
fd20b74b8f Update mapping spawners (#5857)
* Update mapping spawners

* more casing

* Clothing and towels

* minor fixes

* Finalize clothing

* Begin Items

* Final updates

* Fixes

* save button

* `n`

* Add pitbull to animal spawner

* Add random satchels

* Glowsticks

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/animals.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/boardgames.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/boardgames.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/books.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/machineparts.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/schoolgirl.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/seeds.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/towels.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Mobs/NPCs/misc.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Mobs/NPCs/misc.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/mobs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>

* Review changes

---------

Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com>
Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
2026-06-16 23:54:22 -03:00
Delta-V bot d016838687 Automatic changelog update 2026-06-17 03:07:15 +02:00
Vanessa 160aa17052 Pitbull Fixes (#6046)
pitbull fixes
2026-06-16 22:06:55 -03:00
Delta-V bot 2b6729fa64 Automatic changelog update 2026-06-16 22:02:38 +02:00
Field Commandandpre-commit-ci[bot] 3439340530 Updated to ghost bar syndicate prison (#6006)
* Updated to ghost bar syndicate prison

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Some last minute fixes

* Update to prison and sprites

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-16 15:02:18 -05:00
Delta-V bot 28c2223a8a Automatic changelog update 2026-06-16 19:43:30 +02:00
CarolandVanessa 1b47c246c9 Added dynamic cost to Ratkings OnRaiseArmy ability (#6017)
* added dynamic cost to Ratkings OnRaiseArmy ability

* Update Content.Server/RatKing/RatKingSystem.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Carol <292806487+CarolCuchara@users.noreply.github.com>

* switched summary to one line

* added using directive for Linq

---------

Signed-off-by: Carol <292806487+CarolCuchara@users.noreply.github.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
2026-06-16 12:43:10 -05:00
Delta-V bot 8141512cc4 Automatic changelog update 2026-06-16 19:09:52 +02:00
DisposableCrewmember42 fd986fec6e feat: Effigy-driven Colossus (mid-round Colossus rework) (#5565)
* feat: effigy-driven colossus

* feat: impl final stage, tweaks, randomization, fixes

* fix: actually broadcast first effigy audio

* style: add some newlines

* feat: notify player about updated target location in chat

* tweak: effigy always grows

* feat: make alwaysGrow show on anom scanner

* tweak: raise minimumPlayers to 35

* refactor: move FTL additions to _DV

* refactor: break out buffs into comps, add event

also added missing docs and made effigies heal colossi fully, as
specified in the PR body. I accidentally left it at 50% damage taken.

* docs: add effigy comp docs
2026-06-16 13:09:32 -04:00
Delta-V bot dd2a6e1c28 Automatic changelog update 2026-06-16 16:16:28 +02:00
TNEandDisposableCrewmember42 6c7c9de253 fix: don't show SSD examine on dead people/ghost role (#6012)
fix: don't display examine text on dead people or untaken ghost roles

Co-authored-by: DisposableCrewmember42 <disposablecrewmember42@proton.me>
2026-06-16 09:16:08 -05:00
Delta-V bot 2b96bb79f8 Automatic changelog update 2026-06-15 19:27:20 +02:00
CarolandVanessa c1239ef7bf Remove headset from pockets when player get zombified (#6030)
* Remove radio form pocket when secdogs get zombified

* Update Content.Server/Zombies/ZombieSystem.Transform.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Carol <292806487+CarolCuchara@users.noreply.github.com>

---------

Signed-off-by: Carol <292806487+CarolCuchara@users.noreply.github.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
2026-06-15 12:27:00 -05:00
Delta-V bot 62b6401e48 Automatic changelog update 2026-06-15 18:46:55 +02:00
freezer dog f79a599c65 fixing machine pistol magazine issues and clearing gun chambers (#5819)
* kill... slaughter...

* ok why not im gonna clear chambers too

* ok maybe thats all the chambers clear

* time to find out i screwed up everythin xd

* whoops i killed the chambers....

---------

Signed-off-by: freezer dog <iamabanana372456@gmail.com>
2026-06-15 13:46:35 -03:00
Delta-V bot 9acd3f2ecb Automatic changelog update 2026-06-15 18:06:32 +02:00
Delta-V bot 47215d7876 Automatic changelog update 2026-06-15 18:02:26 +02:00
fe53298d27 Delta-V: Wrath of the Pitbulls (#5915)
* DIRECTION KNOWS BEST

DIRECTION KNOWS BEST GLORY TO DELTA-V

* MORE

More names

* It begins

Im not sorry

* Pitbull changes

They fear me because of what I can do.

* They will fuck you up if you Hit them

* Update pitbull.yml

* The infamous pitbull faction

* Update ai_factions.yml

* Update ai_factions.yml

Staff Requested changes

* Update pitbull.yml

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* sprites added!

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* event addition part 1

* Pitbull Crate

* Event work part 2

* obvious

* bullshit fix part 1

* Bullshit fix part 2

* bullshit fixes part 3

* i hate it here

* removes bullshit 5137268

* shortened timer to catch crew off guard

* crash fix 1 i think

* Update cargo_livestock.yml

* They no longer maul II

* they are angry.

* they cant maul you now.

* Update pitbull.ftl

* Update pitbull.yml

* Update events.yml

* Update interaction-popup-component.ftl

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update events.yml

* Update pests.yml

* Update pitbull.yml

* Update events.yml

* Update pitbull.yml

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update pitbull.yml

* 1984 pitbull faction

* Inshallah this will work

* More names

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update pitbull_names.yml

* Update pitbull.ftl

* Update pitbull_names.yml

* Increased amount of mice and hopefully enables higher chance of ventbulls to spawn

* DIRECTION KNOWS BEST

DIRECTION KNOWS BEST
DIRECTION KNOWS BEST
DIRECTION KNOWS BEST
DIRECTION KNOWS BEST
DIRECTION KNOWS BEST

Signed-off-by: Solaris <63768197+StarboundSolaris@users.noreply.github.com>

* DIRECTION KNOWS BEST

DIRECTION KNOWS BEST

Signed-off-by: Solaris <63768197+StarboundSolaris@users.noreply.github.com>

* Refactor inheritence for pitbulls

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* forgot a file

* Makes it so the crate spawns the named versions

* Ensures they have random names.

* Update pitbull.yml

* Prep for mapped pitbull

* Adds Pibble

* Returns id to previous version

* Update pitbull.yml

* Update events.yml

---------

Signed-off-by: Solaris <63768197+StarboundSolaris@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Solaris <63768197+Solaris7518@users.noreply.github.com>
Co-authored-by: Vanessa <vanessalouwagie@gmail.com>
2026-06-15 11:02:06 -05:00
Theo 1aee2519df Kill Poncho Meta (#6001)
numbers
2026-06-15 10:51:38 -05:00
Delta-V bot e8cd052c31 Automatic changelog update 2026-06-15 17:10:19 +02:00
Carol a27fa291e7 Fix: Beer googles not stopping glasses from spilling when throw (#6031)
* fixed beer googles not keeping glasses form spilling on throw

* fixed beer googles not keeping glasses form spilling on throw
2026-06-15 10:09:59 -05:00
Delta-V bot 7e6f66a651 Automatic changelog update 2026-06-15 17:05:49 +02:00
pathetic meowmeow 08c27979a1 Interaction particles pass (#6033) 2026-06-15 10:05:29 -05:00
Delta-V bot b49a94100c Automatic changelog update 2026-06-14 20:30:54 +02:00
Carol cfa24eb20c Added a range check for pager linking (#6032) 2026-06-14 14:30:34 -04:00
Delta-V bot 1787683435 Automatic changelog update 2026-06-14 10:13:44 +02:00
Stop-Signs 389fbf99e1 Reduce WT550 SMG fire-rate (#5581)
* the PR

* comments

* hunt showdown queue times

* comment

* .desc
2026-06-14 08:13:24 +00:00
Delta-V bot 74c7bf0a05 Automatic changelog update 2026-06-14 06:13:17 +02:00
Carol 6764276a47 Changed secborgs fibers to blue (#6025)
changed secborgs fibers to blue
2026-06-14 04:12:57 +00:00
github-actions[bot]andDeltaV-Bot 03934d3fbd Update Credits (#6028)
Co-authored-by: DeltaV-Bot <github@deltav.gay>
2026-06-14 04:59:28 +02:00
SumTwo 468150c817 Adding the Logistics Beret as a loadout option for Cargo Technicians (#6015)
* Step One: Add Hat

Signed-off-by: SumTwo <its.a.joke1000@gmail.com>

* Step Two: Add Hat

Signed-off-by: SumTwo <its.a.joke1000@gmail.com>

* Step Three: Im Stupid

Signed-off-by: SumTwo <its.a.joke1000@gmail.com>

* Step Four: Im Stupid

Signed-off-by: SumTwo <its.a.joke1000@gmail.com>

* Step Five: Obey CR

Signed-off-by: SumTwo <its.a.joke1000@gmail.com>

---------

Signed-off-by: SumTwo <its.a.joke1000@gmail.com>
2026-06-13 21:07:20 -03:00
Delta-V bot 310ac0809b Automatic changelog update 2026-06-13 22:03:35 +02:00
7b71611dc9 [antag variant]Hitman midround variant (#5848)
* Created Hitman_Case_catalog.yml

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

credit:stormyatt. thanks for the help those on discord and the host briefcase

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

credit: stormyatt with help from the discord sever and the hos briefcase pr

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* 16/24 items filled

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* more added

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* top three filled

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman, full disguise kit

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* 27/30

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* 28/30

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* found the doorjack!

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* spyCrewMonitor is cool

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* found you!

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* for hitman

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* hitman: icons

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* Created Hitman_Case.yml

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* Create Hitman_Briefcase.rsi

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* Delete Resources/Textures/_DV/Objects/Tools/Hitman_Briefcase.rsi

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixing it all

* I dont get it either but last of the changes which our just righting things

* Hitman_Case_catalog.yml be like that

* goodbye dupe

* renaming a file and removing a duplicate

* attributions changed to repect the caps

* as requested, name changes and useless line removal

* forgot to control S here

* names changed to try and be both simple and actualy describe the goal

* doing review changes and a small change to prototype order to make sense

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* how did i get where i moved this wrong?

* recharging radio jammer to radio jammer.

* added hitman midround stuff

* made the professional not cause errors and have a ghost warp point

* enter at the ends of these files

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* updated the professional to professional

* decal adds

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixes and stuff

* gave hitmans headset traffic and freelance comms

* all review changes made

* Apply suggestion from @Radezolid

caps my foe

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: storm! <253825839+stormyatt@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* nevermind i distrust this

* Update Resources/Prototypes/_DV/Catalog/Fills/Lockers/hitman_locker.yml

Signed-off-by: Radezolid <snappednexus@gmail.com>

---------

Signed-off-by: stormyatt <dratticuscrabs@gmail.com>
Signed-off-by: storm! <253825839+stormyatt@users.noreply.github.com>
Signed-off-by: Radezolid <snappednexus@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: stormyatt <stormyatt@users.noreply.github.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
2026-06-13 20:03:15 +00:00
Delta-V bot f2153d66c3 Automatic changelog update 2026-06-13 21:29:23 +02:00
Theo b6833cbbcf Remove Fractured Form (#6011)
* remove from the table

* full remove
2026-06-13 14:29:04 -05:00
Delta-V bot 4e2bd0c244 Automatic changelog update 2026-06-13 04:29:23 +02:00
bf0abd7ffa Add SRT Hardsuit and Antelope + Remove DS mentions (#6003)
* Added Chameleon Armor

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* nerfvest

* Lowered Price

* Changed Jackdaw and Atrides fire rates

* Changed battery on Pulse Rifle, upped fire rate

* lowered pulse rifle recharge rate

* Added SRT Hardsuit

* Added Antelope

* Revert "lowered pulse rifle recharge rate"

This reverts commit 73f348c3e7.

* Revert "Changed battery on Pulse Rifle, upped fire rate"

This reverts commit d6f408620d.

* Revert "Changed Jackdaw and Atrides fire rates"

This reverts commit 0779c08d44.

* Revert "Lowered Price"

This reverts commit d798cd92e9.

* Revert "Merge branch 'toastdrip' of https://github.com/einknusprigestoast/Delta-v into toastdrip"

This reverts commit ca038e3699, reversing
changes made to f74f3e506d.

* Revert "nerfvest"

This reverts commit f74f3e506d.

* Revert "Added Chameleon Armor"

This reverts commit b57fa738f3.

* Removed Edge from Mateba

* whoops, forgot comment

* Removed DS mention + Added NV to ERT + Added Thermal Vision to DS

* removed deathsquad mentions on the jumpsuit

* removed deathsquafd mentions on the backpack

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Unfucked my parenting

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* non-localisation maintainer requests

* Added FTLs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* One point, one singular point

* AHHHH

* Fix comments

Signed-off-by: Radezolid <snappednexus@gmail.com>

---------

Signed-off-by: Radezolid <snappednexus@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
2026-06-12 23:29:03 -03:00
Delta-V bot c4d65da8ed Automatic changelog update 2026-06-12 19:16:19 +02:00
Stxcking 6dac24f03a Shoukou / Shoko Map Update - BlueSec (#5940)
* first

sobs

* second

* review fixes
2026-06-12 12:15:59 -05:00
Delta-V bot 9550a7d53d Automatic changelog update 2026-06-11 03:28:51 +02:00
VelcroboyandVelcroboy d826a15ff2 Map Edits: Arena (#6009)
* Map Edits: Arena

* Link lightswitch

* Remove duplicate suit storage

* reveiw edits

---------

Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2026-06-10 20:28:31 -05:00
Delta-V bot 23a9a75703 Automatic changelog update 2026-06-10 20:23:59 +02:00
Vape 79a0000547 Xeno Rework - Pull sprites and audio from RMC (#5891)
* Additions

* Changes per direction

* Update xeno.yml

* Requested Changes
2026-06-10 18:21:13 +00:00
Delta-V bot 71badde1a0 Automatic changelog update 2026-06-09 20:48:54 +02:00
Theo 5ab49cd46b Reduce meat spike build time (#5999) 2026-06-09 14:48:35 -04:00
Delta-V bot 0398c527a1 Automatic changelog update 2026-06-09 20:10:21 +02:00
Cepelinas1 cdafd4c257 More Tips + slight tweaks to weapon names (#6004)
* first

* typo

* figured it out :)

* what reviewers wanted
2026-06-09 15:10:01 -03:00
Delta-V bot 8dc06f010a Automatic changelog update 2026-06-09 15:45:46 +02:00
Theo fee62b449f shadow cat tweaks (#5977)
* add and remove

* remove comment

* oh yeah and this one
2026-06-09 13:45:26 +00:00
Delta-V bot c0d20b3d79 Automatic changelog update 2026-06-08 23:57:59 +02:00
snowywinters b2944fba52 Edge update (#5983)
* Update edge.yml

* Removed surgeon
2026-06-08 16:57:39 -05:00
2e39916957 Admeme gun changes (#5971)
* Added Chameleon Armor

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* nerfvest

* Lowered Price

* Changed Jackdaw and Atrides fire rates

* Changed battery on Pulse Rifle, upped fire rate

* lowered pulse rifle recharge rate

* Update Resources/Prototypes/_DV/Entities/Objects/Weapons/Guns/Rifles/rifles.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: einknusprigestoast <90181825+einknusprigestoast@users.noreply.github.com>

* Update Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: einknusprigestoast <90181825+einknusprigestoast@users.noreply.github.com>

* Update Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: einknusprigestoast <90181825+einknusprigestoast@users.noreply.github.com>

* Update Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml

Co-authored-by: Radezolid <snappednexus@gmail.com>
Signed-off-by: einknusprigestoast <90181825+einknusprigestoast@users.noreply.github.com>

---------

Signed-off-by: einknusprigestoast <90181825+einknusprigestoast@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
2026-06-08 17:35:29 -03:00
Delta-V bot 874bbf4fd2 Automatic changelog update 2026-06-08 21:50:51 +02:00
VelcroboyandVelcroboy e1ff1ea820 Map Edits: Tortuga (#5965)
* Map Edits: Tortuga

* Final commits

* Robo-Proto

* Service jobs

* reveiw fixes

* Jimbo's birthday present

---------

Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2026-06-08 21:50:31 +02:00
Delta-V bot 79a4a15403 Automatic changelog update 2026-06-08 21:36:22 +02:00
Vape 0bb2b6ee7b Updates to Submarine.yml (#5760)
* Commit#1

* Update submarine.yml

* ATTEMPT#2

* CASE SENSITIVITIY

* Committing Changes

* Bluesec

* Review updakes
2026-06-08 12:36:01 -07:00
Delta-V bot 18d9325b60 Automatic changelog update 2026-06-08 19:23:43 +02:00
Field Command f07648bc24 Ghost Bar: Syndicate Prison (#5578)
* Ghost Bar: Syndicate Prison

* Copy-paste is evil...

* formatting fix

* IPC no more

* update

* Fix
2026-06-08 12:23:24 -05:00
Delta-V bot f5da329b98 Automatic changelog update 2026-06-08 07:30:14 +02:00
ConstantlyConfused ad6581f2d3 Make the nuclear fab announce when it recieves new recipes (#5985)
make the nuclear fabiractor announce researches
2026-06-08 02:29:55 -03:00
Delta-V bot 430fef2a10 Automatic changelog update 2026-06-08 06:35:40 +02:00
Theo 6970cb9d28 Add cooldown to ninja emp (#5967)
* add 10 second cooldown

* 5 seconds instead
2026-06-08 00:35:20 -04:00
Delta-V bot ea2b8df2cc Automatic changelog update 2026-06-07 22:49:09 +02:00
Theo b87b113736 cult simplemobs deal blunt damage (#5962)
add blunt damage
2026-06-07 20:48:49 +00:00
Delta-V bot f087b2895e Automatic changelog update 2026-06-07 21:46:37 +02:00
MD360andRadezolid 0a14310574 Remove duplicate towels (#5964)
* duplicate begone

* white too

* Fix comment

Signed-off-by: Radezolid <snappednexus@gmail.com>

* Fix of the comment-fix

Signed-off-by: Radezolid <snappednexus@gmail.com>

---------

Signed-off-by: Radezolid <snappednexus@gmail.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
2026-06-07 19:46:18 +00:00
Delta-V bot d9c2f26438 Automatic changelog update 2026-06-07 21:34:18 +02:00
Stxcking a4f11c63dc Fix: Holoflares not Hurting Skia (#5976)
net
2026-06-07 16:33:58 -03:00
Delta-V bot 4ff05277fa Automatic changelog update 2026-06-07 21:05:35 +02:00
Stxcking a33e12e85e Fix: Glowsticks can now be use for glow in the dark stars! (#5970)
yipe
2026-06-07 16:05:16 -03:00
Delta-V bot 62e9ebc216 Automatic changelog update 2026-06-07 20:13:32 +02:00
biddygelson 25b69a3f9e Academy 1.9.0 BLU+ (#5972)
* * Added Poppy to Eng break room.
* Replaced all faxes with department variants. Note Justice was used over Lawyer; faxes added to Dorms, Chem, Botany, Chef, Bar. MimeLaw.
* Replaced Emergency EVAs with Epistemics EVAs in Artefact.
* Replaced RED-Sec with BLU-Sec; Sec, Brig, and Evac.
* Added Anti-Anom to solars.

* * replaced RED with BLU sec in viro security
* removed condenser in chem
* added condenser in atmos front
* added APC to atmos burn chambers
2026-06-07 20:13:12 +02:00
Field Command 7ed7dbcbb1 Maps: Timer be gone! (#5980)
✔
2026-06-07 18:05:08 +00:00
Delta-V bot fcdf1cbbf4 Automatic changelog update 2026-06-07 18:23:16 +02:00
SirrRequiem 9f786a7c1c Terra 6: Everything is Blue (#5956)
* Terra 6: Everything is Blue

* Curtains and carpets now match
2026-06-07 18:22:57 +02:00
github-actions[bot]andDeltaV-Bot e2e5c40f83 Update Credits (#5974)
Co-authored-by: DeltaV-Bot <github@deltav.gay>
2026-06-07 04:55:51 +02:00
Delta-V bot d0c3cff669 Automatic changelog update 2026-06-06 21:14:23 +02:00
ed1fa64a6e feat: Recent SSD indicator, SSD time in admin overlay (#5699)
* feat: Recent SSD indicator, SSD time in admin overlay

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* tweak: remove colon from ssd time loc string

* feat: add another stage

* feat: ssd examine text

* apply suggestion: make ssdstage serializable, use proper doc comments

Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
Signed-off-by: DisposableCrewmember42 <disposablecrewmember42@proton.me>

* apply suggestion: more compact switch, throw instead of logging if invalid stage

Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
Signed-off-by: DisposableCrewmember42 <disposablecrewmember42@proton.me>

* apply suggestion: missing DeltaV comment

Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
Signed-off-by: DisposableCrewmember42 <disposablecrewmember42@proton.me>

* fix: add missing import

* refactor: remove unnecessary state-keeping

i don't remember why i did it like that

* chore: remove unused import

* refactor: don't separate comp from ent

---------

Signed-off-by: DisposableCrewmember42 <disposablecrewmember42@proton.me>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
2026-06-06 19:14:03 +00:00
Delta-V bot cb245477b8 Automatic changelog update 2026-06-06 14:08:17 +02:00
0fdfef7175 New Lobby/Jukebox Track Made By Me (#5620)
* woah

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* attributions updates

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update Resources/Audio/_DV/Lobby/attributions.yml

Co-authored-by: Tobias Berger <toby@tobot.dev>
Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com>

* Update Resources/Audio/_DV/Jukebox/attributions.yml

Co-authored-by: Tobias Berger <toby@tobot.dev>
Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com>

* Update RobustToolbox

---------

Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tobias Berger <toby@tobot.dev>
2026-06-06 12:07:55 +00:00
Delta-V bot 646d538c4a Automatic changelog update 2026-06-06 11:53:38 +02:00
Stxcking 6ebcf78d08 Fix: Radiation Barrier Projector Works as Intended (#5787)
fix
2026-06-06 11:53:19 +02:00
Delta-V bot 2b7e3c8058 Automatic changelog update 2026-06-05 23:14:09 +02:00
pathetic meowmeow 4736a8db6f Add cyborg wire panel (#5948)
* Add cyborg wire panel

* MORE WIRES RAAAGH

* file capitalization

* renamed

* more comments
2026-06-05 23:13:49 +02:00
Delta-V bot 6057388bae Automatic changelog update 2026-06-05 19:34:29 +02:00
50a651beea Added Chameleon Armor (#5921)
* Added Chameleon Armor

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* nerfvest

* Lowered Price

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Changed Carapace Parent

* Remove irrelevant Braces

Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>

---------

Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
2026-06-05 17:34:09 +00:00
VelcroboyandVelcroboy 7e1308dd12 Map Hotfix: Elegance (#5966)
Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2026-06-05 18:06:03 +02:00
Delta-V bot 338ff271c7 Automatic changelog update 2026-06-05 17:33:16 +02:00
Stxcking fddd9079d3 Feat: Small Nuclear Reactor and Small Gas Turbine for Purchase (#5945)
mhmm
2026-06-05 11:32:56 -04:00
Delta-V bot bedc17ae73 Automatic changelog update 2026-06-05 17:29:04 +02:00
Stxcking ec80e99e26 Tweak: Added More Pride Theme Items in the Pride-o-Mat (#5946)
* gay

* more gay
2026-06-05 11:28:44 -04:00
Delta-V bot 4ceb567439 Automatic changelog update 2026-06-05 17:26:45 +02:00
Theo 6a9984475c Random nukie and ninja appearance (#5922)
* ninja and nukie random appearance component added to gamerules

* ninja and nukie random appearance component added to gamerules

* add keepgender
2026-06-05 15:26:24 +00:00
Delta-V bot 030eb95c63 Automatic changelog update 2026-06-05 17:18:10 +02:00
Stxcking 00abed028d Feat: No Paradox Clone Trait (#5937)
* yip

* whoops
2026-06-05 11:17:50 -04:00
Delta-V bot 0f3d319a6d Automatic changelog update 2026-06-05 17:08:02 +02:00
biddygelson a9ddc102e7 Academy 1.8.1 (#5880)
* added secure windoors to sec disposal chutes
* added secure windoors to epi-brig disposal chute, sec and epi locked.
* tweak medical escape pods for DV 5x7 variant
* tweak justice escape pods for DV 5x7 variant
* replaced all solar grill with random grill spawner
* tweak moved disposal light so it doesn't break from trash
2026-06-05 17:07:41 +02:00
Delta-V bot a51896b08d Automatic changelog update 2026-06-05 17:06:12 +02:00
Stxcking 356106342f Tweak: Revert Upstream Medical Borg Rework + More (#5656)
* Medical Borg Good Again :D

* Indents buh

* im blind

or dumb, theres errors that seem unrelated to the bodybags after this

* IM SORRY I FORGOT ABOUT THIS

* Anchoring it
2026-06-05 11:05:52 -04:00
Delta-V bot a3b450ac07 Automatic changelog update 2026-06-05 15:34:19 +02:00
pathetic meowmeow 4ddb5901b0 Better douner name generation (#5836)
* better douner name generation

* more names

* yet more names

* ugly sins that i hate

* screaming

* e
2026-06-05 15:33:56 +02:00
Delta-V bot 972d852bf4 Automatic changelog update 2026-06-05 14:46:20 +02:00
pathetic meowmeow ef0d317d12 Interaction particle finetuning (#5933) 2026-06-05 08:46:01 -04:00
003e08b583 Flavourful emotes (#5595)
* Changed up how emoting works

This was a long 10 hours just to get the chat to behave slightly different.

At least we can now scream over radio!

Will most definitely need some more work to get bugs out since this is the first implementation.

* Not ok

This was unmarked for some reason... I don't remember unmarking this.

* DeltaV comments

Added comments to explain changes.

Moved localization files to the _DV folder.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Moved code

Moved code to more suitable files and added some comments.

* Rights for robots

Now those with intrinsic radios can also emote!

* Fixes up some small stuff

Changes to fix some small issues with the code to align it more with modern standards.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix by-ref event

Never worked with this event type before so gonna hope this works.

* Update Content.Shared/Chat/SharedChatSystem.Emote.cs

Signed-off-by: pathetic meowmeow <uhhadd@gmail.com>

* Update Content.Shared/Chat/SharedChatSystem.cs

Signed-off-by: pathetic meowmeow <uhhadd@gmail.com>

* Update Content.Server/Chat/Systems/ChatSystem.cs

Signed-off-by: pathetic meowmeow <uhhadd@gmail.com>

* fix style nits

* remove conflicting chatsan

---------

Signed-off-by: pathetic meowmeow <uhhadd@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
2026-06-04 17:25:58 +00:00
Delta-V bot 74739709c4 Automatic changelog update 2026-06-04 12:59:17 +02:00
AeraAulinandSir Warock 61840fd5ab Rename Holy to Radiant and Shadow to Soul (#5952)
* rename shadow to soul and change it all since it's ours, hopefully change holy to radiant while touching as little as possible

* obliterate a mention of shadow

* Update Resources/Prototypes/_Impstation/Entities/Mobs/Anomalites/anomalite_damage.yml

Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
Signed-off-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com>

---------

Signed-off-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com>
Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
2026-06-04 07:58:56 -03:00
Delta-V bot 90f7aa7999 Automatic changelog update 2026-06-03 20:44:15 +02:00
turtlemutt 2260af7ce1 Bluehouse (#5898)
Update lighthouse.yml
2026-06-03 20:43:55 +02:00
774 changed files with 135538 additions and 68396 deletions
+1 -1
View File
@@ -269,7 +269,7 @@ Sentencing modifiers are to be applied by the sentencing officer, judge, or arbi
| style="border: 1px solid black;" | [[File:SL_BreakingAndEntering.png]] | style="border: 1px solid black;" | [[File:SL_BreakingAndEntering.png]]
! style="border: 1px solid black;" | {{anchor|Breaking and Entering}}Breaking and Entering ! style="border: 1px solid black;" | {{anchor|Breaking and Entering}}Breaking and Entering
| style="border: 1px solid black;" | 5 minutes | style="border: 1px solid black;" | 5 minutes
| style="border: 1px solid black;" | To break and enter into a high security area where one is not authorised nor invited, with intent to commit a crime within. | style="border: 1px solid black;" | To break and enter into an area where one is not authorised nor invited.
|- |-
| style="border: 1px solid black;" | 203 | style="border: 1px solid black;" | 203
| style="border: 1px solid black;" | [[File:SL_Rioting.png]] | style="border: 1px solid black;" | [[File:SL_Rioting.png]]
@@ -8,12 +8,14 @@ using Content.Shared.CCVar;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.SSDIndicator; // DeltaV - SSD time indicator
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; // DeltaV - SSD time indicator
namespace Content.Client.Administration; namespace Content.Client.Administration;
@@ -26,6 +28,7 @@ internal sealed class AdminNameOverlay : Overlay
private readonly IUserInterfaceManager _userInterfaceManager; private readonly IUserInterfaceManager _userInterfaceManager;
private readonly SharedRoleSystem _roles; private readonly SharedRoleSystem _roles;
private readonly IPrototypeManager _prototypeManager; private readonly IPrototypeManager _prototypeManager;
private readonly IGameTiming _timing; // DeltaV - Add timing
private readonly Font _font; private readonly Font _font;
private readonly Font _fontBold; private readonly Font _fontBold;
private AdminOverlayAntagFormat _overlayFormat; private AdminOverlayAntagFormat _overlayFormat;
@@ -53,7 +56,8 @@ internal sealed class AdminNameOverlay : Overlay
IUserInterfaceManager userInterfaceManager, IUserInterfaceManager userInterfaceManager,
IConfigurationManager config, IConfigurationManager config,
SharedRoleSystem roles, SharedRoleSystem roles,
IPrototypeManager prototypeManager) IPrototypeManager prototypeManager,
IGameTiming timing) // DeltaV - Add timing
{ {
_system = system; _system = system;
_entityManager = entityManager; _entityManager = entityManager;
@@ -62,6 +66,7 @@ internal sealed class AdminNameOverlay : Overlay
_userInterfaceManager = userInterfaceManager; _userInterfaceManager = userInterfaceManager;
_roles = roles; _roles = roles;
_prototypeManager = prototypeManager; _prototypeManager = prototypeManager;
_timing = timing; // DeltaV - Add timing
ZIndex = 200; ZIndex = 200;
// Setting these to a specific ttf would break the antag symbols // Setting these to a specific ttf would break the antag symbols
_font = resourceCache.NotoStack(); _font = resourceCache.NotoStack();
@@ -231,6 +236,18 @@ internal sealed class AdminNameOverlay : Overlay
currentOffset += lineoffset; currentOffset += lineoffset;
} }
// DeltaV - SSD Time START
if (_entityManager.TryGetComponent<SSDIndicatorComponent>(entity, out var ssdIndicator)
&& ssdIndicator.SsdSince is {} ssdSince)
{
color = Color.MediumPurple;
color.A = alpha;
var ssdText = Loc.GetString("admin-overlay-ssd-time", ("time", (_timing.CurTime - ssdSince).ToString("%hh':'mm':'ss")));
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, ssdText, uiScale, color);
currentOffset += lineoffset;
}
// DeltaV END
// Determine antag symbol // Determine antag symbol
string? symbol; string? symbol;
switch (_overlaySymbolStyle) switch (_overlaySymbolStyle)
@@ -5,6 +5,7 @@ using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; // DeltaV - SSD time indicator
namespace Content.Client.Administration.Systems namespace Content.Client.Administration.Systems
{ {
@@ -19,6 +20,7 @@ namespace Content.Client.Administration.Systems
[Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IGameTiming _timing = default!; // DeltaV - added for SSD time indicator
private AdminNameOverlay _adminNameOverlay = default!; private AdminNameOverlay _adminNameOverlay = default!;
@@ -36,7 +38,8 @@ namespace Content.Client.Administration.Systems
_userInterfaceManager, _userInterfaceManager,
_configurationManager, _configurationManager,
_roles, _roles,
_proto); _proto,
_timing); // DeltaV - Add timing
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated; _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
} }
@@ -21,11 +21,8 @@ public sealed class SignalTimerBoundUserInterface : BoundUserInterface
_window = this.CreateWindow<SignalTimerWindow>(); _window = this.CreateWindow<SignalTimerWindow>();
_window.OnStartTimer += StartTimer; _window.OnStartTimer += StartTimer;
_window.OnCurrentTextChanged += OnTextChanged; _window.OnCurrentTextChanged += OnTextChanged;
// Begin Monolith _window.OnCurrentDelayMinutesChanged += OnDelayChanged;
// _window.OnCurrentDelayMinutesChanged += OnDelayChanged; _window.OnCurrentDelaySecondsChanged += OnDelayChanged;
// _window.OnCurrentDelaySecondsChanged += OnDelayChanged;
_window.OnCurrentDelayChanged += OnDelayChanged;
// End Monolith
} }
public void StartTimer() public void StartTimer()
@@ -38,11 +35,11 @@ public sealed class SignalTimerBoundUserInterface : BoundUserInterface
SendMessage(new SignalTimerTextChangedMessage(newText)); SendMessage(new SignalTimerTextChangedMessage(newText));
} }
private void OnDelayChanged(TimeSpan newDelay) // Monolith - Change from string to TimeSpan private void OnDelayChanged(string newDelay)
{ {
if (_window == null) if (_window == null)
return; return;
SendMessage(new SignalTimerDelayChangedMessage(newDelay)); // Mono SendMessage(new SignalTimerDelayChangedMessage(_window.GetDelay()));
} }
/// <summary> /// <summary>
@@ -57,11 +54,8 @@ public sealed class SignalTimerBoundUserInterface : BoundUserInterface
return; return;
_window.SetCurrentText(cast.CurrentText); _window.SetCurrentText(cast.CurrentText);
// Begin Monolith _window.SetCurrentDelayMinutes(cast.CurrentDelayMinutes);
// _window.SetCurrentDelayMinutes(cast.CurrentDelayMinutes); _window.SetCurrentDelaySeconds(cast.CurrentDelaySeconds);
// _window.SetCurrentDelaySeconds(cast.CurrentDelaySeconds);
_window.SetCurrentDelay(cast.CurrentDelay);
// End Monolith
_window.SetShowText(cast.ShowText); _window.SetShowText(cast.ShowText);
_window.SetTriggerTime(cast.TriggerTime); _window.SetTriggerTime(cast.TriggerTime);
_window.SetTimerStarted(cast.TimerStarted); _window.SetTimerStarted(cast.TimerStarted);
@@ -7,14 +7,10 @@
</BoxContainer> </BoxContainer>
<BoxContainer Name="DelayEdit" Orientation="Horizontal"> <BoxContainer Name="DelayEdit" Orientation="Horizontal">
<Label Name="CurrentDelay" Text="{Loc signal-timer-menu-delay}" /> <Label Name="CurrentDelay" Text="{Loc signal-timer-menu-delay}" />
<!-- Mono: Changed from integer MM:SS to float seconds
<LineEdit Name="CurrentDelayEditMinutes" MinWidth="32" /> <LineEdit Name="CurrentDelayEditMinutes" MinWidth="32" />
<Label Name="Colon" Text=":" /> <Label Name="Colon" Text=":" />
<LineEdit Name="CurrentDelayEditSeconds" MinWidth="32" /> <LineEdit Name="CurrentDelayEditSeconds" MinWidth="32" />
<Label Name="DelayInfo" Text=" (mm:ss)" /> <Label Name="DelayInfo" Text=" (mm:ss)" />
-->
<FloatSpinBox Name="CurrentDelayEdit" MinWidth="32" />
<Label Text="s" />
</BoxContainer> </BoxContainer>
<Button Name="StartTimer" Text="{Loc signal-timer-menu-start}" /> <Button Name="StartTimer" Text="{Loc signal-timer-menu-start}" />
</BoxContainer> </BoxContainer>
@@ -15,11 +15,8 @@ public sealed partial class SignalTimerWindow : DefaultWindow
private const int MaxTextLength = 5; private const int MaxTextLength = 5;
public event Action<string>? OnCurrentTextChanged; public event Action<string>? OnCurrentTextChanged;
// Begin Monolith public event Action<string>? OnCurrentDelayMinutesChanged;
// public event Action<string>? OnCurrentDelayMinutesChanged; public event Action<string>? OnCurrentDelaySecondsChanged;
// public event Action<string>? OnCurrentDelaySecondsChanged;
public event Action<TimeSpan>? OnCurrentDelayChanged;
// End Monolith
private TimeSpan? _triggerTime; private TimeSpan? _triggerTime;
@@ -33,11 +30,8 @@ public sealed partial class SignalTimerWindow : DefaultWindow
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
CurrentTextEdit.OnTextChanged += e => OnCurrentTextChange(e.Text); CurrentTextEdit.OnTextChanged += e => OnCurrentTextChange(e.Text);
// Begin Monolith CurrentDelayEditMinutes.OnTextChanged += e => OnCurrentDelayMinutesChange(e.Text);
// CurrentDelayEditMinutes.OnTextChanged += e => OnCurrentDelayMinutesChange(e.Text); CurrentDelayEditSeconds.OnTextChanged += e => OnCurrentDelaySecondsChange(e.Text);
// CurrentDelayEditSeconds.OnTextChanged += e => OnCurrentDelaySecondsChange(e.Text);
CurrentDelayEdit.OnValueChanged += e => CurrentDelayChange(TimeSpan.FromSeconds(e.Value));
// End Monolith
StartTimer.OnPressed += _ => StartTimerWeh(); StartTimer.OnPressed += _ => StartTimerWeh();
} }
@@ -83,93 +77,81 @@ public sealed partial class SignalTimerWindow : DefaultWindow
OnCurrentTextChanged?.Invoke(text); OnCurrentTextChanged?.Invoke(text);
} }
// Begin Monolith public void OnCurrentDelayMinutesChange(string text)
// public void OnCurrentDelayMinutesChange(string text)
// {
// List<char> toRemove = new();
// foreach (var a in text)
// {
// if (!char.IsDigit(a))
// toRemove.Add(a);
// }
// foreach (var a in toRemove)
// {
// CurrentDelayEditMinutes.Text = text.Replace(a.ToString(),"");
// }
// if (CurrentDelayEditMinutes.Text == "")
// return;
// while (CurrentDelayEditMinutes.Text[0] == '0' && CurrentDelayEditMinutes.Text.Length > 2)
// {
// CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(0, 1);
// }
// if (CurrentDelayEditMinutes.Text.Length > 2)
// {
// CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(2);
// }
// OnCurrentDelayMinutesChanged?.Invoke(CurrentDelayEditMinutes.Text);
// }
// public void OnCurrentDelaySecondsChange(string text)
// {
// List<char> toRemove = new();
// foreach (var a in text)
// {
// if (!char.IsDigit(a))
// toRemove.Add(a);
// }
// foreach (var a in toRemove)
// {
// CurrentDelayEditSeconds.Text = text.Replace(a.ToString(), "");
// }
// if (CurrentDelayEditSeconds.Text == "")
// return;
// while (CurrentDelayEditSeconds.Text[0] == '0' && CurrentDelayEditSeconds.Text.Length > 2)
// {
// CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(0, 1);
// }
// if (CurrentDelayEditSeconds.Text.Length > 2)
// {
// CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(2);
// }
// OnCurrentDelaySecondsChanged?.Invoke(CurrentDelayEditSeconds.Text);
// }
public void CurrentDelayChange(TimeSpan newValue)
{ {
OnCurrentDelayChanged?.Invoke(newValue); List<char> toRemove = new();
foreach (var a in text)
{
if (!char.IsDigit(a))
toRemove.Add(a);
}
foreach (var a in toRemove)
{
CurrentDelayEditMinutes.Text = text.Replace(a.ToString(),"");
}
if (CurrentDelayEditMinutes.Text == "")
return;
while (CurrentDelayEditMinutes.Text[0] == '0' && CurrentDelayEditMinutes.Text.Length > 2)
{
CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(0, 1);
}
if (CurrentDelayEditMinutes.Text.Length > 2)
{
CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(2);
}
OnCurrentDelayMinutesChanged?.Invoke(CurrentDelayEditMinutes.Text);
}
public void OnCurrentDelaySecondsChange(string text)
{
List<char> toRemove = new();
foreach (var a in text)
{
if (!char.IsDigit(a))
toRemove.Add(a);
}
foreach (var a in toRemove)
{
CurrentDelayEditSeconds.Text = text.Replace(a.ToString(), "");
}
if (CurrentDelayEditSeconds.Text == "")
return;
while (CurrentDelayEditSeconds.Text[0] == '0' && CurrentDelayEditSeconds.Text.Length > 2)
{
CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(0, 1);
}
if (CurrentDelayEditSeconds.Text.Length > 2)
{
CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(2);
}
OnCurrentDelaySecondsChanged?.Invoke(CurrentDelayEditSeconds.Text);
} }
// End Monolith
public void SetCurrentText(string text) public void SetCurrentText(string text)
{ {
CurrentTextEdit.Text = text; CurrentTextEdit.Text = text;
} }
// Begin Monolith
// public void SetCurrentDelayMinutes(string delay)
// {
// CurrentDelayEditMinutes.Text = delay;
// }
// public void SetCurrentDelaySeconds(string delay) public void SetCurrentDelayMinutes(string delay)
// {
// CurrentDelayEditSeconds.Text = delay;
// }
public void SetCurrentDelay(TimeSpan delay)
{ {
CurrentDelayEdit.Value = (float)delay.TotalSeconds; CurrentDelayEditMinutes.Text = delay;
} }
// End Monolith
public void SetCurrentDelaySeconds(string delay)
{
CurrentDelayEditSeconds.Text = delay;
}
public void SetShowText(bool showTime) public void SetShowText(bool showTime)
{ {
TextEdit.Visible = showTime; TextEdit.Visible = showTime;
@@ -194,10 +176,8 @@ public sealed partial class SignalTimerWindow : DefaultWindow
public void SetHasAccess(bool hasAccess) public void SetHasAccess(bool hasAccess)
{ {
CurrentTextEdit.Editable = hasAccess; CurrentTextEdit.Editable = hasAccess;
// Begin Monolith CurrentDelayEditMinutes.Editable = hasAccess;
// CurrentDelayEditMinutes.Editable = hasAccess; CurrentDelayEditSeconds.Editable = hasAccess;
// CurrentDelayEditSeconds.Editable = hasAccess;
// End Monolith
StartTimer.Disabled = !hasAccess; StartTimer.Disabled = !hasAccess;
} }
@@ -206,13 +186,10 @@ public sealed partial class SignalTimerWindow : DefaultWindow
/// </summary> /// </summary>
public TimeSpan GetDelay() public TimeSpan GetDelay()
{ {
// Begin Monolith if (!double.TryParse(CurrentDelayEditMinutes.Text, out var minutes))
// if (!double.TryParse(CurrentDelayEditMinutes.Text, out var minutes)) minutes = 0;
// minutes = 0; if (!double.TryParse(CurrentDelayEditSeconds.Text, out var seconds))
// if (!double.TryParse(CurrentDelayEditSeconds.Text, out var seconds)) seconds = 0;
// seconds = 0; return TimeSpan.FromMinutes(minutes) + TimeSpan.FromSeconds(seconds);
// return TimeSpan.FromMinutes(minutes) + TimeSpan.FromSeconds(seconds);
return TimeSpan.FromSeconds(CurrentDelayEdit.Value);
// End Monolith
} }
} }
@@ -20,6 +20,7 @@
<CheckBox Name="ShowLoocAboveHeadCheckBox" Text="{Loc 'ui-options-show-looc-on-head'}" /> <CheckBox Name="ShowLoocAboveHeadCheckBox" Text="{Loc 'ui-options-show-looc-on-head'}" />
<CheckBox Name="FancySpeechBubblesCheckBox" Text="{Loc 'ui-options-fancy-speech'}" /> <CheckBox Name="FancySpeechBubblesCheckBox" Text="{Loc 'ui-options-fancy-speech'}" />
<CheckBox Name="FancyNameBackgroundsCheckBox" Text="{Loc 'ui-options-fancy-name-background'}" /> <CheckBox Name="FancyNameBackgroundsCheckBox" Text="{Loc 'ui-options-fancy-name-background'}" />
<CheckBox Name="ChatFollowButton" Text="{Loc 'ui-options-chat-follow-button'}" />
<Label Text="{Loc 'ui-options-general-cursor'}" <Label Text="{Loc 'ui-options-general-cursor'}"
StyleClasses="LabelKeyText"/> StyleClasses="LabelKeyText"/>
<CheckBox Name="ShowHeldItemCheckBox" Text="{Loc 'ui-options-show-held-item'}" /> <CheckBox Name="ShowHeldItemCheckBox" Text="{Loc 'ui-options-show-held-item'}" />
@@ -63,6 +63,7 @@ public sealed partial class MiscTab : Control
Control.AddOptionCheckBox(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox); Control.AddOptionCheckBox(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox);
Control.AddOptionCheckBox(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox); Control.AddOptionCheckBox(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox);
Control.AddOptionCheckBox(CCVars.StaticStorageUI, StaticStorageUI); Control.AddOptionCheckBox(CCVars.StaticStorageUI, StaticStorageUI);
Control.AddOptionCheckBox(CCVars.InterfaceChatFollowButton, ChatFollowButton);
Control.Initialize(); Control.Initialize();
} }
@@ -19,6 +19,7 @@ public sealed class SSDIndicatorSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly Shared.SSDIndicator.SSDIndicatorSystem _shared = default!; // DeltaV - SSD Recency, don't want to rename the upstream class
public override void Initialize() public override void Initialize()
{ {
@@ -35,13 +36,26 @@ public sealed class SSDIndicatorSystem : EntitySystem
!HasComp<ActiveNPCComponent>(uid) && !HasComp<ActiveNPCComponent>(uid) &&
HasComp<MindExaminableComponent>(uid)) HasComp<MindExaminableComponent>(uid))
{ {
// Begin DeltaV Addition // Begin DeltaV Additions
var ev = new ShowSSDIndicatorEvent(); var ev = new ShowSSDIndicatorEvent();
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);
if (ev.Hidden) if (ev.Hidden)
return; return;
// End DeltaV Addition
args.StatusIcons.Add(_prototype.Index(component.Icon)); // SSD Recency Indicator
var stage = _shared.GetStage(new Entity<SSDIndicatorComponent>(uid, component));
var icon = stage switch
{
SsdStage.VeryRecent => component.VeryRecentIcon,
SsdStage.Recent => component.RecentIcon,
SsdStage.Cryoable => component.Icon,
_ => throw new InvalidOperationException($"{ToPrettyString(uid)} has an invalid SSD stage {stage}."),
};
args.StatusIcons.Add(_prototype.Index(icon));
// End DeltaV Additions
// args.StatusIcons.Add(_prototype.Index(component.Icon)); // DeltaV - commented out. status icon now added above
} }
} }
} }
@@ -1,6 +0,0 @@
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
namespace Content.Client._DV.Psionics.Systems.PsionicPowers;
// This does nothing here. The code is all in the shared/server version.
public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem;
@@ -20,7 +20,11 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
private const string AnimateKey = "particle-animation"; private const string AnimateKey = "particle-animation";
private static readonly EntProtoId InteractionParticleId = "StellarInteractionParticle"; private static readonly Dictionary<StellarInteractionParticleType, EntProtoId> InteractionParticleIds = new ()
{
{ StellarInteractionParticleType.Use, "StellarInteractionParticleUse" },
{ StellarInteractionParticleType.Pull, "StellarInteractionParticlePull" },
};
public override void Initialize() public override void Initialize()
{ {
@@ -38,6 +42,11 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
if (!Exists(performer) || !Exists(target)) if (!Exists(performer) || !Exists(target))
return; return;
if (ev.Type == StellarInteractionParticleType.Pull)
{
(performer, target) = (target, performer);
}
var performerXform = Transform(performer); var performerXform = Transform(performer);
var targetXform = Transform(target); var targetXform = Transform(target);
if (performerXform.MapID == MapId.Nullspace || targetXform.MapID == MapId.Nullspace) if (performerXform.MapID == MapId.Nullspace || targetXform.MapID == MapId.Nullspace)
@@ -47,7 +56,7 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
return; return;
var performerTargetDelta = targetXform.LocalPosition - performerXform.LocalPosition; var performerTargetDelta = targetXform.LocalPosition - performerXform.LocalPosition;
var particle = Spawn(InteractionParticleId, performerXform.Coordinates); var particle = Spawn(InteractionParticleIds[ev.Type], performerXform.Coordinates);
if (used is { } usedEntity && Exists(usedEntity) && TryComp<SpriteComponent>(usedEntity, out var usedSprite)) if (used is { } usedEntity && Exists(usedEntity) && TryComp<SpriteComponent>(usedEntity, out var usedSprite))
{ {
@@ -58,21 +67,27 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
} }
var spriteColor = Comp<SpriteComponent>(particle).Color; var spriteColor = Comp<SpriteComponent>(particle).Color;
_animation.Play(particle, GetAnimation(performerTargetDelta, spriteColor), AnimateKey); var animation = ev.Type switch
{
StellarInteractionParticleType.Use => GetUseAnimation(performerTargetDelta, spriteColor),
StellarInteractionParticleType.Pull => GetPullAnimation(performerTargetDelta, spriteColor),
_ => throw new ArgumentOutOfRangeException(nameof(ev), $"Interaction particle event has unknown particle type {ev.Type}"),
};
_animation.Play(particle, animation, AnimateKey);
} }
private Animation GetAnimation(Vector2 endOffset, Color color) private Animation GetUseAnimation(Vector2 endOffset, Color color)
{ {
var startRotation = _random.NextAngle(Angle.FromDegrees(-80), Angle.FromDegrees(80)); var startRotation = _random.NextAngle(Angle.FromDegrees(-40), Angle.FromDegrees(40));
var endRotation = Angle.Zero; var endRotation = Angle.Zero;
var startScale = new Vector2(0.3f, 0.3f); var startScale = new Vector2(0.3f, 0.3f);
var endScale = new Vector2(1f, 1f); var endScale = new Vector2(1f, 1f);
var rotationLength = TimeSpan.FromMilliseconds(600); var rotationLength = TimeSpan.FromMilliseconds(600);
var startOffset = new Vector2(); var startOffset = new Vector2();
var offsetLength = TimeSpan.FromMilliseconds(200); var offsetLength = TimeSpan.FromMilliseconds(250);
var startColor = color.WithAlpha(color.A * 0.9f); var startColor = color.WithAlpha(color.A * 0.7f);
var endColor = color.WithAlpha(0f); var endColor = color.WithAlpha(0f);
var colorLength = rotationLength + offsetLength; var colorLength = rotationLength + offsetLength;
@@ -89,7 +104,7 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
KeyFrames = KeyFrames =
{ {
new AnimationTrackProperty.KeyFrame(startRotation, 0f), new AnimationTrackProperty.KeyFrame(startRotation, 0f),
new AnimationTrackProperty.KeyFrame(endRotation, (float)rotationLength.TotalSeconds, Easings.OutBounce), new AnimationTrackProperty.KeyFrame(endRotation, (float)rotationLength.TotalSeconds, Easings.OutBack),
}, },
}, },
new AnimationTrackComponentProperty() new AnimationTrackComponentProperty()
@@ -99,7 +114,7 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
KeyFrames = KeyFrames =
{ {
new AnimationTrackProperty.KeyFrame(startScale, 0f), new AnimationTrackProperty.KeyFrame(startScale, 0f),
new AnimationTrackProperty.KeyFrame(endScale, (float)rotationLength.TotalSeconds, Easings.OutBounce), new AnimationTrackProperty.KeyFrame(endScale, (float)rotationLength.TotalSeconds, Easings.OutBack),
}, },
}, },
new AnimationTrackComponentProperty() new AnimationTrackComponentProperty()
@@ -109,7 +124,7 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
KeyFrames = KeyFrames =
{ {
new AnimationTrackProperty.KeyFrame(startOffset, 0f), new AnimationTrackProperty.KeyFrame(startOffset, 0f),
new AnimationTrackProperty.KeyFrame(endOffset, (float)offsetLength.TotalSeconds, Easings.OutBounce), new AnimationTrackProperty.KeyFrame(endOffset, (float)offsetLength.TotalSeconds, Easings.OutBack),
}, },
}, },
new AnimationTrackComponentProperty() new AnimationTrackComponentProperty()
@@ -126,4 +141,44 @@ public sealed class StellarInteractionParticleSystem : EntitySystem
}, },
}; };
} }
private Animation GetPullAnimation(Vector2 endOffset, Color color)
{
var rotationLength = TimeSpan.FromMilliseconds(8f * (1000f / 12f));
var startOffset = new Vector2();
var offsetLength = TimeSpan.FromMilliseconds(4f * (1000f / 12f));
var endColor = color.WithAlpha(0f);
return new Animation
{
Length = rotationLength,
AnimationTracks =
{
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Offset),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(startOffset, 0f),
new AnimationTrackProperty.KeyFrame(endOffset, (float)rotationLength.TotalSeconds, Easings.InOutCirc),
},
},
new AnimationTrackComponentProperty()
{
ComponentType = typeof(SpriteComponent),
Property = nameof(SpriteComponent.Color),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(color, 0f),
new AnimationTrackProperty.KeyFrame(color, (float)offsetLength.TotalSeconds),
new AnimationTrackProperty.KeyFrame(endColor, (float)rotationLength.TotalSeconds, Easings.InOutCirc),
},
},
},
};
}
} }
@@ -245,6 +245,7 @@ namespace Content.IntegrationTests.Tests
"TimedDespawnDetailed", // DeltaV "TimedDespawnDetailed", // DeltaV
// makes an announcement on mapInit. // makes an announcement on mapInit.
"AnnounceOnSpawn", "AnnounceOnSpawn",
"ESTimedDespawn" // DeltaV
}; };
Assert.That(server.CfgMan.GetCVar(CVars.NetPVS), Is.False); Assert.That(server.CfgMan.GetCVar(CVars.NetPVS), Is.False);
+17 -4
View File
@@ -87,7 +87,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
if (anomaly.Comp.CurrentBehavior is not null) if (anomaly.Comp.CurrentBehavior is not null)
RemoveBehavior(anomaly, anomaly.Comp.CurrentBehavior.Value); RemoveBehavior(anomaly, anomaly.Comp.CurrentBehavior.Value);
EndAnomaly(anomaly, spawnCore: false); EndAnomaly(anomaly, spawnCore: false, forced: true);
} }
private void OnStartCollide(Entity<AnomalyComponent> anomaly, ref StartCollideEvent args) private void OnStartCollide(Entity<AnomalyComponent> anomaly, ref StartCollideEvent args)
@@ -143,7 +143,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
return 0; return 0;
var multiplier = 1f; var multiplier = 1f;
if (component.Stability > component.GrowthThreshold) if (component.AlwaysGrow || component.Stability > component.GrowthThreshold) // DeltaV - Add AlwaysGrow
multiplier = component.GrowingPointMultiplier; //more points for unstable multiplier = component.GrowingPointMultiplier; //more points for unstable
//penalty of up to 50% based on health //penalty of up to 50% based on health
@@ -253,7 +253,11 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
else else
{ {
string stateLoc; string stateLoc;
if (anomalyComp.Stability < anomalyComp.DecayThreshold) // DeltaV - Colossus Additions START
if (anomalyComp.AlwaysGrow)
stateLoc = Loc.GetString("anomaly-scanner-stability-high");
// DeltaV - Colossus Additions END
else if (anomalyComp.Stability < anomalyComp.DecayThreshold) // DeltaV - Add else
stateLoc = Loc.GetString("anomaly-scanner-stability-low"); stateLoc = Loc.GetString("anomaly-scanner-stability-low");
else if (anomalyComp.Stability > anomalyComp.GrowthThreshold) else if (anomalyComp.Stability > anomalyComp.GrowthThreshold)
stateLoc = Loc.GetString("anomaly-scanner-stability-high"); stateLoc = Loc.GetString("anomaly-scanner-stability-high");
@@ -345,6 +349,15 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
msg.AddMarkupOrThrow(Loc.GetString("anomaly-behavior-unknown")); msg.AddMarkupOrThrow(Loc.GetString("anomaly-behavior-unknown"));
else else
{ {
// DeltaV - Colossus Additions START
if (anomalyComp.AlwaysGrow)
{
msg.AddMarkupOrThrow("- " + Loc.GetString("anomaly-behavior-always-grow"));
if (anomalyComp.CurrentBehavior != null)
msg.PushNewline();
}
// DeltaV - Colossus Additions END
if (anomalyComp.CurrentBehavior != null) if (anomalyComp.CurrentBehavior != null)
{ {
var behavior = _prototype.Index(anomalyComp.CurrentBehavior.Value); var behavior = _prototype.Index(anomalyComp.CurrentBehavior.Value);
@@ -354,7 +367,7 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
var mod = Math.Floor((behavior.EarnPointModifier) * 100); var mod = Math.Floor((behavior.EarnPointModifier) * 100);
msg.AddMarkupOrThrow("- " + Loc.GetString("anomaly-behavior-point", ("mod", mod))); msg.AddMarkupOrThrow("- " + Loc.GetString("anomaly-behavior-point", ("mod", mod)));
} }
else else if(!anomalyComp.AlwaysGrow) // DeltaV - Add condition, previously regular else
{ {
msg.AddMarkupOrThrow(Loc.GetString("anomaly-behavior-balanced")); msg.AddMarkupOrThrow(Loc.GetString("anomaly-behavior-balanced"));
} }
+49 -2
View File
@@ -5,6 +5,7 @@ using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Administration.Systems; using Content.Server.Administration.Systems;
using Content.Server.Discord.DiscordLink; using Content.Server.Discord.DiscordLink;
using Content.Server.Ghost;
using Content.Server.Players.RateLimiting; using Content.Server.Players.RateLimiting;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Shared.Administration; using Content.Shared.Administration;
@@ -15,6 +16,7 @@ using Content.Shared.Mind;
using Content.Shared.Players; // DeltaV - OOC muting using Content.Shared.Players; // DeltaV - OOC muting
using Content.Shared.Players.RateLimiting; using Content.Shared.Players.RateLimiting;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Replays; using Robust.Shared.Replays;
@@ -47,6 +49,7 @@ internal sealed partial class ChatManager : IChatManager
[Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly ISharedPlayerManager _player = default!;
[Dependency] private readonly DiscordChatLink _discordLink = default!; [Dependency] private readonly DiscordChatLink _discordLink = default!;
[Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
private ISawmill _sawmill = default!; private ISawmill _sawmill = default!;
@@ -342,12 +345,35 @@ internal sealed partial class ChatManager : IChatManager
#region Utility #region Utility
private bool IsValidWarpDestination(EntityUid source)
{
if (!source.Valid)
return false;
if (!_entityManager.TryGetComponent(source, out TransformComponent? transform))
return false;
return transform.MapID != MapId.Nullspace;
}
public string PrependFollowButtonIfAppropriate(string wrappedMessage, EntityUid source, INetChannel recipient)
{
if (IsValidWarpDestination(source) && ShouldShowFollowButton(recipient))
{
var btnText = _localizationManager.GetString("chat-manager-follow-button");
return $"[cmdlink=\"{btnText}\" command=\"{GhostFollowEntityCommand.CommandName} {_entityManager.GetNetEntity(source)}\" /] " + wrappedMessage;
}
return wrappedMessage;
}
public void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, NetUserId? author = null) public void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, NetUserId? author = null)
{ {
var user = author == null ? null : EnsurePlayer(author); var user = author == null ? null : EnsurePlayer(author);
var netSource = _entityManager.GetNetEntity(source); var netSource = _entityManager.GetNetEntity(source);
user?.AddEntity(netSource); user?.AddEntity(netSource);
wrappedMessage = PrependFollowButtonIfAppropriate(wrappedMessage, source, client);
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume); var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
_netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client); _netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client);
@@ -370,8 +396,12 @@ internal sealed partial class ChatManager : IChatManager
var netSource = _entityManager.GetNetEntity(source); var netSource = _entityManager.GetNetEntity(source);
user?.AddEntity(netSource); user?.AddEntity(netSource);
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume); foreach (var client in clients)
_netManager.ServerSendToMany(new MsgChatMessage() { Message = msg }, clients); {
var customWrapMessage = PrependFollowButtonIfAppropriate(wrappedMessage, source, client);
var msg = new ChatMessage(channel, message, customWrapMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
_netManager.ServerSendMessage(new MsgChatMessage { Message = msg }, client);
}
if (!recordReplay) if (!recordReplay)
return; return;
@@ -379,6 +409,7 @@ internal sealed partial class ChatManager : IChatManager
if ((channel & ChatChannel.AdminRelated) == 0 || if ((channel & ChatChannel.AdminRelated) == 0 ||
_configurationManager.GetCVar(CCVars.ReplayRecordAdminChat)) _configurationManager.GetCVar(CCVars.ReplayRecordAdminChat))
{ {
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
_replay.RecordServerMessage(msg); _replay.RecordServerMessage(msg);
} }
} }
@@ -439,6 +470,22 @@ internal sealed partial class ChatManager : IChatManager
} }
#endregion #endregion
private bool ShouldShowFollowButton(INetChannel recipient)
{
if (!_player.TryGetSessionByChannel(recipient, out var session))
return false;
if (_entityManager.TrySystem(out GhostSystem? ghost))
{
if (!ghost.CanGhostWarp(session, out _))
{
return false;
}
}
return _netConfigManager.GetClientCVar(recipient, CCVars.InterfaceChatFollowButton);
}
} }
public enum OOCChatType : byte public enum OOCChatType : byte
@@ -93,7 +93,6 @@ public sealed class ChatSanitizationManager : IChatSanitizationManager
Entry("('=", "chatsan-tearfully-smiles"), Entry("('=", "chatsan-tearfully-smiles"),
Entry("['=", "chatsan-tearfully-smiles"), Entry("['=", "chatsan-tearfully-smiles"),
Entry("?", "chatsan-confused"), //DeltaV Entry("?", "chatsan-confused"), //DeltaV
Entry("!", "chatsan-surprised"), //DeltaV
Entry("…", "chatsan-sighs"), //DeltaV (note that ... doesn't work as that turns into a radio message) Entry("…", "chatsan-sighs"), //DeltaV (note that ... doesn't work as that turns into a radio message)
]; ];
@@ -49,5 +49,7 @@ namespace Content.Server.Chat.Managers
/// <param name="player">The player sending a chat message.</param> /// <param name="player">The player sending a chat message.</param>
/// <returns>False if the player has violated rate limits and should be blocked from sending further messages.</returns> /// <returns>False if the player has violated rate limits and should be blocked from sending further messages.</returns>
RateLimitStatus HandleRateLimit(ICommonSession player); RateLimitStatus HandleRateLimit(ICommonSession player);
string PrependFollowButtonIfAppropriate(string wrappedMessage, EntityUid source, INetChannel recipient);
} }
} }
+90 -9
View File
@@ -8,6 +8,7 @@ using Content.Server.GameTicking;
using Content.Server.Speech.EntitySystems; using Content.Server.Speech.EntitySystems;
using Content.Shared.Speech.Hushing; // DeltaV using Content.Shared.Speech.Hushing; // DeltaV
using Content.Server.Nyanotrasen.Chat; using Content.Server.Nyanotrasen.Chat;
using Content.Server.Speech.Components; // DeltaV
using Content.Server.Speech.Prototypes; using Content.Server.Speech.Prototypes;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
@@ -35,6 +36,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Replays; using Robust.Shared.Replays;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Content.Shared._DV.Chat; // DeltaV - chat enhancements
namespace Content.Server.Chat.Systems; namespace Content.Server.Chat.Systems;
@@ -215,7 +217,7 @@ public sealed partial class ChatSystem : SharedChatSystem
// Was there an emote in the message? If so, send it. // Was there an emote in the message? If so, send it.
if (player != null && emoteStr != message && emoteStr != null) if (player != null && emoteStr != message && emoteStr != null)
{ {
SendEntityEmote(source, emoteStr, range, nameOverride, ignoreActionBlocker); SendEntityEmote(source, emoteStr, range, nameOverride, null, ignoreActionBlocker); // DeltaV - Had to change up for SendEntityEmote
} }
// This can happen if the entire string is sanitized out. // This can happen if the entire string is sanitized out.
@@ -252,9 +254,18 @@ public sealed partial class ChatSystem : SharedChatSystem
case InGameICChatType.Whisper: case InGameICChatType.Whisper:
SendEntityWhisper(source, message, range, null, nameOverride, hideLog, ignoreActionBlocker); SendEntityWhisper(source, message, range, null, nameOverride, hideLog, ignoreActionBlocker);
break; break;
case InGameICChatType.Emote: case InGameICChatType.Emote: // DeltaV - Emote now has different types of emotes.
SendEntityEmote(source, message, range, nameOverride, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker); var type = ProcessEmoteMessage(source, message, out var modMessage);
break; if (type == EmoteType.Audible || type == EmoteType.AudiblePossessive)
{
if (checkRadioPrefix && TryProcessRadioMessage(source, modMessage, out var outputMessage, out var channel, capitalize: false))
SendAudibleEntityEmote(source, outputMessage, range, nameOverride, channel, type, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker);
else
SendAudibleEntityEmote(source, modMessage, range, nameOverride, null, type, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker);
}
else
SendEntityEmote(source, modMessage, range, nameOverride, type, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker);
break; // DeltaV - End
//Nyano - Summary: case adds the telepathic chat sending ability. //Nyano - Summary: case adds the telepathic chat sending ability.
case InGameICChatType.Telepathic: case InGameICChatType.Telepathic:
_nyanoChatSystem.SendTelepathicChat(source, message, range == ChatTransmitRange.HideChat); _nyanoChatSystem.SendTelepathicChat(source, message, range == ChatTransmitRange.HideChat);
@@ -629,6 +640,7 @@ public sealed partial class ChatSystem : SharedChatSystem
string action, string action,
ChatTransmitRange range, ChatTransmitRange range,
string? nameOverride, string? nameOverride,
EmoteType? emoteType, // DeltaV
bool hideLog = false, bool hideLog = false,
bool checkEmote = true, bool checkEmote = true,
bool ignoreActionBlocker = false, bool ignoreActionBlocker = false,
@@ -642,11 +654,21 @@ public sealed partial class ChatSystem : SharedChatSystem
var ent = Identity.Entity(source, EntityManager); var ent = Identity.Entity(source, EntityManager);
string name = FormattedMessage.EscapeText(nameOverride ?? Name(ent)); string name = FormattedMessage.EscapeText(nameOverride ?? Name(ent));
// Begin DeltaV
string wrappedMessage;
// Emotes use Identity.Name, since it doesn't actually involve your voice at all. // Emotes use Identity.Name, since it doesn't actually involve your voice at all.
var wrappedMessage = Loc.GetString("chat-manager-entity-me-wrap-message", if (emoteType == EmoteType.Possessive) // DeltaV - Emote types now get checked.
("entityName", name), wrappedMessage = Loc.GetString("chat-manager-entity-me-possessive-wrap-message",
("entity", ent), ("entityName", name),
("message", FormattedMessage.RemoveMarkupOrThrow(action))); ("entity", ent),
("message", FormattedMessage.RemoveMarkupOrThrow(action)));
else
wrappedMessage = Loc.GetString("chat-manager-entity-me-wrap-message",
("entityName", name),
("entity", ent),
("message", FormattedMessage.RemoveMarkupOrThrow(action)));
// End DeltaV
if (checkEmote && if (checkEmote &&
!TryEmoteChatInput(source, action)) !TryEmoteChatInput(source, action))
@@ -660,6 +682,59 @@ public sealed partial class ChatSystem : SharedChatSystem
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Emote from {source}: {action}"); _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Emote from {source}: {action}");
} }
// DeltaV - Added this to differentiate between emotes that can be heard over radio and those which can't.
protected override void SendAudibleEntityEmote(
EntityUid source,
string action,
ChatTransmitRange range,
string? nameOverride,
RadioChannelPrototype? channel,
EmoteType? emoteType,
bool hideLog = false,
bool checkEmote = true,
bool ignoreActionBlocker = false,
NetUserId? author = null
)
{
if (!_actionBlocker.CanSpeak(source) && !ignoreActionBlocker)
return;
EmoteType type = emoteType ?? EmoteType.Audible;
// get the entity's apparent name (if no override provided).
var ent = Identity.Entity(source, EntityManager);
string name = FormattedMessage.EscapeText(nameOverride ?? Name(ent));
string wrappedMessage;
// Audible emotes use Identity.Name, since that is the status quo. Emotes like scream doesn't currently reveal identities so this won't either. [This may be changed with feedback as you can audibly emote over radios]
if (emoteType == EmoteType.AudiblePossessive)
wrappedMessage = Loc.GetString("chat-manager-entity-me-audible-possessive-wrap-message",
("entityName", name),
("entity", ent),
("message", FormattedMessage.RemoveMarkupOrThrow(action)));
else
wrappedMessage = Loc.GetString("chat-manager-entity-me-audible-wrap-message",
("entityName", name),
("entity", ent),
("message", FormattedMessage.RemoveMarkupOrThrow(action)));
if (checkEmote &&
!TryEmoteChatInput(source, action))
return;
SendInVoiceRange(ChatChannel.Emotes, action, wrappedMessage, source, range, author);
var ev = new EntityAudiblyEmotedEvent(source, action, channel, type);
RaiseLocalEvent(source, ref ev, true);
if (!hideLog)
if (name != Name(source))
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Emote from {source} as {name}: {action}");
else
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Emote from {source}: {action}");
}
// DeltaV - End
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
private void SendLOOC(EntityUid source, ICommonSession player, string message, bool hideChat) private void SendLOOC(EntityUid source, ICommonSession player, string message, bool hideChat)
{ {
@@ -889,6 +964,7 @@ public sealed partial class ChatSystem : SharedChatSystem
var recipients = new Dictionary<ICommonSession, ICChatRecipientData>(); var recipients = new Dictionary<ICommonSession, ICChatRecipientData>();
var ghostHearing = GetEntityQuery<GhostHearingComponent>(); var ghostHearing = GetEntityQuery<GhostHearingComponent>();
var xforms = GetEntityQuery<TransformComponent>(); var xforms = GetEntityQuery<TransformComponent>();
var blockListening = GetEntityQuery<BlockListeningComponent>(); // DeltaV - block listening
var transformSource = xforms.GetComponent(source); var transformSource = xforms.GetComponent(source);
var sourceMapId = transformSource.MapID; var sourceMapId = transformSource.MapID;
@@ -904,6 +980,11 @@ public sealed partial class ChatSystem : SharedChatSystem
if (transformEntity.MapID != sourceMapId) if (transformEntity.MapID != sourceMapId)
continue; continue;
// Begin DeltaV - block listening
if (blockListening.HasComponent(playerEntity))
continue;
// End DeltaV - block listening
var observer = ghostHearing.HasComponent(playerEntity); var observer = ghostHearing.HasComponent(playerEntity);
// even if they are a ghost hearer, in some situations we still need the range // even if they are a ghost hearer, in some situations we still need the range
@@ -971,4 +1052,4 @@ public sealed class CheckTargetedSpeechEvent : EntityEventArgs
{ {
public List<EntityUid> Targets = new List<EntityUid>(); public List<EntityUid> Targets = new List<EntityUid>();
} }
// END Mono // END Mono
@@ -51,11 +51,8 @@ public sealed class SignalTimerSystem : EntitySystem
if (_ui.HasUi(uid, SignalTimerUiKey.Key)) if (_ui.HasUi(uid, SignalTimerUiKey.Key))
{ {
_ui.SetUiState(uid, SignalTimerUiKey.Key, new SignalTimerBoundUserInterfaceState(component.Label, _ui.SetUiState(uid, SignalTimerUiKey.Key, new SignalTimerBoundUserInterfaceState(component.Label,
// Begin Monolith TimeSpan.FromSeconds(component.Delay).Minutes.ToString("D2"),
// TimeSpan.FromSeconds(component.Delay).Minutes.ToString("D2"), TimeSpan.FromSeconds(component.Delay).Seconds.ToString("D2"),
// TimeSpan.FromSeconds(component.Delay).Seconds.ToString("D2"),
TimeSpan.FromSeconds(component.Delay),
// End Monolith
component.CanEditLabel, component.CanEditLabel,
time, time,
active != null, active != null,
@@ -76,11 +73,8 @@ public sealed class SignalTimerSystem : EntitySystem
if (_ui.HasUi(uid, SignalTimerUiKey.Key)) if (_ui.HasUi(uid, SignalTimerUiKey.Key))
{ {
_ui.SetUiState(uid, SignalTimerUiKey.Key, new SignalTimerBoundUserInterfaceState(signalTimer.Label, _ui.SetUiState(uid, SignalTimerUiKey.Key, new SignalTimerBoundUserInterfaceState(signalTimer.Label,
// Begin Monolith TimeSpan.FromSeconds(signalTimer.Delay).Minutes.ToString("D2"),
// TimeSpan.FromSeconds(signalTimer.Delay).Minutes.ToString("D2"), TimeSpan.FromSeconds(signalTimer.Delay).Seconds.ToString("D2"),
// TimeSpan.FromSeconds(signalTimer.Delay).Seconds.ToString("D2"),
TimeSpan.FromSeconds(signalTimer.Delay),
// End Monolith
signalTimer.CanEditLabel, signalTimer.CanEditLabel,
TimeSpan.Zero, TimeSpan.Zero,
false, false,
@@ -382,11 +382,6 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity); var visualEnt = CreateExplosionVisualEntity(pos, queued.Proto.ID, spaceMatrix, spaceData, gridData.Values, iterationIntensity);
// camera shake
// ES START
// CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
// ES END
//For whatever bloody reason, sound system requires ENTITY coordinates. //For whatever bloody reason, sound system requires ENTITY coordinates.
var mapEntityCoords = _transformSystem.ToCoordinates(_map.GetMap(pos.MapId), pos); var mapEntityCoords = _transformSystem.ToCoordinates(_map.GetMap(pos.MapId), pos);
@@ -416,12 +411,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
? queued.Proto.SmallSoundFar ? queued.Proto.SmallSoundFar
: queued.Proto.SoundFar; : queued.Proto.SoundFar;
// ES START CameraShake(iterationIntensity, pos, queued); // Starlight
var farTranslationShake = iterationIntensity.Count < queued.Proto.SmallSoundIterationThreshold
? new ESScreenshakeParameters() { Trauma = 0.4f, DecayRate = 0.2f, Frequency = 0.014f }
: new ESScreenshakeParameters() { Trauma = 0.6f, DecayRate = 0.05f, Frequency = 0.014f };
_shake.Screenshake(filter, farTranslationShake, null);
// ES END
_audio.PlayGlobal(farSound, farFilter, true, farSound.Params); _audio.PlayGlobal(farSound, farFilter, true, farSound.Params);
@@ -444,8 +434,13 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
_damageableSystem); _damageableSystem);
} }
private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity) private void CameraShake(List<float> rangeList, MapCoordinates epicenter, QueuedExplosion queued) // Starlight - replace range with rangeList, totalIntensity with queued
{ {
// Starlight BEGIN
var range = rangeList.Count * 4f;
var totalIntensity = queued.TotalIntensity * 10f;
// Starlight END
var players = Filter.Empty(); var players = Filter.Empty();
players.AddInRange(epicenter, range, _playerManager, EntityManager); players.AddInRange(epicenter, range, _playerManager, EntityManager);
@@ -463,7 +458,40 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
var distance = delta.Length(); var distance = delta.Length();
var effect = 5 * MathF.Pow(totalIntensity, 0.5f) * (1 - distance / range); var effect = 5 * MathF.Pow(totalIntensity, 0.5f) * (1 - distance / range);
if (effect > 0.01f) if (effect > 0.01f)
_recoilSystem.KickCamera(uid, -delta.Normalized() * effect); {
// DeltaV - Camera kick falloff START
// _recoilSystem.KickCamera(uid, -delta.Normalized() * effect);
// Exponential decay: N(t) = N₀ ⋅ e^(-λ ⋅ t)
// In this case, N = effect and we aren't decaying over time but with increasing distance from the epicenter,
// so t = distance / range. Higher values of λ lead to faster decay, lower values to slower decay.
//
// severity is a fixed factor used to decrease the overall severity of the camera kick,
// as the values were so high by default that decay was only becoming really noticeable near range.
//
// these changes are made here instead of directly assigning to the effect variable
// above to maintain the same overall effect range as before.
const float severity = 0.033f;
const float lambda = 4f;
_recoilSystem.KickCamera(uid, -delta.Normalized() * effect * severity * MathF.Exp(-lambda * (distance / range)));
// DeltaV END
// Starlight START
var shakeParams = rangeList.Count < queued.Proto.SmallSoundIterationThreshold
? new ESScreenshakeParameters() { Trauma = 0.4f, DecayRate = 0.2f, Frequency = 0.014f }
: new ESScreenshakeParameters() { Trauma = 0.6f, DecayRate = 0.05f, Frequency = 0.014f };
// DeltaV - Screenshake falloff START
// Linear falloff with increasing distance from epicenter,
// capped at certain values to avoid diminishing the effect completely near range.
shakeParams.DecayRate *= MathF.Min(1 + distance / range, 1.75f);
shakeParams.Frequency *= MathF.Max(1 - distance / range, 0.33f);
shakeParams.Trauma *= MathF.Max(1 - distance / range, 0.33f);
// DeltaV END
_shake.Screenshake(players, shakeParams, null);
// Starlight END
}
} }
} }
} }
+16 -1
View File
@@ -12,6 +12,7 @@ using Content.Shared.Database;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components; using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Events; using Content.Shared.DeviceNetwork.Events;
using Content.Shared.DeviceNetwork.Systems; // DeltaV - map init ordering
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Fax; using Content.Shared.Fax;
using Content.Shared.Fax.Components; using Content.Shared.Fax.Components;
@@ -64,7 +65,7 @@ public sealed class FaxSystem : EntitySystem
// Hooks // Hooks
SubscribeLocalEvent<FaxMachineComponent, ComponentInit>(OnComponentInit); SubscribeLocalEvent<FaxMachineComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<FaxMachineComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<FaxMachineComponent, MapInitEvent>(OnMapInit, after: [typeof(SharedDeviceNetworkSystem)]); // DeltaV - map init order, we need address assigned first
SubscribeLocalEvent<FaxMachineComponent, ComponentRemove>(OnComponentRemove); SubscribeLocalEvent<FaxMachineComponent, ComponentRemove>(OnComponentRemove);
SubscribeLocalEvent<FaxMachineComponent, EntInsertedIntoContainerMessage>(OnItemSlotChanged); SubscribeLocalEvent<FaxMachineComponent, EntInsertedIntoContainerMessage>(OnItemSlotChanged);
@@ -301,6 +302,11 @@ public sealed class FaxSystem : EntitySystem
if (!args.Data.TryGetValue(FaxConstants.FaxPaperNameData, out string? name) || if (!args.Data.TryGetValue(FaxConstants.FaxPaperNameData, out string? name) ||
!args.Data.TryGetValue(FaxConstants.FaxPaperContentData, out string? content)) !args.Data.TryGetValue(FaxConstants.FaxPaperContentData, out string? content))
return; return;
// Begin DeltaV - we removed the power requirement from device network but we still don't want
// unpowered faxes to happen
if (TryComp<ApcPowerReceiverComponent>(uid, out var receiver) && !receiver.Powered)
return;
// End DeltaV
args.Data.TryGetValue(FaxConstants.FaxPaperLabelData, out string? label); args.Data.TryGetValue(FaxConstants.FaxPaperLabelData, out string? label);
args.Data.TryGetValue(FaxConstants.FaxPaperStampStateData, out string? stampState); args.Data.TryGetValue(FaxConstants.FaxPaperStampStateData, out string? stampState);
@@ -582,6 +588,15 @@ public sealed class FaxSystem : EntitySystem
return; return;
var faxName = Loc.GetString("fax-machine-popup-source-unknown"); var faxName = Loc.GetString("fax-machine-popup-source-unknown");
// BEGIN DeltaV - Refresh Fax IDs before recieving
// This will seem odd that we're doing the same if statement twice BUT if its not in the known fax machine list, the
// sending fax might not have been online when the current fax machine was refreshed/init'd. So, if its already known,
// we don't need to refresh. But if its not known, let's attempt to refresh first, then check the fromAddress in the KnownFaxes.
if (fromAddress != null && !component.KnownFaxes.TryGetValue(fromAddress, out var _))
Refresh(uid, component);
// END DeltaV
if (fromAddress != null && component.KnownFaxes.TryGetValue(fromAddress, out var fax)) // If message received from unknown fax address if (fromAddress != null && component.KnownFaxes.TryGetValue(fromAddress, out var fax)) // If message received from unknown fax address
faxName = fax; faxName = fax;
@@ -35,17 +35,17 @@ public sealed partial class PuddleSystem
if (!entity.Comp.SpillWhenThrown || Openable.IsClosed(entity.Owner)) if (!entity.Comp.SpillWhenThrown || Openable.IsClosed(entity.Owner))
return; return;
// DeltaV - Beer Goggles Safe Throw
if ( args.User is { } user && _safeSolutionThrower.GetSafeThrow(user))
{
_physics.SetAngularVelocity(entity, 0);
Transform(entity).LocalRotation = Angle.Zero;
return;
}
// END DeltaV
if (TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, out _, out var solution) && args.User != null) if (TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, out _, out var solution) && args.User != null)
{ {
// DeltaV - Beer Goggles Safe Throw
if (_safeSolutionThrower.GetSafeThrow(args.User.Value))
{
_physics.SetAngularVelocity(entity, 0);
Transform(entity).LocalRotation = Angle.Zero;
return;
}
// END DeltaV
AdminLogger.Add(LogType.Landed, AdminLogger.Add(LogType.Landed,
$"{ToPrettyString(entity.Owner):entity} spilled a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} on landing"); $"{ToPrettyString(entity.Owner):entity} spilled a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} on landing");
} }
@@ -0,0 +1,26 @@
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Ghost;
[AnyCommand]
internal sealed partial class GhostFollowEntityCommand : LocalizedEntityCommands
{
public const string CommandName = "ghost_follow_entity";
[Dependency] private GhostSystem _ghost = null!;
public override string Command => CommandName;
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1 || shell.Player is not { } player)
return;
var target = args[0];
if (!NetEntity.TryParse(target, out var targetEnt))
return;
_ghost.GhostWarpRequest(player, targetEnt);
}
}
+35 -20
View File
@@ -299,10 +299,22 @@ namespace Content.Server.Ghost
#region Warp #region Warp
public bool CanGhostWarp(ICommonSession session, out EntityUid entity)
{
if (session.AttachedEntity is not { Valid: true } sessionEntity
|| !_ghostQuery.HasComp(sessionEntity))
{
entity = default;
return false;
}
entity = sessionEntity;
return true;
}
private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventArgs args) private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventArgs args)
{ {
if (args.SenderSession.AttachedEntity is not {Valid: true} entity if (!CanGhostWarp(args.SenderSession, out var entity))
|| !_ghostQuery.HasComp(entity))
{ {
Log.Warning($"User {args.SenderSession.Name} sent a {nameof(GhostWarpsRequestEvent)} without being a ghost."); Log.Warning($"User {args.SenderSession.Name} sent a {nameof(GhostWarpsRequestEvent)} without being a ghost.");
return; return;
@@ -312,30 +324,33 @@ namespace Content.Server.Ghost
RaiseNetworkEvent(response, args.SenderSession.Channel); RaiseNetworkEvent(response, args.SenderSession.Channel);
} }
public void GhostWarpRequest(ICommonSession player, NetEntity target)
{
if (!CanGhostWarp(player, out var attached))
{
Log.Warning($"User {player.Name} tried to warp to {target} without being a ghost.");
return;
}
var realTarget = GetEntity(target);
if (!Exists(realTarget))
{
Log.Warning($"User {player.Name} tried to warp to an invalid entity id: {target}");
return;
}
WarpTo(attached, realTarget);
}
private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args) private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args)
{ {
if (args.SenderSession.AttachedEntity is not {Valid: true} attached GhostWarpRequest(args.SenderSession, msg.Target);
|| !_ghostQuery.HasComp(attached))
{
Log.Warning($"User {args.SenderSession.Name} tried to warp to {msg.Target} without being a ghost.");
return;
}
var target = GetEntity(msg.Target);
if (!Exists(target))
{
Log.Warning($"User {args.SenderSession.Name} tried to warp to an invalid entity id: {msg.Target}");
return;
}
WarpTo(attached, target);
} }
private void OnGhostnadoRequest(GhostnadoRequestEvent msg, EntitySessionEventArgs args) private void OnGhostnadoRequest(GhostnadoRequestEvent msg, EntitySessionEventArgs args)
{ {
if (args.SenderSession.AttachedEntity is not {} uid if (CanGhostWarp(args.SenderSession, out var uid))
|| !_ghostQuery.HasComp(uid))
{ {
Log.Warning($"User {args.SenderSession.Name} tried to ghostnado without being a ghost."); Log.Warning($"User {args.SenderSession.Name} tried to ghostnado without being a ghost.");
return; return;
@@ -211,7 +211,7 @@ namespace Content.Server.Medical.BiomassReclaimer
_solution.ResolveSolution(toProcess, stream.BloodSolutionName, ref stream.BloodSolution, out var solution)) _solution.ResolveSolution(toProcess, stream.BloodSolutionName, ref stream.BloodSolution, out var solution))
{ {
component.BloodReagents = solution.Clone(); component.BloodReagents = solution.Clone();
component.BloodReagents.ScaleSolution(50 / component.BloodReagents.Volume); //component.BloodReagents.ScaleSolution(50 / component.BloodReagents.Volume); // Delta V - This doesn't need to be here. It just always makes the solution ~50u but also might divide by 0. Just use the current blood level so more blood = more mess.
} }
if (TryComp<ButcherableComponent>(toProcess, out var butcherableComponent)) if (TryComp<ButcherableComponent>(toProcess, out var butcherableComponent))
{ {
@@ -9,6 +9,7 @@ using Content.Shared.Radio.Components;
using Content.Shared.Radio.EntitySystems; using Content.Shared.Radio.EntitySystems;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Content.Shared._DV.Chat;
namespace Content.Server.Radio.EntitySystems; namespace Content.Server.Radio.EntitySystems;
@@ -26,6 +27,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
SubscribeLocalEvent<HeadsetComponent, EncryptionChannelsChangedEvent>(OnKeysChanged); SubscribeLocalEvent<HeadsetComponent, EncryptionChannelsChangedEvent>(OnKeysChanged);
SubscribeLocalEvent<WearingHeadsetComponent, EntitySpokeEvent>(OnSpeak); SubscribeLocalEvent<WearingHeadsetComponent, EntitySpokeEvent>(OnSpeak);
SubscribeLocalEvent<WearingHeadsetComponent, EntityAudiblyEmotedEvent>(OnAudibleEmote); // DeltaV
} }
private void OnKeysChanged(EntityUid uid, HeadsetComponent component, EncryptionChannelsChangedEvent args) private void OnKeysChanged(EntityUid uid, HeadsetComponent component, EncryptionChannelsChangedEvent args)
@@ -48,22 +50,43 @@ public sealed class HeadsetSystem : SharedHeadsetSystem
EnsureComp<ActiveRadioComponent>(uid).Channels = new(keyHolder.Channels); EnsureComp<ActiveRadioComponent>(uid).Channels = new(keyHolder.Channels);
} }
private void OnSpeak(EntityUid uid, WearingHeadsetComponent component, EntitySpokeEvent args) // DeltaV
// WARNING - Be very careful when modifying this method.
// The implementation right now has null forgiving operatiors in OnSpeak() and OnAudibleEmote() since this only returns true if channel is not null
private bool CheckRadioCapable(EntityUid uid, WearingHeadsetComponent headset, RadioChannelPrototype? channel)
{ {
if (args.Channel != null if (channel != null
&& TryComp(component.Headset, out EncryptionKeyHolderComponent? keys) && TryComp(headset.Headset, out EncryptionKeyHolderComponent? keys)
&& keys.Channels.Contains(args.Channel.ID)) && keys.Channels.Contains(channel.ID))
{ {
// Begin DeltaV Additions: No using headsets if you lost your hands or are cuffed
if (!TryComp<HandsComponent>(uid, out var hands) || hands.Count < 1 || if (!TryComp<HandsComponent>(uid, out var hands) || hands.Count < 1 ||
TryComp<CuffableComponent>(uid, out var cuffable) && _cuffable.IsCuffed((uid, cuffable))) TryComp<CuffableComponent>(uid, out var cuffable) && _cuffable.IsCuffed((uid, cuffable)))
{ {
_popup.PopupEntity(Loc.GetString("headset-cant-reach"), uid, uid, PopupType.SmallCaution); _popup.PopupEntity(Loc.GetString("headset-cant-reach"), uid, uid, PopupType.SmallCaution);
return; return false;
} }
// End DeltaV Additions
_radio.SendRadioMessage(uid, args.Message, args.Channel, component.Headset); return true;
}
return false;
}
private void OnAudibleEmote(EntityUid uid, WearingHeadsetComponent component, EntityAudiblyEmotedEvent args)
{
if (CheckRadioCapable(uid, component, args.Channel))
{
_radio.SendRadioMessage(uid, args.Message, args.Channel!, component.Headset, emType: args.Type);
args.Channel = null;
}
}
// DeltaV - End
private void OnSpeak(EntityUid uid, WearingHeadsetComponent component, EntitySpokeEvent args)
{
if (CheckRadioCapable(uid, component, args.Channel)) // DeltaV - Put all the radio checks into the CheckRadioCapable() method.
{
_radio.SendRadioMessage(uid, args.Message, args.Channel!, component.Headset); // DeltaV - Made the args.Channel null ignorant as CheckRadioCapable() guarantees it not null.
args.Channel = null; // prevent duplicate messages from other listeners. args.Channel = null; // prevent duplicate messages from other listeners.
} }
} }
@@ -1,6 +1,9 @@
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.Ghost;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Shared._DV.Chat;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Radio; using Content.Shared.Radio;
@@ -27,6 +30,8 @@ public sealed class RadioSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly GhostSystem _ghost = default!;
// set used to prevent radio feedback loops. // set used to prevent radio feedback loops.
private readonly HashSet<string> _messages = new(); private readonly HashSet<string> _messages = new();
@@ -38,6 +43,7 @@ public sealed class RadioSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<IntrinsicRadioReceiverComponent, RadioReceiveEvent>(OnIntrinsicReceive); SubscribeLocalEvent<IntrinsicRadioReceiverComponent, RadioReceiveEvent>(OnIntrinsicReceive);
SubscribeLocalEvent<IntrinsicRadioTransmitterComponent, EntitySpokeEvent>(OnIntrinsicSpeak); SubscribeLocalEvent<IntrinsicRadioTransmitterComponent, EntitySpokeEvent>(OnIntrinsicSpeak);
SubscribeLocalEvent<IntrinsicRadioTransmitterComponent, EntityAudiblyEmotedEvent>(OnIntrinsicAudibleEmote); // DeltaV - Robots should be allowed to emote over radio.
_exemptQuery = GetEntityQuery<TelecomExemptComponent>(); _exemptQuery = GetEntityQuery<TelecomExemptComponent>();
} }
@@ -53,16 +59,43 @@ public sealed class RadioSystem : EntitySystem
private void OnIntrinsicReceive(EntityUid uid, IntrinsicRadioReceiverComponent component, ref RadioReceiveEvent args) private void OnIntrinsicReceive(EntityUid uid, IntrinsicRadioReceiverComponent component, ref RadioReceiveEvent args)
{ {
if (TryComp(uid, out ActorComponent? actor)) if (!TryComp(uid, out ActorComponent? actor))
_netMan.ServerSendMessage(args.ChatMsg, actor.PlayerSession.Channel); return;
var msg = args.ChatMsg;
if (_ghost.CanGhostWarp(actor.PlayerSession, out _))
{
msg = new MsgChatMessage
{
Message = new ChatMessage(args.ChatMsg.Message)
{
WrappedMessage = _chatManager.PrependFollowButtonIfAppropriate(
args.ChatMsg.Message.WrappedMessage,
args.MessageSource,
actor.PlayerSession.Channel),
},
};
}
_netMan.ServerSendMessage(msg, actor.PlayerSession.Channel);
} }
// DeltaV
private void OnIntrinsicAudibleEmote(EntityUid uid, IntrinsicRadioTransmitterComponent component, EntityAudiblyEmotedEvent args)
{
if (args.Channel != null && component.Channels.Contains(args.Channel.ID))
{
SendRadioMessage(uid, args.Message, args.Channel, uid, emType: args.Type);
}
}
// DeltaV - End
/// <summary> /// <summary>
/// Send radio message to all active radio listeners /// Send radio message to all active radio listeners
/// </summary> /// </summary>
public void SendRadioMessage(EntityUid messageSource, string message, ProtoId<RadioChannelPrototype> channel, EntityUid radioSource, bool escapeMarkup = true) public void SendRadioMessage(EntityUid messageSource, string message, ProtoId<RadioChannelPrototype> channel, EntityUid radioSource, bool escapeMarkup = true, EmoteType? emType = null) // DeltaV - EmoteType? added.
{ {
SendRadioMessage(messageSource, message, _prototype.Index(channel), radioSource, escapeMarkup: escapeMarkup); SendRadioMessage(messageSource, message, _prototype.Index(channel), radioSource, escapeMarkup: escapeMarkup, emType: emType);
} }
/// <summary> /// <summary>
@@ -70,7 +103,7 @@ public sealed class RadioSystem : EntitySystem
/// </summary> /// </summary>
/// <param name="messageSource">Entity that spoke the message</param> /// <param name="messageSource">Entity that spoke the message</param>
/// <param name="radioSource">Entity that picked up the message and will send it, e.g. headset</param> /// <param name="radioSource">Entity that picked up the message and will send it, e.g. headset</param>
public void SendRadioMessage(EntityUid messageSource, string message, RadioChannelPrototype channel, EntityUid radioSource, bool escapeMarkup = true) public void SendRadioMessage(EntityUid messageSource, string message, RadioChannelPrototype channel, EntityUid radioSource, bool escapeMarkup = true, EmoteType? emType = null) // DeltaV - EmoteType? added.
{ {
// TODO if radios ever garble / modify messages, feedback-prevention needs to be handled better than this. // TODO if radios ever garble / modify messages, feedback-prevention needs to be handled better than this.
if (!_messages.Add(message)) if (!_messages.Add(message))
@@ -92,14 +125,31 @@ public sealed class RadioSystem : EntitySystem
? FormattedMessage.EscapeText(message) ? FormattedMessage.EscapeText(message)
: message; : message;
var wrappedMessage = Loc.GetString(speech.Bold ? "chat-radio-message-wrap-bold" : "chat-radio-message-wrap", // DeltaV - This change is to change up how the messages are wrapped up. Basically changing the formatting depending on the emote type.
("color", channel.Color), string wrappedMessage;
("fontType", speech.FontId),
("fontSize", speech.FontSize), if (emType == EmoteType.Audible)
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))), wrappedMessage = Loc.GetString("chat-radio-message-audible-emote-wrap",
("channel", $"\\[{channel.LocalizedName}\\]"), ("color", channel.Color),
("name", name), ("channel", $"\\[{channel.LocalizedName}\\]"),
("message", content)); ("name", name),
("message", content));
else if (emType == EmoteType.AudiblePossessive)
wrappedMessage = Loc.GetString("chat-radio-message-audible-possessive-emote-wrap",
("color", channel.Color),
("channel", $"\\[{channel.LocalizedName}\\]"),
("name", name),
("message", content));
else
wrappedMessage = Loc.GetString(speech.Bold ? "chat-radio-message-wrap-bold" : "chat-radio-message-wrap",
("color", channel.Color),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("channel", $"\\[{channel.LocalizedName}\\]"),
("name", name),
("message", content));
// DeltaV - End
// most radios are relayed to chat, so lets parse the chat message beforehand // most radios are relayed to chat, so lets parse the chat message beforehand
var chat = new ChatMessage( var chat = new ChatMessage(
@@ -159,7 +209,7 @@ public sealed class RadioSystem : EntitySystem
} }
/// <inheritdoc cref="TelecomServerComponent"/> /// <inheritdoc cref="TelecomServerComponent"/>
private bool HasActiveServer(MapId mapId, string channelId) public bool HasActiveServer(MapId mapId, string channelId) // DeltaV - we need this
{ {
var servers = EntityQuery<TelecomServerComponent, EncryptionKeyHolderComponent, ApcPowerReceiverComponent, TransformComponent>(); var servers = EntityQuery<TelecomServerComponent, EncryptionKeyHolderComponent, ApcPowerReceiverComponent, TransformComponent>();
foreach (var (_, keys, power, transform) in servers) foreach (var (_, keys, power, transform) in servers)
+24 -2
View File
@@ -1,3 +1,4 @@
using System.Linq; // DeltaV
using System.Numerics; using System.Numerics;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
@@ -8,6 +9,8 @@ using Content.Server.Popups;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Dataset; using Content.Shared.Dataset;
using Content.Shared.Mobs; // DeltaV
using Content.Shared.Mobs.Components; // DeltaV
using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Pointing; using Content.Shared.Pointing;
@@ -47,14 +50,33 @@ namespace Content.Server.RatKing
if (!TryComp<HungerComponent>(uid, out var hunger)) if (!TryComp<HungerComponent>(uid, out var hunger))
return; return;
// DeltaV - modify cost of Raise Army based on the amount of alive servants
// Check on how many servants are alive to calculate the cost of a new servant
var aliveServants = component.Servants.Count(servant
=> TryComp<MobStateComponent>(servant, out var mobState) && mobState.CurrentState == MobState.Alive);
// calculate the cost multiplier
var multiplier = aliveServants switch
{
< 5 => 1.0f, // 1-5 servants: 10 hunger
< 10 => 1.5f, // 6-10 servants: 15 hunger
< 15 => 2.5f, // 11-15 servants: 25 hunger
< 20 => 5.0f, // 16-20 servants: 50 hunger
< 25 => 10.0f, // 21-25 servants: 100 hunger
_ => 15.0f, // Above 25 servants: 150 hunger
};
var hungerPerArmyUseAdjusted = component.HungerPerArmyUse * multiplier;
//make sure the hunger doesn't go into the negatives //make sure the hunger doesn't go into the negatives
if (_hunger.GetHunger(hunger) < component.HungerPerArmyUse) if (_hunger.GetHunger(hunger) < hungerPerArmyUseAdjusted)
{ {
_popup.PopupEntity(Loc.GetString("rat-king-too-hungry"), uid, uid); _popup.PopupEntity(Loc.GetString("rat-king-too-hungry"), uid, uid);
return; return;
} }
args.Handled = true; args.Handled = true;
_hunger.ModifyHunger(uid, -component.HungerPerArmyUse, hunger); _hunger.ModifyHunger(uid, - hungerPerArmyUseAdjusted, hunger);
// DeltaV - end to the modified cost of Raise Army
var servant = Spawn(component.ArmyMobSpawnId, Transform(uid).Coordinates); var servant = Spawn(component.ArmyMobSpawnId, Transform(uid).Coordinates);
var comp = EnsureComp<RatKingServantComponent>(servant); var comp = EnsureComp<RatKingServantComponent>(servant);
comp.King = uid; comp.King = uid;
@@ -27,6 +27,11 @@ public sealed partial class BorgSystem
var query = EntityQueryEnumerator<BorgTransponderComponent, BorgChassisComponent, DeviceNetworkComponent, MetaDataComponent>(); var query = EntityQueryEnumerator<BorgTransponderComponent, BorgChassisComponent, DeviceNetworkComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out var comp, out var chassis, out var device, out var meta)) while (query.MoveNext(out var uid, out var comp, out var chassis, out var device, out var meta))
{ {
// DeltaV Begin
if (!comp.Active)
return;
// DeltaV End
if (comp.NextDisable is { } nextDisable && now >= nextDisable) if (comp.NextDisable is { } nextDisable && now >= nextDisable)
DoDisable((uid, comp, chassis, meta)); DoDisable((uid, comp, chassis, meta));
@@ -83,6 +88,11 @@ public sealed partial class BorgSystem
private void OnPacketReceived(Entity<BorgTransponderComponent> ent, ref DeviceNetworkPacketEvent args) private void OnPacketReceived(Entity<BorgTransponderComponent> ent, ref DeviceNetworkPacketEvent args)
{ {
// DeltaV Begin
if (!ent.Comp.Active)
return;
// DeltaV End
var payload = args.Data; var payload = args.Data;
if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command)) if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command))
return; return;
@@ -197,4 +207,17 @@ public sealed partial class BorgSystem
return true; return true;
} }
// DeltaV Begin
/// <summary>
/// DeltaV - sets if the borg transponder is active
/// </summary>
public void SetTransponderActive(Entity<BorgTransponderComponent?> ent, bool active)
{
if (!Resolve(ent, ref ent.Comp))
return;
ent.Comp.Active = active;
}
// DeltaV End
} }
@@ -29,12 +29,14 @@ public sealed partial class SpeechWireAction : ComponentWireAction<SpeechCompone
public override bool Cut(EntityUid user, Wire wire, SpeechComponent component) public override bool Cut(EntityUid user, Wire wire, SpeechComponent component)
{ {
_speech.SetSpeech(wire.Owner, false, component); _speech.SetSpeech(wire.Owner, false, component);
EntityManager.GetComponentOrNull<Components.UnblockableSpeechComponent>(user)?.Active = false; // DeltaV - we need this to override unblockable speech
return true; return true;
} }
public override bool Mend(EntityUid user, Wire wire, SpeechComponent component) public override bool Mend(EntityUid user, Wire wire, SpeechComponent component)
{ {
_speech.SetSpeech(wire.Owner, true, component); _speech.SetSpeech(wire.Owner, true, component);
EntityManager.GetComponentOrNull<Components.UnblockableSpeechComponent>(user)?.Active = true; // DeltaV - we need this to override unblockable speech
return true; return true;
} }
@@ -3,5 +3,9 @@ namespace Content.Server.Speech.Components
[RegisterComponent] [RegisterComponent]
public sealed partial class UnblockableSpeechComponent : Component public sealed partial class UnblockableSpeechComponent : Component
{ {
// Begin DeltaV
[DataField]
public bool Active = true;
// End DeltaV
} }
} }
@@ -12,7 +12,7 @@ namespace Content.Server.Speech.EntitySystems
private void OnCheck(EntityUid uid, UnblockableSpeechComponent component, CheckIgnoreSpeechBlockerEvent args) private void OnCheck(EntityUid uid, UnblockableSpeechComponent component, CheckIgnoreSpeechBlockerEvent args)
{ {
args.IgnoreBlocker = true; args.IgnoreBlocker = component.Active; // DeltaV
} }
} }
} }
@@ -1,4 +1,3 @@
using Content.Server._DV.Psionics.Systems;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Body.Components; using Content.Server.Body.Components;
@@ -14,7 +13,6 @@ using Content.Server.NPC.HTN;
using Content.Server.NPC.Systems; using Content.Server.NPC.Systems;
using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Components;
using Content.Server.Speech.Components; using Content.Server.Speech.Components;
using Content.Shared._DV.Psionics.Components; // DeltaV
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.CombatMode; using Content.Shared.CombatMode;
using Content.Shared.CombatMode.Pacification; using Content.Shared.CombatMode.Pacification;
@@ -27,7 +25,6 @@ using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.NameModifier.EntitySystems; using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.NPC.Components; // DeltaV
using Content.Shared.NPC.Systems; using Content.Shared.NPC.Systems;
using Content.Shared.Nutrition.AnimalHusbandry; using Content.Shared.Nutrition.AnimalHusbandry;
using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.Components;
@@ -72,7 +69,6 @@ public sealed partial class ZombieSystem
[Dependency] private readonly NPCSystem _npc = default!; [Dependency] private readonly NPCSystem _npc = default!;
[Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly ISharedPlayerManager _player = default!;
[Dependency] private readonly PsionicSystem _psionic = default!; // DeltaV
private static readonly ProtoId<TagPrototype> InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; private static readonly ProtoId<TagPrototype> InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell";
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide"; private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
@@ -134,6 +130,9 @@ public sealed partial class ZombieSystem
//you're a real zombie now, son. //you're a real zombie now, son.
var zombiecomp = AddComp<ZombieComponent>(target); var zombiecomp = AddComp<ZombieComponent>(target);
// DeltaV - Save factions, psionics, etc. before modifications are made.
PreserveEntityComponentState((target, zombiecomp));
//we need to basically remove all of these because zombies shouldn't //we need to basically remove all of these because zombies shouldn't
//get diseases, breath, be thirst, be hungry, die in space, get double sentience, have offspring or be paraplegic. //get diseases, breath, be thirst, be hungry, die in space, get double sentience, have offspring or be paraplegic.
RemComp<RespiratorComponent>(target); RemComp<RespiratorComponent>(target);
@@ -146,12 +145,6 @@ public sealed partial class ZombieSystem
RemComp<ComplexInteractionComponent>(target); RemComp<ComplexInteractionComponent>(target);
RemComp<SentienceTargetComponent>(target); RemComp<SentienceTargetComponent>(target);
// DeltaV Start - Prevent Psionic Zombies
RemComp<PotentialPsionicComponent>(target);
if (HasComp<PsionicComponent>(target))
_psionic.MindBreakEntity(target, false, true);
// DeltaV End - Prevent Psionic Zombies
//funny voice //funny voice
var accentType = "zombie"; var accentType = "zombie";
if (TryComp<ZombieAccentOverrideComponent>(target, out var accent)) if (TryComp<ZombieAccentOverrideComponent>(target, out var accent))
@@ -245,6 +238,9 @@ public sealed partial class ZombieSystem
//Should prevent instances of zombies using comms for information they shouldnt be able to have. //Should prevent instances of zombies using comms for information they shouldnt be able to have.
_inventory.TryUnequip(target, "ears", true, true); _inventory.TryUnequip(target, "ears", true, true);
// DeltaV - Extra zombification removals, etc.
ZombifyEntityDV((target, zombiecomp), mobState);
//popup //popup
_popup.PopupEntity(Loc.GetString("zombie-transform", ("target", target)), target, PopupType.LargeCaution); _popup.PopupEntity(Loc.GetString("zombie-transform", ("target", target)), target, PopupType.LargeCaution);
@@ -261,7 +257,6 @@ public sealed partial class ZombieSystem
_faction.ClearFactions(target, dirty: false); _faction.ClearFactions(target, dirty: false);
_faction.AddFaction(target, ZombieFaction); _faction.AddFaction(target, ZombieFaction);
EnsureComp<NoFriendlyFireComponent>(target); // DeltaV - prevent shitters biting other zombies
//gives it the funny "Zombie ___" name. //gives it the funny "Zombie ___" name.
_nameMod.RefreshNameModifiers(target); _nameMod.RefreshNameModifiers(target);
+3 -1
View File
@@ -78,6 +78,8 @@ namespace Content.Server.Zombies
SubscribeLocalEvent<IncurableZombieComponent, MapInitEvent>(OnPendingMapInit); SubscribeLocalEvent<IncurableZombieComponent, MapInitEvent>(OnPendingMapInit);
SubscribeLocalEvent<ZombifyOnDeathComponent, MobStateChangedEvent>(OnDamageChanged); SubscribeLocalEvent<ZombifyOnDeathComponent, MobStateChangedEvent>(OnDamageChanged);
InitializeDV(); // DeltaV - Additional Subscriptions for ZombieComponent
} }
private void OnBeforeRemoveAnomalyOnDeath(Entity<PendingZombieComponent> ent, ref BeforeRemoveAnomalyOnDeathEvent args) private void OnBeforeRemoveAnomalyOnDeath(Entity<PendingZombieComponent> ent, ref BeforeRemoveAnomalyOnDeathEvent args)
@@ -325,7 +327,7 @@ namespace Content.Server.Zombies
// Remove the role when getting cloned, getting gibbed and borged, or leaving the body via any other method. // Remove the role when getting cloned, getting gibbed and borged, or leaving the body via any other method.
private void OnMindRemoved(Entity<ZombieComponent> ent, ref MindRemovedMessage args) private void OnMindRemoved(Entity<ZombieComponent> ent, ref MindRemovedMessage args)
{ {
_role.MindRemoveRole<ZombieRoleComponent>((args.Mind.Owner, args.Mind.Comp)); _role.MindRemoveRole<ZombieRoleComponent>((args.Mind.Owner, args.Mind.Comp));
} }
} }
} }
@@ -6,12 +6,6 @@ namespace Content.Server._DV.CartridgeLoader.Cartridges;
[RegisterComponent, Access(typeof(NanoChatCartridgeSystem))] [RegisterComponent, Access(typeof(NanoChatCartridgeSystem))]
public sealed partial class NanoChatCartridgeComponent : Component public sealed partial class NanoChatCartridgeComponent : Component
{ {
/// <summary>
/// Station entity to keep track of.
/// </summary>
[DataField]
public EntityUid? Station;
/// <summary> /// <summary>
/// The NanoChat card to keep track of. /// The NanoChat card to keep track of.
/// </summary> /// </summary>
@@ -2,9 +2,8 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.CartridgeLoader; using Content.Server.CartridgeLoader;
using Content.Server.Power.Components;
using Content.Server.Radio; using Content.Server.Radio;
using Content.Server.Station.Systems; using Content.Server.Radio.EntitySystems;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.CartridgeLoader; using Content.Shared.CartridgeLoader;
using Content.Shared.CCVar; using Content.Shared.CCVar;
@@ -30,7 +29,7 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedNanoChatSystem _nanoChat = default!; [Dependency] private readonly SharedNanoChatSystem _nanoChat = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!; [Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
[Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly RadioSystem _radio = default!;
private EntityQuery<PdaComponent> _pdaQuery; private EntityQuery<PdaComponent> _pdaQuery;
private EntityQuery<NanoChatCardComponent> _cardQuery; private EntityQuery<NanoChatCardComponent> _cardQuery;
@@ -456,9 +455,7 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
{ {
// First verify we can send from this device // First verify we can send from this device
var channel = _prototype.Index(sender.Comp.RadioChannel); var channel = _prototype.Index(sender.Comp.RadioChannel);
var sendAttemptEvent = new RadioSendAttemptEvent(channel, sender); if (!CanSend(sender))
RaiseLocalEvent(ref sendAttemptEvent);
if (sendAttemptEvent.Cancelled)
return (true, new List<Entity<NanoChatCardComponent>>()); return (true, new List<Entity<NanoChatCardComponent>>());
var foundRecipients = new List<Entity<NanoChatCardComponent>>(); var foundRecipients = new List<Entity<NanoChatCardComponent>>();
@@ -487,26 +484,19 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
if (receiverCart.Card != recipient.Owner) if (receiverCart.Card != recipient.Owner)
continue; continue;
// Check if devices are on same station/map var receiverMapId = Transform(receiverUid).MapID;
var recipientStation = _station.GetOwningStation(receiverUid); var senderMapId = Transform(sender).MapID;
var senderStation = _station.GetOwningStation(sender);
// Both entities must be on a station // Must be on the same map unless long range is allowed.
if (recipientStation == null || senderStation == null) if (!channel.LongRange && receiverMapId != senderMapId)
continue; continue;
// Must be on same map/station unless long range allowed // Check if telecommunications are active on both ends
if (!channel.LongRange && recipientStation != senderStation) if (!_radio.HasActiveServer(receiverMapId, receiverCart.RadioChannel) || !_radio.HasActiveServer(senderMapId, sender.Comp.RadioChannel))
continue;
// Needs telecomms
if (!HasActiveServer(senderStation.Value) || !HasActiveServer(recipientStation.Value))
continue; continue;
// Check if recipient can receive // Check if recipient can receive
var receiveAttemptEv = new RadioReceiveAttemptEvent(channel, sender, receiverUid); if (!CanReceive(sender, receiverUid))
RaiseLocalEvent(ref receiveAttemptEv);
if (receiveAttemptEv.Cancelled)
continue; continue;
// Found valid cartridge that can receive // Found valid cartridge that can receive
@@ -519,21 +509,26 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
} }
/// <summary> /// <summary>
/// Checks if there are any active telecomms servers on the given station /// Tests if a NanoChat cartridge can send messages
/// </summary> /// </summary>
private bool HasActiveServer(EntityUid station) /// <param name="sender">The NanoChat cartridge trying to send</param>
private bool CanSend(Entity<NanoChatCartridgeComponent> sender)
{ {
// I have no idea why this isn't public in the RadioSystem var sendAttemptEvent = new RadioSendAttemptEvent(_prototype.Index(sender.Comp.RadioChannel), sender);
var query = RaiseLocalEvent(ref sendAttemptEvent);
EntityQueryEnumerator<TelecomServerComponent, EncryptionKeyHolderComponent, ApcPowerReceiverComponent>(); return !sendAttemptEvent.Cancelled;
}
while (query.MoveNext(out var uid, out _, out _, out var power)) /// <summary>
{ /// Tests if a receiver can receive from a given NanoChat cartridge
if (_station.GetOwningStation(uid) == station && power.Powered) /// </summary>
return true; /// <param name="sender">The NanoChat cartridge trying to send</param>
} /// <param name="receiver">The receiver cartridge trying to receive</param>
private bool CanReceive(Entity<NanoChatCartridgeComponent> sender, EntityUid receiver)
return false; {
var receiveAttemptEv = new RadioReceiveAttemptEvent(_prototype.Index(sender.Comp.RadioChannel), sender, receiver);
RaiseLocalEvent(ref receiveAttemptEv);
return !receiveAttemptEv.Cancelled;
} }
/// <summary> /// <summary>
@@ -688,16 +683,15 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
private void UpdateUI(Entity<NanoChatCartridgeComponent> ent, EntityUid loader) private void UpdateUI(Entity<NanoChatCartridgeComponent> ent, EntityUid loader)
{ {
List<NanoChatRecipient>? contacts; List<NanoChatRecipient>? contacts;
if (_station.GetOwningStation(loader) is { } station)
{
ent.Comp.Station = station;
if (CanSend(ent) && _radio.HasActiveServer(Transform(ent).MapID, ent.Comp.RadioChannel))
{
contacts = []; contacts = [];
var query = AllEntityQuery<NanoChatCardComponent, IdCardComponent>(); var query = AllEntityQuery<NanoChatCardComponent, IdCardComponent>();
while (query.MoveNext(out var entityId, out var nanoChatCard, out var idCardComponent)) while (query.MoveNext(out var entityId, out var nanoChatCard, out var idCardComponent))
{ {
if (nanoChatCard.ListNumber && nanoChatCard.Number is uint nanoChatNumber && idCardComponent.FullName is string fullName && _station.GetOwningStation(entityId) == station) if (nanoChatCard.ListNumber && nanoChatCard.Number is uint nanoChatNumber && idCardComponent.FullName is string fullName)
{ {
contacts.Add(new NanoChatRecipient(nanoChatNumber, fullName)); contacts.Add(new NanoChatRecipient(nanoChatNumber, fullName));
} }
@@ -706,7 +700,7 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
var borgQuery = AllEntityQuery<NanoChatCardComponent, BorgChassisComponent>(); var borgQuery = AllEntityQuery<NanoChatCardComponent, BorgChassisComponent>();
while (borgQuery.MoveNext(out var borgId, out var borgChatCard, out var _)) while (borgQuery.MoveNext(out var borgId, out var borgChatCard, out var _))
{ {
if (borgChatCard.ListNumber && borgChatCard.Number is uint nanoChatNumber && _station.GetOwningStation(borgId) == station) if (borgChatCard.ListNumber && borgChatCard.Number is uint nanoChatNumber)
{ {
contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(borgId).EntityName)); contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(borgId).EntityName));
} }
@@ -715,7 +709,7 @@ public sealed class NanoChatCartridgeSystem : EntitySystem
var aiQuery = AllEntityQuery<NanoChatCardComponent, StationAiHeldComponent>(); var aiQuery = AllEntityQuery<NanoChatCardComponent, StationAiHeldComponent>();
while (aiQuery.MoveNext(out var aiId, out var aiChatCard, out var _)) while (aiQuery.MoveNext(out var aiId, out var aiChatCard, out var _))
{ {
if (aiChatCard.ListNumber && aiChatCard.Number is uint nanoChatNumber && _station.GetOwningStation(aiId) == station) if (aiChatCard.ListNumber && aiChatCard.Number is uint nanoChatNumber)
{ {
contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(aiId).EntityName)); contacts.Add(new NanoChatRecipient(nanoChatNumber, MetaData(aiId).EntityName));
} }
@@ -0,0 +1,63 @@
using Content.Server._DV.CosmicCult.Components;
using Content.Shared._DV.CosmicCult.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Weapons.Melee;
using Robust.Shared.Prototypes;
namespace Content.Server._DV.CosmicCult.Abilities.Colossus;
public sealed class CosmicColossusBuffsSystem : EntitySystem
{
[Dependency] private readonly DamageableSystem _damage = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MultiplyAttackRateOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleAttackRate);
SubscribeLocalEvent<FlatAttackBonusOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleDamageBonus);
SubscribeLocalEvent<MultiplyCorruptingSpeedOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleCorruptingSpeed);
SubscribeLocalEvent<HealOnSupercriticalComponent, CosmicColossusEffigySupercriticalEvent>(HandleHeal);
}
private void HandleAttackRate(Entity<MultiplyAttackRateOnSupercriticalComponent> ent,
ref CosmicColossusEffigySupercriticalEvent args)
{
if (TryComp<MeleeWeaponComponent>(ent, out var weapon))
{
weapon.AttackRate *= ent.Comp.Multiplier;
}
}
private void HandleDamageBonus(Entity<FlatAttackBonusOnSupercriticalComponent> ent,
ref CosmicColossusEffigySupercriticalEvent args)
{
if (!TryComp<CosmicColossusComponent>(ent, out var colossusComp))
return;
colossusComp.BonusDamage +=
new DamageSpecifier(_proto.Index(ent.Comp.BonusDamageType), ent.Comp.BonusDamage);
}
private void HandleCorruptingSpeed(Entity<MultiplyCorruptingSpeedOnSupercriticalComponent> ent,
ref CosmicColossusEffigySupercriticalEvent args)
{
if (TryComp<CosmicCorruptingComponent>(ent, out var corrupting))
{
corrupting.CorruptionSpeed *= ent.Comp.Multiplier;
}
}
private void HandleHeal(Entity<HealOnSupercriticalComponent> ent, ref CosmicColossusEffigySupercriticalEvent args)
{
if (TryComp<DamageableComponent>(ent, out var damageable))
{
_damage.TryChangeDamage(ent.Owner,
-damageable.Damage * Math.Abs(ent.Comp.DamageFractionHealed), // just in case someone sets a negative value in YML
true);
}
}
}
@@ -0,0 +1,8 @@
using Content.Server._DV.CosmicCult.Components;
namespace Content.Server._DV.CosmicCult.Abilities.Colossus;
[ByRefEvent]
public readonly record struct CosmicColossusEffigySupercriticalEvent(
Entity<CosmicEffigyComponent> Effigy
);
@@ -1,15 +1,24 @@
using System.Numerics; using System.Numerics;
using Content.Server._DV.CosmicCult.Abilities.Colossus;
using Content.Server._DV.CosmicCult.Components;
using Content.Server.Actions; using Content.Server.Actions;
using Content.Server.Chat.Managers;
using Content.Server.Objectives.Components; using Content.Server.Objectives.Components;
using Content.Server.Objectives.Systems; using Content.Server.Objectives.Systems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared._DV.CosmicCult; using Content.Shared._DV.CosmicCult;
using Content.Shared._DV.CosmicCult.Components; using Content.Shared._DV.CosmicCult.Components;
using Content.Shared.Anomaly.Components;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Popups;
using Content.Shared.Warps; using Content.Shared.Warps;
using Robust.Server.Audio;
using Robust.Server.Player;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server._DV.CosmicCult.Abilities; namespace Content.Server._DV.CosmicCult.Abilities;
@@ -23,12 +32,93 @@ public sealed class CosmicEffigySystem : EntitySystem
[Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly SharedMapSystem _map = default!;
[Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly CosmicCultObjectiveSystem _cultObjective = default!;
[Dependency] private readonly IGameTiming _time = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly IPlayerManager _player = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<CosmicColossusComponent, EventCosmicColossusEffigy>(OnColossusEffigy); SubscribeLocalEvent<CosmicColossusComponent, EventCosmicColossusEffigy>(OnColossusEffigy);
SubscribeLocalEvent<CosmicEffigyComponent, AnomalySupercriticalEvent>(OnSupercritical);
SubscribeLocalEvent<CosmicEffigyComponent, AnomalyShutdownEvent>(OnAnomShutdown);
}
private void OnAnomShutdown(Entity<CosmicEffigyComponent> ent, ref AnomalyShutdownEvent args)
{
if (args.Forced || args.Supercritical || !Exists(ent.Comp.Colossus) || !TryComp<CosmicColossusComponent>(ent.Comp.Colossus, out var colossusComp))
return;
colossusComp.DeathTimer = _time.CurTime;
colossusComp.Timed = true;
}
private void OnSupercritical(Entity<CosmicEffigyComponent> ent, ref AnomalySupercriticalEvent args)
{
if (!Exists(ent.Comp.Colossus)
|| !TryComp<CosmicColossusComponent>(ent.Comp.Colossus, out var colossusComp)
|| !_mind.TryGetMind(ent.Comp.Colossus.Value, out _, out var mind))
return;
var colossus = ent.Comp.Colossus.Value;
var ev = new CosmicColossusEffigySupercriticalEvent(ent);
RaiseLocalEvent(colossus, ref ev);
var transform = Transform(ent.Comp.Colossus.Value);
Spawn(colossusComp.BuffVfx, transform.Coordinates);
if (colossusComp.CompletedEffigies == 0)
{
_audio.PlayStatic(colossusComp.ReawakenSfx,
Filter.BroadcastMap(transform.MapID),
transform.Coordinates,
true);
}
else
{
_audio.PlayPvs(colossusComp.ReawakenSfx, ent);
}
colossusComp.CompletedEffigies += 1;
if (colossusComp.CompletedEffigies >= colossusComp.MaxEffigies)
{
colossusComp.Timed = false;
_popup.PopupEntity(Loc.GetString("colossus-buff-final-popup"), colossus, PopupType.Large);
return;
}
_popup.PopupEntity(Loc.GetString("colossus-buff-popup"), colossus, PopupType.Large);
var objIndex = mind.Objectives.FindIndex(HasComp<CosmicEffigyConditionComponent>);
if (objIndex == -1 ||
!TryComp<CosmicEffigyConditionComponent>(mind.Objectives[objIndex], out var conditionComp))
{
Log.Error($"Failed to find effigy objective on {ToPrettyString(colossus)}!");
return;
}
var objective = mind.Objectives[objIndex];
if (_cultObjective.RandomizeEffigyTarget(objective, conditionComp, setDescription: true) is not {} nextTarget)
{
Log.Error("Failed to randomize effigy objective location!");
return;
}
if (mind.UserId is { } userId)
{
_chat.DispatchServerMessage(_player.GetSessionById(userId), Loc.GetString("colossus-next-target", ("location", nextTarget)));
}
_codeCondition.SetCompleted(objective, false);
colossusComp.EffigyPlaceActionEntity = _actions.AddAction(colossus, colossusComp.EffigyPlaceAction);
colossusComp.DeathTimer = _time.CurTime + colossusComp.DeathWaitEffigy;
colossusComp.Timed = true;
} }
private void OnColossusEffigy(Entity<CosmicColossusComponent> ent, ref EventCosmicColossusEffigy args) private void OnColossusEffigy(Entity<CosmicColossusComponent> ent, ref EventCosmicColossusEffigy args)
@@ -38,8 +128,16 @@ public sealed class CosmicEffigySystem : EntitySystem
_actions.RemoveAction(ent.Owner, ent.Comp.EffigyPlaceActionEntity); _actions.RemoveAction(ent.Owner, ent.Comp.EffigyPlaceActionEntity);
_codeCondition.SetCompleted(ent.Owner, ent.Comp.EffigyObjective); _codeCondition.SetCompleted(ent.Owner, ent.Comp.EffigyObjective);
Spawn(ent.Comp.EffigyPrototype, pos); var effigy = Spawn(ent.Comp.EffigyPrototype, pos);
ent.Comp.Timed = false; ent.Comp.Timed = false;
if (!TryComp<CosmicEffigyComponent>(effigy, out var effigyComp))
{
Log.Error("Colossus tried to place Effigy prototype missing CosmicEffigyComponent!");
return;
}
effigyComp.Colossus = ent.Owner;
} }
private bool VerifyPlacement(Entity<CosmicColossusComponent> ent, out EntityCoordinates outPos) private bool VerifyPlacement(Entity<CosmicColossusComponent> ent, out EntityCoordinates outPos)
@@ -0,0 +1,56 @@
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Server._DV.CosmicCult.Components;
/// <summary>
/// The owning colossus's attack rate will be multiplied by
/// the given multiplier once its effigy goes supercritical.
/// Component meant to be applied to Cosmic Colossus.
/// </summary>
[RegisterComponent]
public sealed partial class MultiplyAttackRateOnSupercriticalComponent : Component
{
[DataField]
public float Multiplier = 1.1f;
}
/// <summary>
/// The owning colossus's corrupting speed will be multiplied by
/// the given multiplier once its effigy goes supercritical.
/// Component meant to be applied to Cosmic Colossus.
/// </summary>
[RegisterComponent]
public sealed partial class MultiplyCorruptingSpeedOnSupercriticalComponent : Component
{
[DataField]
public float Multiplier = 0.9f;
}
/// <summary>
/// The owning colossus will receive a flat bonus to
/// its melee attack damage once its effigy goes supercritical.
/// Component meant to be applied to Cosmic Colossus.
/// </summary>
[RegisterComponent]
public sealed partial class FlatAttackBonusOnSupercriticalComponent : Component
{
[DataField]
public FixedPoint2 BonusDamage = 10;
[DataField]
public ProtoId<DamageTypePrototype> BonusDamageType = "Blunt";
}
/// <summary>
/// The owning colossus will be healed for `-CurrentDamage * Abs(DamageFractionHealed)` damage
/// once its effigy goes supercritical.
/// Component meant to be applied to Cosmic Colossus.
/// </summary>
[RegisterComponent]
public sealed partial class HealOnSupercriticalComponent : Component
{
[DataField]
public float DamageFractionHealed = 1.0f;
}
@@ -1,3 +1,4 @@
using Content.Server._DV.CosmicCult.Abilities.Colossus;
using Content.Server._DV.CosmicCult.EntitySystems; using Content.Server._DV.CosmicCult.EntitySystems;
using Content.Shared.Maps; using Content.Shared.Maps;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -5,7 +6,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server._DV.CosmicCult.Components; namespace Content.Server._DV.CosmicCult.Components;
[RegisterComponent, Access(typeof(CosmicCorruptingSystem))] [RegisterComponent, Access(typeof(CosmicCorruptingSystem), typeof(CosmicColossusBuffsSystem))]
[AutoGenerateComponentPause] [AutoGenerateComponentPause]
public sealed partial class CosmicCorruptingComponent : Component public sealed partial class CosmicCorruptingComponent : Component
{ {
@@ -0,0 +1,19 @@
using Content.Server._DV.CosmicCult.Abilities;
using Content.Server._DV.CosmicCult.EntitySystems;
namespace Content.Server._DV.CosmicCult.Components;
/// <summary>
/// Component for cosmic effigy anomalies, spawned by Cosmic Colossi.
/// </summary>
/// <seelso cref="CosmicEffigySystem"/>.
/// <seelso cref="CosmicColossusSystem"/>.
[RegisterComponent]
public sealed partial class CosmicEffigyComponent : Component
{
/// <summary>
/// The colossus that placed this effigy.
/// </summary>
[DataField]
public EntityUid? Colossus;
}
@@ -35,6 +35,12 @@ public sealed class CosmicCultObjectiveSystem : EntitySystem
if (args.Cancelled || !_roles.MindHasRole<CosmicColossusRoleComponent>(args.MindId)) if (args.Cancelled || !_roles.MindHasRole<CosmicColossusRoleComponent>(args.MindId))
return; return;
if (RandomizeEffigyTarget(uid, comp) is null)
args.Cancelled = true;
}
public string? RandomizeEffigyTarget(EntityUid uid, CosmicEffigyConditionComponent comp, bool setDescription = false)
{
var warps = new List<EntityUid>(); var warps = new List<EntityUid>();
var query = EntityQueryEnumerator<WarpPointComponent>(); var query = EntityQueryEnumerator<WarpPointComponent>();
var effigyBlacklist = comp.Blacklist; var effigyBlacklist = comp.Blacklist;
@@ -51,10 +57,22 @@ public sealed class CosmicCultObjectiveSystem : EntitySystem
if (warps.Count <= 0) if (warps.Count <= 0)
{ {
args.Cancelled = true; return null;
return;
} }
comp.EffigyTarget = _random.Pick(warps);
var newWarp = _random.Pick(warps);
var warpComp = Comp<WarpPointComponent>(newWarp);
comp.EffigyTarget = newWarp;
if (setDescription)
{
_metaData.SetEntityDescription(uid,
warpComp.Location != null
? Loc.GetString("objective-condition-effigy", ("location", warpComp.Location))
: Loc.GetString("objective-condition-effigy-no-target"));
}
return warpComp.Location;
} }
private void OnEffigyAfterAssign(EntityUid uid, CosmicEffigyConditionComponent comp, ref ObjectiveAfterAssignEvent args) private void OnEffigyAfterAssign(EntityUid uid, CosmicEffigyConditionComponent comp, ref ObjectiveAfterAssignEvent args)
@@ -13,6 +13,7 @@ using Content.Shared.Popups;
using Content.Shared.Station.Components; using Content.Shared.Station.Components;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Content.Shared.Warps; using Content.Shared.Warps;
using Content.Shared.Weapons.Melee.Events;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
@@ -41,6 +42,7 @@ public sealed class CosmicColossusSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<CosmicColossusComponent, ComponentInit>(OnSpawn); SubscribeLocalEvent<CosmicColossusComponent, ComponentInit>(OnSpawn);
SubscribeLocalEvent<CosmicColossusComponent, MobStateChangedEvent>(OnMobStateChanged); SubscribeLocalEvent<CosmicColossusComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<CosmicColossusComponent, MeleeHitEvent>(OnMeleeHit);
} }
public override void Update(float frameTime) public override void Update(float frameTime)
@@ -82,7 +84,7 @@ public sealed class CosmicColossusSystem : EntitySystem
private void OnSpawn(Entity<CosmicColossusComponent> ent, ref ComponentInit args) // I WANT THIS BIG GUY HURLED TOWARDS THE STATION private void OnSpawn(Entity<CosmicColossusComponent> ent, ref ComponentInit args) // I WANT THIS BIG GUY HURLED TOWARDS THE STATION
{ {
ent.Comp.DeathTimer = _timing.CurTime + ent.Comp.DeathWait; ent.Comp.DeathTimer = _timing.CurTime + ent.Comp.DeathWaitSpawn;
var station = _station.GetStationInMap(Transform(ent).MapID); var station = _station.GetStationInMap(Transform(ent).MapID);
if (TryComp<StationDataComponent>(station, out var stationData)) if (TryComp<StationDataComponent>(station, out var stationData))
{ {
@@ -115,4 +117,9 @@ public sealed class CosmicColossusSystem : EntitySystem
RemComp<WarpPointComponent>(ent); RemComp<WarpPointComponent>(ent);
RemComp<CosmicCorruptingComponent>(ent); RemComp<CosmicCorruptingComponent>(ent);
} }
private void OnMeleeHit(Entity<CosmicColossusComponent> colossus, ref MeleeHitEvent args)
{
args.BonusDamage += colossus.Comp.BonusDamage;
}
} }
@@ -1,5 +1,6 @@
using Content.Server._DV.Psionics.Systems; using Content.Server._DV.Psionics.Systems;
using Content.Shared._DV.Psionics.Components; using Content.Shared._DV.Psionics.Components;
using Content.Shared._DV.Traits.Assorted;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Roles.Components; using Content.Shared.Roles.Components;
@@ -17,10 +18,12 @@ public sealed partial class ParadoxCloneRuleSystem
private void FilterTargets(HashSet<Entity<MindComponent>> minds) private void FilterTargets(HashSet<Entity<MindComponent>> minds)
{ {
// TODO: use generic IMindFilter // TODO: use generic IMindFilter
// no picking other antags or non-crew // no picking other antags or non-crew and entities with no paradox clone trait
minds.RemoveWhere(mind => _role.MindIsAntagonist(mind) || minds.RemoveWhere(mind => _role.MindIsAntagonist(mind) ||
!_role.MindHasRole<JobRoleComponent>((mind, mind), out var role) || !_role.MindHasRole<JobRoleComponent>((mind, mind), out var role) ||
role?.Comp1.JobPrototype == null); role?.Comp1.JobPrototype == null ||
(mind.Comp.OwnedEntity is { } entity && HasComp<NoParadoxCloneComponent>(entity))
);
} }
private void PostClone(EntityUid mob) private void PostClone(EntityUid mob)
@@ -1,212 +0,0 @@
using Content.Server.Cloning;
using Content.Server.Mind;
using Content.Server.Station.Systems;
using Content.Shared._DV.Psionics.Components;
using Content.Shared._DV.Psionics.Components.PsionicPowers;
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
using Content.Shared._DV.Species;
using Content.Shared.Bed.Sleep;
using Content.Shared.Body.Components;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Mobs.Components;
using Content.Shared.Popups;
using Content.Shared.Preferences;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Server.Player;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server._DV.Psionics.Systems.PsionicPowers;
public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly CloningSystem _cloning = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly PvsOverrideSystem _pvsOverride = default!;
// holy initialize performance? but better for it to happen once than the double dict lookup every tick!!
private EntityQuery<FracturedFormBodyComponent> _bodyQuery;
private EntityQuery<SleepingComponent> _sleepingQuery;
public override void Initialize()
{
base.Initialize();
_bodyQuery = GetEntityQuery<FracturedFormBodyComponent>();
_sleepingQuery = GetEntityQuery<SleepingComponent>();
}
protected override void OnPowerInit(Entity<FracturedFormPowerComponent> power, ref MapInitEvent args)
{
base.OnPowerInit(power, ref args);
// The next random swap is between 5 and 20 minutes.
var randomTime = Random.Next(power.Comp.NextSwapMinTime, power.Comp.NextSwapMaxTime);
power.Comp.NextSwap = Timing.CurTime + randomTime;
power.Comp.NextVoluntarySwap = Timing.CurTime + power.Comp.VoluntarySwapCooldown;
// Don't generate a new body if we're already part of a network.
if (HasComp<FracturedFormBodyComponent>(power))
return;
// Don't make bodies if there is no body. This is solely for test fails.
if (!HasComp<BodyComponent>(power))
return;
var bodyComp = AddComp<FracturedFormBodyComponent>(power);
bodyComp.ControllingForm = power.Owner;
power.Comp.Bodies.Add(power);
var body = GenerateForm(power);
// hide the SSD indicator.
if (SsdQuery.TryComp(body, out var ssdComp))
ssdComp.IsSSD = false;
}
private EntityUid GenerateForm(Entity<FracturedFormPowerComponent> original)
{
// Form:
// - Same appearance as original
// - Different apperance, still humanoid
// Equipment:
// - Same as original body
// - Nude and helpless
var xform = Transform(original);
var hasGear = Random.Prob(original.Comp.HasGearChance);
if (Random.Prob(original.Comp.DifferentSpeciesChance) || !_cloning.TryCloning(original, _transform.GetMapCoordinates(original), hasGear ? original.Comp.CopyClothed : original.Comp.CopyNaked, out var newBody)) // Slightly lower chance to copy the original body
{
// Either the dice rolled poorly, or the cloning failed. Either way, make a new body instead. (Or try to)
var validSpecies = new List<ProtoId<SpeciesPrototype>>();
var speciesPrototypes = _prototype.EnumeratePrototypes<SpeciesPrototype>();
foreach (var proto in speciesPrototypes)
{
var speciesEntityPrototype = _prototype.Index<EntityPrototype>(proto.Prototype);
// If they have the PotentialPsionicComponent, they can be psionic.
if (proto.RoundStart && speciesEntityPrototype.TryGetComponent<PotentialPsionicComponent>(out _, Factory) && !SpeciesHiderSystem.IsHidden(proto.ID))
validSpecies.Add(proto.ID);
}
var species = Random.Pick(validSpecies);
var character = HumanoidCharacterProfile.RandomWithSpecies(species);
newBody = _stationSpawning.SpawnPlayerMob(xform.Coordinates, hasGear ? original.Comp.VisitorJob : original.Comp.NakedJob, character, _station.GetOwningStation(original.Owner));
if (newBody is not { } bodyV || Deleted(bodyV))
{
Log.Error($"Failed to create a new body for {ToPrettyString(original)}. This is a bug.");
return EntityUid.Invalid;
}
}
if (newBody is not { } body || Deleted(body))
return default!;
var bodyComp = AddComp<FracturedFormBodyComponent>(body);
original.Comp.Bodies.Add(body);
bodyComp.ControllingForm = original.Owner;
if (_player.TryGetSessionByEntity(original, out var session))
_pvsOverride.AddSessionOverride(body, session);
Dirty(original);
return body;
}
private bool TryGetValidBody(Entity<FracturedFormPowerComponent> psionic, out EntityUid validBody)
{
foreach (var body in psionic.Comp.Bodies)
{
if (!IsValidBody(psionic, body))
continue;
validBody = body;
return true;
}
validBody = default;
return false;
}
private void Swap(Entity<FracturedFormPowerComponent> psionic)
{
if (!TryGetValidBody(psionic, out var targetBody))
return;
_audio.PlayPvs(psionic.Comp.SwapSound, psionic);
// Transfer mind if present
if (MindContainerQuery.TryComp(psionic, out var mindContainer) && mindContainer.Mind.HasValue)
_mind.TransferTo(mindContainer.Mind.Value, targetBody);
// Wake up the new body
Sleeping.TryWaking(targetBody);
// Remove the action.
Action.RemoveAction(psionic.Comp.ActionEntity);
// Create new component on target and copy data
var duplicate = EnsureComp<FracturedFormPowerComponent>(targetBody);
duplicate.Bodies = psionic.Comp.Bodies;
// Update all body references
foreach (var body in duplicate.Bodies)
{
if (_bodyQuery.TryComp(body, out var bodyComp))
bodyComp.ControllingForm = targetBody;
}
if (_player.TryGetSessionByEntity(targetBody, out var session))
{
_pvsOverride.AddSessionOverride(psionic, session);
_pvsOverride.RemoveSessionOverride(targetBody, session);
}
RemCompDeferred<FracturedFormPowerComponent>(psionic);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
List<Entity<FracturedFormPowerComponent>> swapTargets = [];
var entities = EntityQueryEnumerator<FracturedFormPowerComponent, MobStateComponent>();
while (entities.MoveNext(out var uid, out var comp, out var mobState))
{
// Check sleep warning
if (!comp.SleepWarned && Timing.CurTime > comp.NextSwap - comp.WarningTimeBeforeSleep)
{
comp.SleepWarned = true;
Popup.PopupEntity(Loc.GetString("psionic-power-fractured-form-sleepy"), uid, uid, PopupType.LargeCaution);
Chat.TryEmoteWithChat(uid, "Yawn");
}
// Swap check
if ((_sleepingQuery.HasComp(uid) || MobState.IsIncapacitated(uid, mobState)) && Timing.CurTime > comp.NextVoluntarySwap
|| Timing.CurTime > comp.NextSwap)
swapTargets.Add((uid, comp));
}
foreach (var target in swapTargets)
{
Swap(target);
}
// Process bodies
var bodies = EntityQueryEnumerator<FracturedFormBodyComponent, MobStateComponent>();
while (bodies.MoveNext(out var uid, out var comp, out var mobState))
{
// Put to sleep if no sleeping component and no mind
if (!_sleepingQuery.HasComp(uid) && !_mind.GetMind(uid).HasValue && !FracturedQuery.HasComp(uid))
Sleeping.TrySleeping((uid, mobState));
// Cleanup invalid bodies
if (!comp.ControllingForm.IsValid()
|| Deleted(comp.ControllingForm)
|| !FracturedQuery.HasComp(comp.ControllingForm))
{
RemCompDeferred<FracturedFormBodyComponent>(uid);
}
}
}
}
@@ -0,0 +1,39 @@
using Content.Server.Wires;
using Content.Shared._DV.Silicons.Borgs;
using Content.Shared._DV.Silicons;
using Content.Shared.Wires;
namespace Content.Server._DV.Silicons;
/// <summary>
/// Controls whether the cyborg's ID chip slot is active
/// </summary>
public sealed partial class BorgIdChipWireAction : ComponentWireAction<IdChipSlotComponent>
{
public override string Name { get; set; } = "wire-name-borg-id-chip";
public override Color Color { get; set; } = Color.Thistle;
public override object StatusKey => BorgWireActionKey.ChipKey;
public override StatusLightState? GetLightState(Wire wire, IdChipSlotComponent component)
{
return component.Active ? StatusLightState.On : StatusLightState.Off;
}
public override bool Cut(EntityUid user, Wire wire, IdChipSlotComponent component)
{
EntityManager.System<IdChipSlotSystem>()
.SetActive((wire.Owner, component), false);
return true;
}
public override bool Mend(EntityUid user, Wire wire, IdChipSlotComponent component)
{
EntityManager.System<IdChipSlotSystem>()
.SetActive((wire.Owner, component), true);
return true;
}
public override void Pulse(EntityUid user, Wire wire, IdChipSlotComponent component)
{
}
}
@@ -0,0 +1,40 @@
using Content.Server.Silicons.Borgs;
using Content.Server.Wires;
using Content.Shared._DV.Silicons;
using Content.Shared.Silicons.Borgs.Components;
using Content.Shared.Wires;
namespace Content.Server._DV.Silicons;
/// <summary>
/// Controls whether the cyborg's transponder is active
/// </summary>
public sealed partial class BorgTransponderWireAction : ComponentWireAction<BorgTransponderComponent>
{
public override string Name { get; set; } = "wire-name-borg-transponder";
public override Color Color { get; set; } = Color.YellowGreen;
public override object StatusKey => BorgWireActionKey.TransponderKey;
public override StatusLightState? GetLightState(Wire wire, BorgTransponderComponent component)
{
return component.Active ? StatusLightState.On : StatusLightState.Off;
}
public override bool Cut(EntityUid user, Wire wire, BorgTransponderComponent component)
{
EntityManager.System<BorgSystem>()
.SetTransponderActive((wire.Owner, component), false);
return true;
}
public override bool Mend(EntityUid user, Wire wire, BorgTransponderComponent component)
{
EntityManager.System<BorgSystem>()
.SetTransponderActive((wire.Owner, component), true);
return true;
}
public override void Pulse(EntityUid user, Wire wire, BorgTransponderComponent component)
{
}
}
@@ -13,6 +13,7 @@ namespace Content.Server._DV.Silicons.Laws;
public sealed class SlavedBorgSystem : SharedSlavedBorgSystem public sealed class SlavedBorgSystem : SharedSlavedBorgSystem
{ {
[Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedSiliconLawSystem _siliconLaws = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -24,12 +25,12 @@ public sealed class SlavedBorgSystem : SharedSlavedBorgSystem
private void OnGetSiliconLaws(Entity<SlavedBorgComponent> ent, ref GetSiliconLawsEvent args) private void OnGetSiliconLaws(Entity<SlavedBorgComponent> ent, ref GetSiliconLawsEvent args)
{ {
if (ent.Comp.Added || !TryComp<SiliconLawProviderComponent>(ent, out var provider)) if (ent.Comp.HasBeenAdded || !TryComp<SiliconLawProviderComponent>(ent, out var provider))
return; return;
if (provider.Lawset is {} lawset) if (provider.Lawset is {} lawset && ent.Comp.ShouldBeAdded)
AddLaw(lawset, ent.Comp.Law); AddLaw(lawset, ent.Comp.Law);
ent.Comp.Added = true; // prevent opening the ui adding more law 0's ent.Comp.HasBeenAdded = true; // prevent opening the ui adding more law 0's
} }
/// <summary> /// <summary>
@@ -39,4 +40,30 @@ public sealed class SlavedBorgSystem : SharedSlavedBorgSystem
{ {
lawset.Laws.Insert(0, _proto.Index(law).ShallowClone()); lawset.Laws.Insert(0, _proto.Index(law).ShallowClone());
} }
/// <summary>
/// Sets whether the slaving is active.
/// </summary>
public void SetShouldBeAdded(Entity<SlavedBorgComponent?> ent, bool active)
{
if (!Resolve(ent, ref ent.Comp))
return;
if (!TryComp<SiliconLawProviderComponent>(ent, out var provider) || provider.Lawset is not { } lawset)
return;
if (!ent.Comp.ShouldBeAdded && active)
{
AddLaw(lawset, ent.Comp.Law);
ent.Comp.HasBeenAdded = true;
_siliconLaws.NotifyLawsChanged(ent);
}
else if (ent.Comp.ShouldBeAdded && !active)
{
lawset.Laws.Remove(_proto.Index(ent.Comp.Law));
ent.Comp.HasBeenAdded = false;
_siliconLaws.NotifyLawsChanged(ent);
}
ent.Comp.ShouldBeAdded = active;
}
} }
@@ -0,0 +1,40 @@
using Content.Server.Wires;
using Content.Shared._DV.Silicons;
using Content.Shared.PowerCell;
using Content.Shared.PowerCell.Components;
using Content.Shared.Wires;
namespace Content.Server._DV.Silicons;
/// <summary>
/// Controls whether the power cell slot is active
/// </summary>
public sealed partial class PowerCellSlotWireAction : ComponentWireAction<PowerCellSlotComponent>
{
public override string Name { get; set; } = "wire-name-power-cell";
public override Color Color { get; set; } = Color.BurlyWood;
public override object StatusKey => BorgWireActionKey.CellKey;
public override StatusLightState? GetLightState(Wire wire, PowerCellSlotComponent component)
{
return component.Active ? StatusLightState.On : StatusLightState.Off;
}
public override bool Cut(EntityUid user, Wire wire, PowerCellSlotComponent component)
{
EntityManager.System<PowerCellSystem>()
.SetSlotActive((wire.Owner, component), false);
return true;
}
public override bool Mend(EntityUid user, Wire wire, PowerCellSlotComponent component)
{
EntityManager.System<PowerCellSystem>()
.SetSlotActive((wire.Owner, component), true);
return true;
}
public override void Pulse(EntityUid user, Wire wire, PowerCellSlotComponent component)
{
}
}
@@ -0,0 +1,41 @@
using Content.Server._DV.Silicons.Laws;
using Content.Server.Wires;
using Content.Shared.Doors;
using Content.Shared._DV.Silicons.Laws;
using Content.Shared._DV.Silicons;
using Content.Shared.Wires;
namespace Content.Server._DV.Silicons;
/// <summary>
/// Controls whether the cyborg's transponder is active
/// </summary>
public sealed partial class SlavedBorgWireAction : ComponentWireAction<SlavedBorgComponent>
{
public override string Name { get; set; } = "wire-name-slaved-borg";
public override Color Color { get; set; } = Color.Coral;
public override object StatusKey => BorgWireActionKey.SlavedKey;
public override StatusLightState? GetLightState(Wire wire, SlavedBorgComponent component)
{
return component.ShouldBeAdded ? StatusLightState.On : StatusLightState.Off;
}
public override bool Cut(EntityUid user, Wire wire, SlavedBorgComponent component)
{
EntityManager.System<SlavedBorgSystem>()
.SetShouldBeAdded((wire.Owner, component), false);
return true;
}
public override bool Mend(EntityUid user, Wire wire, SlavedBorgComponent component)
{
EntityManager.System<SlavedBorgSystem>()
.SetShouldBeAdded((wire.Owner, component), true);
return true;
}
public override void Pulse(EntityUid user, Wire wire, SlavedBorgComponent component)
{
}
}
+124
View File
@@ -0,0 +1,124 @@
using Content.Server._DV.Psionics.Systems;
using Content.Server.Antag;
using Content.Shared._DV.Movement.Components;
using Content.Shared._DV.Psionics.Components;
using Content.Shared.CharacterInfo;
using Content.Shared.Cloning.Events;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.NPC.Components;
using Content.Shared.Radio.Components;
using Content.Shared.Roles.Components;
using Content.Shared.Zombies;
using Robust.Shared.Prototypes;
namespace Content.Server.Zombies;
public sealed partial class ZombieSystem
{
//private static readonly string MindRoleInitialInfected = "MindRoleInitialInfected";
private static readonly EntProtoId InitialInfectedFailureSurviveObjective = "InitialInfectedFailureSurviveObjective";
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly PsionicSystem _psionic = default!; // DeltaV
[Dependency] private readonly SharedJetpackSystem _jetpack = default!; // DeltaV - Prevent Jetpacks on Zombies
private void InitializeDV()
{
// This needs to be done before CloneEvent or else the proper attributes won't be restored or modified on
// the new entity.
SubscribeLocalEvent<ZombieComponent, CloningAttemptEvent>(OnBeforeUnzombify);
SubscribeLocalEvent<InitialInfectedComponent, CloningEvent>(OnInitialInfectedCloning);
}
/// <summary>
/// DeltaV - Extra things we want to ensure, remove, or take care of when someone is zombified.
/// </summary>
private void ZombifyEntityDV(Entity<ZombieComponent> ent, MobStateComponent? mobState = null)
{
// Remove innate radio
RemComp<ActiveRadioComponent>(ent); // If the zombie has an innate radio, get rid of it.
// Remove headsets in pockets
for (var i = 1; i <= 4; i++) // Arachnids have 4 pockets
{
if (_inventory.TryGetSlotEntity(ent, $"pocket{i}", out var headset) && HasComp<HeadsetComponent>(headset))
_inventory.TryUnequip(ent, $"pocket{i}", true, true);
}
// Prevent Psionic Zombies
RemComp<PotentialPsionicComponent>(ent);
_psionic.MindBreakEntity(ent.Owner, false, true);
// Prevent Jetpacks on Zombies
if (TryComp<JetpackUserComponent>(ent, out var jetpackUser))
{
if (TryComp<JetpackComponent>(jetpackUser.Jetpack, out var jetpack))
_jetpack.SetEnabled(jetpackUser.Jetpack, jetpack, false, ent);
}
RemComp<AutomaticJetpackUserComponent>(ent);
// Prevent shitters biting other zombies
EnsureComp<NoFriendlyFireComponent>(ent);
}
/// <summary>
/// DeltaV - We need to save some extra things to the ZombieComponent so we can restore them when
/// they are about to be cloned.
/// </summary>
/// <param name="ent"></param>
private void PreserveEntityComponentState(Entity<ZombieComponent> ent)
{
SaveFactionsBeforeZombification(ent);
// Zombification removes pacifism but lets track if they have it so the pacifist players will be... pacified.
ent.Comp.WasPacisfistBeforeZombification = HasComp<PacifiedComponent>(ent);
}
private void SaveFactionsBeforeZombification(Entity<ZombieComponent> ent)
{
if (!TryComp<NpcFactionMemberComponent>(ent, out var faction))
return;
ent.Comp.FactionsBeforeZombification.AddRange(faction.Factions);
}
/// <summary>
/// DeltaV - Restores certain components to their previous state that the upstream unzombify doesn't handle.
/// We need to do this before CloningEvent so the cloned body will have the updated components BEFORE cloning happens.
/// </summary>
/// <param name="ent"></param>
/// <param name="args"></param>
private void OnBeforeUnzombify(Entity<ZombieComponent> ent, ref CloningAttemptEvent args)
{
// Restore factions
_faction.ClearFactions(ent.Owner); // Should only be zombie, but might as well clear the whole list in case.
ent.Comp.FactionsBeforeZombification.ForEach(previousFaction => _faction.AddFaction(ent.Owner, previousFaction));
// Restore pacifism
if (ent.Comp.WasPacisfistBeforeZombification)
EnsureComp<PacifiedComponent>(ent);
}
/// <summary>
/// DeltaV - Adds a survival objective to Initial Infected who are cloned. Chances are that the crew has recovered, so
/// bioterrorism time is probably over.
/// </summary>
/// <param name="ent"></param>
/// <param name="args"></param>
private void OnInitialInfectedCloning(Entity<InitialInfectedComponent> ent, ref CloningEvent args)
{
// Change II objectives so they no longer have infect objectives. Now, they'll have survival objectives. Live to infect another day. :)
_mind.TryGetMind(ent.Owner, out var mindId, out var mindContainer);
// If we can't find the mind, oh well. We tried.
if (mindContainer is not { } mind)
return;
if (_role.MindHasRole<InitialInfectedRoleComponent>(mindId))
_mind.TryAddObjective(mindId, mind, InitialInfectedFailureSurviveObjective);
}
}
@@ -33,37 +33,33 @@ public sealed class InteractorSystem : SharedInteractorSystem
return; return;
} }
// Mono var i = count - 1;
for (var i = count - 1; i >= 0; i--) var netEnt = ent.Comp.TargetEntities[i].Item1;
var target = GetEntity(netEnt);
_constructionQuery.TryComp(target, out var construction);
var originalCount = construction?.InteractionQueue?.Count ?? 0;
if (!InteractWith(ent, target))
{ {
var netEnt = ent.Comp.TargetEntities[i].Item1; // have to remove it since user's filter was bad due to unhandled interaction
var target = GetEntity(netEnt); RemoveTarget(ent, target);
_constructionQuery.TryComp(target, out var construction); Machine.Failed(ent.Owner);
var originalCount = construction?.InteractionQueue?.Count ?? 0; return;
if (!InteractWith(ent, target)) }
{
// have to remove it since user's filter was bad due to unhandled interaction
// RemoveTarget(ent, target); // Mono
Machine.Failed(ent.Owner);
continue; // Mono
}
// construction supercode queues it instead of starting a doafter now, assume that queuing means it has started // construction supercode queues it instead of starting a doafter now, assume that queuing means it has started
var newCount = construction?.InteractionQueue?.Count ?? 0; var newCount = construction?.InteractionQueue?.Count ?? 0;
if (newCount > originalCount if (newCount > originalCount
|| HasDoAfter(ent)) || HasDoAfter(ent))
{ {
Machine.Started(ent.Owner); Machine.Started(ent.Owner);
UpdateAppearance(ent, InteractorState.Active); UpdateAppearance(ent, InteractorState.Active);
} }
else else
{ {
// no doafter, complete it immediately // no doafter, complete it immediately
TryRemoveTarget(ent, target); TryRemoveTarget(ent, target);
Machine.Completed(ent.Owner); Machine.Completed(ent.Owner);
UpdateAppearance(ent); UpdateAppearance(ent);
}
break; // Mono
} }
} }
} }
@@ -1,19 +0,0 @@
using Content.Shared.DeviceLinking;
using Content.Shared.Research.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Server._Goobstation.Lathe;
/// <summary>
/// Lets a lathe produce the last made recipe, controlled by signal port.
/// The port must be added by something else e.g. AutomationSlots
/// </summary>
[RegisterComponent, Access(typeof(LatheAutomationSystem))]
public sealed partial class LatheAutomationComponent : Component
{
[ViewVariables]
public LatheRecipePrototype? LastRecipe;
[DataField]
public ProtoId<SinkPortPrototype> PrintPort = "LathePrint";
}
@@ -1,40 +0,0 @@
using Content.Shared._Goobstation.Factory;
using Content.Server.Lathe;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Lathe;
namespace Content.Server._Goobstation.Lathe;
public sealed class LatheAutomationSystem : EntitySystem
{
[Dependency] private readonly AutomationSystem _automation = default!;
[Dependency] private readonly LatheSystem _lathe = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LatheAutomationComponent, LatheStartPrintingEvent>(OnStartPrinting);
SubscribeLocalEvent<LatheAutomationComponent, SignalReceivedEvent>(OnSignalReceived);
}
private void OnStartPrinting(Entity<LatheAutomationComponent> ent, ref LatheStartPrintingEvent args)
{
ent.Comp.LastRecipe = args.Recipe;
}
private void OnSignalReceived(Entity<LatheAutomationComponent> ent, ref SignalReceivedEvent args)
{
if (!_automation.IsAutomated(ent))
return;
if (args.Port != ent.Comp.PrintPort)
return;
if (ent.Comp.LastRecipe is not {} recipe)
return;
_lathe.TryAddToQueue(ent.Owner, recipe, 1);
_lathe.TryStartProducing(ent.Owner); // Won't do anything otherwise
}
}
@@ -285,6 +285,11 @@ public sealed partial class AnomalyComponent : Component
[DataField] [DataField]
public bool DeleteEntity = true; public bool DeleteEntity = true;
// DeltaV - Colossus additions START
[DataField]
public bool AlwaysGrow;
// DeltaV - Colossus additions END
} }
/// <summary> /// <summary>
@@ -308,7 +313,7 @@ public readonly record struct AnomalySupercriticalEvent(EntityUid Anomaly, float
/// <param name="Anomaly">The anomaly being shut down.</param> /// <param name="Anomaly">The anomaly being shut down.</param>
/// <param name="Supercritical">Whether or not the anomaly shut down passively or via a supercritical event.</param> /// <param name="Supercritical">Whether or not the anomaly shut down passively or via a supercritical event.</param>
[ByRefEvent] [ByRefEvent]
public readonly record struct AnomalyShutdownEvent(EntityUid Anomaly, bool Supercritical); public readonly record struct AnomalyShutdownEvent(EntityUid Anomaly, bool Supercritical, bool Forced); // DeltaV - Add Forced
/// <summary> /// <summary>
/// Event broadcast when an anomaly's severity is changed. /// Event broadcast when an anomaly's severity is changed.
+13 -4
View File
@@ -84,7 +84,7 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
Log.Info($"Performing anomaly pulse. Entity: {ToPrettyString(uid)}"); Log.Info($"Performing anomaly pulse. Entity: {ToPrettyString(uid)}");
// if we are above the growth threshold, then grow before the pulse // if we are above the growth threshold, then grow before the pulse
if (component.Stability > component.GrowthThreshold) if (component.AlwaysGrow || component.Stability > component.GrowthThreshold) // DeltaV - Add AlwaysGrow
{ {
ChangeAnomalySeverity(uid, GetSeverityIncreaseFromGrowth(component), component); ChangeAnomalySeverity(uid, GetSeverityIncreaseFromGrowth(component), component);
} }
@@ -192,7 +192,8 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
/// <param name="supercritical">Whether or not the anomaly ended via supercritical event</param> /// <param name="supercritical">Whether or not the anomaly ended via supercritical event</param>
/// <param name="spawnCore">Create anomaly cores based on the result of completing an anomaly?</param> /// <param name="spawnCore">Create anomaly cores based on the result of completing an anomaly?</param>
/// <param name="logged">Whether or not the anomaly decaying/going supercritical is logged</param> /// <param name="logged">Whether or not the anomaly decaying/going supercritical is logged</param>
public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false, bool spawnCore = true, bool logged = false) /// <param name="forced">Whether or not the anomaly shutdown was caused by component shutdown</param> // DeltaV - Add forced
public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false, bool spawnCore = true, bool logged = false, bool forced = false) // DeltaV - Add forced
{ {
if (logged) if (logged)
{ {
@@ -207,7 +208,7 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
var ev = new AnomalyShutdownEvent(uid, supercritical); var ev = new AnomalyShutdownEvent(uid, supercritical, forced); // DeltaV - Add forced
RaiseLocalEvent(uid, ref ev, true); RaiseLocalEvent(uid, ref ev, true);
if (Terminating(uid) || _net.IsClient) if (Terminating(uid) || _net.IsClient)
@@ -345,7 +346,7 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
// if the stability is under the death threshold, // if the stability is under the death threshold,
// update it every second to start killing it slowly. // update it every second to start killing it slowly.
if (anomaly.Stability < anomaly.DecayThreshold) if (!anomaly.AlwaysGrow && anomaly.Stability < anomaly.DecayThreshold) // DeltaV - Add AlwaysGrow
{ {
ChangeAnomalyHealth(ent, anomaly.HealthChangePerSecond * frameTime, anomaly); ChangeAnomalyHealth(ent, anomaly.HealthChangePerSecond * frameTime, anomaly);
} }
@@ -479,6 +480,14 @@ public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Mad
if (!Resolve(ent, ref ent.Comp, logMissing: false)) if (!Resolve(ent, ref ent.Comp, logMissing: false))
return false; return false;
// DeltaV - Colossus Additions START
if (ent.Comp.AlwaysGrow)
{
visual = AnomalyStabilityVisuals.Growing;
return true;
}
// DeltaV - Colossus Additions END
visual = AnomalyStabilityVisuals.Stable; visual = AnomalyStabilityVisuals.Stable;
if (ent.Comp.Stability <= ent.Comp.DecayThreshold) if (ent.Comp.Stability <= ent.Comp.DecayThreshold)
{ {
+6
View File
@@ -135,4 +135,10 @@ public sealed partial class CCVars
/// </summary> /// </summary>
public static readonly CVarDef<int> AdminOverlayStackMax = public static readonly CVarDef<int> AdminOverlayStackMax =
CVarDef.Create("ui.admin_overlay_stack_max", 3, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.admin_overlay_stack_max", 3, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// If true, ghosts will see an "(F)" button next to chat messages, which can be used to follow the sender.
/// </summary>
public static readonly CVarDef<bool> InterfaceChatFollowButton =
CVarDef.Create("ui.chat_follow_button", true, CVar.CLIENT | CVar.REPLICATED | CVar.ARCHIVE);
} }
+14
View File
@@ -53,6 +53,20 @@ namespace Content.Shared.Chat
AudioPath = audioPath; AudioPath = audioPath;
AudioVolume = audioVolume; AudioVolume = audioVolume;
} }
public ChatMessage(ChatMessage copyFrom)
{
Channel = copyFrom.Channel;
Message = copyFrom.Message;
WrappedMessage = copyFrom.WrappedMessage;
SenderEntity = copyFrom.SenderEntity;
SenderKey = copyFrom.SenderKey;
HideChat = copyFrom.HideChat;
MessageColorOverride = copyFrom.MessageColorOverride;
AudioPath = copyFrom.AudioPath;
AudioVolume = copyFrom.AudioVolume;
Read = copyFrom.Read;
}
} }
/// <summary> /// <summary>
+44 -1
View File
@@ -1,5 +1,6 @@
using System.Collections.Frozen; using System.Collections.Frozen;
using System.Collections.Immutable; using System.Collections.Immutable;
using Content.Shared._DV.Chat; // DeltaV - chat
using Content.Shared.Chat.Prototypes; using Content.Shared.Chat.Prototypes;
using Content.Shared.Speech; using Content.Shared.Speech;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -98,7 +99,7 @@ public abstract partial class SharedChatSystem
{ {
// not all emotes are loc'd, but for the ones that are we pass in entity // not all emotes are loc'd, but for the ones that are we pass in entity
var action = Loc.GetString(_random.Pick(emote.ChatMessages), ("entity", source)); var action = Loc.GetString(_random.Pick(emote.ChatMessages), ("entity", source));
SendEntityEmote(source, action, range, nameOverride, hideLog: hideLog, checkEmote: false, ignoreActionBlocker: ignoreActionBlocker); SendEntityEmote(source, action, range, nameOverride, null, hideLog: hideLog, checkEmote: false, ignoreActionBlocker: ignoreActionBlocker); // DeltaV
} }
return didEmote; return didEmote;
@@ -208,6 +209,48 @@ public abstract partial class SharedChatSystem
return true; return true;
} }
// DeltaV
/// <summary>
/// Checks which type of emote the message contains and returns the type while outputting the string without the prefixes.
/// </summary>
public static EmoteType? ProcessEmoteMessage(EntityUid source, string input, out string output)
{
output = input.Trim();
EmoteType? type = null;
if (input.Length == 0)
return type;
if (!(input.StartsWith(AudibleEmotePrefix) || input.StartsWith(PossessiveEmotePrefix)))
{
type = EmoteType.Normal;
return type;
}
var emoteType = input[0];
output = input[1..].TrimStart();
if (emoteType == AudibleEmotePrefix)
{
emoteType = input[1]; // Check for if we want AudiblePossessiveEmote
type = EmoteType.Audible;
}
if (emoteType == PossessiveEmotePrefix)
{
if (type == EmoteType.Audible)
{
type = EmoteType.AudiblePossessive;
output = input[2..].TrimStart();
}
else
type = EmoteType.Possessive;
}
return type;
}
// DeltaV - End
/// <summary> /// <summary>
/// Creates and raises <see cref="BeforeEmoteEvent"/> and then <see cref="EmoteEvent"/> to let other systems do things like play audio. /// Creates and raises <see cref="BeforeEmoteEvent"/> and then <see cref="EmoteEvent"/> to let other systems do things like play audio.
/// In the case that the Before event is cancelled, EmoteEvent will NOT be raised, and will optionally show a message to the player /// In the case that the Before event is cancelled, EmoteEvent will NOT be raised, and will optionally show a message to the player
+25 -3
View File
@@ -1,5 +1,6 @@
using System.Collections.Frozen; using System.Collections.Frozen;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Content.Shared._DV.Chat;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Chat.Prototypes; using Content.Shared.Chat.Prototypes;
using Content.Shared.Popups; using Content.Shared.Popups;
@@ -29,6 +30,8 @@ public abstract partial class SharedChatSystem : EntitySystem
public const char OOCPrefix = '['; public const char OOCPrefix = '[';
public const char EmotesPrefix = '@'; public const char EmotesPrefix = '@';
public const char EmotesAltPrefix = '*'; public const char EmotesAltPrefix = '*';
public const char AudibleEmotePrefix = '!'; // DeltaV - You may now scream audibly!
public const char PossessiveEmotePrefix = '\''; // DeltaV - You may now be possessive of things! Whatever that means.
public const char AdminPrefix = ']'; public const char AdminPrefix = ']';
public const char WhisperPrefix = ','; public const char WhisperPrefix = ',';
public const char TelepathicPrefix = '='; //Nyano - Summary: Adds the telepathic channel's prefix. public const char TelepathicPrefix = '='; //Nyano - Summary: Adds the telepathic channel's prefix.
@@ -152,6 +155,7 @@ public abstract partial class SharedChatSystem : EntitySystem
string input, string input,
out string output, out string output,
out RadioChannelPrototype? channel, out RadioChannelPrototype? channel,
bool capitalize = true, // DeltaV - We might not want to capitalize the first letter if we send in emotes.
bool quiet = false) bool quiet = false)
{ {
output = input.Trim(); output = input.Trim();
@@ -162,7 +166,7 @@ public abstract partial class SharedChatSystem : EntitySystem
if (input.StartsWith(RadioCommonPrefix)) if (input.StartsWith(RadioCommonPrefix))
{ {
output = SanitizeMessageCapital(input[1..].TrimStart()); output = capitalize ? SanitizeMessageCapital(input[1..].TrimStart()) : input[1..].TrimStart(); // DeltaV
channel = _prototypeManager.Index<RadioChannelPrototype>(CommonChannel); channel = _prototypeManager.Index<RadioChannelPrototype>(CommonChannel);
return true; return true;
} }
@@ -172,7 +176,7 @@ public abstract partial class SharedChatSystem : EntitySystem
if (input.Length < 2 || char.IsWhiteSpace(input[1])) if (input.Length < 2 || char.IsWhiteSpace(input[1]))
{ {
output = SanitizeMessageCapital(input[1..].TrimStart()); output = capitalize ? SanitizeMessageCapital(input[1..].TrimStart()) : input[1..].TrimStart(); // DeltaV
if (!quiet) if (!quiet)
_popup.PopupEntity(Loc.GetString("chat-manager-no-radio-key"), source, source); _popup.PopupEntity(Loc.GetString("chat-manager-no-radio-key"), source, source);
return true; return true;
@@ -180,7 +184,7 @@ public abstract partial class SharedChatSystem : EntitySystem
var channelKey = input[1]; var channelKey = input[1];
channelKey = char.ToLower(channelKey); channelKey = char.ToLower(channelKey);
output = SanitizeMessageCapital(input[2..].TrimStart()); output = capitalize ? SanitizeMessageCapital(input[2..].TrimStart()) : input[2..].TrimStart(); // DeltaV
if (channelKey == DefaultChannelKey) if (channelKey == DefaultChannelKey)
{ {
@@ -314,11 +318,29 @@ public abstract partial class SharedChatSystem : EntitySystem
return rawmsg.Substring(tagStart, tagEnd - tagStart); return rawmsg.Substring(tagStart, tagEnd - tagStart);
} }
// DeltaV
protected virtual void SendAudibleEntityEmote(
EntityUid source,
string action,
ChatTransmitRange range,
string? nameOverride,
RadioChannelPrototype? channel,
EmoteType? emoteType,
bool hideLog = false,
bool checkEmote = true,
bool ignoreActionBlocker = false,
NetUserId? author = null
)
{
}
// DeltaV - End
protected virtual void SendEntityEmote( protected virtual void SendEntityEmote(
EntityUid source, EntityUid source,
string action, string action,
ChatTransmitRange range, ChatTransmitRange range,
string? nameOverride, string? nameOverride,
EmoteType? emoteType,
bool hideLog = false, bool hideLog = false,
bool checkEmote = true, bool checkEmote = true,
bool ignoreActionBlocker = false, bool ignoreActionBlocker = false,
+53 -6
View File
@@ -17,6 +17,17 @@ namespace Content.Shared.Humanoid
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
// DeltaV - i hate this hack but https://github.com/space-wizards/RobustToolbox/issues/6576
private ISawmill _locSawmill = default!;
public override void Initialize()
{
base.Initialize();
_locSawmill = Logger.GetSawmill("loc");
}
// End DeltaV - i hate this hack
public string GetName(string species, Gender? gender = null) public string GetName(string species, Gender? gender = null)
{ {
// if they have an old species or whatever just fall back to human I guess? // if they have an old species or whatever just fall back to human I guess?
@@ -47,31 +58,67 @@ namespace Content.Shared.Humanoid
return Loc.GetString("namepreset-lastfirst", return Loc.GetString("namepreset-lastfirst",
("last", GetLastName(speciesProto)), ("first", GetFirstName(speciesProto, gender))); ("last", GetLastName(speciesProto)), ("first", GetFirstName(speciesProto, gender)));
case SpeciesNaming.FirstLast: case SpeciesNaming.FirstLast:
// Begin DeltaV - more complex naming
default: default:
{
var firstId = GetFirstNameId(speciesProto, gender);
var lastId = GetLastNameId(speciesProto);
var plural = false;
var oldLevel =_locSawmill.Level;
_locSawmill.Level = LogLevel.Fatal; // this is a hack to avoid testfails because TryGetString still logs errors for not-found stuff anyways (wtf)
if (Loc.TryGetString($"{lastId}.plural", out var pluralStr))
plural = pluralStr == "true";
var last = Loc.GetString(lastId);
if (Loc.TryGetString($"{firstId}.intersperse", out var firstIntersperse, ("last", last), ("lastPlural", plural)))
{
_locSawmill.Level = oldLevel;
return firstIntersperse;
}
_locSawmill.Level = oldLevel;
return Loc.GetString("namepreset-firstlast", return Loc.GetString("namepreset-firstlast",
("first", GetFirstName(speciesProto, gender)), ("last", GetLastName(speciesProto))); ("first", Loc.GetString(firstId)),
("last", last),
("lastPlural", plural));
}
// End DeltaV - more complex naming
} }
} }
// Begin DeltaV - we want IDs
public string GetFirstName(SpeciesPrototype speciesProto, Gender? gender = null) public string GetFirstName(SpeciesPrototype speciesProto, Gender? gender = null)
{
return Loc.GetString(GetFirstNameId(speciesProto, gender));
}
public string GetFirstNameId(SpeciesPrototype speciesProto, Gender? gender = null)
{ {
switch (gender) switch (gender)
{ {
case Gender.Male: case Gender.Male:
return _random.Pick(_prototypeManager.Index(speciesProto.MaleFirstNames)); return _random.PickId(_prototypeManager.Index(speciesProto.MaleFirstNames));
case Gender.Female: case Gender.Female:
return _random.Pick(_prototypeManager.Index(speciesProto.FemaleFirstNames)); return _random.PickId(_prototypeManager.Index(speciesProto.FemaleFirstNames));
default: default:
if (_random.Prob(0.5f)) if (_random.Prob(0.5f))
return _random.Pick(_prototypeManager.Index(speciesProto.MaleFirstNames)); return _random.PickId(_prototypeManager.Index(speciesProto.MaleFirstNames));
else else
return _random.Pick(_prototypeManager.Index(speciesProto.FemaleFirstNames)); return _random.PickId(_prototypeManager.Index(speciesProto.FemaleFirstNames));
} }
} }
public string GetLastNameId(SpeciesPrototype speciesProto)
{
return _random.PickId(_prototypeManager.Index(speciesProto.LastNames));
}
public string GetLastName(SpeciesPrototype speciesProto) public string GetLastName(SpeciesPrototype speciesProto)
{ {
return _random.Pick(_prototypeManager.Index(speciesProto.LastNames)); return Loc.GetString(GetLastNameId(speciesProto));
} }
// End DeltaV - we want IDs
} }
} }
@@ -28,6 +28,11 @@ public sealed class ActivateInWorldEvent : HandledEntityEventArgs, ITargetedInte
/// </summary> /// </summary>
public bool WasLogged { get; set; } public bool WasLogged { get; set; }
/// <summary>
/// Stellar - set to false if this interaction shouldn't have an interaction particle
/// </summary>
public bool InteractionParticle = true;
public ActivateInWorldEvent(EntityUid user, EntityUid target, bool complex) public ActivateInWorldEvent(EntityUid user, EntityUid target, bool complex)
{ {
User = user; User = user;
@@ -30,6 +30,11 @@ public sealed class InteractUsingEvent : HandledEntityEventArgs
/// </summary> /// </summary>
public EntityCoordinates ClickLocation { get; } public EntityCoordinates ClickLocation { get; }
/// <summary>
/// Stellar - set to false if this interaction shouldn't have an interaction particle
/// </summary>
public bool InteractionParticle = true;
public InteractUsingEvent(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation) public InteractUsingEvent(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation)
{ {
// Interact using should not have the same used and target. // Interact using should not have the same used and target.
@@ -31,6 +31,7 @@ using Content.Shared.UserInterface;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Content.Shared.Wall; using Content.Shared.Wall;
using Content.Shared._Goobstation.DoAfter; // Goobstation using Content.Shared._Goobstation.DoAfter; // Goobstation
using Content.Shared.Inventory.VirtualItem; // Stellar - interaction particles
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Input; using Robust.Shared.Input;
@@ -1073,8 +1074,8 @@ namespace Content.Shared.Interaction
var userInteractUsingEvent = new UserInteractUsingEvent(user, used, target, clickLocation); var userInteractUsingEvent = new UserInteractUsingEvent(user, used, target, clickLocation);
RaiseLocalEvent(user, userInteractUsingEvent, true); RaiseLocalEvent(user, userInteractUsingEvent, true);
DoContactInteraction(user, used, null, true, interactUsingEvent); // Stellar - interaction particles DoContactInteraction(user, used, null, true, interactUsingEvent, interactionParticles: interactUsingEvent.InteractionParticle); // Stellar - interaction particles
DoContactInteraction(user, target, used, true, interactUsingEvent); // Stellar - interaction particles DoContactInteraction(user, target, used, true, interactUsingEvent, interactionParticles: interactUsingEvent.InteractionParticle); // Stellar - interaction particles
// Contact interactions are currently only used for forensics, so we don't raise used -> target // Contact interactions are currently only used for forensics, so we don't raise used -> target
if (interactUsingEvent.Handled || userInteractUsingEvent.Handled) if (interactUsingEvent.Handled || userInteractUsingEvent.Handled)
return true; return true;
@@ -1189,7 +1190,7 @@ namespace Content.Shared.Interaction
RaiseLocalEvent(used, activateMsg, true); RaiseLocalEvent(used, activateMsg, true);
if (activateMsg.Handled) if (activateMsg.Handled)
{ {
DoContactInteraction(user, used, null, true); // Interaction particles DoContactInteraction(user, used, null, true, interactionParticles: activateMsg.InteractionParticle); // Stellar - Interaction particles
if (!activateMsg.WasLogged) if (!activateMsg.WasLogged)
_adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); _adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
@@ -1437,7 +1438,8 @@ namespace Content.Shared.Interaction
/// <param name="predicted">Whether this interaction is predicted. <see cref="uidA"/> is assumed to be the client entity.</param> /// <param name="predicted">Whether this interaction is predicted. <see cref="uidA"/> is assumed to be the client entity.</param>
/// <param name="args">Optional handleable entity event to check.</param> /// <param name="args">Optional handleable entity event to check.</param>
/// <param name="interactionParticles">Whether to spawn interaction particles on this contact.</param> /// <param name="interactionParticles">Whether to spawn interaction particles on this contact.</param>
public void DoContactInteraction(EntityUid uidA, EntityUid? uidB, EntityUid? used, bool predicted, HandledEntityEventArgs? args = null, bool interactionParticles = true) // Stellar/ES - interaction particles /// <param name="interactionParticleType">The type of interaction particle to spawn for this event.</param>
public void DoContactInteraction(EntityUid uidA, EntityUid? uidB, EntityUid? used, bool predicted, HandledEntityEventArgs? args = null, bool interactionParticles = true, StellarInteractionParticleType interactionParticleType = StellarInteractionParticleType.Use) // Stellar/ES - interaction particles
{ {
if (uidB == null || args?.Handled == false) if (uidB == null || args?.Handled == false)
return; return;
@@ -1459,7 +1461,7 @@ namespace Content.Shared.Interaction
RaiseLocalEvent(uidB.Value, ev); RaiseLocalEvent(uidB.Value, ev);
// Begin Stellar/ES Additions - Interaction particles // Begin Stellar/ES Additions - Interaction particles
if (!interactionParticles) if (!interactionParticles || HasComp<VirtualItemComponent>(uidB))
return; return;
if (_net.IsServer) if (_net.IsServer)
@@ -1468,11 +1470,11 @@ namespace Content.Shared.Interaction
? Filter.PvsExcept(uidA, entityManager: EntityManager) ? Filter.PvsExcept(uidA, entityManager: EntityManager)
: Filter.Pvs(uidA, entityManager: EntityManager); : Filter.Pvs(uidA, entityManager: EntityManager);
RaiseNetworkEvent(new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), false), filter); RaiseNetworkEvent(new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), false, interactionParticleType), filter);
} }
else if (_gameTiming.IsFirstTimePredicted) else if (_gameTiming.IsFirstTimePredicted)
{ {
var evt = new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), true); var evt = new StellarInteractionParticleEvent(GetNetEntity(uidA), GetNetEntity(used), GetNetEntity(uidB.Value), true, interactionParticleType);
RaiseLocalEvent(evt); RaiseLocalEvent(evt);
} }
// End Stellar/ES Additions - Interaction particles // End Stellar/ES Additions - Interaction particles
@@ -15,33 +15,24 @@ public enum SignalTimerUiKey : byte
public sealed class SignalTimerBoundUserInterfaceState : BoundUserInterfaceState public sealed class SignalTimerBoundUserInterfaceState : BoundUserInterfaceState
{ {
public string CurrentText; public string CurrentText;
// Begin Monolith public string CurrentDelayMinutes;
// public string CurrentDelayMinutes; public string CurrentDelaySeconds;
// public string CurrentDelaySeconds;
public TimeSpan CurrentDelay;
// End Monolith
public bool ShowText; public bool ShowText;
public TimeSpan TriggerTime; public TimeSpan TriggerTime;
public bool TimerStarted; public bool TimerStarted;
public bool HasAccess; public bool HasAccess;
public SignalTimerBoundUserInterfaceState(string currentText, public SignalTimerBoundUserInterfaceState(string currentText,
// Begin Monolith string currentDelayMinutes,
// string currentDelayMinutes, string currentDelaySeconds,
// string currentDelaySeconds,
TimeSpan currentDelay,
// End Monolith
bool showText, bool showText,
TimeSpan triggerTime, TimeSpan triggerTime,
bool timerStarted, bool timerStarted,
bool hasAccess) bool hasAccess)
{ {
CurrentText = currentText; CurrentText = currentText;
// Begin Monolith CurrentDelayMinutes = currentDelayMinutes;
// CurrentDelayMinutes = currentDelayMinutes; CurrentDelaySeconds = currentDelaySeconds;
// CurrentDelaySeconds = currentDelaySeconds;
CurrentDelay = currentDelay;
// End Monolith
ShowText = showText; ShowText = showText;
TriggerTime = triggerTime; TriggerTime = triggerTime;
TimerStarted = timerStarted; TimerStarted = timerStarted;
+5
View File
@@ -122,6 +122,11 @@ public sealed class MindExamineSystem : EntitySystem
else else
ent.Comp.State = MindState.None; ent.Comp.State = MindState.None;
// DeltaV - SSD Recency START
var ev = new MindStateUpdatedEvent(ent.Comp.State);
RaiseLocalEvent(ent, ref ev);
// DeltaV END
Dirty(ent); Dirty(ent);
} }
} }
@@ -1,3 +1,4 @@
using Content.Shared._ST.Interaction; // Stellar - interaction particles
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Alert; using Content.Shared.Alert;
@@ -545,7 +546,7 @@ public sealed class PullingSystem : EntitySystem
// Pulling confirmed // Pulling confirmed
_interaction.DoContactInteraction(pullableUid, pullerUid, null, true); // Stellar - Interaction particles _interaction.DoContactInteraction(pullerUid, pullableUid,null, true, interactionParticleType: StellarInteractionParticleType.Pull); // Stellar - Interaction particles
// Use net entity so it's consistent across client and server. // Use net entity so it's consistent across client and server.
pullableComp.PullJointId = $"pull-joint-{GetNetEntity(pullableUid)}"; pullableComp.PullJointId = $"pull-joint-{GetNetEntity(pullableUid)}";
@@ -23,5 +23,12 @@ public sealed partial class PowerCellSlotComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public bool FitsInCharger = true; public bool FitsInCharger = true;
// Begin DeltaV
/// <summary>
/// DeltaV - power cell slots
/// </summary>
[DataField, AutoNetworkedField]
public bool Active = true;
// End DeltaV
} }
@@ -39,6 +39,14 @@ public sealed partial class PowerCellSystem
return false; return false;
} }
// Begin DeltaV
if (!ent.Comp.Active)
{
battery = null;
return false;
}
// End DeltaV
if (!_itemSlots.TryGetSlot(ent.Owner, ent.Comp.CellSlotId, out var slot)) if (!_itemSlots.TryGetSlot(ent.Owner, ent.Comp.CellSlotId, out var slot))
{ {
battery = null; battery = null;
@@ -153,6 +161,21 @@ public sealed partial class PowerCellSystem
[PublicAPI] [PublicAPI]
public bool HasCharge(Entity<PowerCellSlotComponent?> ent, float charge, EntityUid? user = null, bool predicted = false) public bool HasCharge(Entity<PowerCellSlotComponent?> ent, float charge, EntityUid? user = null, bool predicted = false)
{ {
// Begin DeltaV
if (Resolve(ent, ref ent.Comp, false) && !ent.Comp.Active)
{
if (user == null)
return false;
if (predicted)
_popup.PopupClient(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value);
else
_popup.PopupEntity(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value);
return false;
}
// End DeltaV
if (!TryGetBatteryFromSlot(ent, out var battery)) if (!TryGetBatteryFromSlot(ent, out var battery))
{ {
if (user == null) if (user == null)
@@ -192,6 +215,21 @@ public sealed partial class PowerCellSystem
[PublicAPI] [PublicAPI]
public bool TryUseCharge(Entity<PowerCellSlotComponent?> ent, float charge, EntityUid? user = null, bool predicted = false) public bool TryUseCharge(Entity<PowerCellSlotComponent?> ent, float charge, EntityUid? user = null, bool predicted = false)
{ {
// Begin DeltaV
if (Resolve(ent, ref ent.Comp, false) && !ent.Comp.Active)
{
if (user == null)
return false;
if (predicted)
_popup.PopupClient(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value);
else
_popup.PopupEntity(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value);
return false;
}
// End DeltaV
if (!TryGetBatteryFromSlot(ent, out var battery)) if (!TryGetBatteryFromSlot(ent, out var battery))
{ {
if (user == null) if (user == null)
@@ -247,6 +285,29 @@ public sealed partial class PowerCellSystem
return _battery.GetMaxUses(battery.Value.AsNullable(), cost); return _battery.GetMaxUses(battery.Value.AsNullable(), cost);
} }
// Begin DeltaV
/// <summary>
/// Sets whether a power cell slot is capable of drawing from a battery
/// </summary>
[PublicAPI]
public void SetSlotActive(Entity<PowerCellSlotComponent?> ent, bool active)
{
if (!Resolve(ent, ref ent.Comp))
return;
ent.Comp.Active = active;
Dirty(ent, ent.Comp);
if (!active)
{
var emptyEv = new PowerCellSlotEmptyEvent();
RaiseLocalEvent(ent, ref emptyEv);
}
_battery.RefreshChargeRate(ent.Owner);
}
// End DeltaV
} }
// Begin DeltaV - event-based search for battery // Begin DeltaV - event-based search for battery
@@ -14,13 +14,21 @@ namespace Content.Shared.Random.Helpers
return random.Pick(prototype.Values); return random.Pick(prototype.Values);
} }
/// <summary>
/// DeltaV - Randomly selects an entry from <paramref name="prototype"/> and returns the result.
/// </summary>
public static string PickId(this IRobustRandom random, LocalizedDatasetPrototype prototype)
{
var index = random.Next(prototype.Values.Count);
return prototype.Values[index];
}
/// <summary> /// <summary>
/// Randomly selects an entry from <paramref name="prototype"/>, attempts to localize it, and returns the result. /// Randomly selects an entry from <paramref name="prototype"/>, attempts to localize it, and returns the result.
/// </summary> /// </summary>
public static string Pick(this IRobustRandom random, LocalizedDatasetPrototype prototype) public static string Pick(this IRobustRandom random, LocalizedDatasetPrototype prototype)
{ {
var index = random.Next(prototype.Values.Count); return Loc.GetString(random.PickId(prototype)); // DeltaV - we need LocIds too
return Loc.GetString(prototype.Values[index]);
} }
public static string Pick(this IWeightedRandomPrototype prototype, System.Random random) public static string Pick(this IWeightedRandomPrototype prototype, System.Random random)
+3 -1
View File
@@ -18,11 +18,13 @@ public sealed partial class RatKingComponent : Component
[DataField("actionRaiseArmyEntity")] [DataField("actionRaiseArmyEntity")]
public EntityUid? ActionRaiseArmyEntity; public EntityUid? ActionRaiseArmyEntity;
// Delta-V - switched to a base cost modified by the amount of living servants
/// <summary> /// <summary>
/// The amount of hunger one use of Raise Army consumes /// The amount of hunger one use of Raise Army consumes
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("hungerPerArmyUse", required: true)] [ViewVariables(VVAccess.ReadWrite), DataField("hungerPerArmyUse", required: true)]
public float HungerPerArmyUse = 25f; public float HungerPerArmyUse = 10f;
// end DeltaV
/// <summary> /// <summary>
/// The entity prototype of the mob that Raise Army summons /// The entity prototype of the mob that Raise Army summons
@@ -46,4 +46,49 @@ public sealed partial class SSDIndicatorComponent : Component
/// </summary> /// </summary>
[DataField] [DataField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
// DeltaV - SSD Recency Additions START
/// <summary>
/// The time at which the player became SSD.
/// This will remain unset on SSD entities that never had minds attached, such as newly spawn ghost roles.
/// </summary>
[AutoNetworkedField, AutoPausedField]
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan? SsdSince;
/// <summary>
/// The icon displayed next to the associated entity when it is recently SSD (stage 2).
/// </summary>
[DataField]
public ProtoId<SsdIconPrototype> RecentIcon = "RecentSSDIcon";
/// <summary>
/// The icon displayed next to the associated entity when it is very recently SSD (stage 1).
/// </summary>
[DataField]
public ProtoId<SsdIconPrototype> VeryRecentIcon = "VeryRecentSSDIcon";
// DeltaV END
} }
// DeltaV - SSD Recency START
// If you change this enum, remember to update `Resources/Locale/en-US/_DV/ssdIndicator/examine.ftl`
public enum SsdStage : byte
{
/// <summary>
/// Stage 1: SSD Indicator is red. They might just be recovering from a crash/timeout.
/// </summary>
VeryRecent,
/// <summary>
/// Stage 2: SSD Indicator is yellow. They've been gone for a bit, but they shouldn't be moved to cryo yet.
/// </summary>
Recent,
/// <summary>
/// Stage 3: SSD Indicator is green/default. They've been gone for a long time, they can be moved to cryo.
/// </summary>
Cryoable,
}
// DeltaV END
@@ -1,4 +1,9 @@
using Content.Shared._DV.CCVars; // DeltaV - SSD Recency
using Content.Shared._DV.Mind; // DeltaV - SSD Recency
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Examine; // DeltaV - SSD Recency
using Content.Shared.Mobs.Systems; // DeltaV - SSD Recency
using Content.Shared.Mind.Components; // DeltaV - SSD Recency
using Content.Shared.StatusEffectNew; using Content.Shared.StatusEffectNew;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -17,20 +22,75 @@ public sealed class SSDIndicatorSystem : EntitySystem
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!; [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; // DeltaV - SSD Recency
private bool _icSsdSleep; private bool _icSsdSleep;
private float _icSsdSleepTime; private float _icSsdSleepTime;
private TimeSpan _cryoableSsdSeconds; // DeltaV - SSD Recency
private TimeSpan _recentSsdSeconds; // DeltaV - SSD Recency
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<SSDIndicatorComponent, PlayerAttachedEvent>(OnPlayerAttached); SubscribeLocalEvent<SSDIndicatorComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<SSDIndicatorComponent, PlayerDetachedEvent>(OnPlayerDetached); SubscribeLocalEvent<SSDIndicatorComponent, PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<SSDIndicatorComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<SSDIndicatorComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SSDIndicatorComponent, MindStateUpdatedEvent>(OnMindStateUpdated); // DeltaV - SSD Recency
_cfg.OnValueChanged(CCVars.ICSSDSleep, obj => _icSsdSleep = obj, true); _cfg.OnValueChanged(CCVars.ICSSDSleep, obj => _icSsdSleep = obj, true);
_cfg.OnValueChanged(CCVars.ICSSDSleepTime, obj => _icSsdSleepTime = obj, true); _cfg.OnValueChanged(CCVars.ICSSDSleepTime, obj => _icSsdSleepTime = obj, true);
_cfg.OnValueChanged(DCCVars.SsdIndicatorCryoableAfterSeconds, obj => _cryoableSsdSeconds = TimeSpan.FromSeconds(obj), true); // DeltaV - SSD Recency
_cfg.OnValueChanged(DCCVars.SsdIndicatorRecentAfterSeconds, obj => _recentSsdSeconds = TimeSpan.FromSeconds(obj), true); // DeltaV - SSD Recency
SubscribeLocalEvent<SSDIndicatorComponent, ExaminedEvent>(OnExamine); // DeltaV - SSD Recency
} }
// DeltaV - SSD Recency START
private void OnExamine(Entity<SSDIndicatorComponent> ent, ref ExaminedEvent args)
{
if (ent.Comp.SsdSince is not { } ssdSince)
return;
if (_mobState.IsDead(ent))
return;
using (args.PushGroup(nameof(SSDIndicatorComponent)))
{
var timestamp = (_timing.CurTime - ssdSince).ToString("%hh':'mm':'ss");
args.PushMarkup(Loc.GetString("ssd-examine-duration", ("time", timestamp)));
args.PushMarkup(Loc.GetString($"ssd-examine-{GetStage(ent).ToString().ToLower()}"));
}
}
public SsdStage GetStage(Entity<SSDIndicatorComponent> ent)
{
var curTime = _timing.CurTime;
if (ent.Comp.SsdSince + _recentSsdSeconds >= curTime)
{
return SsdStage.VeryRecent;
}
if (ent.Comp.SsdSince + _cryoableSsdSeconds >= curTime)
{
return SsdStage.Recent;
}
return SsdStage.Cryoable;
}
public void OnMindStateUpdated(Entity<SSDIndicatorComponent> ent, ref MindStateUpdatedEvent args)
{
if (args.State is MindState.SSD or MindState.DeadSSD) {
if (ent.Comp.SsdSince is null)
ent.Comp.SsdSince = _timing.CurTime;
}
else
ent.Comp.SsdSince = null;
Dirty(ent, ent.Comp);
}
// DeltaV END
private void OnPlayerAttached(EntityUid uid, SSDIndicatorComponent component, PlayerAttachedEvent args) private void OnPlayerAttached(EntityUid uid, SSDIndicatorComponent component, PlayerAttachedEvent args)
{ {
component.IsSSD = false; component.IsSSD = false;
@@ -77,4 +77,10 @@ public sealed partial class BorgTransponderComponent : Component
/// </summary> /// </summary>
[DataField] [DataField]
public bool FakeDisabled; public bool FakeDisabled;
/// <summary>
/// DeltaV - whether the transponder is active
/// </summary>
[DataField]
public bool Active = true;
} }
@@ -62,6 +62,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem
if (_whitelistSystem.IsWhitelistFail(component.Blacklist, args.User)) if (_whitelistSystem.IsWhitelistFail(component.Blacklist, args.User))
{ {
TryEmitSound(uid, component, args.User); TryEmitSound(uid, component, args.User);
args.InteractionParticle = true; // Stellar - interaction particles
} }
} }
@@ -106,6 +106,7 @@ public abstract partial class SharedStackSystem : EntitySystem
var localRotation = Transform(args.Used).LocalRotation; var localRotation = Transform(args.Used).LocalRotation;
_storage.PlayPickupAnimation(args.Used, popupPos, userCoords, localRotation, args.User); _storage.PlayPickupAnimation(args.Used, popupPos, userCoords, localRotation, args.User);
args.InteractionParticle = false; // Stellar
} }
/// <summary> /// <summary>
@@ -718,8 +718,13 @@ public abstract class SharedStrippableSystem : EntitySystem
if (args.Handled || !args.Complex || args.Target == args.User) if (args.Handled || !args.Complex || args.Target == args.User)
return; return;
// Begin Stellar Changes - don't play an interact particle for examining the strip UI
if (TryOpenStrippingUi(args.User, (uid, component))) if (TryOpenStrippingUi(args.User, (uid, component)))
{
args.Handled = true; args.Handled = true;
args.InteractionParticle = false;
}
// End Stellar Changes - don't play an interact particle for examining the strip UI
} }
/// <summary> /// <summary>
@@ -52,6 +52,11 @@ public sealed class AfterActivatableUIOpenEvent(EntityUid user) : EntityEventArg
/// The player that opened the UI. /// The player that opened the UI.
/// </summary> /// </summary>
public readonly EntityUid User = user; public readonly EntityUid User = user;
/// <summary>
/// Stellar - if an interaction particle should be played for this event.
/// </summary>
public bool InteractionParticle;
} }
/// <summary> /// <summary>
@@ -21,6 +21,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -72,7 +73,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
args.Verbs.Add(new ActivationVerb args.Verbs.Add(new ActivationVerb
{ {
Act = () => InteractUI(args.User, uid, component), Act = () => InteractUI(args.User, (uid, component)), // Stellar - interaction particles
Text = Loc.GetString(component.VerbText), Text = Loc.GetString(component.VerbText),
// TODO VERB ICON find a better icon // TODO VERB ICON find a better icon
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
@@ -86,7 +87,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
args.Verbs.Add(new Verb args.Verbs.Add(new Verb
{ {
Act = () => InteractUI(args.User, uid, component), Act = () => InteractUI(args.User, (uid, component)), // Stellar - interaction particles
Text = Loc.GetString(component.VerbText), Text = Loc.GetString(component.VerbText),
// TODO VERB ICON find a better icon // TODO VERB ICON find a better icon
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
@@ -133,7 +134,8 @@ public sealed partial class ActivatableUISystem : EntitySystem
if (component.RequiredItems != null) if (component.RequiredItems != null)
return; return;
args.Handled = InteractUI(args.User, uid, component); var interactionParticle = false; // Stellar - interaction particles
args.Handled = InteractUI(args.User, uid, component, ref interactionParticle); // Stellar - interaction particles
} }
private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args) private void OnActivate(EntityUid uid, ActivatableUIComponent component, ActivateInWorldEvent args)
@@ -147,7 +149,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
if (component.RequiredItems != null) if (component.RequiredItems != null)
return; return;
args.Handled = InteractUI(args.User, uid, component); args.Handled = InteractUI(args.User, uid, component, ref args.InteractionParticle); // Stellar - interaction particles
} }
private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, InteractUsingEvent args) private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, InteractUsingEvent args)
@@ -164,7 +166,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
if (_whitelistSystem.IsWhitelistFail(component.RequiredItems, args.Used)) if (_whitelistSystem.IsWhitelistFail(component.RequiredItems, args.Used))
return; return;
args.Handled = InteractUI(args.User, uid, component); args.Handled = InteractUI(args.User, uid, component, ref args.InteractionParticle); // Stellar - interaction particles
} }
private void OnUIClose(EntityUid uid, ActivatableUIComponent component, BoundUIClosedEvent args) private void OnUIClose(EntityUid uid, ActivatableUIComponent component, BoundUIClosedEvent args)
@@ -180,13 +182,23 @@ public sealed partial class ActivatableUISystem : EntitySystem
SetCurrentSingleUser(uid, null, component); SetCurrentSingleUser(uid, null, component);
} }
private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUIComponent aui) // Begin Stellar - interaction particles
private void InteractUI(EntityUid user, Entity<ActivatableUIComponent> ui)
{
var interactionParticle = false;
InteractUI(user, ui, ui, ref interactionParticle);
_interaction.DoContactInteraction(user, ui, null, true, interactionParticles: interactionParticle);
}
// End Stellar - interaction particles
private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUIComponent aui, ref bool interactionParticle) // Stellar - interaction particles
{ {
if (aui.Key == null || !_uiSystem.HasUi(uiEntity, aui.Key)) if (aui.Key == null || !_uiSystem.HasUi(uiEntity, aui.Key))
return false; return false;
if (_uiSystem.IsUiOpen(uiEntity, aui.Key, user)) if (_uiSystem.IsUiOpen(uiEntity, aui.Key, user))
{ {
interactionParticle = false; // Stellar - interaction particles
_uiSystem.CloseUi(uiEntity, aui.Key, user); _uiSystem.CloseUi(uiEntity, aui.Key, user);
return true; return true;
} }
@@ -243,6 +255,8 @@ public sealed partial class ActivatableUISystem : EntitySystem
var aae = new AfterActivatableUIOpenEvent(user); var aae = new AfterActivatableUIOpenEvent(user);
RaiseLocalEvent(uiEntity, aae); RaiseLocalEvent(uiEntity, aae);
interactionParticle = aae.InteractionParticle; // Stellar
return true; return true;
} }
@@ -31,6 +31,7 @@ using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Wieldable.Components; // Starlight | ES Screenshake
using Content.Shared.Zombies; // DeltaV - Buff Zombies using Content.Shared.Zombies; // DeltaV - Buff Zombies
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
@@ -597,18 +598,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
if (damageResult.GetTotal() > FixedPoint2.Zero) if (damageResult.GetTotal() > FixedPoint2.Zero)
{ {
DoDamageEffect(targets, user, targetXform); DoDamageEffect(targets, user, targetXform);
DoScreenshake(meleeUid, damageResult, user, targets); // Starlight | ES Screenshake
// ES START
// dog shit copy plaste but thats melee for you
var userShakeRotation = new ESScreenshakeParameters()
{ Trauma = 0.08f, DecayRate = 1.0f, Frequency = 0.009f };
var otherShakeTranslation = new ESScreenshakeParameters() { Trauma = 0.45f, DecayRate = 1.1f, Frequency = 0.04f };
_shake.Screenshake(user, null, userShakeRotation);
foreach (var shakeTarget in targets)
{
_shake.Screenshake(shakeTarget, otherShakeTranslation, null);
}
// ES END
} }
} }
@@ -774,21 +764,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
_meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component); _meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component);
} }
// ES START
// dog shit copy plaste but thats melee for you
var userShakeRotation = new ESScreenshakeParameters()
{ Trauma = 0.08f, DecayRate = 1.0f, Frequency = 0.009f };
var otherShakeTranslation = new ESScreenshakeParameters() { Trauma = 0.45f, DecayRate = 1.1f, Frequency = 0.04f };
_shake.Screenshake(user, null, userShakeRotation);
foreach (var shakeTarget in targets)
{
_shake.Screenshake(shakeTarget, otherShakeTranslation, null);
}
// ES END
if (appliedDamage.GetTotal() > FixedPoint2.Zero) if (appliedDamage.GetTotal() > FixedPoint2.Zero)
{ {
DoDamageEffect(targets, user, Transform(targets[0])); DoDamageEffect(targets, user, Transform(targets[0]));
DoScreenshake(meleeUid, damage, user, targets); // Starlight | ES Screenshake
} }
return true; return true;
@@ -1104,4 +1083,55 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
} }
} }
} }
//Starlight begin | ES Screenshake
private void DoScreenshake(EntityUid weapon, DamageSpecifier damage, EntityUid attacker, List<EntityUid> targets)
{
if(damage.GetTotal()>4) // only show to others if it hurts real bad // DeltaV - reduce from 8 to 4
{
var otherTranslation = new ESScreenshakeParameters
{
Trauma = 0.45f,
DecayRate = 1.1f,
Frequency = 0.04f,
};
foreach(var target in targets)
_shake.Screenshake(target, otherTranslation, null);
}
// only show to attacker if they put real oompf into it, or the weapon is just THAT strong
// var bluntRequirement = damage.DamageDict.TryGetValue(BluntDamageName, out var blunt) && blunt >= 20; // DeltaV - unused
var isWielding = TryComp<WieldableComponent>(weapon, out var wieldable) && wieldable.Wielded;
// DeltaV - unused
// if (!bluntRequirement && !wieldRequirement)
// return;
// DeltaV - heavy/light screenshake variants START
ESScreenshakeParameters userRotation;
if (damage.GetTotal() >= 15 || isWielding)
{
// heavy damage or two-handed
userRotation = new ESScreenshakeParameters
{
Trauma = 0.08f,
DecayRate = 1,
Frequency = 0.009f,
};
}
else
{
// light damage
userRotation = new ESScreenshakeParameters
{
Trauma = 0.06f,
DecayRate = 1,
Frequency = 0.0045f,
};
}
// DeltaV END
_shake.Screenshake(attacker, null, userRotation);
}
//Starlight end
} }
+15
View File
@@ -316,4 +316,19 @@ public sealed partial class DCCVars
/// </summary> /// </summary>
public static readonly CVarDef<bool> EsScreenshakeDisabled = public static readonly CVarDef<bool> EsScreenshakeDisabled =
CVarDef.Create("deltav.es_screenshake.disabled", false, CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("deltav.es_screenshake.disabled", false, CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// The total time a player has to be SSD to be considered cryoable (stage 3).
/// Default is 20 minutes. Value should be bigger than <see cref="SsdIndicatorRecentAfterSeconds"/>.
/// </summary>
public static readonly CVarDef<float> SsdIndicatorCryoableAfterSeconds =
CVarDef.Create("deltav.ssd.cryoable_after_seconds", 1200f, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// The total time a player has to be SSD to be considered recently SSD (stage 2).
/// If the player has been SSD for less than this time, they are considered "very recently" SSD (stage 1).
/// Default is 5 minutes. Value should be smaller than <see cref="SsdIndicatorCryoableAfterSeconds"/>.
/// </summary>
public static readonly CVarDef<float> SsdIndicatorRecentAfterSeconds =
CVarDef.Create("deltav.ssd.recent_after_seconds", 300f, CVar.SERVER | CVar.REPLICATED);
} }
@@ -0,0 +1,6 @@
using Content.Shared.Radio;
namespace Content.Shared._DV.Chat;
[ByRefEvent]
public record struct EntityAudiblyEmotedEvent(EntityUid Source, string Message, RadioChannelPrototype? Channel, EmoteType? Type);
@@ -0,0 +1,15 @@
namespace Content.Shared._DV.Chat;
// Note for future: If you want to make this more robust to handle more types of emotes while still being able to check off audible as an option for it then it would
// probably be better to make it a struct and have audible be a flag for it and then the type of emote. This would avoid having to make two different types for audible and visual.
/// <summary>
/// Different ways of emoting. For that little extra in RP!
/// </summary>
public enum EmoteType : byte
{
Normal, // Character emotes
Audible, // Character screams
Possessive, // Character's emote
AudiblePossessive // Character's scream
}
@@ -1,3 +1,5 @@
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -36,6 +38,8 @@ public sealed partial class CosmicColossusComponent : Component
[DataField] public EntProtoId Attack1Vfx = "CosmicColossusAttack1Vfx"; [DataField] public EntProtoId Attack1Vfx = "CosmicColossusAttack1Vfx";
[DataField] public EntProtoId BuffVfx = "ColossusBuffVfx";
[DataField] public EntProtoId TileDetonations = "MobTileDamageZone"; [DataField] public EntProtoId TileDetonations = "MobTileDamageZone";
[DataField] public EntProtoId EffigyPrototype = "CosmicEffigy"; [DataField] public EntProtoId EffigyPrototype = "CosmicEffigy";
@@ -52,13 +56,23 @@ public sealed partial class CosmicColossusComponent : Component
[DataField] public TimeSpan HibernationWait = TimeSpan.FromSeconds(30); [DataField] public TimeSpan HibernationWait = TimeSpan.FromSeconds(30);
[DataField] public TimeSpan DeathWait = TimeSpan.FromMinutes(15); [DataField] public TimeSpan DeathWaitSpawn = TimeSpan.FromMinutes(15);
[DataField] public TimeSpan DeathWaitEffigy = TimeSpan.FromMinutes(10);
[DataField] public bool Attacking; [DataField] public bool Attacking;
[DataField] public bool Hibernating; [DataField] public bool Hibernating;
[DataField] public bool Timed; [DataField] public bool Timed;
[DataField] public short CompletedEffigies;
[DataField] public short MaxEffigies = 3;
[DataField] public DamageSpecifier BonusDamage = new();
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]
@@ -0,0 +1,6 @@
namespace Content.Shared._DV.Mind;
using Content.Shared.Mind.Components;
[ByRefEvent]
public record struct MindStateUpdatedEvent(MindState State);
@@ -1,3 +1,4 @@
using Content.Shared.ActionBlocker;
using Content.Shared._DV.Movement.Components; using Content.Shared._DV.Movement.Components;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
@@ -7,12 +8,19 @@ namespace Content.Shared.Movement.Systems;
public abstract partial class SharedJetpackSystem public abstract partial class SharedJetpackSystem
{ {
[Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
private void OnJetpackToggle(Entity<JetpackComponent> jetpack, ref ToggleJetpackEvent args) private void OnJetpackToggle(Entity<JetpackComponent> jetpack, ref ToggleJetpackEvent args)
{ {
if (args.Handled) if (args.Handled)
return; return;
if (!_actionBlocker.CanComplexInteract(args.Performer))
{
_popup.PopupClient(Loc.GetString("jetpack-too-complex"), jetpack, args.Performer);
return;
}
jetpack.Comp.AutomaticMode = !jetpack.Comp.AutomaticMode; jetpack.Comp.AutomaticMode = !jetpack.Comp.AutomaticMode;
jetpack.Comp.AutomaticUser = args.Performer; jetpack.Comp.AutomaticUser = args.Performer;
Dirty(jetpack); Dirty(jetpack);
+3
View File
@@ -42,6 +42,9 @@ public sealed class PagerSystem : EntitySystem
private void OnAfterInteract(Entity<PagerComponent> ent, ref AfterInteractEvent args) private void OnAfterInteract(Entity<PagerComponent> ent, ref AfterInteractEvent args)
{ {
if (!args.CanReach)
return;
if (args.Handled || !TryComp<DeviceNetworkComponent>(args.Target, out var targetNetwork) || !HasComp<PageSenderComponent>(args.Target)) if (args.Handled || !TryComp<DeviceNetworkComponent>(args.Target, out var targetNetwork) || !HasComp<PageSenderComponent>(args.Target))
return; return;
@@ -1,13 +0,0 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Psionics.Components.PsionicPowers;
[RegisterComponent, NetworkedComponent]
public sealed partial class FracturedFormBodyComponent : Component
{
/// <summary>
/// The entity uid of the form that is currently being controlled.
/// </summary>
[DataField]
public EntityUid ControllingForm { get; set; }
}
@@ -1,124 +0,0 @@
using Content.Shared.Cloning;
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Psionics.Components.PsionicPowers;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
public sealed partial class FracturedFormPowerComponent : BasePsionicPowerComponent
{
public override EntProtoId ActionProtoId { get; set; } = "ActionFracturedForm";
public override string PowerName { get; set; } = "psionic-power-name-fractured-form";
public override int MinGlimmerChanged { get; set; } = 5;
public override int MaxGlimmerChanged { get; set; } = 15;
/// <summary>
/// The chance for the clone to have the starting gear of the psionic.
/// </summary>
[DataField]
public float HasGearChance = 0.4f;
/// <summary>
/// The chance for the clone to have a different species than the psionic.
/// </summary>
[DataField]
public float DifferentSpeciesChance = 0.6f;
/// <summary>
/// These are settings for creating the second body.
/// </summary>
[DataField]
public ProtoId<CloningSettingsPrototype> CopyNaked = "CloningPod";
/// <summary>
/// These are settings for creating the second body.
/// </summary>
[DataField]
public ProtoId<CloningSettingsPrototype> CopyClothed = "Antag";
/// <summary>
/// These are settings for creating the second body.
/// </summary>
[DataField]
public ProtoId<JobPrototype> VisitorJob = "Passenger";
/// <summary>
/// These are settings for creating the second body.
/// </summary>
[DataField]
public ProtoId<JobPrototype>? NakedJob; // Scary null, but we explicitly want no job for naked spawns.
/// <summary>
/// The minimum time between forced swaps.
/// </summary>
[DataField]
public TimeSpan NextSwapMinTime = TimeSpan.FromMinutes(5);
/// <summary>
/// The maximum time between swaps.
/// </summary>
[DataField]
public TimeSpan NextSwapMaxTime = TimeSpan.FromMinutes(20);
/// <summary>
/// The current timer for when the next swap is forced.
/// </summary>
[DataField, AutoPausedField]
public TimeSpan NextSwap = TimeSpan.FromMinutes(5);
/// <summary>
/// The minimum time between voluntary swaps by sleeping.
/// </summary>
[DataField]
public TimeSpan VoluntarySwapCooldown = TimeSpan.FromSeconds(10);
/// <summary>
/// The minimum time between voluntary swaps by sleeping.
/// </summary>
[DataField, AutoPausedField]
public TimeSpan NextVoluntarySwap = TimeSpan.FromSeconds(10);
/// <summary>
/// The length of the DoAfter for swapping bodies.
/// </summary>
[DataField]
public float ManualSwapTime = 5f;
/// <summary>
/// The time the psionic needs to be asleep to swap bodies.
/// </summary>
[DataField]
public TimeSpan SwapTimeAsleep = TimeSpan.FromSeconds(3);
/// <summary>
/// The time length where the warning is sent before the forced swap.
/// </summary>
[DataField]
public TimeSpan WarningTimeBeforeSleep = TimeSpan.FromSeconds(5);
/// <summary>
/// A boolean check for when the person has been warned.
/// </summary>
[DataField]
public bool SleepWarned;
/// <summary>
/// The bodies that the psionic user can swap into.
/// </summary>
[DataField, AutoNetworkedField]
public List<EntityUid> Bodies { get; set; } = [];
/// <summary>
/// The sound that plays when the swap happens.
/// </summary>
[DataField]
public SoundSpecifier SwapSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg")
{
Params = AudioParams.Default.WithVariation(0.05f),
};
}
@@ -1,5 +0,0 @@
using Content.Shared.Actions;
namespace Content.Shared._DV.Psionics.Events.PowerActionEvents;
public sealed partial class FracturedFormPowerActionEvent : InstantActionEvent;
@@ -1,7 +0,0 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._DV.Psionics.Events.PowerDoAfterEvents;
[Serializable, NetSerializable]
public sealed partial class FracturedFormDoAfterEvent : SimpleDoAfterEvent;
@@ -1,150 +0,0 @@
using Content.Shared._DV.Psionics.Components.PsionicPowers;
using Content.Shared._DV.Psionics.Events;
using Content.Shared._DV.Psionics.Events.PowerActionEvents;
using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents;
using Content.Shared.Bed.Sleep;
using Content.Shared.Chat;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.SSDIndicator;
using Robust.Shared.Player;
namespace Content.Shared._DV.Psionics.Systems.PsionicPowers;
public abstract class SharedFracturedFormPowerSystem : BasePsionicPowerSystem<FracturedFormPowerComponent, FracturedFormPowerActionEvent>
{
[Dependency] protected readonly SharedChatSystem Chat = default!;
[Dependency] protected readonly MobStateSystem MobState = default!;
[Dependency] protected readonly SleepingSystem Sleeping = default!;
protected EntityQuery<FracturedFormPowerComponent> FracturedQuery;
private EntityQuery<ForcedSleepingStatusEffectComponent> _forcedSleepQuery;
protected EntityQuery<MindContainerComponent> MindContainerQuery;
private EntityQuery<MobStateComponent> _mobStateQuery;
protected EntityQuery<SSDIndicatorComponent> SsdQuery;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FracturedFormPowerComponent, FracturedFormDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<FracturedFormBodyComponent, PlayerDetachedEvent>(OnPlayerDetached, after: [typeof(SSDIndicatorSystem)]);
SubscribeLocalEvent<FracturedFormBodyComponent, ExaminedEvent>(OnExamine);
FracturedQuery = GetEntityQuery<FracturedFormPowerComponent>();
_forcedSleepQuery = GetEntityQuery<ForcedSleepingStatusEffectComponent>();
MindContainerQuery = GetEntityQuery<MindContainerComponent>();
_mobStateQuery = GetEntityQuery<MobStateComponent>();
SsdQuery = GetEntityQuery<SSDIndicatorComponent>();
}
protected override void OnPowerUsed(Entity<FracturedFormPowerComponent> psionic, ref FracturedFormPowerActionEvent args)
{
if (!CanSwap(psionic))
{
Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-nobodies"), psionic, args.Performer, PopupType.Large);
return;
}
Chat.TryEmoteWithChat(psionic.Owner, "Yawn");
Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-sleepy"), psionic, args.Performer, PopupType.LargeCaution);
var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, psionic.Comp.ManualSwapTime, new FracturedFormDoAfterEvent(), psionic);
if (!DoAfter.TryStartDoAfter(doAfterArgs, out var doAfterId))
return;
psionic.Comp.SaveDoAfterId(doAfterId.Value);
Dirty(psionic);
AfterPowerUsed(psionic, args.Performer);
}
private void OnDoAfter(Entity<FracturedFormPowerComponent> psionic, ref FracturedFormDoAfterEvent args)
{
if (args.Cancelled || args.Handled)
return;
args.Handled = true;
psionic.Comp.RemoveSavedDoAfterId();
Dirty(psionic);
Sleeping.TrySleeping(psionic.Owner);
}
private void OnPlayerDetached(Entity<FracturedFormBodyComponent> body, ref PlayerDetachedEvent args)
{
if (!SsdQuery.TryComp(body, out var comp)
|| FracturedQuery.HasComp(body))
return;
comp.IsSSD = false;
}
private void OnExamine(Entity<FracturedFormBodyComponent> psionic, ref ExaminedEvent args)
{
if (HasComp<FracturedFormPowerComponent>(psionic))
return;
if (TryComp<FracturedFormPowerComponent>(args.Examiner, out var fracturedHost) && fracturedHost.Bodies.Contains(psionic.Owner))
args.PushMarkup($"[color=yellow]{Loc.GetString("psionic-power-fractured-form-examine-self", ("ent", psionic))}[/color]");
else
args.PushMarkup($"[color=yellow]{Loc.GetString("psionic-power-fractured-form-ssd", ("ent", psionic))}[/color]");
}
/// <summary>
/// We're overriding the dispelled method to introduce some special behavior.
/// </summary>
protected override void OnDispelled(Entity<FracturedFormPowerComponent> psionic, ref DispelledEvent args)
{
if (args.Handled)
return;
args.Handled = true;
// This resets the swap timer.
var randomTime = Random.Next(psionic.Comp.NextSwapMinTime, psionic.Comp.NextSwapMaxTime);
psionic.Comp.NextSwap = Timing.CurTime + randomTime;
if (psionic.Comp.GetDoAfterId() is not { } doAfterId)
{
Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-dispelled"), psionic, PopupType.MediumCaution);
return;
}
DoAfter.Cancel(doAfterId);
Popup.PopupClient(Loc.GetString("psionic-dispelled"), psionic, PopupType.MediumCaution);
psionic.Comp.RemoveSavedDoAfterId();
}
protected bool IsValidBody(Entity<FracturedFormPowerComponent> psionic, EntityUid otherBody)
{
if (otherBody == psionic.Owner)
return false;
if (!psionic.Comp.Bodies.Contains(otherBody))
return false;
if (!MindContainerQuery.TryComp(otherBody, out var cmind))
return false;
if (cmind.HasMind)
return false;
if (_forcedSleepQuery.HasComp(otherBody))
return false;
if (!_mobStateQuery.TryComp(otherBody, out var mobState))
return false;
if (MobState.IsIncapacitated(otherBody, mobState))
return false;
return true;
}
public bool CanSwap(Entity<FracturedFormPowerComponent> psionic)
{
foreach (var body in psionic.Comp.Bodies)
{
if (IsValidBody(psionic, body))
return true;
}
return false;
}
}
@@ -0,0 +1,12 @@
using Robust.Shared.Serialization;
namespace Content.Shared._DV.Silicons;
[Serializable, NetSerializable]
public enum BorgWireActionKey : byte
{
SlavedKey,
TransponderKey,
ChipKey,
CellKey,
}
@@ -9,7 +9,7 @@ namespace Content.Shared._DV.Silicons.Borgs;
/// Adds an id chip slot for a borg which will control its access. /// Adds an id chip slot for a borg which will control its access.
/// Enables an id chip's access when inserted to an id chip slot. /// Enables an id chip's access when inserted to an id chip slot.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(IdChipSlotSystem))] [RegisterComponent, NetworkedComponent, Access(typeof(IdChipSlotSystem)), AutoGenerateComponentState]
public sealed partial class IdChipSlotComponent : Component public sealed partial class IdChipSlotComponent : Component
{ {
/// <summary> /// <summary>
@@ -35,6 +35,12 @@ public sealed partial class IdChipSlotComponent : Component
/// </summary> /// </summary>
[DataField(required: true)] [DataField(required: true)]
public EntityWhitelist Whitelist = new(); public EntityWhitelist Whitelist = new();
/// <summary>
/// Whether the ID chip slot is active
/// </summary>
[DataField, AutoNetworkedField]
public bool Active = true;
} }
/// <summary> /// <summary>
@@ -73,7 +73,7 @@ public sealed class IdChipSlotSystem : EntitySystem
private void OnGetAdditionalAccess(Entity<IdChipSlotComponent> ent, ref GetAdditionalAccessEvent args) private void OnGetAdditionalAccess(Entity<IdChipSlotComponent> ent, ref GetAdditionalAccessEvent args)
{ {
if (ent.Comp.Chip is {} chip) if (ent.Comp is { Active: true, Chip: {} chip })
args.Entities.Add(chip); args.Entities.Add(chip);
} }
@@ -83,7 +83,7 @@ public sealed class IdChipSlotSystem : EntitySystem
return; return;
// enable its access so the borg can use it // enable its access so the borg can use it
_access.SetAccessEnabled(args.Entity, true); _access.SetAccessEnabled(args.Entity, ent.Comp.Active);
} }
private void OnChipRemoved(Entity<IdChipSlotComponent> ent, ref EntRemovedFromContainerMessage args) private void OnChipRemoved(Entity<IdChipSlotComponent> ent, ref EntRemovedFromContainerMessage args)
@@ -105,4 +105,14 @@ public sealed class IdChipSlotSystem : EntitySystem
_container.Remove(chip, ent.Comp.Container); _container.Remove(chip, ent.Comp.Container);
_hands.TryPickupAnyHand(args.Actor, chip); _hands.TryPickupAnyHand(args.Actor, chip);
} }
public void SetActive(Entity<IdChipSlotComponent?> ent, bool active)
{
if (!Resolve(ent, ref ent.Comp))
return;
ent.Comp.Active = active;
_access.SetAccessEnabled(ent.Owner, active && ent.Comp.Chip is not null);
Dirty(ent);
}
} }
@@ -19,8 +19,14 @@ public sealed partial class SlavedBorgComponent : Component
public ProtoId<SiliconLawPrototype> Law; public ProtoId<SiliconLawPrototype> Law;
/// <summary> /// <summary>
/// Prevents adding the same law twice. /// Prevents adding the same law 0 mutliple times during law updates.
/// </summary> /// </summary>
[DataField] [DataField]
public bool Added; public bool HasBeenAdded;
/// <summary>
/// Whether the law 0 should be added to the lawset.
/// </summary>
[DataField]
public bool ShouldBeAdded = true;
} }
@@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Traits.Assorted;
/// <summary>
/// This entity will not be candidate for a paradox clone.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class NoParadoxCloneComponent : Component;
@@ -0,0 +1,10 @@
using Content.Shared.Trigger.Components.Effects;
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Trigger.Components.Effects;
/// <summary>
/// is used for implants and such so the right paramters can be passed
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CreateHitmanCardOnTriggerComponent : BaseXOnTriggerComponent;
@@ -0,0 +1,30 @@
using Content.Shared._DV.Trigger.Components.Effects;
using Content.Shared.Trigger;
using Content.Shared.Hands.EntitySystems;
namespace Content.Shared._DV.Trigger.Systems;
public sealed class CreateHitmanCardOnTriggerSystem : EntitySystem
{
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CreateHitmanCardOnTriggerComponent, TriggerEvent>(OnTrigger);
}
//just spawns a card on the given target. uses predicated or spawn next to based on if. as trigger effect doesnt seem to have predicted on it ill just check if this is sever first and if not assume its predicted.
private void OnTrigger(Entity<CreateHitmanCardOnTriggerComponent> ent, ref TriggerEvent args)
{
if (args.Handled || args.User is not { } user)
return;
var mapCoords = _transform.GetMapCoordinates(ent);
var card = EntityManager.PredictedSpawn("HitmanBusinessCard", mapCoords);
_hands.TryPickupAnyHand(user, card);
args.Handled = true;
}
}
@@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Whitelist.Holoprojectors;
/// <summary>
/// Marker component for holoradiationblockers, used for reclaiming charges of the projector.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class HoloRadiationblockerComponent : Component;

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