Upstream stable sync (#1708)

* moth displacement map fixes

* Make VendingMachineInventoryEntry a data definition for post-init savegrid (#38406)

fix: make VendingMachineInventoryEntry a data definition

* Automatic changelog update

* Exo - Major Sec changes, and more! (#39295)

* Automatic changelog update

* Fix variantize command not respecting tile rotation (#39314)

Oopsiedoodle

* Renames slugcat jelly-donuts to scurret jelly-donuts (#39308)

* renames slugcat jelly-donuts to scurret jelly-donuts

* renames slugcat jelly-donuts to scurret jelly-donuts

* renames slugcat jelly-donuts to scurret jelly-donuts

* missing end of file new line

---------

Co-authored-by: Arcane-Waffle <FR_Waffle@proton.me>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Predict EmitterSystem ExamineEvent and GetVerbsEvent (#39318)

* ididathing.exe

* commit

* cleanup

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Predict ExamineEvent for CryoPodSystem. (#39322)

commit

* Fix tabletop grids rarely spawning on top of another (#39327)

* fix: fix off-by-one for tabletop map positions

Ulam spirals start at 1, not 0.

* fix: make the ulam spiral a ulam spiral

* Automatic changelog update

* Add test of `StaminaComponent` crit vs animation thresholds (#39249)

Add test of StaminaComponent crit vs animation thresholds

* HumanoidCharacterProfileFix (#39333)

* Automatic changelog update

* Fix StoreTests EventBus usage (#38489)

Fix split off from #37349 to avoid needing to sync the content/engine PRs.

* Don't purge note buffer when starting/switching MIDI songs (#39335)

Stop stuck notes on remote when changing MIDI song

* Berry Delight (#38881)

* Berry delight

* Uncook the YAML

* Move stuff in meal_recipes

* BERRY DELIGHT IS INEVITABLE

* Automatic changelog update

* Fix horizontal space men in replays (#39338)

* Fix horizontal space men in replays

Visualizer should not bail if data unavailable.

* Update Content.Client/Rotation/RotationVisualizerSystem.cs

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Fix ActionsSystem.IsCooldownActive always returning false if curTime is null (#39329)

* Make dirt non-compressible (#39220)

This sets the new rsic: false flag in dirt.rsi. One of the interior PNGs is directly accessed by a tile definition, which would otherwise cause a game startup failure with the new packaging improvements: c4dff678a9

* Predict anomaly synchronizer (#39321)

* predict anomaly synchronizer

* pvs

* lambda

* Update Resources/Locale/en-US/anomaly/anomaly.ftl

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* parrotMemory is onGetVerbs now in shared (#39341)

* parrotMemory is onGetVerbs now in shared

* code review

* code review popup on client rename parrotMemoryComponent

* code rev create client system

* forgot usings

* is server now

* Fix rotate verbs not being predicted (#38165)

* Fix rotate verbs not being predicted

* fixes

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Automatic changelog update

* Update Credits (#39343)

Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>

* Advanced Clowning Module (#35797)

* pAIs can now accept keys, but can't talk in said channels

* added dummy module

* added adv clown module alongside projector

* holopeel & projector sprite added, it's destroyable too

* added experimental pie cannon

* exp pie thrower throws pies, added tool icon, added bananium horn

* removed the weird pAI changes, my bad!

* okay NOW the pAI stuff is all gone

* added icon, recipe, renamed tech for unlocking

* removed bananium horn

* Added in-hand sprites, credited to TiniestShark. Changed holopeel projector description to let the user know it recharges over time.

* change bagel genpop biocube fabricator into biogenerator (#39313)

* Hardsuit helmet text fix + CBurn Vox Fix (#39345)

* text fix and vox helm fix

* oop one line

* Localize Refund Button (#39346)

* Localize Refund Button

* Requested changes

* Make wallmount screen, telescreen, and signal timer destructible (#39340)

* make wallmount screen destructible

* louder

* fix indent

* fix indent

* Trigger Refactor (#39034)

* Xenoborgs part 4 (#36935)

Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
Co-authored-by: Quantum-cross <7065792+Quantum-cross@users.noreply.github.com>
Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
Co-authored-by: Southbridge <7013162+southbridge-fur@users.noreply.github.com>
Co-authored-by: WarPigeon <DaedalusTheGamer@gmail.com>
Co-authored-by: Kowlin <git@wyvern.blue>
Co-authored-by: ScarKy0 <scarky0@onet.eu>

* fix: auto-update mailing unit + gas canister UIs on state (#39289)

* fix: auto-update mailing unit + gas canister UIs

* fix: make FollowerComponent auto handle state

* refactor: kill AfterAutoHandleState for Follower

* flakeops

* Updated syndicate throwing knives description (#39374)

Co-authored-by: seanpimble <149889301+seanpimble@users.noreply.github.com>

* Automatic changelog update

* Revert "Fix bug with pipe color" (#39135)

* Move `HeadstandComponent` to Shared (#39377)

Move HeadstandComponent to Shared

* Fix head mappers codeowners (#39378)

webedit ops codeowners

* Fix vox inhand displacements (#38507)

fix

* Add Offset Canes + Trinket Canes Group (#39272)

* Added offset cane

* Added offset cane colors

* Added canes to the trinkets menu

* added color to names instead of suffix

* removes some stripes from the mime cane icon

* update file organization

* standard -> standard.rsi, stop making commits at nearly 3 in the morning.

* updated comment to be more explicit in what doesnt work

* Cane refactor :godo:

* git makes me very upset sometimes (fixed cane yaml)

* wooden->wood

* apparently this didnt push

* Standardize comments

* Removed comment

* Removed comment

* Adds red accents to mime cane

* Indent fixes

* Automatic changelog update

* add: air alarm scrubber select all gases button (#39296)

* add select all gases button

* now make it work

* localize

* refactor

* remove redundant Orientation

Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>

* remove useless HorizontalExpand

Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>

* add nice newline

Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>

* deduplicate Enum.GetValues<Gas> usage

---------

Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>

* Automatic changelog update

* Fix ATS Anchor (#39389)

* fix: hide timer trigger's cycle time verb if DelayOptions is empty (#39388)

fix: hide cycle time verb if DelayOptions is empty

* Automatic changelog update

* Removes ItemToggle from Cryo Pods to prevent a latent event ordering bug (#39197)

Removes ItemToggle component from the cryo pod as it was useless and risked a latent verb order bug

* remove space from Sleeping Carp.png (#39369)

* Rename Sleeping Carp.png to SleepingCarp.png

* Update meta.json

* localization support to air alarms, wire panels and more (#39307)

* Add localization to the air alarms, wire panels, network configurator list menu and loadout window

* delete unused

* redo gas localization, delete unused

* removed the extra key

* Moved and renamed air-alarm-ui-thresholds-gas-name

* Moved localization to the XAML

* Use existing strings for gas names

* it just works

* Rename _atmosphereSystem in ScrubberControl.xaml.cs

_atmosphereSystem -> atmosphereSystem

* Rename _atmosphereSystem in SensorInfo.xaml.cs

_atmosphereSystem -> atmosphereSystem

* Fixed Issues with Screens and Station Maps (#39393)

* Predict warp point location examines. (#39402)

commit

* Reduced SalvageStructureComponent to atoms. (#39400)

* commit

* whoopsie daisy

* Update SpawnSalvageMissionJob.cs

* Update SpawnSalvageMissionJob.cs

* cleanup

* Predict Nav Beacon Examine (#39408)

* commit

* Update Content.Shared/Pinpointer/SharedNavMapSystem.cs

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Add guard to unbuckling to help it to not act upon terminating entities (#39410)

* Add guard to unbuckling to help it to not act upon terminating entities

* Refactor guard for unbuckling

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Revert "Refactor guard for unbuckling"

This reverts commit bf975fbd6f5cfac45324a3d5d74e592ad17ad291.

---------

Co-authored-by: Luxeator <Luxeator>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Automatic changelog update

* fix: properly respect AllowRepeatedMorphs (#39411)

* fix: properly respect AllowRepeatedMorphs

* feat: add IgnoreAllowRepeatedMorphs

* Predict cryopods (#39385)

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>

* feat: properly perform predicted porta pottys (fix toilet prediction) (#39394)

* Remove redundant return statement in InventoryUIController (#39381)

* Update attributions for lightning audiofiles (#39395)

* Predict PickRandom verb (#39326)

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>

* Adds infinite debug power APC, substation, SMES (#39317)

* make debug power infinite

* demarcate infinite and empty debug power

* fix duplicate ID

* change infinite suffix to autorecharge

* Status effects disable light occluding (1-line PR) (#39418)

Update StatusEffectsSystem.cs

* Move scale command to content and turn it into a toolshed command (#39349)

* scale command

* fix namespaces

* MessyDrinker for dogs (#38852)

* Automatic changelog update

* Update RT to 266.0.0

* Predict EMP Examine (#39419)

* another one bites the dust

* Update Content.Shared/Emp/SharedEmpSystem.cs

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Predict base and damage examines of cartridge ammo. (#39401)

* commit

* requested changes +

* fix DoAfter DistanceThreshold (#39276)

* Automatic changelog update

* convert dwarfs to use ScaleVisualsComponent (#39422)

* Move solution examine subscription from DrinkComponent to ExaminableSolutionComponent (#39362)

* initial it works

* clean it up

* yml

* datafield the LocIds

* move from the other branch

* no max vol on puddles and anoms

* closed

* Changes inspired by #39008

* small bug and more color range

* puddle changes and more examinable solutions

* lint

* small change

* requested changes

* un-delete

* tiny comment

* 1 less loc id in this world

* request and last second change

---------

Co-authored-by: iaada <iaada@users.noreply.github.com>

* Automatic changelog update

* Debody Food and Drink Systems, Combine Food and Drink into One System. (#39031)

* Shelve

* 22 file diff

* What if it was just better

* Hold that thought

* Near final Commit, then YAML hell

* 95% done with cs

* Working Commit

* Final Commit (Before reviews tear it apart and kill me)

* Add a really stupid comment.

* KILL

* EXPLODE TEST FAILS WITH MY MIND

* I hate it here

* TACTICAL NUCLEAR STRIKE

* Wait what the fuck was I doing?

* Comments

* Me when I'm stupid

* Food doesn't need solutions

* API improvements with some API weirdness

* Move non-API out of API

* Better comment

* Fixes and spelling mistakes

* Final fixes

* Final fixes for real...

* Kill food and drink localization files because I hate them.

* Water droplet fix

* Utensil fixes

* Fix verb priority (It should've been 2)

* A few minor localization fixes

* merge conflict and stuff

* MERGE CONFLICT NUCLEAR WAR!!!

* Cleanup

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Fun with cardboard! (#37363)

* learning???

* made card walls work, then made game unlaunchable (:

* Still broken, added notes that I thought of while in bed

* wall, door, table and chair are now bare min functional, yay

* learnt why not to web edit...

* added floors, walls and floors fully complete

* added swords, shields, armour, helmets and arrows

* added funny sound and cleanup small issues

* cleanup

* cleanup

* credited myself

* card to cardboard

* fixed licence issue and meta thingy

* adjusted arrow stam-damage

* made card carpets more regular

* simplified sprite, reduced stam damage

* formatting fixes

---------

Co-authored-by: beck-thompson <beck314159@hotmail.com>

* Automatic changelog update

* add scale:multiplyvector toolshed command (#39424)

* Automatic changelog update

* fix repeated scale visuals removal/ensuring (#39432)

* Miscellaneous Body Decoupling (#38958)

* Turn some implants into triggers (#39364)

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>

* Changeling devour and transform  (#34002)

* Initial:

Create Devour componentry, preliminary identity storage and the systems
for Devouring

* I have genuinely no idea what i'm doing

- added the radial menu, it has nothing in it.

- trying to get it to populate. the event under the event is broken,
i don't know why, but apparently it's not typed right

- Added a placeholder transform

- oh also fixed up some devour stuff and moved some things around.

* Holey moley, Transform, better devour, oh my!

- Move DnaComponent into Shared because we need it for the DNA cloning

- Make Transform MOSTLY work on the LAST identity devoured.

- Fix some issues on devour that involved prediction, canceling and
Damage exeucting (Thanks Plykiya for pointing out AttemptFrequency!)

* Proper tail stealing and Damage modifier attempt

Add check to add a wagging component on the Changeling if the victim's
species Prototype had one.

attempt to add the Damage mitigation check

* MAJOR CLEANUP AND FIXES AUGH 3 DAYS!!!

- Nullspaced a clone of a victim

- fix audio using server virtualized Pvs (i hate this)

- fix the mispredicted doafters

- Clean up a wholelotta code

- utilize clone systems to clone appearances

- Move CloneAppearance from server to shared So we can actually access
it

* Examine stuff, more cleanup, Jumpsuit ripping

- make rotting prevent the action

- Add ripping of clothing (guh why is it also server)

- add some System stuff for pushing husked corpse inspection

- clean up more badcode

* Doing things properly, UI sorta kinda works.

- Utilize Relayed events for Devour checking

- Get a UI that partially works, Says the name of identities, doesn't
show their looks

- Make use of the New Dynamic BUI assignment

- commit the sin of no client prediction cause nullspace entities aren't
networked

* Got an entity for the Frontend transform

Issue with the looks

* Stick a camera into a fake mobs forehead

- Get the UI to see the net entity in pause space by using a
ViewSubscriber to get the Pvs from the initially stored identity entity

- Remove all the other parts used to try to get this to work before hand

* Raaaaadiallllllls also fix protection coefficents

- Change FancyWindow to Radial

- Fix Issue where coeffeient checking was the wrong way round

* absolutely massive cleanup, no more camera in mobs

- cleaned up event variables that are not needed

- Removed the use of a Pause space and go back to Nullspace usage

- use a PvsOverride rather than ViewSharing

- Remove old commented out code and Lots of unused code

* Fix "Ui elements" dying  on the screen

- some minor cleanup

- don't start the entities that get cloned

* ftl, cleanup, and fixing missing transform details

- add replace functionality to TypingIndicatorSystem and
BodyEmotesSystem

- add placeholder sounds and functions to TransformBodyEmotes

- add extra Pvs handling for later use

- attributions for the funny straw sound

- Sound collections for all of the sounds

- various cleanups

* Some extra cleanup

* Fix some false assumptions about TypingIndicator

- Bubbles now transfer on spawned humans rather than used humans

- Clean up YET MORE CODE

- make it so you can't eat yourself

* Oooprs, forgot to add a Husked Corpse Loc

* Missing period in the husked corpse loc

* bad devour windup placeholder

* Husking and WIP Lungs

- Husking now will be prevented from Revival fully and will change
the appearance of players

* Add finalized Sprites for actions and final meta

- add devour on and off sprites

- add transform action sprite

- Add Armblade sprite for future use

- Credit obscenelytinyshark for the sprites <3

* Remove ling lungs, Entity<> everything

- Remove the ling lungs stuff for now... body system is overly
complicated, makes my head hurt

- Switch every method to use Entity<> from Uid, Comp format

* cleanup, admin logging, WIP Roles

* Admin verb, Roundstart, gamerule stuff

- add a Admin verb to make Changelingification easy!

- Add game rule stuff for admin verb and to tell the hapless
goober how to be a changeling... sorta

- clean up parts to make VV easy... USE THE VERB!!

* Armor Coefficent Check

- Remove bespoke changeling armor check and replace it
with a generic armor coefficient query.

* move to UnrevivableComponent instead of husked

- Move UnrevivableComponent to shared

- add Analyzable and ReasonMessage to UnrevivableComponent
to give granular control of the message and whether or not it shows up
in the analyzer

- remove the check for HuskedComponent in DefibrillatorSystem

* aaaaaaa CopyComp

- Some cleanup

- make Vocal system shared

- make VocalSystem Not make more Actions than it needs

- Use some code from ChameleonProjector so we can copy components

- partially ungod method the Transform system

* Cleanup, Moving more things to CopyComp

- TransformBodyEmotes now uses CopyComp (it's a server component so i
need to tell the server to deal with it

- TypingIndicatorComponent also now uses CopyComp

- cleaned up old, now unused "replace" methods in favor of CopyComp

- BodyEmotesSystem now has a publically accessable LoadSounds to deal
with the same problem Screaming had

* WIP

* Devour Windup noise, ForensicsSystem cleanup

* Revert VocalSystem Changes

- Reverted Moving VocalSystem to shared, copy comp acomplishes it

- added component.ScreamActionEntity = null; for copy comp

* cleanup unneeded comments

* revert an accidental line removal

* Remove duplicate SharedHumanoidAppearanceSystem

* Cleanup Typo's and import Forensics components for Dna

* Some more forensics calls

* cleanup use CopyComp for now until CopyComps

* CR cleanup

* Undo some SharedHumanoidAppearanceSystem changes

* Confound these spaces

* Some Copycomp stuff and fixing some PVS override

* use the proper TryCopyComps that are merged

* Change TransformMenu with RadialWithSector

* All sounds done, Fix lack of typing indicator issue

* Updated attributions to include used sound authors

* some ftl typos and mind_role text issue

* DNA, Screaming, appearance, grammar, wagging

- reduced all of the above using ApplyComponentChanges

- Issue still remains with bodyEmotes sticking around in the UI

* Fix UI stuff, partials, entprotoid, good practices

- bunch of partials added

- UI now has a predicted message

- EntProtoID in the admin verb

- RipClothing now uses Entity<ButcherableComponent>

- husking is now optional (off by default) for testing/till we have
hivemind/when we figure out what were doing with devour

- remove TransformGrammarSet

* More CR stuff and documentation

- Make TargetIsProtected less of a meme, with a prototype
set of DamageTypes to check

- Documenation everywhere

- Move DevourEvents into its own file

* Predicted sounds and fix the comp clone list

- Made all start and stop sounds shared

- Split out the rest of the events and UI stuff into subfiles

- Fixed some Clone comp list issues where comments had -'s causing them
to be read incorrectly

* Damage cap check, Identity Shutdown cleanup, cleanup

* Sound stuff (but actually this time)

* Missed documentation

* Missed Documentation and a EntProtoId

* Remove unused dependency

* Remove a nullcheck

* Some dummy minplayers

* CR - Husked now uses a rem/ensure

* Update Actions in the Prototype

* Fixup mindswap handover

- cleanup and handover PVS on mindswap

* Fixup Missing meta from accidental "Take-theirs"

* Add the Armblade to the roundstart-role

* Cleanup, CR (everything but the UI and renames)

* missed a spot

* missed some more whitespace

* Renames

* Primary constructor and a space in these trying times

* User interface stuff for Slime transformation

* popup prediction

* Ling devour no longer makes duplicate identities

- added a key to identities to the original victim

- Add some extra clone settings

* add guard statements to OnClones

* SentOnlyToOwner additions

* fix for sound stoppage error

* Move Organ deleter into soon to be atomized husk

* clone event inventory

* mono sounds

* lower sound volume

* Fix networked sound warning

* Clone comps thing

* review

* attributions

* Fix clobbered changes

* I'm gonna weh out

- whole bunch of CR changes

* fix some very buggy git

* okay its fixed

* address most review points

* fix inventory

* we hate entityuids

* fix test and more cleanup

* move this

* fix more stuff

* fix validation and rootable

* Remove Quickswitch due to some UI quirks

* oops left out some better explanation

* remove dangling LastConsumed component fields

* fix test fail

* try this

* cleanup cloning subscriptions, add movement speed modifier

* fix slime storage

* fix cloning setting inheritance

* Add session information to transform admin logs

* slay the integration test hydra

* dwarf size

* more volume tweaks

* comments

* improve comments and unpredict deletion due to errors when shutting down the server

* fix displancement cloning

---------

Co-authored-by: ScarKy0 <scarky0@onet.eu>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Fix inventory flickering and missing InventoryTemplateUpdated event (#39379)

* fix flickering

* move InitClothing

* fix this

* Automatic changelog update

* Fix showvalue Ui for melee weapons (#38703)

Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>

* docs: update comment on config saving in tests (#39438)

* Ingestion Bugfixes (#39436)

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Bloonion mutation  (#33375)

* Automatic changelog update

* Fix title2.ogg attribution (#39435)

Co-authored-by: kait <kait@azumanga.gay>

* Resized baseball bats to be more realistic (#38392)

* Resized baseball bats to be more realistic

* Added new vertical icon for the baseball bat & incomplete variant, sprites by TiniestShark

* Added requested changes

* Added suggested changes for the incomplete sprite

* Automatic changelog update

* Fix Ingestion Localization Pop-ups (#39437)

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: ScarKy0 <scarky0@onet.eu>

* place stored changeling identities next to each other (#39452)

* Fix changeling typing indicator (#39454)

init

* give paused maps from polymorph and cryostorage a name (#39453)

* Automatic changelog update

* fixes items with complex shapes failing to insert sometimes (#38896)

* fixes item insertion bug

fixes bug where items with complex shapes would fail to insert if the item's StoredRotation wasn't a right angle

* independence from StoredRotation

* Automatic changelog update

* Add changeling briefing sound (#39465)

* init

* guh

* Disable changeling fixture cloning (#39467)

init

* Move some Station methods into shared (#38976)

* make objectives use yml defined mind filters (#36030)

* add MindHasRole whitelist overload

* add mind filters framework

* add different mind filters and pools

* update traitor stuff to use mind filters

* line

* don't duplicate kill objectives

* g

* gs

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: ScarKy0 <scarky0@onet.eu>
Co-authored-by: SlamBamActionman <slambamactionman@gmail.com>

* Sentry turrets - Part 8: AI notifications (#35277)

* Automatic changelog update

* Starting glasses for Captain and HoP (#35531)

* Automatic changelog update

* fix: fix emote wheel icons (#39481)

* Automatic changelog update

* Remove NamesGolem (#39478)

* deleted golem.ftl which contained golem names

* deleted golem.yml that went along with golem.ftl

* Cleanup Base food and drink a little (#39485)

Edible base prototype convention compliance

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Fix Whoopie Cushions from lagging the game. (#39194)

* Fix bar and base signs (#39487)

sorry

* Buttons are now Free-placeable (again) (#39425)

Fixed buttons

* fix mind role filter (#39499)

* Fix: Water Bottles Verb Priority and Prediction (#39482)

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Network StationMember properly (#39509)

* Mapping - Box station - Tie the RD's disposal bin to the disposals system. (#39507)

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>

* Automatic changelog update

* Update Credits (#39512)

Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>

* Crawling Fixes 1: Dragons and Borgs can't do the worm. (#39084)

* Init Commit

* Remove unused code, fix stun visuals bug

* Update Content.Shared/Stunnable/SharedStunSystem.cs

* Some initial changes

* first batch of changes

* Commit

* One line cleanup

* KnockdownStatusEffect ain't worth it.

* Fix 2 bugs

* Fixes

* Remove that actually,

* Commit

* Better solution

* Alright final commit I think

* Add better remarks

* How the fuck did this not get pushed???

* Wait no why was my ryder trying to push that??? I didn't make that change! DON'T DO THAT!!!

* Review

* Don't log that

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>

* Automatic changelog update

* Add voice locks to various hidden syndicate items (#39310)

* Automatic changelog update

* Fire stacks trigger (#39530)

* Simple as

* whoops

* not gonna work

* chopped

* missed something

* Better name

* formatting

---------

Co-authored-by: iaada <iaada@users.noreply.github.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Status Effect Alerts and Time Bugfixes (#39529)

* Bugefix

* Clean up

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Adds rare Hamlet variant: Fragile Hamlet (#39531)

* slippery gib hamlet

* add suffix for easier admeme

* address review

* increase odds

* parent MobHamsterHamlet, predicted emitsound

---------

Co-authored-by: Jessica M <jessica@maybe.sh>

* Automatic changelog update

* Trigger on round end (#39545)

* works if it works

* small rewording

---------

Co-authored-by: iaada <iaada@users.noreply.github.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* fix: reform dionas via SpawnNextToOrDrop (#39505)

* Automatic changelog update

* Actions examine (#39558)

* Predict suitsensor system (#39325)

* adwadsdwasadwas

* dev

* fix

* review

* some more cleanup

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Oasis: Add atmos network monitor (#39331)

* Automatic changelog update

* Base changeling objective(s) (#39562)

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Better robotics console (#38023)

* Automatic changelog update

* ExtinguishOnTrigger and TriggerOnInteractHand (#39537)

* simplely one commit

* simplelly two commit

* requested changes

---------

Co-authored-by: iaada <iaada@users.noreply.github.com>

* [HOTFIX] Stop players from clipping through Windoors (#39564)

* Don't have standing state edit soft fixtures?

* Bugfix

* Cherry pick acquired

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Reduce LevelOfParallelism to 2 for integration tests (#39566)

less paralelliism

* [HOTFIX] Stop players from clipping through Windoors (#39564)

* Don't have standing state edit soft fixtures?

* Bugfix

* Cherry pick acquired

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* Reduce LevelOfParallelism to 2 for integration tests (#39566)

less paralelliism

* Predict PoweredLights (#36541)

* Move PoweredLight to shared

* Predict the rest of the owl

* reacher

* compinit & anim

* Fix names

* Revert this?

* Fix these

* chicken drummies

* deita

* Fix

* review

* fix

* fixes

* fix PVS weirdness

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Automatic changelog update

* Fix ReagentQuantity Equality check (#39574)

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>

* feat: make ReagentId hash by value (#39494)

* Fix antag objective assignment (again) (#39565)

init

* Fix dock radar colours (#38942)

* Fix docking colours

* Add comments and fallback

* Better comments!

* Slightly shorten examine text for splashing a solution with a melee attack (#39428)

smaller text.

Co-authored-by: iaada <iaada@users.noreply.github.com>

* Borg hands & hand whitelisting (#38668)

* Borg hands & hand whitelisting

* yaml linted

* yaml linted (x2)

* yaml linted (x3)

* my storage tests so pass

* no need for SetCount

* ok new stuff you can get fixed too

* oops

* staque

* what if we addressed feedback

* my place so holder

* what if we addresesd feedback

* what if i did it correctly

* terminating or deleted

* Automatic changelog update

* fix: fix lights not always enabling correctly (#39585)

* Automatic changelog update

* RandomChance trigger condition (#39543)

* branch names don't matter anyway

* commits are a window to the soul

* requested change

* also requested

* ship it

* remove key

---------

Co-authored-by: iaada <iaada@users.noreply.github.com>

* Fix: Break do_after if target/tool becomes inaccessible (#35079)

* Predict GetVerbsEvent in PowerSwitchableSystem (#39589)

* Prediction

* Update

* Move mind role components to shared (#39606)

* Disable the lock/unlock verb if we can't do that (#39605)

* Fix

* Update

* StaminaDamageOnTriggerComponent (#39607)

* InflictStaminaOnTriggerSystem

Surprised this wasn't done alongside the damage one.

* Correct docstring

* Moar docstring changes!!!

* Resolve PR comment

* a

* Update Content.Shared/Trigger/Systems/StaminaDamageOnTriggerSystem.cs

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Weapon Resizing (#36473)

* Automatic changelog update

* Compact Security Jetpacks (#39569)

Made security jetpacks functionally identical to mini jetpacks.

Signed-off-by: Nox38 <nebulousnox38@gmail.com>

* Revert "Fix: Break do_after if target/tool becomes inaccessible" (#39617)

* fix lightbulb color (#39623)

* Automatic changelog update

* In Memoriam - Memorializing those who've passed within the SS13+SS14 community (#39621)

In Memoriam - adds a section to the credits memorializing those who've passed

* Automatic changelog update

* Add trigger-refactor components and systems: Batch 1 (#39391)

* Adds the following batch of trigger refactor components and their associated systems:

TriggerOnLand: LandEvent
TriggerOnExamined: ExaminedEvent
TriggerOnUnbuckle: UnbuckledEvent
TriggerOnBuckle: BuckledEvent
TriggerOnStrap: StrappedEvent
TriggerOnUnstrapped: UnstrappedEvent

* Removes unnecessary lines from comment

* Fix comment formatting, corrects grammar and increases comment clarity.

* adds last forgotten edit to comments

* Update Content.Shared/Trigger/Systems/TriggerOnStrappedOrBuckledSystem.cs

Removes unnecessary comments

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Content.Shared/Trigger/Components/Triggers/TriggerOnBuckledComponent.cs

Increases comment clarity

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Content.Shared/Trigger/Components/Triggers/TriggerOnExaminedComponent.cs

Increases comment clarity

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Content.Shared/Trigger/Components/Triggers/TriggerOnLandComponent.cs

Increases comment clarity

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Content.Shared/Trigger/Components/Triggers/TriggerOnStrappedComponent.cs

Increases comment clarity

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Content.Shared/Trigger/Components/Triggers/TriggerOnUnbuckledComponent.cs

Increases comment clarity

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Content.Shared/Trigger/Components/Triggers/TriggerOnUnstrappedComponent.cs

Increases comment clarity

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* refactored TriggerOnStrappedOrBuckledSystem.cs
removed TriggerOnExaminedSystem.cs and moved it into TriggerSystem.Interaction.cs

Changes currently untested, not sure how to make it so modders can change what method they want sending out the appropriate trigger key but want to save progress working on it and get feedback from maintainers

* Removed component which already exists as part of TriggerSystem.Interaction.cs

* Restores accidentally removed component

* Apply suggestions from code review

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Add myself to codeowners file (#39636)

* RemoveComponentsOnTrigger, ToggleComponentsOnTrigger (#39639)

* Rebalance advanced Brute chems, and more (#39472)

* Initial commit

* Update based on feedback

* Minor fix

* Update to match playtest

* Update reaction behavior and test to work; max temp is no longer an inclusive value.

* Revert "Update reaction behavior and test to work; max temp is no longer an inclusive value."

This reverts commit 2a2c4a17a623cc7ddc15b63d91a1b8b441e95cea.

* Incorporate heal values & OD for adv. chems from 39464, revert adv. brutes recipes

* Automatic changelog update

* Fix industrial reagent grinder bug, second attempt (#39690)

* fix recycler 3

* Update Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml

* unbreak StandingStateSystem

* Added "highly illegal" contraband to guidebook (#38176)

* Added extreme contraband to guidebook

* Extreme -> Highly Illegal

* Whoops, one-letter typo

* Fixed a capital letter while I'm here

* adapt codebase

* Update water_creation.yml

* Update water_creation.yml

* remove random food

* Refactor Food component to Edible in meal and cooking entities

Replaced the deprecated 'Food' component with the 'Edible' component in bowl, plate, and pie_pan YAML prototypes. Updated base cooking entity whitelist to include 'Edible' for compatibility. This change standardizes food consumption logic and improves maintainability.

---------

Signed-off-by: Nox38 <nebulousnox38@gmail.com>
Co-authored-by: Flareguy <woaj9999@outlook.com>
Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>
Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>
Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Co-authored-by: Arcane-Waffle <denisliazhev@gmail.com>
Co-authored-by: Arcane-Waffle <FR_Waffle@proton.me>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Co-authored-by: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com>
Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
Co-authored-by: Krosus777 <38509947+Krosus777@users.noreply.github.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: Hannah Giovanna Dawson <karakkaraz@gmail.com>
Co-authored-by: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
Co-authored-by: Token <56667933+TokenStyle@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ThatGuyUSA <thatguyusa123@gmail.com>
Co-authored-by: qwerltaz <69696513+qwerltaz@users.noreply.github.com>
Co-authored-by: Tiniest Shark <head.rebel@yahoo.com>
Co-authored-by: Zeneganto <fantik8800@gmail.com>
Co-authored-by: Samuka-C <47865393+Samuka-C@users.noreply.github.com>
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
Co-authored-by: Quantum-cross <7065792+Quantum-cross@users.noreply.github.com>
Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
Co-authored-by: Southbridge <7013162+southbridge-fur@users.noreply.github.com>
Co-authored-by: WarPigeon <DaedalusTheGamer@gmail.com>
Co-authored-by: Kowlin <git@wyvern.blue>
Co-authored-by: ScarKy0 <scarky0@onet.eu>
Co-authored-by: alexalexmax <149889301+alexalexmax@users.noreply.github.com>
Co-authored-by: seanpimble <149889301+seanpimble@users.noreply.github.com>
Co-authored-by: Mora <46364955+TrixxedHeart@users.noreply.github.com>
Co-authored-by: Minemoder5000 <minemoder50000@gmail.com>
Co-authored-by: Marlyn <marlyn@marlyn.cloud>
Co-authored-by: lzk <124214523+lzk228@users.noreply.github.com>
Co-authored-by: Ser11y <160628372+Ser1-1y@users.noreply.github.com>
Co-authored-by: Luxeator <derkerl1@gmail.com>
Co-authored-by: PGray <77597544+PGrayCS@users.noreply.github.com>
Co-authored-by: Serylis of Five <kasper.west-hansen+github@fivetail.dk>
Co-authored-by: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com>
Co-authored-by: Myra <vasilis@pikachu.systems>
Co-authored-by: āda <ss.adasts@gmail.com>
Co-authored-by: iaada <iaada@users.noreply.github.com>
Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com>
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: Tao <56692749+TaoNewt@users.noreply.github.com>
Co-authored-by: beck-thompson <beck314159@hotmail.com>
Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Co-authored-by: poklj <compgeek223@gmail.com>
Co-authored-by: Thinbug <101073555+Thinbug0@users.noreply.github.com>
Co-authored-by: Stefano Pigozzi <me@steffo.eu>
Co-authored-by: kait <kait@azumanga.gay>
Co-authored-by: Kittygyat <202250949+Kittygyat@users.noreply.github.com>
Co-authored-by: lolman360 <22850904+lolman360@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SlamBamActionman <slambamactionman@gmail.com>
Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com>
Co-authored-by: M87S <147015589+M87S@users.noreply.github.com>
Co-authored-by: kosticia <kosticia46@gmail.com>
Co-authored-by: Serylis of Five <stormy-git@stormweyr.dk>
Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com>
Co-authored-by: Jessica M <jessica@jessicamaybe.com>
Co-authored-by: Jessica M <jessica@maybe.sh>
Co-authored-by: Alex <firestar@firestar4.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: UpAndLeaves <92269094+Alpha-Two@users.noreply.github.com>
Co-authored-by: Winkarst-cpu <74284083+Winkarst-cpu@users.noreply.github.com>
Co-authored-by: Nox <nebulousnox38@gmail.com>
Co-authored-by: deathride58 <deathride58@users.noreply.github.com>
Co-authored-by: Studio Fae-Wilds <studio.faewilds@gmail.com>
Co-authored-by: Simon <63975668+Simyon264@users.noreply.github.com>
Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com>
Co-authored-by: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com>
This commit is contained in:
Red
2025-09-08 13:00:39 +03:00
committed by GitHub
1092 changed files with 42444 additions and 29718 deletions

4
.github/CODEOWNERS vendored
View File

@@ -1,6 +1,2 @@
# TheShuEd
* @TheShuEd
# TornadoTechnology
*.cs @Tornado-Technology
*.xaml @Tornado-Technology

View File

@@ -6,7 +6,6 @@ using BenchmarkDotNet.Attributes;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Mind;
using Content.Server.Warps;
using Content.Shared.Warps;
using Robust.Shared;
using Robust.Shared.Analyzers;

View File

@@ -1,10 +0,0 @@
using Content.Shared.Administration.Components;
using Robust.Shared.GameStates;
namespace Content.Client.Administration.Components;
[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{
}

View File

@@ -1,4 +1,4 @@
using Content.Client.Administration.Components;
using Content.Shared.Administration.Components;
using Robust.Client.GameObjects;
namespace Content.Client.Administration.Systems;

View File

@@ -76,7 +76,7 @@ public sealed partial class ObjectsTab : Control
switch (selection)
{
case ObjectsTabSelection.Stations:
entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().Stations);
entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().GetStationNames());
break;
case ObjectsTabSelection.Grids:
{

View File

@@ -0,0 +1,5 @@
using Content.Shared.Animals.Systems;
namespace Content.Client.Animals.Systems;
public sealed class ParrotMemorySystem : SharedParrotMemorySystem;

View File

@@ -1,6 +1,6 @@
using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Effects;
using Content.Shared.Body.Components;
using Content.Shared.Humanoid;
using Robust.Client.GameObjects;
namespace Content.Client.Anomaly.Effects;
@@ -25,9 +25,8 @@ public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap);
if (TryComp<BodyComponent>(ent, out var body) &&
body.Prototype is not null &&
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
if (TryComp<HumanoidAppearanceComponent>(ent, out var humanoidAppearance) &&
ent.Comp.SpeciesSprites.TryGetValue(humanoidAppearance.Species, out var speciesSprite))
{
_sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite);
}

View File

@@ -1,46 +1,11 @@
using Content.Client.Atmos.Components;
using Robust.Client.GameObjects;
using Content.Client.UserInterface.Systems.Storage.Controls;
using Content.Shared.Atmos.Piping;
using Content.Shared.Hands;
using Content.Shared.Atmos.Components;
using Content.Shared.Item;
namespace Content.Client.Atmos.EntitySystems;
public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisualsComponent>
{
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PipeColorVisualsComponent, GetInhandVisualsEvent>(OnGetVisuals);
SubscribeLocalEvent<PipeColorVisualsComponent, BeforeRenderInGridEvent>(OnDrawInGrid);
}
/// <summary>
/// This method is used to display the color changes of the pipe on the screen..
/// </summary>
private void OnGetVisuals(Entity<PipeColorVisualsComponent> item, ref GetInhandVisualsEvent args)
{
foreach (var (_, layerData) in args.Layers)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
layerData.Color = pipeColor.Color;
}
}
/// <summary>
/// This method is used to change the pipe's color in a container grid.
/// </summary>
private void OnDrawInGrid(Entity<PipeColorVisualsComponent> item, ref BeforeRenderInGridEvent args)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
args.Color = pipeColor.Color;
}
protected override void OnAppearanceChange(EntityUid uid, PipeColorVisualsComponent component, ref AppearanceChangeEvent args)
{
if (TryComp<SpriteComponent>(uid, out var sprite)
@@ -50,8 +15,6 @@ public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisual
var layer = sprite[PipeVisualLayers.Pipe];
layer.Color = color.WithAlpha(layer.Color.A);
}
_itemSystem.VisualsChanged(uid);
}
}

View File

@@ -59,7 +59,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPumpDirection>())
{
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value);
_pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
}
_pumpDirection.SelectId((int) _data.PumpDirection);
@@ -72,7 +72,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPressureBound>())
{
_pressureCheck.AddItem(Loc.GetString($"{value}"), (int) value);
_pressureCheck.AddItem(Loc.GetString($"air-alarm-ui-pressure-bound-{value.ToString().ToLower()}"), (int) value);
}
_pressureCheck.SelectId((int) _data.PressureChecks);

View File

@@ -27,9 +27,15 @@
</BoxContainer>
<!-- Lower row: every single gas -->
<Collapsible Margin="2 2 2 2">
<CollapsibleHeading Title="Gas filters" />
<CollapsibleHeading Title="{Loc 'air-alarm-ui-widget-gas-filters'}" />
<CollapsibleBody Margin="20 0 0 0">
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" />
<BoxContainer Orientation="Vertical">
<BoxContainer Margin="2">
<Button Name="CSelectAll" Text="{Loc 'air-alarm-ui-scrubber-select-all-gases-label'}" />
<Button Name="CDeselectAll" Text="{Loc 'air-alarm-ui-scrubber-deselect-all-gases-label'}" />
</BoxContainer>
<GridContainer Name="CGasContainer" Columns="3" />
</BoxContainer>
</CollapsibleBody>
</Collapsible>
</BoxContainer>

View File

@@ -1,15 +1,21 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences]
public sealed partial class ScrubberControl : BoxContainer
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
private GasVentScrubberData _data;
private string _address;
@@ -22,12 +28,18 @@ public sealed partial class ScrubberControl : BoxContainer
private FloatSpinBox _volumeRate => CVolumeRate;
private CheckBox _wideNet => CWideNet;
private Button _copySettings => CCopySettings;
private Button _selectAll => CSelectAll;
private Button _deselectAll => CDeselectAll;
private GridContainer _gases => CGasContainer;
private Dictionary<Gas, Button> _gasControls = new();
public ScrubberControl(GasVentScrubberData data, string address)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this);
Name = address;
@@ -61,7 +73,7 @@ public sealed partial class ScrubberControl : BoxContainer
foreach (var value in Enum.GetValues<ScrubberPumpDirection>())
{
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value);
_pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
}
_pumpDirection.SelectId((int) _data.PumpDirection);
@@ -78,12 +90,28 @@ public sealed partial class ScrubberControl : BoxContainer
ScrubberDataCopied?.Invoke(_data);
};
foreach (var value in Enum.GetValues<Gas>())
var allGases = Enum.GetValues<Gas>();
_selectAll.OnPressed += _ =>
{
_data.FilterGases = new HashSet<Gas>(allGases);
ScrubberDataChanged?.Invoke(_address, _data);
};
_deselectAll.OnPressed += _ =>
{
_data.FilterGases = [];
ScrubberDataChanged?.Invoke(_address, _data);
};
foreach (var value in allGases)
{
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(value);
var gasName = _prototypeManager.Index(gasProtoId).Name;
var gasButton = new Button
{
Name = value.ToString(),
Text = Loc.GetString($"{value}"),
Text = Loc.GetString(gasName),
ToggleMode = true,
HorizontalExpand = true,
Pressed = _data.FilterGases.Contains(value)

View File

@@ -1,16 +1,22 @@
using Content.Client.Message;
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Prototypes;
using Content.Shared.Temperature;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences]
public sealed partial class SensorInfo : BoxContainer
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
public Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? OnThresholdUpdate;
public event Action<AtmosSensorData>? SensorDataCopied;
private string _address;
@@ -23,6 +29,9 @@ public sealed partial class SensorInfo : BoxContainer
public SensorInfo(AtmosSensorData data, string address)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this);
_address = address;
@@ -45,8 +54,12 @@ public sealed partial class SensorInfo : BoxContainer
var label = new RichTextLabel();
var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", $"{gas}"),
("gas", Loc.GetString(gasName)),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}")));
@@ -54,7 +67,7 @@ public sealed partial class SensorInfo : BoxContainer
_gasLabels.Add(gas, label);
var threshold = data.GasThresholds[gas];
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title"), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
gasThresholdControl.Margin = new Thickness(20, 2, 2, 2);
gasThresholdControl.ThresholdDataChanged += (type, alarmThreshold, arg3) =>
{
@@ -90,6 +103,9 @@ public sealed partial class SensorInfo : BoxContainer
public void ChangeData(AtmosSensorData data)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState));
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
@@ -112,8 +128,12 @@ public sealed partial class SensorInfo : BoxContainer
}
var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", $"{gas}"),
("gas", Loc.GetString(gasName)),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}")));

View File

@@ -2,6 +2,6 @@
HorizontalExpand="True" Orientation="Vertical"
Margin = "20 0 0 0" MinSize="160 0" >
<Label Name="CBoundLabel" HorizontalAlignment="Center" />
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'Enable'}" Pressed="True" />
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'air-alarm-ui-widget-enable'}" Pressed="True" />
<FloatSpinBox Name="CSpinner" />
</BoxContainer>

View File

@@ -6,7 +6,7 @@
<CollapsibleBody Margin="20 0 0 0">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<CheckBox Name="CEnabled" Text="{Loc 'Enabled'}" />
<CheckBox Name="CEnabled" Text="{Loc 'air-alarm-ui-widget-enable'}" />
</BoxContainer>
<!-- Upper row: Danger bounds -->
<BoxContainer Name="CDangerBounds" Orientation="Horizontal" Margin="0 0 0 2"/>

View File

@@ -1,8 +1,8 @@
using System.Numerics;
using Content.Shared.Body.Components;
using Content.Shared.CardboardBox;
using Content.Shared.CardboardBox.Components;
using Content.Shared.Examine;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Components;
using Robust.Client.GameObjects;
@@ -15,13 +15,13 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
private EntityQuery<BodyComponent> _bodyQuery;
private EntityQuery<MobStateComponent> _mobStateQuery;
public override void Initialize()
{
base.Initialize();
_bodyQuery = GetEntityQuery<BodyComponent>();
_mobStateQuery = GetEntityQuery<MobStateComponent>();
SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
}
@@ -66,8 +66,8 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
continue;
// no effect for anything too exotic
if (!_bodyQuery.HasComp(mob))
// no effect for non-mobs that have MobMover, such as mechs and vehicles.
if (!_mobStateQuery.HasComp(mob))
continue;
var ent = Spawn(box.Effect, mapPos);

View File

@@ -0,0 +1,35 @@
using Content.Shared.Changeling.Transform;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Changeling.Transform;
[UsedImplicitly]
public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private ChangelingTransformMenu? _window;
protected override void Open()
{
base.Open();
_window = this.CreateWindow<ChangelingTransformMenu>();
_window.OnIdentitySelect += SendIdentitySelect;
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not ChangelingTransformBoundUserInterfaceState current)
return;
_window?.UpdateState(current);
}
public void SendIdentitySelect(NetEntity identityId)
{
SendPredictedMessage(new ChangelingTransformIdentitySelectMessage(identityId));
}
}

View File

@@ -0,0 +1,8 @@
<ui:RadialMenu
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True">
<ui:RadialContainer Name="Main">
</ui:RadialContainer>
</ui:RadialMenu>

View File

@@ -0,0 +1,60 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Changeling.Transform;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Changeling.Transform;
[GenerateTypedNameReferences]
public sealed partial class ChangelingTransformMenu : RadialMenu
{
[Dependency] private readonly IEntityManager _entity = default!;
public event Action<NetEntity>? OnIdentitySelect;
public ChangelingTransformMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
public void UpdateState(ChangelingTransformBoundUserInterfaceState state)
{
Main.DisposeAllChildren();
foreach (var identity in state.Identites)
{
var identityUid = _entity.GetEntity(identity);
if (!_entity.TryGetComponent<MetaDataComponent>(identityUid, out var metadata))
continue;
var identityName = metadata.EntityName;
var button = new ChangelingTransformMenuButton()
{
StyleClasses = { "RadialMenuButton" },
SetSize = new Vector2(64, 64),
ToolTip = identityName,
};
var entView = new SpriteView()
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill,
};
entView.SetEntity(identityUid);
button.OnButtonUp += _ =>
{
OnIdentitySelect?.Invoke(identity);
Close();
};
button.AddChild(entView);
Main.AddChild(button);
}
}
}
public sealed class ChangelingTransformMenuButton : RadialMenuTextureButtonWithSector;

View File

@@ -0,0 +1,5 @@
using Content.Shared.Cloning;
namespace Content.Client.Cloning;
public sealed partial class CloningSystem : SharedCloningSystem;

View File

@@ -356,6 +356,7 @@ public sealed partial class CreditsWindow : DefaultWindow
AddSection(Loc.GetString("credits-window-contributors-section-title"), "GitHub.txt");
AddSection(Loc.GetString("credits-window-codebases-section-title"), "SpaceStation13.txt");
AddSection(Loc.GetString("credits-window-original-remake-team-section-title"), "OriginalRemake.txt");
AddSection(Loc.GetString("credits-window-immortals-title"), "Immortals.txt", true);
AddSection(Loc.GetString("credits-window-special-thanks-section-title"), "SpecialThanks.txt", true);
var linkGithub = _cfg.GetCVar(CCVars.InfoLinksGithub);

View File

@@ -1,7 +0,0 @@
using Content.Shared.Explosion.EntitySystems;
namespace Content.Client.Explosion;
public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem
{
}

View File

@@ -1,7 +0,0 @@
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
namespace Content.Client.Explosion;
[RegisterComponent, Access(typeof(TriggerSystem))]
public sealed partial class TriggerOnProximityComponent : SharedTriggerOnProximityComponent {}

View File

@@ -1,10 +0,0 @@
namespace Content.Client.Explosion;
public sealed partial class TriggerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
InitializeProximity();
}
}

View File

@@ -0,0 +1,5 @@
using Content.Shared.Forensics.Systems;
namespace Content.Client.Forensics.Systems;
public sealed class ForensicsSystem : SharedForensicsSystem;

View File

@@ -1,5 +1,6 @@
using Content.Shared.HotPotato;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.HotPotato;
@@ -10,6 +11,9 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private readonly EntProtoId _hotPotatoEffectId = "HotPotatoEffect";
// TODO: particle system
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -23,7 +27,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
if (_timing.CurTime < comp.TargetTime)
continue;
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
Spawn("HotPotatoEffect", _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
Spawn(_hotPotatoEffectId, _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
}
}
}

View File

@@ -294,7 +294,6 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
SetMaster(uid, null);
TrySetChannels(uid, data);
instrument.MidiEventBuffer.Clear();
instrument.Renderer.OnMidiEvent += instrument.MidiEventBuffer.Add;
return true;
}

View File

@@ -115,6 +115,13 @@ namespace Content.Client.Inventory
OnLinkInventorySlots?.Invoke(uid, component);
}
protected override void OnInit(Entity<InventoryComponent> ent, ref ComponentInit args)
{
base.OnInit(ent, ref args);
_clothingVisualsSystem.InitClothing(ent.Owner, ent.Comp);
}
public override void Shutdown()
{
CommandBinds.Unregister<ClientInventorySystem>();
@@ -261,7 +268,6 @@ namespace Content.Client.Inventory
TryAddSlotData((ent.Owner, inventorySlots), (SlotData)slot);
}
_clothingVisualsSystem.InitClothing(ent, ent.Comp);
if (ent.Owner == _playerManager.LocalEntity)
ReloadInventory(inventorySlots);
}

View File

@@ -1,36 +1,46 @@
using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Robust.Client.GameObjects;
namespace Content.Client.Light.Visualizers;
namespace Content.Client.Light.EntitySystems;
public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
public sealed class LightBulbSystem : SharedLightBulbSystem
{
protected override void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LightBulbComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
private void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
// update sprite state
if (AppearanceSystem.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
if (_appearance.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
{
switch (state)
{
case LightBulbState.Normal:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
break;
case LightBulbState.Broken:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
break;
case LightBulbState.Burned:
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
break;
}
}
// also update sprites color
if (AppearanceSystem.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
if (_appearance.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
{
SpriteSystem.SetColor((uid, args.Sprite), color);
_sprite.SetColor((uid, args.Sprite), color);
}
}
}

View File

@@ -0,0 +1,5 @@
using Content.Shared.Light.EntitySystems;
namespace Content.Client.Light.EntitySystems;
public sealed class PoweredLightSystem : SharedPoweredLightSystem;

View File

@@ -1021,7 +1021,7 @@ namespace Content.Client.Lobby.UI
_loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection)
{
Title = jobProto?.ID + "-loadout",
Title = Loc.GetString("loadout-window-title-loadout", ("job", $"{jobProto?.LocalizedName}")),
};
// Refresh the buttons etc.

View File

@@ -1,7 +1,5 @@
using System.Numerics;
using Content.Shared.Emag.Systems;
using Content.Shared.Medical.Cryogenics;
using Content.Shared.Verbs;
using Robust.Client.GameObjects;
namespace Content.Client.Medical.Cryogenics;
@@ -15,11 +13,6 @@ public sealed class CryoPodSystem : SharedCryoPodSystem
{
base.Initialize();
SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
SubscribeLocalEvent<CryoPodComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<InsideCryoPodComponent, ComponentStartup>(OnCryoPodInsertion);
SubscribeLocalEvent<InsideCryoPodComponent, ComponentRemove>(OnCryoPodRemoval);
@@ -53,8 +46,8 @@ public sealed class CryoPodSystem : SharedCryoPodSystem
return;
}
if (!_appearance.TryGetData<bool>(uid, CryoPodComponent.CryoPodVisuals.ContainsEntity, out var isOpen, args.Component)
|| !_appearance.TryGetData<bool>(uid, CryoPodComponent.CryoPodVisuals.IsOn, out var isOn, args.Component))
if (!_appearance.TryGetData<bool>(uid, CryoPodVisuals.ContainsEntity, out var isOpen, args.Component)
|| !_appearance.TryGetData<bool>(uid, CryoPodVisuals.IsOn, out var isOn, args.Component))
{
return;
}

View File

@@ -0,0 +1,5 @@
using Content.Shared.Medical.SuitSensors;
namespace Content.Client.Medical.SuitSensors;
public sealed class SuitSensorSystem : SharedSuitSensorSystem;

View File

@@ -5,15 +5,15 @@
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" />
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1">
<Button Name="Set" Text="Set" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-set'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Add" Text="Add" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-add'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Set" Text="{Loc 'network-configurator-text-set'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-set'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Add" Text="{Loc 'network-configurator-text-add'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-add'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<!-- Edit might not be needed -->
<!--<Button Name="Edit" Text="Edit" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-edit'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>-->
<Button Name="Clear" Text="Clear" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-clear'}" HorizontalExpand="True"/>
<Button Name="Clear" Text="{Loc 'network-configurator-text-clear'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-clear'}" HorizontalExpand="True"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 0 8 8">
<Button Name="Copy" Text="Copy" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
<Button Name="Show" Text="Show" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
<Button Name="Copy" Text="{Loc 'network-configurator-text-copy'}" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
<Button Name="Show" Text="{Loc 'network-configurator-text-show'}" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
</BoxContainer>
<Label Name="Count" StyleClasses="LabelSubText" HorizontalAlignment="Right" Margin="0 0 12 8"/>
</BoxContainer>

View File

@@ -94,7 +94,7 @@ public sealed class ClientsidePlaytimeTrackingManager
return;
}
// At less than 1 minute of time diff, there's not much point, and saving regardless will brick tests
// At less than 1 minute of time diff, there's not much point
// The reason this isn't checking for 0 is because TotalMinutes is fractional, rather than solely whole minutes
if (timeDiffMinutes < 1)
return;

View File

@@ -5,4 +5,5 @@ namespace Content.Client.Power.Components;
[RegisterComponent]
public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent
{
public override float Load { get; set; }
}

View File

@@ -28,6 +28,8 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
public EntityUid Entity;
private bool _allowBorgControl = true;
public RoboticsConsoleWindow()
{
RobustXamlLoader.Load(this);
@@ -72,6 +74,7 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
public void UpdateState(RoboticsConsoleState state)
{
_cyborgs = state.Cyborgs;
_allowBorgControl = state.AllowBorgControl;
// clear invalid selection
if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
@@ -85,8 +88,8 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
PopulateData();
var locked = _lock.IsLocked(Entity);
DangerZone.Visible = !locked;
LockedMessage.Visible = locked;
DangerZone.Visible = !locked && _allowBorgControl;
LockedMessage.Visible = locked && _allowBorgControl; // Only show if locked AND control is allowed
}
private void PopulateCyborgs()
@@ -120,11 +123,19 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
BorgSprite.Texture = _sprite.Frame0(data.ChassisSprite!);
var batteryColor = data.Charge switch {
< 0.2f => "red",
< 0.4f => "orange",
< 0.6f => "yellow",
< 0.8f => "green",
_ => "blue"
< 0.2f => "#FF6C7F", // red
< 0.4f => "#EF973C", // orange
< 0.6f => "#E8CB2D", // yellow
< 0.8f => "#30CC19", // green
_ => "#00D3B8" // cyan
};
var hpPercentColor = data.HpPercent switch {
< 0.2f => "#FF6C7F", // red
< 0.4f => "#EF973C", // orange
< 0.6f => "#E8CB2D", // yellow
< 0.8f => "#30CC19", // green
_ => "#00D3B8" // cyan
};
var text = new FormattedMessage();
@@ -132,12 +143,14 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
text.AddMarkupOrThrow(Loc.GetString("robotics-console-designation"));
text.AddText($" {data.Name}\n"); // prevent players trolling by naming borg [color=red]satan[/color]
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-battery", ("charge", (int)(data.Charge * 100f)), ("color", batteryColor))}\n");
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-hp", ("hp", (int)(data.HpPercent * 100f)), ("color", hpPercentColor))}\n");
text.AddMarkupOrThrow($"{Loc.GetString("robotics-console-brain", ("brain", data.HasBrain))}\n");
text.AddMarkupOrThrow(Loc.GetString("robotics-console-modules", ("count", data.ModuleCount)));
BorgInfo.SetMessage(text);
// how the turntables
DisableButton.Disabled = !(data.HasBrain && data.CanDisable);
DisableButton.Disabled = !_allowBorgControl || !(data.HasBrain && data.CanDisable);
DestroyButton.Disabled = !_allowBorgControl;
}
protected override void FrameUpdate(FrameEventArgs args)

View File

@@ -24,7 +24,7 @@ public sealed class RotationVisualizerSystem : SharedRotationVisualsSystem
return;
if (!_appearance.TryGetData<RotationState>(uid, RotationVisuals.RotationState, out var state, args.Component))
return;
state = RotationState.Vertical;
switch (state)
{

View File

@@ -40,6 +40,8 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
private readonly HashSet<DockingPortState> _drawnDocks = new();
private readonly Dictionary<DockingPortState, Button> _dockButtons = new();
private readonly Color _fallbackHighlightedColor = Color.Magenta;
/// <summary>
/// Store buttons for every other dock
/// </summary>
@@ -213,11 +215,11 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
if (HighlightedDock == dock.Entity)
{
otherDockColor = Color.ToSrgb(Color.Magenta);
otherDockColor = Color.ToSrgb(dock.HighlightedColor);
}
else
{
otherDockColor = Color.ToSrgb(Color.Purple);
otherDockColor = Color.ToSrgb(dock.Color);
}
/*
@@ -311,7 +313,7 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl
ScalePosition(Vector2.Transform(new Vector2(-0.5f, 0.5f), rotation)),
ScalePosition(Vector2.Transform(new Vector2(0.5f, -0.5f), rotation)));
var dockColor = Color.Magenta;
var dockColor = _viewedState?.HighlightedColor ?? _fallbackHighlightedColor;
var connectionColor = Color.Pink;
handle.DrawRect(ourDockConnection, connectionColor.WithAlpha(0.2f));

View File

@@ -308,7 +308,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
-dockRadius * UIScale,
(Size.X + dockRadius) * UIScale,
(Size.Y + dockRadius) * UIScale);
if (_docks.TryGetValue(nent, out var docks))
{
foreach (var state in docks)
@@ -321,7 +321,7 @@ public sealed partial class ShuttleNavControl : BaseShuttleControl
continue;
}
var color = Color.ToSrgb(Color.Magenta);
var color = Color.ToSrgb(state.HighlightedColor);
var verts = new[]
{

View File

@@ -12,6 +12,8 @@ public sealed class EmitterSystem : SharedEmitterSystem
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EmitterComponent, AppearanceChangeEvent>(OnAppearanceChange);
}

View File

@@ -0,0 +1,38 @@
using System.Numerics;
using Content.Shared.Sprite;
using Robust.Client.GameObjects;
namespace Content.Client.Sprite;
public sealed class ScaleVisualsSystem : SharedScaleVisualsSystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ScaleVisualsComponent, AppearanceChangeEvent>(OnChangeData);
}
private void OnChangeData(Entity<ScaleVisualsComponent> ent, ref AppearanceChangeEvent args)
{
if (!args.AppearanceData.TryGetValue(ScaleVisuals.Scale, out var scale) ||
args.Sprite == null) return;
// save the original scale
ent.Comp.OriginalScale ??= args.Sprite.Scale;
var vecScale = (Vector2)scale;
_sprite.SetScale((ent.Owner, args.Sprite), vecScale);
}
// revert to the original scale
protected override void ResetScale(Entity<ScaleVisualsComponent> ent)
{
base.ResetScale(ent);
if (ent.Comp.OriginalScale != null)
_sprite.SetScale(ent.Owner, ent.Comp.OriginalScale.Value);
}
}

View File

@@ -28,22 +28,8 @@ namespace Content.Client.Stack
base.SetCount(uid, amount, component);
if (component.Lingering &&
TryComp<SpriteComponent>(uid, out var sprite))
{
// tint the stack gray and make it transparent if it's lingering.
var color = component.Count == 0 && component.Lingering
? Color.DarkGray.WithAlpha(0.65f)
: Color.White;
for (var i = 0; i < sprite.AllLayers.Count(); i++)
{
_sprite.LayerSetColor((uid, sprite), i, color);
}
}
// TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call.
if (component.Count <= 0 && !component.Lingering)
if (component.Count <= 0)
{
Xform.DetachEntity(uid, Transform(uid));
return;

View File

@@ -2,34 +2,5 @@
namespace Content.Client.Station;
/// <summary>
/// This handles letting the client know stations are a thing. Only really used by an admin menu.
/// </summary>
public sealed partial class StationSystem : SharedStationSystem
{
private readonly List<(string Name, NetEntity Entity)> _stations = new();
/// <summary>
/// All stations that currently exist.
/// </summary>
/// <remarks>
/// I'd have this just invoke an entity query, but we're on the client and the client barely knows about stations.
/// </remarks>
// TODO: Stations have a global PVS override now, this can probably be changed into a query.
public IReadOnlyList<(string Name, NetEntity Entity)> Stations => _stations;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<StationsUpdatedEvent>(StationsUpdated);
}
private void StationsUpdated(StationsUpdatedEvent ev)
{
_stations.Clear();
// TODO this needs to be done in component states and with the Ensure() methods
_stations.AddRange(ev.Stations);
}
}
/// <inheritdoc/>
public sealed partial class StationSystem : SharedStationSystem;

View File

@@ -40,6 +40,11 @@ public sealed class StorageSystem : SharedStorageSystem
component.MaxItemSize = state.MaxItemSize;
component.Whitelist = state.Whitelist;
component.Blacklist = state.Blacklist;
component.StorageInsertSound = state.StorageInsertSound;
component.StorageRemoveSound = state.StorageRemoveSound;
component.StorageOpenSound = state.StorageOpenSound;
component.StorageCloseSound = state.StorageCloseSound;
component.DefaultStorageOrientation = state.DefaultStorageOrientation;
_oldStoredItems.Clear();

View File

@@ -21,7 +21,7 @@
Name="RefundButton"
MinWidth="64"
HorizontalAlignment="Right"
Text="Refund" />
Text="{Loc 'store-ui-refund-text'}" />
</BoxContainer>
<LineEdit Name="SearchBar" Margin="4" PlaceHolder="Search" HorizontalExpand="True"/>
<PanelContainer VerticalExpand="True">

View File

@@ -1,7 +1,8 @@
using Content.Client.Trigger.Systems;
using Robust.Client.Animations;
using Robust.Shared.Audio;
namespace Content.Client.Trigger;
namespace Content.Client.Trigger.Components;
[RegisterComponent]
[Access(typeof(TimerTriggerVisualizerSystem))]
@@ -16,28 +17,27 @@ public sealed partial class TimerTriggerVisualsComponent : Component
/// <summary>
/// The RSI state used while the device has not been primed.
/// </summary>
[DataField("unprimedSprite")]
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public string UnprimedSprite = "icon";
/// <summary>
/// The RSI state used when the device is primed.
/// Not VVWrite-able because it's only used at component init to construct the priming animation.
/// </summary>
[DataField("primingSprite")]
[DataField]
public string PrimingSprite = "primed";
/// <summary>
/// The sound played when the device is primed.
/// Not VVWrite-able because it's only used at component init to construct the priming animation.
/// </summary>
[DataField("primingSound")]
[DataField, ViewVariables]
public SoundSpecifier? PrimingSound;
/// <summary>
/// The actual priming animation.
/// Constructed at component init from the sprite and sound.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[ViewVariables]
public Animation PrimingAnimation = default!;
}

View File

@@ -1,11 +1,12 @@
using Content.Shared.Trigger;
using Content.Shared.Trigger.Components.Triggers;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
namespace Content.Client.Explosion;
namespace Content.Client.Trigger.Systems;
public sealed partial class TriggerSystem
public sealed class ProximityTriggerAnimationSystem : EntitySystem
{
[Dependency] private readonly AnimationPlayerSystem _player = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -18,7 +19,7 @@ public sealed partial class TriggerSystem
private const string AnimKey = "proximity";
private static readonly Animation _flasherAnimation = new Animation
private static readonly Animation FlasherAnimation = new Animation
{
Length = TimeSpan.FromSeconds(0.6f),
AnimationTracks = {
@@ -42,8 +43,10 @@ public sealed partial class TriggerSystem
}
};
private void InitializeProximity()
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TriggerOnProximityComponent, ComponentInit>(OnProximityInit);
SubscribeLocalEvent<TriggerOnProximityComponent, AppearanceChangeEvent>(OnProxAppChange);
SubscribeLocalEvent<TriggerOnProximityComponent, AnimationCompletedEvent>(OnProxAnimation);
@@ -94,7 +97,7 @@ public sealed partial class TriggerSystem
break;
case ProximityTriggerVisuals.Active:
if (_player.HasRunningAnimation(uid, player, AnimKey)) return;
_player.Play((uid, player), _flasherAnimation, AnimKey);
_player.Play((uid, player), FlasherAnimation, AnimKey);
break;
case ProximityTriggerVisuals.Off:
default:

View File

@@ -0,0 +1,5 @@
using Content.Shared.Trigger.Systems;
namespace Content.Client.Trigger.Systems;
public sealed class ReleaseGasOnTriggerSystem : SharedReleaseGasOnTriggerSystem;

View File

@@ -1,11 +1,10 @@
using Content.Client.Trigger.Components;
using Content.Shared.Trigger;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.GameObjects;
namespace Content.Client.Trigger;
namespace Content.Client.Trigger.Systems;
public sealed class TimerTriggerVisualizerSystem : VisualizerSystem<TimerTriggerVisualsComponent>
{
@@ -17,25 +16,26 @@ public sealed class TimerTriggerVisualizerSystem : VisualizerSystem<TimerTrigger
SubscribeLocalEvent<TimerTriggerVisualsComponent, ComponentInit>(OnComponentInit);
}
private void OnComponentInit(EntityUid uid, TimerTriggerVisualsComponent comp, ComponentInit args)
private void OnComponentInit(Entity<TimerTriggerVisualsComponent> ent, ref ComponentInit args)
{
comp.PrimingAnimation = new Animation
ent.Comp.PrimingAnimation = new Animation
{
Length = TimeSpan.MaxValue,
AnimationTracks = {
new AnimationTrackSpriteFlick() {
new AnimationTrackSpriteFlick()
{
LayerKey = TriggerVisualLayers.Base,
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.PrimingSprite, 0f) }
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(ent.Comp.PrimingSprite, 0f) }
}
},
};
if (comp.PrimingSound != null)
if (ent.Comp.PrimingSound != null)
{
comp.PrimingAnimation.AnimationTracks.Add(
ent.Comp.PrimingAnimation.AnimationTracks.Add(
new AnimationTrackPlaySound()
{
KeyFrames = { new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(comp.PrimingSound), 0) }
KeyFrames = { new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(ent.Comp.PrimingSound), 0) }
}
);
}

View File

@@ -1,9 +1,11 @@
using System.Numerics;
using Content.Client.Cooldown;
using Content.Client.UserInterface.Systems.Inventory.Controls;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Prototypes;
namespace Content.Client.UserInterface.Controls
{
@@ -20,6 +22,7 @@ namespace Content.Client.UserInterface.Controls
public CooldownGraphic CooldownDisplay { get; }
private SpriteView SpriteView { get; }
private EntityPrototypeView ProtoView { get; }
public EntityUid? Entity => SpriteView.Entity;
@@ -141,6 +144,13 @@ namespace Content.Client.UserInterface.Controls
SetSize = new Vector2(DefaultButtonSize, DefaultButtonSize),
OverrideDirection = Direction.South
});
AddChild(ProtoView = new EntityPrototypeView
{
Visible = false,
Scale = new Vector2(2, 2),
SetSize = new Vector2(DefaultButtonSize, DefaultButtonSize),
OverrideDirection = Direction.South
});
AddChild(HoverSpriteView = new SpriteView
{
@@ -209,12 +219,35 @@ namespace Content.Client.UserInterface.Controls
HoverSpriteView.SetEntity(null);
}
/// <summary>
/// Causes the control to display a placeholder prototype, optionally faded
/// </summary>
public void SetEntity(EntityUid? ent)
{
SpriteView.SetEntity(ent);
SpriteView.Visible = true;
ProtoView.Visible = false;
UpdateButtonTexture();
}
/// <summary>
/// Causes the control to display a placeholder prototype, optionally faded
/// </summary>
public void SetPrototype(EntProtoId? proto, bool fade)
{
ProtoView.SetPrototype(proto);
SpriteView.Visible = false;
ProtoView.Visible = true;
UpdateButtonTexture();
if (ProtoView.Entity is not { } ent || !fade)
return;
var sprites = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SpriteSystem>();
sprites.SetColor((ent.Owner, ent.Comp1), Color.DarkGray.WithAlpha(0.65f));
}
private void UpdateButtonTexture()
{
var fullTexture = Theme.ResolveTextureOrNull(_fullButtonTexturePath);

View File

@@ -5,7 +5,7 @@
<ScrollContainer HorizontalExpand="True"
VerticalExpand="True"
SizeFlagsStretchRatio="6">
<GridContainer Name="Values"/>
<GridContainer Name="Values" HSeparationOverride="0" VSeparationOverride="15"/>
</ScrollContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -4,7 +4,6 @@ using Content.Client.Actions.UI;
using Content.Client.Cooldown;
using Content.Client.Stylesheets;
using Content.Shared.Actions.Components;
using Content.Shared.Charges.Components;
using Content.Shared.Charges.Systems;
using Content.Shared.Examine;
using Robust.Client.GameObjects;
@@ -27,7 +26,6 @@ public sealed class ActionButton : Control, IEntityControl
private IPlayerManager _player;
private SpriteSystem? _spriteSys;
private ActionUIController? _controller;
private SharedChargesSystem _sharedChargesSys;
private bool _beingHovered;
private bool _depressed;
private bool _toggled;
@@ -71,7 +69,6 @@ public sealed class ActionButton : Control, IEntityControl
_entities = entities;
_player = IoCManager.Resolve<IPlayerManager>();
_spriteSys = spriteSys;
_sharedChargesSys = _entities.System<SharedChargesSystem>();
_controller = controller;
MouseFilter = MouseFilterMode.Pass;

View File

@@ -21,17 +21,20 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.EmotesButton;
private SimpleRadialMenu? _menu;
private static readonly Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)> EmoteGroupingInfo
= new Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)>
{
[EmoteCategory.General] = ("emote-menu-category-general", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"))),
[EmoteCategory.Hands] = ("emote-menu-category-hands", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"))),
[EmoteCategory.Vocal] = ("emote-menu-category-vocal", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Emotes/vocal.png"))),
};
private static readonly Dictionary<EmoteCategory, (string Tooltip, SpriteSpecifier Sprite)> EmoteGroupingInfo =
new()
{
[EmoteCategory.General] = ("emote-menu-category-general",
new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Head/Soft/mimesoft.rsi"), "icon")),
[EmoteCategory.Hands] = ("emote-menu-category-hands",
new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Hands/Gloves/latex.rsi"), "icon")),
[EmoteCategory.Vocal] = ("emote-menu-category-vocal",
new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Emotes/vocal.png"))),
};
public void OnStateEntered(GameplayState state)
{
@@ -135,7 +138,7 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
var whitelistSystem = EntitySystemManager.GetEntitySystem<EntityWhitelistSystem>();
var player = _playerManager.LocalSession?.AttachedEntity;
Dictionary<EmoteCategory, List<RadialMenuOption>> emotesByCategory = new();
Dictionary<EmoteCategory, List<RadialMenuOption>> emotesByCategory = new();
foreach (var emote in emotePrototypes)
{
if(emote.Category == EmoteCategory.Invalid)

View File

@@ -12,6 +12,7 @@ using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Shared.Input;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -73,7 +74,8 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
{
if (entity.Owner != _player.LocalEntity)
return;
AddHand(name, location);
if (_handsSystem.TryGetHand((entity.Owner, entity.Comp), name, out var hand))
AddHand(name, hand.Value);
}
private void OnRemoveHand(Entity<HandsComponent> entity, string name)
@@ -139,7 +141,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
_playerHandsComponent = handsComp;
foreach (var (name, hand) in handsComp.Comp.Hands)
{
var handButton = AddHand(name, hand.Location);
var handButton = AddHand(name, hand);
if (_handsSystem.TryGetHeldItem(handsComp.AsNullable(), name, out var held) &&
_entities.TryGetComponent(held, out VirtualItemComponent? virt))
@@ -147,11 +149,25 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
handButton.SetEntity(virt.BlockingEntity);
handButton.Blocked = true;
}
else
else if (held != null)
{
handButton.SetEntity(held);
handButton.Blocked = false;
}
else
{
if (hand.EmptyRepresentative is { } representative)
{
// placeholder, view it
SetRepresentative(handButton, representative);
}
else
{
// otherwise empty
handButton.SetEntity(null);
}
handButton.Blocked = false;
}
}
if (handsComp.Comp.ActiveHandId == null)
@@ -159,6 +175,11 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
SetActiveHand(handsComp.Comp.ActiveHandId);
}
private void SetRepresentative(HandButton handButton, EntProtoId prototype)
{
handButton.SetPrototype(prototype, true);
}
private void HandBlocked(string handName)
{
if (!_handLookup.TryGetValue(handName, out var hand))
@@ -203,7 +224,12 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
hand.Blocked = false;
}
UpdateHandStatus(hand, entity);
if (_playerHandsComponent != null &&
_player.LocalSession?.AttachedEntity is { } playerEntity &&
_handsSystem.TryGetHand((playerEntity, _playerHandsComponent), name, out var handData))
{
UpdateHandStatus(hand, entity, handData);
}
}
private void OnItemRemoved(string name, EntityUid entity)
@@ -212,8 +238,19 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
if (hand == null)
return;
if (_playerHandsComponent != null &&
_player.LocalSession?.AttachedEntity is { } playerEntity &&
_handsSystem.TryGetHand((playerEntity, _playerHandsComponent), name, out var handData))
{
UpdateHandStatus(hand, null, handData);
if (handData?.EmptyRepresentative is { } representative)
{
SetRepresentative(hand, representative);
return;
}
}
hand.SetEntity(null);
UpdateHandStatus(hand, null);
}
private HandsContainer GetFirstAvailableContainer()
@@ -276,13 +313,13 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
if (foldedLocation == HandUILocation.Left)
{
_statusHandLeft = handControl;
HandsGui.UpdatePanelEntityLeft(heldEnt);
HandsGui.UpdatePanelEntityLeft(heldEnt, hand.Value);
}
else
{
// Middle or right
_statusHandRight = handControl;
HandsGui.UpdatePanelEntityRight(heldEnt);
HandsGui.UpdatePanelEntityRight(heldEnt, hand.Value);
}
HandsGui.SetHighlightHand(foldedLocation);
@@ -295,9 +332,9 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
return handControl;
}
private HandButton AddHand(string handName, HandLocation location)
private HandButton AddHand(string handName, Hand hand)
{
var button = new HandButton(handName, location);
var button = new HandButton(handName, hand.Location);
button.StoragePressed += StorageActivate;
button.Pressed += HandPressed;
@@ -313,10 +350,16 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
GetFirstAvailableContainer().AddButton(button);
}
if (hand.EmptyRepresentative is { } representative)
{
SetRepresentative(button, representative);
}
UpdateHandStatus(button, null, hand);
// If we don't have a status for this hand type yet, set it.
// This means we have status filled by default in most scenarios,
// otherwise the user'd need to switch hands to "activate" the hands the first time.
if (location.GetUILocation() == HandUILocation.Left)
if (hand.Location.GetUILocation() == HandUILocation.Left)
_statusHandLeft ??= button;
else
_statusHandRight ??= button;
@@ -480,12 +523,12 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
}
}
private void UpdateHandStatus(HandButton hand, EntityUid? entity)
private void UpdateHandStatus(HandButton hand, EntityUid? entity, Hand? handData)
{
if (hand == _statusHandLeft)
HandsGui?.UpdatePanelEntityLeft(entity);
HandsGui?.UpdatePanelEntityLeft(entity, handData);
if (hand == _statusHandRight)
HandsGui?.UpdatePanelEntityRight(entity);
HandsGui?.UpdatePanelEntityRight(entity, handData);
}
}

View File

@@ -19,14 +19,14 @@ public sealed partial class HotbarGui : UIWidget
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
}
public void UpdatePanelEntityLeft(EntityUid? entity)
public void UpdatePanelEntityLeft(EntityUid? entity, Hand? hand)
{
StatusPanelLeft.Update(entity);
StatusPanelLeft.Update(entity, hand);
}
public void UpdatePanelEntityRight(EntityUid? entity)
public void UpdatePanelEntityRight(EntityUid? entity, Hand? hand)
{
StatusPanelRight.Update(entity);
StatusPanelRight.Update(entity, hand);
}
public void SetHighlightHand(HandUILocation? hand)

View File

@@ -17,6 +17,7 @@ public sealed partial class ItemStatusPanel : Control
[Dependency] private readonly IEntityManager _entityManager = default!;
[ViewVariables] private EntityUid? _entity;
[ViewVariables] private Hand? _hand;
// Tracked so we can re-run SetSide() if the theme changes.
private HandUILocation _side;
@@ -101,29 +102,45 @@ public sealed partial class ItemStatusPanel : Control
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdateItemName();
UpdateItemName(_hand);
}
public void Update(EntityUid? entity)
public void Update(EntityUid? entity, Hand? hand)
{
ItemNameLabel.Visible = entity != null;
NoItemLabel.Visible = entity == null;
if (entity == _entity && hand == _hand)
return;
_hand = hand;
if (entity == null)
{
ItemNameLabel.Text = "";
ClearOldStatus();
_entity = null;
if (hand?.EmptyLabel is { } label)
{
ItemNameLabel.Visible = true;
NoItemLabel.Visible = false;
ItemNameLabel.Text = Loc.GetString(label);
}
else
{
ItemNameLabel.Visible = false;
NoItemLabel.Visible = true;
ItemNameLabel.Text = "";
}
return;
}
if (entity != _entity)
{
_entity = entity.Value;
BuildNewEntityStatus();
ItemNameLabel.Visible = true;
NoItemLabel.Visible = false;
UpdateItemName();
}
_entity = entity.Value;
BuildNewEntityStatus();
UpdateItemName(hand);
}
public void UpdateHighlight(bool highlight)
@@ -131,14 +148,14 @@ public sealed partial class ItemStatusPanel : Control
HighlightPanel.Visible = highlight;
}
private void UpdateItemName()
private void UpdateItemName(Hand? hand)
{
if (_entity == null)
return;
if (!_entityManager.TryGetComponent<MetaDataComponent>(_entity, out var meta) || meta.Deleted)
{
Update(null);
Update(null, hand);
return;
}

View File

@@ -194,8 +194,6 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
}
}
return;
int GetIndex(Vector2i position)
{
return position.Y * maxWidth + position.X;

View File

@@ -185,12 +185,7 @@ public sealed class ItemGridPiece : Control, IEntityControl
handle.SetTransform(pos, iconRotation);
var box = new UIBox2(root, root + sprite.Size * scale);
var ev = new BeforeRenderInGridEvent(new Color(255, 255, 255));
_entityManager.EventBus.RaiseLocalEvent(Entity, ev);
handle.DrawTextureRect(sprite, box, ev.Color);
handle.DrawTextureRect(sprite, box);
handle.SetTransform(GlobalPixelPosition, Angle.Zero);
}
else
@@ -303,19 +298,6 @@ public sealed class ItemGridPiece : Control, IEntityControl
public EntityUid? UiEntity => Entity;
}
/// <summary>
/// This event gets raised before a sprite gets drawn in a grid and lets to change the sprite color for several gameobjects that have special sprites to render in containers.
/// </summary>
public sealed class BeforeRenderInGridEvent : EntityEventArgs
{
public Color Color { get; set; }
public BeforeRenderInGridEvent(Color color)
{
Color = color;
}
}
public enum ItemGridPieceMarks
{
First,

View File

@@ -10,7 +10,6 @@ using Content.Shared._CP14.Cooking.Components;
using Content.Shared.DisplacementMap;
using Content.Shared.Rounding;
using Robust.Client.GameObjects;
using Robust.Shared.Random;
namespace Content.Client._CP14.Cooking;

View File

@@ -5,4 +5,4 @@
// https://github.com/dotnet/runtime/issues/107197
// So we can't really parallelize integration tests harder either until the runtime fixes that,
// *or* we fix serv3 to not spam expression trees.
[assembly: LevelOfParallelism(3)]
[assembly: LevelOfParallelism(2)]

View File

@@ -0,0 +1,29 @@
using Content.Shared.Damage.Components;
namespace Content.IntegrationTests.Tests.Damageable;
public sealed class StaminaComponentTest
{
[Test]
public async Task ValidatePrototypes()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var protos = pair.GetPrototypesWithComponent<StaminaComponent>();
await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
foreach (var (proto, comp) in protos)
{
Assert.That(comp.AnimationThreshold, Is.LessThan(comp.CritThreshold),
$"Animation threshold on {proto.ID} must be less than its crit threshold.");
}
});
});
await pair.CleanReturnAsync();
}
}

View File

@@ -1,11 +1,9 @@
#nullable enable
using Content.Server.Cuffs;
using Content.Shared.Body.Components;
using Content.Shared.Cuffs.Components;
using Content.Shared.Hands.Components;
using Robust.Server.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
{
@@ -22,9 +20,15 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
components:
- type: Cuffable
- type: Hands
hands:
hand_right:
location: Right
hand_left:
location: Left
sortedHands:
- hand_right
- hand_left
- type: ComplexInteraction
- type: Body
prototype: Human
- type: entity
name: HandcuffsDummy
@@ -47,7 +51,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
HandsComponent hands = default!;
var entityManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var host = server.ResolveDependency<IServerConsoleHost>();
var map = await pair.CreateTestMap();
@@ -73,7 +76,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
{
Assert.That(entityManager.TryGetComponent(human, out cuffed!), $"Human has no {nameof(CuffableComponent)}");
Assert.That(entityManager.TryGetComponent(human, out hands!), $"Human has no {nameof(HandsComponent)}");
Assert.That(entityManager.TryGetComponent(human, out BodyComponent? _), $"Human has no {nameof(BodyComponent)}");
Assert.That(entityManager.TryGetComponent(cuffs, out HandcuffComponent? _), $"Handcuff has no {nameof(HandcuffComponent)}");
Assert.That(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}");
});

View File

@@ -9,7 +9,6 @@ using Content.Server.Mind;
using Content.Server.Roles;
using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components;
using Content.Server.Station.Components;
using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
@@ -20,6 +19,7 @@ using Content.Shared.NPC.Prototypes;
using Content.Shared.NPC.Systems;
using Content.Shared.NukeOps;
using Content.Shared.Pinpointer;
using Content.Shared.Roles.Components;
using Content.Shared.Station.Components;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;

View File

@@ -3,7 +3,6 @@ using System.Linq;
using Content.Server.Ghost.Roles;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Mind;
using Content.Server.Roles;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
@@ -11,7 +10,7 @@ using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Players;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using Content.Shared.Roles.Components;
using Robust.Server.Console;
using Robust.Server.GameObjects;
using Robust.Server.Player;

View File

@@ -1,7 +1,5 @@
using System.Linq;
using Content.Server.Roles;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using Content.Shared.Roles.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Reflection;

View File

@@ -1,6 +1,6 @@
using Content.IntegrationTests.Tests.Interaction;
using Content.Server.Explosion.Components;
using Content.Shared.Explosion.Components;
using Content.Shared.Trigger.Components;
using Content.Shared.Trigger.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
@@ -25,19 +25,19 @@ public sealed class ModularGrenadeTests : InteractionTest
await InteractUsing(Cable);
// Insert & remove trigger
AssertComp<OnUseTimerTriggerComponent>(false);
AssertComp<TimerTriggerComponent>(false);
await InteractUsing(Trigger);
AssertComp<OnUseTimerTriggerComponent>();
AssertComp<TimerTriggerComponent>();
await FindEntity(Trigger, LookupFlags.Uncontained, shouldSucceed: false);
await InteractUsing(Pry);
AssertComp<OnUseTimerTriggerComponent>(false);
AssertComp<TimerTriggerComponent>(false);
// Trigger was dropped to floor, not deleted.
await FindEntity(Trigger, LookupFlags.Uncontained);
// Re-insert
await InteractUsing(Trigger);
AssertComp<OnUseTimerTriggerComponent>();
AssertComp<TimerTriggerComponent>();
// Insert & remove payload.
await InteractUsing(Payload);
@@ -56,13 +56,14 @@ public sealed class ModularGrenadeTests : InteractionTest
await Pickup();
AssertComp<ActiveTimerTriggerComponent>(false);
await UseInHand();
AssertComp<ActiveTimerTriggerComponent>(true);
// So uhhh grenades in hands don't destroy themselves when exploding. Maybe that will be fixed eventually.
await Drop();
// Wait until grenade explodes
var timer = Comp<ActiveTimerTriggerComponent>();
while (timer.TimeRemaining >= 0)
var triggerSys = SEntMan.System<TriggerSystem>();
while (Target != null && triggerSys.GetRemainingTime(SEntMan.GetEntity(Target.Value))?.TotalSeconds >= 0.0)
{
await RunTicks(10);
}

View File

@@ -2,9 +2,9 @@ using System.Linq;
using Content.Server.GameTicking;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Systems;
using Content.Server.Station.Components;
using Content.Shared.CCVar;
using Content.Shared.Shuttles.Components;
using Content.Shared.Station.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components;

View File

@@ -128,7 +128,7 @@ public sealed class StoreTests
var buyMsg = new StoreBuyListingMessage(discountedListingItem.ID){Actor = human};
server.EntMan.EventBus.RaiseComponentEvent(pda, storeComponent, buyMsg);
server.EntMan.EventBus.RaiseLocalEvent(pda, buyMsg);
var newBalance = storeComponent.Balance[UplinkSystem.TelecrystalCurrencyPrototype];
Assert.That(newBalance.Value, Is.EqualTo((originalBalance - plainDiscountedCost).Value), "Expected to have balance reduced by discounted cost");
@@ -141,7 +141,7 @@ public sealed class StoreTests
Assert.That(costAfterBuy.Value, Is.EqualTo(prototypeCost.Value), "Expected cost after discount refund to be equal to prototype cost.");
var refundMsg = new StoreRequestRefundMessage { Actor = human };
server.EntMan.EventBus.RaiseComponentEvent(pda, storeComponent, refundMsg);
server.EntMan.EventBus.RaiseLocalEvent(pda, refundMsg);
// get refreshed item after refund re-generated items
discountedListingItem = storeComponent.FullListingsCatalog.First(x => x.ID == itemId);

View File

@@ -13,6 +13,7 @@ using Content.Server.Clothing.Systems;
using Content.Server.Implants;
using Content.Shared.Implants;
using Content.Shared.Inventory;
using Content.Shared.Lock;
using Content.Shared.PDA;
namespace Content.Server.Access.Systems
@@ -25,6 +26,7 @@ namespace Content.Server.Access.Systems
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ChameleonClothingSystem _chameleon = default!;
[Dependency] private readonly ChameleonControllerSystem _chamController = default!;
[Dependency] private readonly LockSystem _lock = default!;
public override void Initialize()
{
@@ -79,7 +81,8 @@ namespace Content.Server.Access.Systems
private void OnAfterInteract(EntityUid uid, AgentIDCardComponent component, AfterInteractEvent args)
{
if (args.Target == null || !args.CanReach || !TryComp<AccessComponent>(args.Target, out var targetAccess) || !HasComp<IdCardComponent>(args.Target))
if (args.Target == null || !args.CanReach || _lock.IsLocked(uid) ||
!TryComp<AccessComponent>(args.Target, out var targetAccess) || !HasComp<IdCardComponent>(args.Target))
return;
if (!TryComp<AccessComponent>(uid, out var access) || !HasComp<IdCardComponent>(uid))

View File

@@ -44,7 +44,7 @@ public sealed class VariantizeCommand : IConsoleCommand
foreach (var tile in mapsSystem.GetAllTiles(euid.Value, gridComp))
{
var def = turfSystem.GetContentTileDefinition(tile);
var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, tileSystem.PickVariant(def));
var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, tileSystem.PickVariant(def), tile.Tile.RotationMirroring);
mapsSystem.SetTile(euid.Value, gridComp, tile.GridIndices, newTile);
}
}

View File

@@ -1,6 +1,5 @@
using System.Linq;
using System.Numerics;
using Content.Server.Warps;
using Content.Shared.Administration;
using Content.Shared.Follower;
using Content.Shared.Ghost;

View File

@@ -1,10 +0,0 @@
using Content.Shared.Administration.Components;
using Robust.Shared.GameStates;
namespace Content.Server.Administration.Components;
[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{
}

View File

@@ -1,7 +1,6 @@
using System.Linq;
using Content.Server.Administration.Managers;
using Content.Server.Chat.Managers;
using Content.Server.Forensics;
using Content.Server.GameTicking;
using Content.Server.Hands.Systems;
using Content.Server.Mind;
@@ -21,6 +20,7 @@ using Content.Shared.PDA;
using Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Popups;
using Content.Shared.Roles;
using Content.Shared.Roles.Components;
using Content.Shared.Roles.Jobs;
using Content.Shared.StationRecords;
using Content.Shared.Throwing;

View File

@@ -29,15 +29,16 @@ public sealed partial class AdminVerbSystem
private static readonly EntProtoId DefaultNukeOpRule = "LoneOpsSpawn";
private static readonly EntProtoId DefaultRevsRule = "Revolutionary";
private static readonly EntProtoId DefaultThiefRule = "Thief";
private static readonly EntProtoId DefaultChangelingRule = "Changeling";
private static readonly EntProtoId ParadoxCloneRuleId = "ParadoxCloneSpawn";
private static readonly ProtoId<StartingGearPrototype> PirateGearId = "PirateGear";
//CP14
private static readonly EntProtoId CP14VampireUnnameable = "CP14GameRuleVampireClanUnnameable";
private static readonly EntProtoId CP14VampireDevourers = "CP14GameRuleVampireClanDevourers";
private static readonly EntProtoId CP14VampireNightChildrens = "CP14GameRuleVampireClanNightChildrens";
//CP14 end
private static readonly EntProtoId ParadoxCloneRuleId = "ParadoxCloneSpawn";
// All antag verbs have names so invokeverb works.
private void AddAntagVerbs(GetVerbsEvent<Verb> args)
{
@@ -108,7 +109,7 @@ public sealed partial class AdminVerbSystem
_antag.ForceMakeAntag<TraitorRuleComponent>(targetPlayer, DefaultTraitorRule);
},
Impact = LogImpact.High,
Message = string.Join(": ", traitorName, Loc.GetString("admin-verb-make-traitor")),
Message = string.Join(": ", traitorName, Loc.GetString("admin-verb-make-traitor")),
};
args.Verbs.Add(traitor);
@@ -203,6 +204,21 @@ public sealed partial class AdminVerbSystem
};
args.Verbs.Add(thief);
var changelingName = Loc.GetString("admin-verb-text-make-changeling");
Verb changeling = new()
{
Text = changelingName,
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Objects/Weapons/Melee/armblade.rsi"), "icon"),
Act = () =>
{
_antag.ForceMakeAntag<ChangelingRuleComponent>(targetPlayer, DefaultChangelingRule);
},
Impact = LogImpact.High,
Message = string.Join(": ", changelingName, Loc.GetString("admin-verb-make-changeling")),
};
args.Verbs.Add(changeling);
var paradoxCloneName = Loc.GetString("admin-verb-text-make-paradox-clone");
Verb paradox = new()
{

View File

@@ -2,15 +2,12 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Server.Administration.Components;
using Content.Server.Atmos;
using Content.Server.Atmos.Components;
using Content.Server.Cargo.Components;
using Content.Server.Doors.Systems;
using Content.Server.Hands.Systems;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Stack;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Server.Weapons.Ranged.Systems;
using Content.Shared.Access;
@@ -28,6 +25,7 @@ using Content.Shared.Hands.Components;
using Content.Shared.Inventory;
using Content.Shared.PDA;
using Content.Shared.Stacks;
using Content.Shared.Station.Components;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Components;
using Robust.Server.Physics;

View File

@@ -1,33 +0,0 @@
using Content.Server.AlertLevel.Systems;
namespace Content.Server.AlertLevel;
/// <summary>
/// This component is for changing the alert level of the station when triggered.
/// </summary>
[RegisterComponent, Access(typeof(AlertLevelChangeOnTriggerSystem))]
public sealed partial class AlertLevelChangeOnTriggerComponent : Component
{
///<summary>
///The alert level to change to when triggered.
///</summary>
[DataField]
public string Level = "blue";
/// <summary>
///Whether to play the sound when the alert level changes.
/// </summary>
[DataField]
public bool PlaySound = true;
/// <summary>
///Whether to say the announcement when the alert level changes.
/// </summary>
[DataField]
public bool Announce = true;
/// <summary>
///Force the alert change. This applies if the alert level is not selectable or not.
/// </summary>
[DataField]
public bool Force = false;
}

View File

@@ -5,13 +5,13 @@ using Content.Server.Animals.Components;
using Content.Server.Mind;
using Content.Server.Popups;
using Content.Server.Radio;
using Content.Server.Speech;
using Content.Server.Speech.Components;
using Content.Server.Vocalization.Systems;
using Content.Shared.Animals.Components;
using Content.Shared.Animals.Systems;
using Content.Shared.Database;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Speech;
using Content.Shared.Speech.Components;
using Content.Shared.Whitelist;
using Robust.Shared.Network;
using Robust.Shared.Random;
@@ -25,7 +25,7 @@ namespace Content.Server.Animals.Systems;
/// (radiovocalizer) and stores them in a list. When an entity with a VocalizerComponent attempts to vocalize, this will
/// try to set the message from memory.
/// </summary>
public sealed partial class ParrotMemorySystem : EntitySystem
public sealed partial class ParrotMemorySystem : SharedParrotMemorySystem
{
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
@@ -42,8 +42,6 @@ public sealed partial class ParrotMemorySystem : EntitySystem
SubscribeLocalEvent<EraseEvent>(OnErase);
SubscribeLocalEvent<ParrotMemoryComponent, GetVerbsEvent<Verb>>(OnGetVerbs);
SubscribeLocalEvent<ParrotListenerComponent, MapInitEvent>(ListenerOnMapInit);
SubscribeLocalEvent<ParrotListenerComponent, ListenEvent>(OnListen);
@@ -57,30 +55,6 @@ public sealed partial class ParrotMemorySystem : EntitySystem
DeletePlayerMessages(args.PlayerNetUserId);
}
private void OnGetVerbs(Entity<ParrotMemoryComponent> entity, ref GetVerbsEvent<Verb> args)
{
var user = args.User;
// limit this to admins
if (!_admin.IsAdmin(user))
return;
// simple verb that just clears the memory list
var clearMemoryVerb = new Verb()
{
Text = Loc.GetString("parrot-verb-clear-memory"),
Category = VerbCategory.Admin,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/clear-parrot.png")),
Act = () =>
{
entity.Comp.SpeechMemories.Clear();
_popup.PopupEntity(Loc.GetString("parrot-popup-memory-cleared"), entity, user, PopupType.Medium);
},
};
args.Verbs.Add(clearMemoryVerb);
}
private void ListenerOnMapInit(Entity<ParrotListenerComponent> entity, ref MapInitEvent args)
{
// If an entity has a ParrotListenerComponent it really ought to have an ActiveListenerComponent

View File

@@ -1,6 +1,5 @@
using Content.Server.Anomaly.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Station.Components;
using Content.Shared.Anomaly;
using Content.Shared.CCVar;
using Content.Shared.Materials;
@@ -164,8 +163,7 @@ public sealed partial class AnomalySystem
var xform = Transform(uid);
if (_station.GetStationInMap(xform.MapID) is not { } station ||
!TryComp<StationDataComponent>(station, out var data) ||
_station.GetLargestGrid(data) is not { } grid)
_station.GetLargestGrid(station) is not { } grid)
{
if (xform.GridUid == null)
return;

View File

@@ -1,22 +1,19 @@
using Content.Shared.Atmos.EntitySystems;
using Robust.Shared.GameStates;
using Content.Server.Atmos.Piping.EntitySystems;
using JetBrains.Annotations;
namespace Content.Shared.Atmos.Components;
namespace Content.Server.Atmos.Piping.Components;
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState]
[RegisterComponent]
public sealed partial class AtmosPipeColorComponent : Component
{
[DataField]
[AutoNetworkedField]
public Color Color { get; set; } = Color.White;
[ViewVariables(VVAccess.ReadWrite), UsedImplicitly]
public Color ColorVV
{
get => Color;
set => IoCManager.Resolve<IEntityManager>().System<AtmosPipeColorSystem>().SetColor((Owner, this), value);
set => IoCManager.Resolve<IEntityManager>().System<AtmosPipeColorSystem>().SetColor(Owner, this, value);
}
}

View File

@@ -0,0 +1,48 @@
using Content.Server.Atmos.Piping.Components;
using Content.Shared.Atmos.Piping;
using Robust.Server.GameObjects;
namespace Content.Server.Atmos.Piping.EntitySystems
{
public sealed class AtmosPipeColorSystem : EntitySystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AtmosPipeColorComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<AtmosPipeColorComponent, ComponentShutdown>(OnShutdown);
}
private void OnStartup(EntityUid uid, AtmosPipeColorComponent component, ComponentStartup args)
{
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
_appearance.SetData(uid, PipeColorVisuals.Color, component.Color, appearance);
}
private void OnShutdown(EntityUid uid, AtmosPipeColorComponent component, ComponentShutdown args)
{
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
_appearance.SetData(uid, PipeColorVisuals.Color, Color.White, appearance);
}
public void SetColor(EntityUid uid, AtmosPipeColorComponent component, Color color)
{
component.Color = color;
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
_appearance.SetData(uid, PipeColorVisuals.Color, color, appearance);
var ev = new AtmosPipeColorChangedEvent(color);
RaiseLocalEvent(uid, ref ev);
}
}
}

View File

@@ -1,47 +1,46 @@
using Content.Server.Body.Components;
using Content.Server.Ghost.Components;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Pointing;
namespace Content.Server.Body.Systems
namespace Content.Server.Body.Systems;
public sealed class BrainSystem : EntitySystem
{
public sealed class BrainSystem : EntitySystem
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
public override void Initialize()
{
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
base.Initialize();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
}
SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
}
private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
{
if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
return;
private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
{
if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
return;
EnsureComp<MindContainerComponent>(newEntity);
EnsureComp<MindContainerComponent>(oldEntity);
EnsureComp<MindContainerComponent>(newEntity);
EnsureComp<MindContainerComponent>(oldEntity);
var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity);
ghostOnMove.MustBeDead = HasComp<MobStateComponent>(newEntity); // Don't ghost living players out of their bodies.
var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity);
if (HasComp<BodyComponent>(newEntity))
ghostOnMove.MustBeDead = true;
if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind))
return;
if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind))
return;
_mindSystem.TransferTo(mindId, newEntity, mind: mind);
}
_mindSystem.TransferTo(mindId, newEntity, mind: mind);
}
private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
{
args.Cancel();
}
private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
{
args.Cancel();
}
}

View File

@@ -1,8 +1,8 @@
using System.Linq;
using Content.Server.Station.Components;
using Content.Shared.Cargo;
using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Prototypes;
using Content.Shared.Station.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Cargo.Components;

View File

@@ -1,7 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Cargo.Components;
using Content.Server.Station.Components;
using Content.Shared.Cargo;
using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Components;
@@ -13,8 +12,8 @@ using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Labels.Components;
using Content.Shared.Paper;
using Content.Shared.Station.Components;
using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;

View File

@@ -3,11 +3,11 @@ using System.Linq;
using Content.Server.Cargo.Components;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Station.Components;
using Content.Shared.Cargo;
using Content.Shared.Cargo.Components;
using Content.Shared.DeviceLinking;
using Content.Shared.Power;
using Content.Shared.Station.Components;
using Robust.Shared.Audio;
using Robust.Shared.Random;
using Robust.Shared.Utility;

View File

@@ -92,19 +92,22 @@ public sealed class PricingSystem : EntitySystem
if (args.Handled)
return;
if (!TryComp<BodyComponent>(uid, out var body) || !TryComp<MobStateComponent>(uid, out var state))
if (!TryComp<MobStateComponent>(uid, out var state))
{
Log.Error($"Tried to get the mob price of {ToPrettyString(uid)}, which has no {nameof(BodyComponent)} and no {nameof(MobStateComponent)}.");
Log.Error($"Tried to get the mob price of {ToPrettyString(uid)}, which has no {nameof(MobStateComponent)}.");
return;
}
// TODO: Better handling of missing.
var partList = _bodySystem.GetBodyChildren(uid, body).ToList();
var totalPartsPresent = partList.Sum(_ => 1);
var totalParts = partList.Count;
var partPenalty = 0.0;
if (TryComp<BodyComponent>(uid, out var body))
{
var partList = _bodySystem.GetBodyChildren(uid, body).ToList();
var totalPartsPresent = partList.Sum(_ => 1);
var totalParts = partList.Count;
var partRatio = totalPartsPresent / (double) totalParts;
var partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty;
var partRatio = totalPartsPresent / (double) totalParts;
partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty;
}
args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty);
}

View File

@@ -1,17 +0,0 @@
using Content.Shared.Dataset;
using Robust.Shared.Prototypes;
namespace Content.Server.Chat;
/// <summary>
/// Makes the entity speak when triggered. If the item has UseDelay component, the system will respect that cooldown.
/// </summary>
[RegisterComponent]
public sealed partial class SpeakOnTriggerComponent : Component
{
/// <summary>
/// The identifier for the dataset prototype containing messages to be spoken by this entity.
/// </summary>
[DataField(required: true)]
public ProtoId<LocalizedDatasetPrototype> Pack = string.Empty;
}

View File

@@ -67,6 +67,9 @@ public sealed class SuicideSystem : EntitySystem
if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, CannotSuicideTag))
return false;
// TODO: fix this
// This is a handled event, but the result is never used
// It looks like TriggerOnMobstateChange is supposed to prevent you from suiciding
var suicideEvent = new SuicideEvent(victim);
RaiseLocalEvent(victim, suicideEvent);

View File

@@ -0,0 +1,105 @@
using Content.Server.Chat.Managers;
using Content.Shared.Chat;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Mind;
using Content.Shared.Roles;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Chat.Systems;
/// <summary>
/// This system is used to notify specific players of the occurance of predefined events.
/// </summary>
public sealed partial class ChatNotificationSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IChatManager _chats = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;
// The following data does not need to be saved
// Local cache for rate limiting chat notifications by source
// (Recipient, ChatNotification) -> Dictionary<Source, next allowed TOA>
private readonly Dictionary<(EntityUid, ProtoId<ChatNotificationPrototype>), Dictionary<EntityUid, TimeSpan>> _chatNotificationsBySource = new();
// Local cache for rate limiting chat notifications by type
// (Recipient, ChatNotification) -> next allowed TOA
private readonly Dictionary<(EntityUid, ProtoId<ChatNotificationPrototype>), TimeSpan> _chatNotificationsByType = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ActorComponent, ChatNotificationEvent>(OnChatNotification);
_sawmill = _logManager.GetSawmill("chatnotification");
}
/// <summary>
/// Triggered when the specified player recieves a chat notification event.
/// </summary>
/// <param name="ent">The player receiving the chat notification.</param>
/// <param name="args">The chat notification event</param>
public void OnChatNotification(Entity<ActorComponent> ent, ref ChatNotificationEvent args)
{
if (!_proto.TryIndex(args.ChatNotification, out var chatNotification))
{
_sawmill.Warning("Attempted to index ChatNotificationPrototype " + args.ChatNotification + " but the prototype does not exist.");
return;
}
var source = args.Source;
var playerNotification = (ent, args.ChatNotification);
// Exit without notifying the player if we received a notification before the appropriate time has elasped
if (chatNotification.NotifyBySource)
{
if (!_chatNotificationsBySource.TryGetValue(playerNotification, out var trackedSources))
trackedSources = new();
trackedSources.TryGetValue(source, out var timeSpan);
trackedSources[source] = _timing.CurTime + chatNotification.NextDelay;
_chatNotificationsBySource[playerNotification] = trackedSources;
if (_timing.CurTime < timeSpan)
return;
}
else
{
_chatNotificationsByType.TryGetValue(playerNotification, out var timeSpan);
_chatNotificationsByType[playerNotification] = _timing.CurTime + chatNotification.NextDelay;
if (_timing.CurTime < timeSpan)
return;
}
var sourceName = args.SourceNameOverride ?? Name(source);
var userName = args.UserNameOverride ?? (args.User.HasValue ? Name(args.User.Value) : string.Empty);
var targetName = Name(ent);
var message = Loc.GetString(chatNotification.Message, ("source", sourceName), ("user", userName), ("target", targetName));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chats.ChatMessageToOne(
ChatChannel.Notifications,
message,
wrappedMessage,
default,
false,
ent.Comp.PlayerSession.Channel,
colorOverride: chatNotification.Color
);
if (chatNotification.Sound != null && _mind.TryGetMind(ent, out var mindId, out _))
_roles.MindPlaySound(mindId, chatNotification.Sound);
}
}

View File

@@ -9,7 +9,6 @@ using Content.Server.GameTicking;
using Content.Server.Speech.Prototypes;
using Content.Server.Speech.EntitySystems;
using Content.Server.Speech.Prototypes;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.ActionBlocker;
using Content.Shared.Administration;
@@ -23,6 +22,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Players;
using Content.Shared.Players.RateLimiting;
using Content.Shared.Radio;
using Content.Shared.Station.Components;
using Content.Shared.Whitelist;
using Robust.Server.Player;
using Robust.Shared.Audio;
@@ -62,11 +62,6 @@ public sealed partial class ChatSystem : SharedChatSystem
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
public const int VoiceRange = 10; // how far voice goes in world units
public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
public const string DefaultAnnouncementSound = "/Audio/_CP14/Announce/event_boom.ogg"; //CP14 replaced default sound
private bool _loocEnabled = true;
private bool _deadLoocEnabled;
private bool _critLoocEnabled;
@@ -194,13 +189,6 @@ public sealed partial class ChatSystem : SharedChatSystem
if (!CanSendInGame(message, shell, player))
return;
//CP14 Prevent god from default speaking. In waiting of chatcode refactor
var ev = new CP14SpokeAttemptEvent(message, desiredType, player);
RaiseLocalEvent(source, ev);
if (ev.Cancelled)
return;
//CP14 end
ignoreActionBlocker = CheckIgnoreSpeechBlocker(source, ignoreActionBlocker);
// this method is a disaster

View File

@@ -1,11 +1,16 @@
using Content.Server.Forensics;
using Content.Server.Speech.EntitySystems;
using Content.Shared.Cloning.Events;
using Content.Shared.Clothing.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Content.Shared.Labels.Components;
using Content.Shared.Labels.EntitySystems;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Paper;
using Content.Shared.Stacks;
using Content.Shared.Speech.Components;
using Content.Shared.Storage;
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
@@ -13,47 +18,58 @@ using Robust.Shared.Prototypes;
namespace Content.Server.Cloning;
/// <summary>
/// The part of item cloning responsible for copying over important components.
/// This is used for <see cref="CopyItem"/>.
/// Anything not copied over here gets reverted to the values the item had in its prototype.
/// The part of item cloning responsible for copying over important components.
/// </summary>
/// <remarks>
/// This method of copying items is of course not perfect as we cannot clone every single component, which would be pretty much impossible with our ECS.
/// We only consider the most important components so the paradox clone gets similar equipment.
/// This method of using subscriptions was chosen to make it easy for forks to add their own custom components that need to be copied.
/// These are all not part of their corresponding systems because we don't want systems every system to depend on a CloningSystem namespace import, which is still heavily coupled to med code.
/// TODO: Create a more generic "CopyEntity" method/event (probably in RT) that doesn't have this problem and then move all these subscriptions.
/// </remarks>
public sealed partial class CloningSystem : EntitySystem
public sealed partial class CloningSystem
{
[Dependency] private readonly SharedStackSystem _stack = default!;
[Dependency] private readonly LabelSystem _label = default!;
[Dependency] private readonly ForensicsSystem _forensics = default!;
[Dependency] private readonly PaperSystem _paper = default!;
[Dependency] private readonly VocalSystem _vocal = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StackComponent, CloningItemEvent>(OnCloneStack);
SubscribeLocalEvent<LabelComponent, CloningItemEvent>(OnCloneLabel);
SubscribeLocalEvent<PaperComponent, CloningItemEvent>(OnClonePaper);
SubscribeLocalEvent<ForensicsComponent, CloningItemEvent>(OnCloneForensics);
SubscribeLocalEvent<StoreComponent, CloningItemEvent>(OnCloneStore);
// These are used for <see cref="CopyItem"/>.
// Anything not copied over here gets reverted to the values the item had in its prototype.
// This method of copying items is of course not perfect as we cannot clone every single component, which would be pretty much impossible with our ECS.
// We only consider the most important components so the paradox clone gets similar equipment.
// This method of using subscriptions was chosen to make it easy for forks to add their own custom components that need to be copied.
SubscribeLocalEvent<StackComponent, CloningItemEvent>(OnCloneItemStack);
SubscribeLocalEvent<LabelComponent, CloningItemEvent>(OnCloneItemLabel);
SubscribeLocalEvent<PaperComponent, CloningItemEvent>(OnCloneItemPaper);
SubscribeLocalEvent<ForensicsComponent, CloningItemEvent>(OnCloneItemForensics);
SubscribeLocalEvent<StoreComponent, CloningItemEvent>(OnCloneItemStore);
// These are for cloning components that cannot be cloned using CopyComp.
// Put them into CloningSettingsPrototype.EventComponents to have them be applied to the clone.
SubscribeLocalEvent<VocalComponent, CloningEvent>(OnCloneVocal);
SubscribeLocalEvent<StorageComponent, CloningEvent>(OnCloneStorage);
SubscribeLocalEvent<InventoryComponent, CloningEvent>(OnCloneInventory);
SubscribeLocalEvent<MovementSpeedModifierComponent, CloningEvent>(OnCloneInventory);
}
private void OnCloneStack(Entity<StackComponent> ent, ref CloningItemEvent args)
private void OnCloneItemStack(Entity<StackComponent> ent, ref CloningItemEvent args)
{
// if the clone is a stack as well, adjust the count of the copy
if (TryComp<StackComponent>(args.CloneUid, out var cloneStackComp))
_stack.SetCount(args.CloneUid, ent.Comp.Count, cloneStackComp);
}
private void OnCloneLabel(Entity<LabelComponent> ent, ref CloningItemEvent args)
private void OnCloneItemLabel(Entity<LabelComponent> ent, ref CloningItemEvent args)
{
// copy the label
_label.Label(args.CloneUid, ent.Comp.CurrentLabel);
}
private void OnClonePaper(Entity<PaperComponent> ent, ref CloningItemEvent args)
private void OnCloneItemPaper(Entity<PaperComponent> ent, ref CloningItemEvent args)
{
// copy the text and any stamps
if (TryComp<PaperComponent>(args.CloneUid, out var clonePaperComp))
@@ -63,13 +79,13 @@ public sealed partial class CloningSystem : EntitySystem
}
}
private void OnCloneForensics(Entity<ForensicsComponent> ent, ref CloningItemEvent args)
private void OnCloneItemForensics(Entity<ForensicsComponent> ent, ref CloningItemEvent args)
{
// copy any forensics to the cloned item
_forensics.CopyForensicsFrom(ent.Comp, args.CloneUid);
}
private void OnCloneStore(Entity<StoreComponent> ent, ref CloningItemEvent args)
private void OnCloneItemStore(Entity<StoreComponent> ent, ref CloningItemEvent args)
{
// copy the current amount of currency in the store
// at the moment this takes care of uplink implants and the portable nukie uplinks
@@ -80,4 +96,35 @@ public sealed partial class CloningSystem : EntitySystem
}
}
private void OnCloneVocal(Entity<VocalComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_vocal.CopyComponent(ent.AsNullable(), args.CloneUid);
}
private void OnCloneStorage(Entity<StorageComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_storage.CopyComponent(ent.AsNullable(), args.CloneUid);
}
private void OnCloneInventory(Entity<InventoryComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_inventory.CopyComponent(ent.AsNullable(), args.CloneUid);
}
private void OnCloneInventory(Entity<MovementSpeedModifierComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_movementSpeedModifier.CopyComponent(ent.AsNullable(), args.CloneUid);
}
}

View File

@@ -24,7 +24,7 @@ namespace Content.Server.Cloning;
/// System responsible for making a copy of a humanoid's body.
/// For the cloning machines themselves look at CloningPodSystem, CloningConsoleSystem and MedicalScannerSystem instead.
/// </summary>
public sealed partial class CloningSystem : EntitySystem
public sealed partial class CloningSystem : SharedCloningSystem
{
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
@@ -84,13 +84,7 @@ public sealed partial class CloningSystem : EntitySystem
return true;
}
/// <summary>
/// Copy components from one entity to another based on a CloningSettingsPrototype.
/// </summary>
/// <param name="original">The orignal Entity to clone components from.</param>
/// <param name="clone">The target Entity to clone components to.</param>
/// <param name="settings">The clone settings prototype containing the list of components to clone.</param>
public void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings)
public override void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings)
{
var componentsToCopy = settings.Components;
var componentsToEvent = settings.EventComponents;
@@ -128,7 +122,8 @@ public sealed partial class CloningSystem : EntitySystem
}
// If the original does not have the component, then the clone shouldn't have it either.
RemComp(clone, componentRegistration.Type);
if (!HasComp(original, componentRegistration.Type))
RemComp(clone, componentRegistration.Type);
}
var cloningEv = new CloningEvent(settings, clone);

View File

@@ -1,4 +1,4 @@
using Content.Server.Station.Components;
using Content.Shared.Station.Components;
using Robust.Shared.Console;
namespace Content.Server.Commands;

View File

@@ -276,8 +276,8 @@ namespace Content.Server.Construction
if(!insertStep.EntityValid(insert, EntityManager, Factory))
return HandleResult.False;
// Unremovable items can't be inserted, unless they are a lingering stack
if(HasComp<UnremoveableComponent>(insert) && (!TryComp<StackComponent>(insert, out var comp) || !comp.Lingering))
// Unremovable items can't be inserted
if(HasComp<UnremoveableComponent>(insert))
return HandleResult.False;
// If we're only testing whether this step would be handled by the given event, then we're done.

View File

@@ -1,7 +1,6 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.EUI;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Server.StationRecords;
using Content.Server.StationRecords.Systems;
@@ -10,12 +9,12 @@ using Content.Shared.CCVar;
using Content.Shared.CrewManifest;
using Content.Shared.GameTicking;
using Content.Shared.Roles;
using Content.Shared.Station.Components;
using Content.Shared.StationRecords;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.CrewManifest;

View File

@@ -1,50 +0,0 @@
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
namespace Content.Server.Damage.Systems;
// System for damage that occurs on specific trigger, towards the user..
public sealed class DamageUserOnTriggerSystem : EntitySystem
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
SubscribeLocalEvent<DamageUserOnTriggerComponent, TriggerEvent>(OnTrigger);
}
private void OnTrigger(EntityUid uid, DamageUserOnTriggerComponent component, TriggerEvent args)
{
if (args.User is null)
return;
args.Handled |= OnDamageTrigger(uid, args.User.Value, component);
}
private bool OnDamageTrigger(EntityUid source, EntityUid target, DamageUserOnTriggerComponent? component = null)
{
if (!Resolve(source, ref component))
{
return false;
}
var damage = new DamageSpecifier(component.Damage);
var ev = new BeforeDamageUserOnTriggerEvent(damage, target);
RaiseLocalEvent(source, ev);
return _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: source) is not null;
}
}
public sealed class BeforeDamageUserOnTriggerEvent : EntityEventArgs
{
public DamageSpecifier Damage { get; set; }
public EntityUid Tripper { get; }
public BeforeDamageUserOnTriggerEvent(DamageSpecifier damage, EntityUid target)
{
Damage = damage;
Tripper = target;
}
}

View File

@@ -1,5 +1,4 @@
using Content.Server.Defusable.Components;
using Content.Server.Explosion.Components;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups;
using Content.Server.Wires;
@@ -8,13 +7,13 @@ using Content.Shared.Construction.Components;
using Content.Shared.Database;
using Content.Shared.Defusable;
using Content.Shared.Examine;
using Content.Shared.Explosion.Components;
using Content.Shared.Explosion.Components.OnTrigger;
using Content.Shared.Popups;
using Content.Shared.Trigger.Components;
using Content.Shared.Trigger.Components.Effects;
using Content.Shared.Trigger.Systems;
using Content.Shared.Verbs;
using Content.Shared.Wires;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
namespace Content.Server.Defusable.Systems;
@@ -74,12 +73,13 @@ public sealed class DefusableSystem : SharedDefusableSystem
{
args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
}
else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp))
else if (comp.Activated)
{
if (comp.DisplayTime)
var remaining = _trigger.GetRemainingTime(uid);
if (comp.DisplayTime && remaining != null)
{
args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
("time", MathF.Floor(activeComp.TimeRemaining))));
("time", Math.Floor(remaining.Value.TotalSeconds))));
}
else
{
@@ -139,16 +139,9 @@ public sealed class DefusableSystem : SharedDefusableSystem
SetActivated(comp, true);
_popup.PopupEntity(Loc.GetString("defusable-popup-begun", ("name", uid)), uid);
if (TryComp<OnUseTimerTriggerComponent>(uid, out var timerTrigger))
if (TryComp<TimerTriggerComponent>(uid, out var timerTrigger))
{
_trigger.HandleTimerTrigger(
uid,
user,
timerTrigger.Delay,
timerTrigger.BeepInterval,
timerTrigger.InitialBeepDelay,
timerTrigger.BeepSound
);
_trigger.ActivateTimerTrigger((uid, timerTrigger));
}
RaiseLocalEvent(uid, new BombArmedEvent(uid));
@@ -168,7 +161,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
RaiseLocalEvent(uid, new BombDetonatedEvent(uid));
_explosion.TriggerExplosive(uid, user:detonator);
_explosion.TriggerExplosive(uid, user: detonator);
QueueDel(uid);
_appearance.SetData(uid, DefusableVisuals.Active, comp.Activated);
@@ -188,7 +181,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
{
SetUsable(comp, false);
RemComp<ExplodeOnTriggerComponent>(uid);
RemComp<OnUseTimerTriggerComponent>(uid);
RemComp<TimerTriggerComponent>(uid);
}
RemComp<ActiveTimerTriggerComponent>(uid);
@@ -246,7 +239,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
if (comp is not { Activated: true, DelayWireUsed: false })
return;
_trigger.TryDelay(wire.Owner, 30f);
_trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(30));
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
comp.DelayWireUsed = true;
}
@@ -268,7 +261,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
if (comp is { Activated: true, ProceedWireUsed: false })
{
comp.ProceedWireUsed = true;
_trigger.TryDelay(wire.Owner, -15f);
_trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(-15));
}
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-proceed-pulse", ("name", wire.Owner)), wire.Owner);
@@ -298,7 +291,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
{
if (!comp.ActivatedWireUsed)
{
_trigger.TryDelay(wire.Owner, 30f);
_trigger.TryDelay(wire.Owner, TimeSpan.FromSeconds(30));
_popup.PopupEntity(Loc.GetString("defusable-popup-wire-chirp", ("name", wire.Owner)), wire.Owner);
comp.ActivatedWireUsed = true;
}

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Systems;
@@ -14,15 +15,13 @@ using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.Destructible;
using Content.Shared.FixedPoint;
using Content.Shared.Humanoid;
using Content.Shared.Trigger.Systems;
using JetBrains.Annotations;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using System.Linq;
using Content.Shared.Humanoid;
using Robust.Shared.Player;
namespace Content.Server.Destructible
{

View File

@@ -5,6 +5,6 @@ public sealed partial class TimerStartBehavior : IThresholdBehavior
{
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
{
system.TriggerSystem.StartTimer(owner, cause);
system.TriggerSystem.ActivateTimerTrigger(owner, cause);
}
}

View File

@@ -1,10 +1,18 @@
namespace Content.Server.Destructible.Thresholds.Behaviors;
using Content.Shared.Trigger.Systems;
namespace Content.Server.Destructible.Thresholds.Behaviors;
[DataDefinition]
public sealed partial class TriggerBehavior : IThresholdBehavior
{
/// <summary>
/// The trigger key to use when triggering.
/// </summary>
[DataField]
public string? KeyOut { get; set; } = TriggerSystem.DefaultTriggerKey;
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
{
system.TriggerSystem.Trigger(owner, cause);
system.TriggerSystem.Trigger(owner, cause, KeyOut);
}
}

View File

@@ -1,4 +1,6 @@
using Content.Server.DeviceLinking.Components;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceLinking.Events;

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