Compare commits

...

711 Commits

Author SHA1 Message Date
Ed
bc2f6fab7c fix 2024-08-22 13:05:03 +03:00
Ed
0f1c4f144e Merge branch 'master' into ed-17-07-2024-dungenagain 2024-08-22 12:57:42 +03:00
Ed
3d864eb0fd Merge pull request #413 from crystallpunk-14/ed-19-08-2024-upstream
Ed 19 08 2024 upstream
2024-08-21 16:35:11 +03:00
Ed
9c59dab6a2 Merge branch 'ed-19-08-2024-upstream' of https://github.com/crystallpunk-14/crystall-punk-14 into ed-19-08-2024-upstream 2024-08-21 14:33:46 +03:00
Ed
5c4b7a87aa fuck 2024-08-21 14:19:44 +03:00
Ed
2383db6a22 d 2024-08-20 22:52:25 +03:00
Ed
cfeb5fae80 Update secret_weights.yml 2024-08-19 19:16:49 +03:00
Ed
53a9eb259e some ignoring 2024-08-19 19:07:09 +03:00
Ed
c82b17a65e Merge remote-tracking branch 'upstream/master' into ed-19-08-2024-upstream
# Conflicts:
#	Content.IntegrationTests/Tests/PostMapInitTest.cs
#	Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs
#	Content.Shared/Clothing/Components/ClothingComponent.cs
#	Resources/Prototypes/Accents/word_replacements.yml
#	Resources/Prototypes/Maps/Pools/default.yml
#	Resources/Prototypes/Maps/atlas.yml
#	Resources/Prototypes/Maps/bagel.yml
#	Resources/Prototypes/Maps/cluster.yml
#	Resources/Prototypes/Maps/europa.yml
#	Resources/Prototypes/Maps/omega.yml
#	Resources/Prototypes/Maps/origin.yml
#	Resources/Prototypes/Traits/speech.yml
#	Resources/Prototypes/Voice/disease_emotes.yml
#	Resources/Prototypes/Voice/speech_emotes.yml
#	Resources/Prototypes/game_presets.yml
#	Resources/Prototypes/secret_weights.yml
2024-08-19 19:06:38 +03:00
Tornado Tech
f6d81c04c0 Added world sprite system (#397)
* Added world sprite system

* Added important comment
2024-08-19 18:41:56 +03:00
Nim
34f90f1bce clothing and armour package (#408)
Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
2024-08-19 18:41:35 +03:00
lzk
fc33df0e49 give guides for antags (#31208)
* give sleeper traitor default traitor guidebook

* guidebook for thief
2024-08-19 17:40:34 +02:00
Ed
4cb93fc965 Food update (#412)
* meat refactor

* document and remove verb

* cooking table

* cutlets

* dough and cheese update

* flat dough

* size edit, egg added

* Update test-ship.yml

* meat and cheese randomsprite
2024-08-19 18:39:47 +03:00
slarticodefast
05efea3118 quick shuttle wall test fix (#31206)
* quick shuttle wall fix

* this time for real
2024-08-19 17:38:25 +02:00
c4llv07e
df6dbd2ac7 Add reaction randomization. (#308)
* Add random products datafield to the reaciton prototype.

One of the items in the list will be used as additional products as a
result of the reaction and will be persistent per round.

Resolves #245

* Another try

Add getter and setter for the reaction products field and
add random products there.

* There is no such product as CP14BasicEffectHealHeat

* Try to visualize random products in the guidebook

* experimental recipes

* fix

* file restructurization

* Guidebook alchemy update

* Update Alchemy.xml

* fix

* Update mixing_simple.yml

---------

Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
2024-08-19 18:04:12 +03:00
PJBot
e438b8b25f Automatic changelog update 2024-08-19 14:04:13 +00:00
tosatur
8eb4e4fa2d added a few chatsan entries (#31085)
* added a few chatsan entries

* added 2 more

* added 4 more and fixed errors

* fixed error again

* added allg

* removed fullstops
2024-08-20 00:03:07 +10:00
Caconym27
7a0ee1a074 Added nose coverings on gaiters (#31116)
Added 2 pixels on the front of the left and right views of the red and black gaiter sprites so that they would properly cover the nose of their wearers, as they do in reality.
2024-08-20 00:00:16 +10:00
SlamBamActionman
84e5565918 Fix shuttle walls rusting when initialized on station grid (#31162)
* Initial commit

* Also reinforced
2024-08-19 15:43:31 +02:00
Cojoke
6c3dad4330 Fix Pacified Damage Dealing Thrown Objects (#31183) 2024-08-19 13:56:40 +02:00
Ed
055dfe6f0b No pacifist or blind sec (#30777)
* no pacifist sec

* also block blindness

* Update warden.yml

* Update security_officer.yml

* Update head_of_security.yml

* Update security_cadet.yml

* Update detective.yml
2024-08-19 14:42:55 +03:00
PJBot
015cf36469 Automatic changelog update 2024-08-19 11:15:36 +00:00
Boaz1111
2f923d88e4 New head of security weapon:Energy Shotgun (#30643)
* Reset hopefully?

* removes the emergency security orders

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

Rewrites the description based on review

Co-authored-by: NakataRin <45946146+NakataRin@users.noreply.github.com>

* 7 shots, recharge taking 42 seconds

* minor renaming :godo:

* please work

* Revert "please work"

This reverts commit e84a2618d95ec17f5fd608331176df2ab8b0966c.

* please fix

---------

Co-authored-by: NakataRin <45946146+NakataRin@users.noreply.github.com>
2024-08-19 21:14:30 +10:00
PJBot
972bd566f5 Automatic changelog update 2024-08-19 10:34:47 +00:00
beck-thompson
d297852ba8 Seed vend restock price increase (Fix test fails) (#31185)
First commit
2024-08-19 12:33:41 +02:00
PJBot
61f756de8a Automatic changelog update 2024-08-19 07:55:41 +00:00
lzk
c3f42438f2 Cheese tweaks (#31168)
* Cheese tweaks

* 22221241256247849`
2024-08-19 10:54:34 +03:00
Spessmann
8486b24db9 Cog update (#31188)
buffed cog atmos so now its massive
2024-08-19 01:10:43 -06:00
PJBot
e85ab9195c Automatic changelog update 2024-08-19 03:40:06 +00:00
Killerqu00
2c98d0a519 clarify what is an important role in rule 12 (#30818)
clarify what is an important role
2024-08-18 21:39:45 -06:00
Nemanja
476c7751c4 Recycler Overhaul (#30802)
* Recycler overhaul

* remove
2024-08-18 21:39:00 -06:00
PJBot
1fa447f769 Automatic changelog update 2024-08-19 03:33:22 +00:00
Flareguy
94c746abe9 Minor vox customization update: new hairstyles & facial hair (#30815)
* new vox customization stuff

* tests fix
2024-08-18 21:32:15 -06:00
PJBot
1a3dc66d24 Automatic changelog update 2024-08-19 03:29:56 +00:00
Plykiya
7ba049be25 Convert bookshelves from StorageFill to EntityTables (#30895)
* Convert bookshelves to EntityTables

* the fixy

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-18 21:29:52 -06:00
Nemanja
18c146cc7f increase material prices (#30869)
* increase material prices

* ack
2024-08-18 21:28:46 -06:00
PJBot
d11a4ba1f5 Automatic changelog update 2024-08-19 03:22:50 +00:00
Brandon Hu
dd36c85b61 add(AdminGhost): Add a multitool to the admin ghost backpack (#31002) 2024-08-18 21:22:10 -06:00
DieselMohawk
802d478e21 Security Helmet Reshape (#30961)
Mildly Reshapes The Sec Helmet
2024-08-18 21:21:44 -06:00
PJBot
1ba508aef2 Automatic changelog update 2024-08-19 03:20:25 +00:00
TheWaffleJesus
1c29310faf ERT chaplains can now use bibles (#30993)
ert chaplains can now use bibles
2024-08-18 21:19:19 -06:00
PJBot
9b8e30a0b7 Automatic changelog update 2024-08-19 03:17:11 +00:00
UBlueberry
2577eb10fd Guidebook updates for Antagonist entries (#31075)
* main antag page + minor antags

* nuuk operatives (greenland)

* "coup d'flop" = "flukies" but for revs

* manifest edgelord

* traitor tot (potato)

* they're coming to get you, barbara

* general changes. posters.

* tfw last minute commit

* traitoration rewritification

* created thieves.xml

* thieves, wow!!
2024-08-18 21:16:05 -06:00
PJBot
e2ecd804b6 Automatic changelog update 2024-08-19 03:14:09 +00:00
Zachary Yona
f272097e07 Device-list limiting (#30997)
* PDA can no longer be linked to multitool

* Ignore previous commit, it would've broken things

* Added SavableAddress field to DeviceNetwork component and PDA for testing. Still need to implement

* System works, borg and pda addresses cannot be saved, but they still connect to the net.

* Suit sensors can no longer be added to network configurators, still work for monitoring purposes.

---------

Co-authored-by: Zachary Yona <magicalusf@gmail.com>
2024-08-18 21:13:03 -06:00
Boaz1111
ed3e10741f New Projectile Effect:Temperature Change (#31004)
* bah

* all done, thanks slarti

* makes the watcher do the thing

* Update TemperatureSystem.cs

Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>

* changes something

---------

Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
2024-08-18 21:11:17 -06:00
PJBot
ebf62aaca1 Automatic changelog update 2024-08-19 03:06:31 +00:00
to4no_fix
171884df8f Tweaking things that restrict freedom (#31095)
* Adding a straitjacket production recipe

* Adding unequipDelay and equipDelay to the muzzle

* Adding unequipDelay and equipDelay to the blindfold

* Sorting recipes
2024-08-18 21:05:25 -06:00
Nemanja
0a05ee9a27 add space carp and sharkminnow teeth (#31070) 2024-08-18 21:04:59 -06:00
PJBot
da3c3b5538 Automatic changelog update 2024-08-19 03:03:24 +00:00
Nemanja
edc91621d3 fix selfunremoveable component being bypassed by hand pickup verb (#31089)
* fix selfunremoveable component being bypassed by hand pickup verb

* fix logic
2024-08-18 21:02:38 -06:00
Milon
3e15e00af5 fix admin pda health analyzer popup (#31097)
* damn it

* whoopsie

* no more fucky wucky fucky wuckies are GONE
2024-08-18 21:02:18 -06:00
themias
db427dc58f Fix cooking sizzle noise (#31096) 2024-08-18 21:01:43 -06:00
Moomoobeef
529e4b767c Added a pitcher for the chef to use in serving beverages (#31105)
* changed weird red pixel on pitcher sprite

* added pitcher prototype

* added pitcher to dinnervend

* realized I need to change the other icon sprites too :P
2024-08-18 21:01:26 -06:00
Red Mushie
91e2fdc4b9 Fix news management console publish timeout activating when nothing was published (#31161)
Fix news management console updating publish timeout, now updating it *after* access checks instead of before
2024-08-18 20:56:58 -06:00
PJBot
19b0ce3007 Automatic changelog update 2024-08-19 02:56:50 +00:00
Red Mushie
9240786b53 Fix news management console access checks (#31160)
* Fix news management console access checks using manifest instead of AccessReader component

* Fix one tiny mistake

* Fix one tiny mistake, properly this time

* Revert accidental changes unrelated to this PR

* Add missing .Owner on delete check
2024-08-18 20:55:43 -06:00
PJBot
a2c762dbef Automatic changelog update 2024-08-19 02:49:53 +00:00
tosatur
68d650653f Made hydroponics alert light more orange (#31088)
made light more orange
2024-08-18 20:48:47 -06:00
PJBot
45b0543e89 Automatic changelog update 2024-08-19 02:44:05 +00:00
Goldminermac
864956c66b Pancakes Fix (#31123)
* Edited blueberry pancake textures to seem more blueberry-stained

* Added new blueberry pancake sprites to allow stacking up to 9

* Added more sprites for chocolate pancakes so they can stack up to 9

* Fixed up the blueberry pancake sprites to be more consistent

* Updated the stack components of blueberry and chocolate pancakes in the yml prototype file.

* Updates the stack size in the consumable stacks yaml file.

* Added the new pancake sprites to the sprite meta json file.

* Removed an unnecessary space from the baked food YAML file

* Updated branch and fully resolved merge conflict

* Decreased the copy-pastiness of some of the blueberry sprites to make them less repetitive

* Diversified the textures some more for good measure.

* Fixed some missing rims around the pancakes because I'm bored
2024-08-18 20:42:58 -06:00
PJBot
0754a0fdaf Automatic changelog update 2024-08-19 02:42:33 +00:00
DrSmugleaf
7e3b9fb2fe Allow a specific hand to be passed into TrySpawnVirtualItemInHand (#31124) 2024-08-18 20:42:22 -06:00
slarticodefast
3dcfe0d850 fix mice and revs being able to toggle suit pieces (#31152) 2024-08-18 20:41:27 -06:00
PJBot
c9854c32ba Automatic changelog update 2024-08-19 01:56:55 +00:00
Nemanja
d9e0681a70 make ore indestructible (#31182) 2024-08-18 19:55:49 -06:00
PJBot
03f5bf4c97 Automatic changelog update 2024-08-18 23:42:19 +00:00
Psychpsyo
9a68cf0b0e Re-add improved random sentience event (#29123)
* Re-add improved random sentience event

* Make randomly sentient PDA more likely

* Make vending machine sentience less likely

* Make requested changes

* Make randomly sentient captain's gear more likely

* Sentient captain sabre has pirate accent

* Tweak new random sentient object a bit more

* Sentient PDA improvements

* Apply recommended fixes

* Add requested changes

* Fix merge conflict
2024-08-18 17:41:12 -06:00
geraeumig
c48a96ac15 Trim punctuation marks in the chat emote system (#28612)
Co-authored-by: geraeumig <alfenos@proton.me>
2024-08-18 16:49:07 -06:00
PJBot
3cb67a3564 Automatic changelog update 2024-08-18 22:35:49 +00:00
deltanedas
ad691931c6 add memory cell and rework logic construction (#24983)
* rework construction to be deconstructable, add memory cell

* update textures

* add code

* add memory cell and ports, empty circuit

* d

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-08-18 16:34:43 -06:00
PJBot
6cda6c8ba5 Automatic changelog update 2024-08-18 22:29:24 +00:00
potato1234_x
1952618203 Tea Plant and Blue Pumpkin (#25092)
* tea plant, blumpkin

* fix linter

* fix tests pls

* bloompkin

* forgor rsi

* work please

* WORK PLEASE

* migration of the blumpkins

* it is now glumpkin

* lowercase

* uppercase

* adjust values and fix blorbo

wtf when did blumpkin donut turn into blorbo donut what

* fix test

* update

* fix stupid mistake

* Update meta.json

* totally not a webedit

* not a webedit 2

* nitpicks
2024-08-18 16:28:18 -06:00
PJBot
7f5c11afaa Automatic changelog update 2024-08-18 22:26:08 +00:00
Preston Smith
900e57c6de Added LV power cables to Marathon library (#31131) 2024-08-18 16:25:42 -06:00
slarticodefast
86b358be4b Fix raditation collectors gaslighting you (#31175)
fix raditation collectors gaslighting you
2024-08-18 16:25:02 -06:00
DrSmugleaf
4fcbb99072 Fix collidable held entities blocking themselves while being dropped (#31119) 2024-08-18 16:24:28 -06:00
PJBot
b4ef1e9a0f Automatic changelog update 2024-08-18 22:19:13 +00:00
Deatherd
878384dbb0 Sharks Go RAWR! (#31142)
shark makes RAWR!
2024-08-19 01:18:07 +03:00
PJBot
2dfcd5cf73 Automatic changelog update 2024-08-18 21:56:49 +00:00
lzk
8830e26e0e small figurines voicelines update (#31167) 2024-08-18 15:56:35 -06:00
lzk
d0bd26d575 pizza and pizza box tweaks (#31171) 2024-08-18 15:55:41 -06:00
IProduceWidgets
c636b3b25d Update Oasis (#31174)
golden toiler
2024-08-18 15:55:11 -06:00
PJBot
d38d497e62 Automatic changelog update 2024-08-18 21:19:28 +00:00
beck-thompson
78b3e3253f Cutting food now moves the food a little bit. (#31166)
First commit
2024-08-19 00:18:20 +03:00
Emisse
529f4dae64 bagel update (#31172) 2024-08-18 14:24:15 -06:00
Spessmann
f7fd991bc8 Cog updates (#31169)
updated cog a ton
2024-08-18 13:51:46 -06:00
PJBot
0c078f6cc4 Automatic changelog update 2024-08-18 16:23:42 +00:00
Nemanja
a540b8840e Goliath mob (#30839)
* Goliath mob

* Update asteroid.yml

* mcfuck yourself

* add cloak

* fixes

* Update materials.yml
2024-08-18 12:22:36 -04:00
PJBot
d673bdfe67 Automatic changelog update 2024-08-18 16:01:49 +00:00
Unkn0wn_Gh0st
73331416e3 Coughing and coughing changes (#30893)
* Coughing

* Merge branch 'space-wizards:master' into coughing

* Coughing

* Coughing
2024-08-18 12:00:42 -04:00
SlamBamActionman
7d48cc5e24 Add atmos directional sign (#31155)
* Xeno walls

* Fix icon smoothing for the walls

* Added a bunch of items, and the map/shuttle

* Fixed construction graphs

* Put the map in the right place gosh

* Fix shuttle walls rusting, invalid IDs, latejoin cryo, deprecated properties

* Fix APC names and stuff

* Updated to v1.1
2024-08-18 11:44:09 -04:00
PJBot
0d969f61a8 Automatic changelog update 2024-08-18 15:32:58 +00:00
Iztok Bajcar
031ddc0420 Guidebook typo fixes (#31159)
* Fixed typo: access --> accessed

* Fixed typo: 'access restricted' --> 'access-restricted'

* Fixed typo: added 'be'

* Fixed typo: added 'a'

* Fixed typo: 'Asssistants' --> 'Assistants'

* Fixed typo: 'overpressure' --> 'overpressurize'

* Fixed typo: 'outisde' --> 'outside'

* Fixed typo: added a comma

* Fixed typo: added a comma

* Fixed typo: 'aformentioned' --> 'aforementioned'

* Fixed typo: added a comma

* Fixed typo: added a comma

* Fixed typo: 'radition' --> 'radiation'

* Fixed typo: 'turing' --> 'turning'

* Fixed typo: 'appropiate' --> 'appropriate'

* Fixed typo: added 'the'

* Fixed typo: added a dot to ellipsis

* Fixed typo: 'aswell' --> 'as well'

* Fixed typo: 'permanant' --> 'permanent'

* Fixed typo: 'flammmable' --> 'flammable'

* Fixed typo: 'recommmended' --> 'recommended'

* Fixed typo: 'recipies' --> 'recipes'

* Fixed typo: 'pre-adminsitered' --> 'pre-administered'

* Fixed typo: 'resusitated' --> 'resuscitated'

* Fixed typo: 'types' --> 'type'

* Fixed typo: `someones` --> `someone's`

* Fixed typo: 'appy' --> 'apply'

* Fixed typo: added 'to'

* Fixed typo: 'CentCom' --> 'CentComm' (as per https://github.com/space-wizards/space-station-14/pull/29217)

* Fixed typo: 'emergencys' --> 'emergencies'

* Fixed typo: removed a dash
2024-08-18 11:31:48 -04:00
TGRCDev
517f7ad344 Fixed IP bans preventing non-banned players from connecting to SQLite-backed servers (#31154)
Fixed IP bans causing server errors with SQLite DB
2024-08-18 13:53:09 +02:00
Emisse
77a60bd3b8 bagel update (#31150)
* bagel update

* variantize

* fix nitrogen room air explosion

* dumb
2024-08-18 04:10:52 -06:00
Nemanja
047a7a488d fix some salvage spawner oversights (#31114)
* fix some salvage spawner oversights

* adjust carp rates

* more tweaks and some additions
2024-08-18 03:55:38 -06:00
Brandon Hu
1545fd5787 fix(fland): Connect the Cell Window Shutters remote signaler to the Cell Window Shutters (#31136) 2024-08-18 03:55:21 -06:00
Spessmann
f09dd465a7 Cog update (#31148)
added a bunch of air alarms and fixed stuff
2024-08-18 00:49:36 -06:00
github-actions[bot]
16e1c23ffe Update Credits (#31141)
Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>
2024-08-17 22:09:00 -04:00
Crotalus
9c9bfda4d2 Fix botany bugs (#27210)
* Fixed bug with missing gas not getting reset

* Fix bug with MutateInt not using min/max in prob calculation

* Add divison by zero check

* Fix styling
2024-08-18 03:05:26 +02:00
Mervill
ad4d36f34f Replace obsolete map functions in disposals code (#31133) 2024-08-17 23:29:04 +02:00
Spessmann
1f6223229a Cog update (#31129)
changed all the firelocks to glass firelocks
2024-08-17 11:49:11 -06:00
PJBot
71ef47fe01 Automatic changelog update 2024-08-17 16:06:24 +00:00
Milon
a4b0bef12c add confirmation to the delete button in objects tab (#30946)
ui shitcode
2024-08-17 12:05:17 -04:00
PJBot
c3b3955832 Automatic changelog update 2024-08-17 14:33:10 +00:00
Ubaser
9d62570385 Red crowbars no longer fit in pockets (#30939)
* small

* update
2024-08-17 16:32:04 +02:00
PJBot
979661a7b3 Automatic changelog update 2024-08-17 14:10:49 +00:00
themias
ef3de39364 Add croissant recipe (#30825)
* Add croissant recipe

* comma

* fix description

* fix merge error
2024-08-18 00:09:42 +10:00
Emisse
f13e4c3c4f derotate train while i work on it (#31121)
webedit
2024-08-17 01:01:33 -06:00
TemporalOroboros
3091893055 Fix msCompile problem matcher on VScode on Windows (#31068)
Either VSCode's integrated shell or dotnet has a default behaviour where it inserts newlines into stdout/stderr to make the lines wrap at the console width. Since msCompile works based on lines this makes it fail to detect build warnings correctly. Depending on where the line break occurs this can result in a truncated error message, a correct error message with a truncated filepath for the error, or the errror just straight up missing.
Adding 'ForceNoAlign' to the logging parameters for dotnet build disables this behaviour and gives msCompile actually useful input to sift for errors. End result is all the errors are detected and listed with the correct error messages and filepaths.
2024-08-16 23:33:16 -07:00
Emisse
5cc2b12f4e standardize map protos and fill gaps (#31120)
update protos
2024-08-17 00:23:57 -06:00
Spessmann
3ce8f16cb9 Updated cog (#31117)
* updated cog and increased popcap to 80

* back to 70 cap
2024-08-16 23:41:03 -06:00
PJBot
633099e1d3 Automatic changelog update 2024-08-17 05:14:01 +00:00
Nemanja
ada6455b17 Make printing from ore procecssors slightly more bearable (#31074)
* Make printing from ore procecssors slightly more bearable

* Update sheet.yml
2024-08-17 15:12:55 +10:00
PJBot
7562b8fd47 Automatic changelog update 2024-08-17 05:10:27 +00:00
Nemanja
8440fb6acb anonymize salvage wrecks (#31087)
* anonymize salvage wrecks

* display wreck size
2024-08-17 15:09:21 +10:00
Nemanja
1e7fb2f4d8 Fix npcs throwing themselves straight into space (#31118) 2024-08-17 15:08:13 +10:00
PJBot
38e698e7df Automatic changelog update 2024-08-17 04:59:29 +00:00
slarticodefast
aa9bf9f3ec Fix bluespace anomaly teleporting borg brains and PAIs in container slots (#30744)
* Fix bluespace

* filter all containers
2024-08-17 00:58:23 -04:00
Preston Smith
7066b4a3e0 Modified bone-spear to correctly break into 1-2 bones when breaking (#30951) 2024-08-17 00:54:53 -04:00
Mervill
4692c69ce1 Remove obsolete functions from RadiationDebugOverlay (#31111)
remove obsolete functions from radiation
2024-08-17 13:12:45 +10:00
PJBot
9343023ffc Automatic changelog update 2024-08-17 02:50:17 +00:00
Boaz1111
969f17a960 phlog now ignites when consumed as well (#30955) 2024-08-17 12:49:11 +10:00
PJBot
9f595164f2 Automatic changelog update 2024-08-17 02:10:31 +00:00
Token
27793111bb Plants scream mutation now have more scream variety (#30862)
* seed screams now SoundCollectionSpecifier
More types of scream sounds

* plants scream mutation have 10+ scream types

* plant scream reduce scream volume
And scream powerness get's from yml instead of hardcode, so from prototypes could be changed pretty much easilly

* plants review change
2024-08-16 22:09:25 -04:00
to4no_fix
4ee9c00200 Localization hud layout (#31013) 2024-08-16 22:04:33 -04:00
PJBot
20786c12fa Automatic changelog update 2024-08-17 02:03:58 +00:00
Mervill
63762d85ca Fixs suffocation alerts not appearing. (#31115)
Fixs suffocation alerts not appearing
2024-08-16 22:02:51 -04:00
PJBot
3cf2fca993 Automatic changelog update 2024-08-17 01:01:29 +00:00
Alzore
9775975c53 Disabler SMG no longer fits in boots (#31110) 2024-08-16 21:00:21 -04:00
themias
79ae2cceb4 Add sound for turning on grill (#31099)
* Add sound for turning on grill

* remove view variable
2024-08-16 20:41:55 -04:00
Velcroboy
cc445e07b8 Update some food spawners w/ entity tables (#30957)
Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2024-08-16 10:47:37 -04:00
PJBot
f1f16800d6 Automatic changelog update 2024-08-16 10:49:00 +00:00
SlamBamActionman
49dd17dcab Fix reagents with ReagentData being duplicated (#30983)
Initial commit
2024-08-16 12:47:53 +02:00
Emisse
99bc389fd5 omega update (#31079) 2024-08-16 02:53:59 -06:00
Emisse
0b1eae362f packed update (#31078) 2024-08-16 02:53:50 -06:00
Mervill
61a3562451 Replace obsolete map functions in IconSmoothSystem (#30958)
* first part

* second part
2024-08-16 00:55:20 -04:00
PJBot
4ecc2c8468 Automatic changelog update 2024-08-16 04:54:40 +00:00
IgorAnt028
1d2c72da80 Fix bug with holding objects (#31009)
Main
2024-08-16 00:53:34 -04:00
Brandon Hu
0a9535e67c remove(NukeopsRuleComponent): Remove a trap from the component file (#31029)
ah
2024-08-16 00:52:36 -04:00
PJBot
4c523b11d7 Automatic changelog update 2024-08-16 04:00:52 +00:00
metalgearsloth
c43323dba7 Fix mains wire light (#31066)
No idea how long this was broken; mains wire relies upon this id being accurate which means it needs doing before the calls.
2024-08-15 23:59:46 -04:00
Spessmann
c9bffa82ee updated cogmap's evac shuttle (#31071) 2024-08-15 21:18:55 -06:00
PJBot
7de5156084 Automatic changelog update 2024-08-16 01:45:01 +00:00
Nemanja
3cdd62b0dd Mining Rebalance (#30920)
* first pass

* this shit too

* ok fix that shit

* buff

* actually fix that
2024-08-16 11:43:54 +10:00
PJBot
921ed4ec5d Automatic changelog update 2024-08-15 23:31:27 +00:00
Psychpsyo
42316f8ed0 Add German accent (#30541)
* German accent

* Add German motherfucker

* German accent improvements

* Fix failing tests

* Only replace th at start of words

* Correct plural for "warnungen"

* hello and goodbye in German

* Fix German again

* Remove motherfucker from German accent

* German goodbye refactor

* 'fix' indents in speech.yml

* German accent improvements

* Reduce German umlautness

* Add umlaut cooldown

so consecutive vowels don't get umlauted

* Fix german capitalization of th => zh

* Add more banger words to German accent

* Improve German tesla replacement

* German akzent

* German scientist gaming

* Men and women, now in German

* Also replace warops in German accent

Co-Authored-By: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Make requested changes

* Make some of the requested changes

* Fix linter errors

* Address remaining feedback

* RE-add toned-down random umlauts

* cabbage -> kraut

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
2024-08-16 01:30:21 +02:00
PJBot
28f41e143d Automatic changelog update 2024-08-15 22:23:08 +00:00
Emisse
af2ee4a9e6 remove origin (#31059)
derotate origin
2024-08-15 16:22:01 -06:00
PJBot
a17498ee91 Automatic changelog update 2024-08-15 21:11:15 +00:00
Emisse
9c1b8c18de remove atlas, cluster, europa, saltern (#31058)
* goodbye sweet maps

* Update EvacShuttleTest.cs

* Update EvacShuttleTest.cs

* fix tests

* fix tests

* nightmare nightmare nightmare

* rider is fucking me

* jesus christ
2024-08-15 15:10:06 -06:00
Emisse
8cdc8d7a0d fland update (#31057)
* fland update

* exit button on med
2024-08-15 14:19:00 -06:00
Emisse
53e2dfd496 box update (#31056) 2024-08-15 14:18:51 -06:00
Emisse
17b4ede7a7 Marathon Update (#31054) 2024-08-15 14:18:36 -06:00
Emisse
cf4f8b630f bagel update (#31053) 2024-08-15 14:18:27 -06:00
Emisse
18259d1d2e evac lox update (#31052)
lox update
2024-08-15 14:18:19 -06:00
PJBot
8161402e35 Automatic changelog update 2024-08-15 19:34:23 +00:00
IProduceWidgets
0ae5426fe4 Event scheduler setting tweaks (#31018)
tweaks
2024-08-15 13:33:17 -06:00
PJBot
6c6ee2731f Automatic changelog update 2024-08-15 19:31:15 +00:00
themias
f06176ac39 Cook-able cutlets for kebabs/tacos (#31048) 2024-08-15 13:30:08 -06:00
Brandon Hu
796287d695 fix(box): Make medbay exit button functional (#31003) 2024-08-15 13:26:54 -06:00
Nemanja
c41fe93040 Salvage Spawners (#31020)
* treasure entities and spawners

* no more smiles
2024-08-15 13:26:02 -06:00
PJBot
49fd12a65f Automatic changelog update 2024-08-15 19:25:06 +00:00
Nim
84af71eb7c Golden toilet (#31049)
* golden toilet

* Destructible

* sprite

* fix

* copyright
2024-08-15 13:23:59 -06:00
JustCone
03f9dee244 Packed Engi Solar Controller (#31042)
* Replace alerts with solar console

* Sci doesn't need two protolathes & engi frontdesk windoors shouldn't be atmos locked
2024-08-15 13:22:31 -06:00
Plykiya
dc66386e5f Contraband Review Pass - The Threequel (#30970)
Contraband pass number three
2024-08-15 20:52:46 +02:00
Errant
40b9fd4ea3 Namespace cleanup around Mind Roles (#30965)
* namespaces

* Comment does not need a semicolon

---------

Co-authored-by: Vasilis <vascreeper@yahoo.com>
2024-08-15 20:26:57 +02:00
Plykiya
f523df821a Contra Review Passs - Round 2 (#30942)
* Contra pass round two

* command too

* Stamps

* oopsied a bracket
2024-08-15 19:35:06 +02:00
IgorAnt028
84f9dd0f0b Fix false and true in player-panel.ftl (#31043)
Fix false and true in player-panel

Add a new True string
2024-08-15 18:00:56 +03:00
PJBot
5da2b32099 Automatic changelog update 2024-08-15 14:46:20 +00:00
Mervill
2e3365793c Greatly improve the usability of the Gas Analyzer. (#30763)
* greatly improve how the gas analyzer behaves

* don't close the analyzer when the object goes out of range

* cleanup

* always switch to the device tab when a new device is analyzed

* modern api part one

* modern api part 2

* modern api part three

* file scope namespace
2024-08-15 10:45:13 -04:00
PJBot
25074d0719 Automatic changelog update 2024-08-15 14:31:47 +00:00
to4no_fix
6567fa36e4 Adding shock collar and electropack (#30529)
* Adding shock collar with the new ShockOnTrigger

* Cleaning and updating the shock collar

* Add StripDelay datafield to ClothingComponent

* Adding SelfUnremovableClothingComponent

* ShockCollar Update

* Correction of the shock collar

* Correction of the shock collar 2

* Renaming the DamageSpecifier DataField to Damage

* Fixing the damage field in ShockCollar

* Cleaning the ShockCollar

* Renaming ShockCollar to ClothingNeckShockCollar

* Adding ClothingNeckShockCollar as a stealTarget to a thief

* Fixing a typo of the sprite path in ClothingNeckShockCollar

* Cleaning the ShockOnTriggerComponent

* Revision of SelfUnremovableClothing

* Adding a ClothingBackpackElectropack

* Sprite fix

* Code review

* Shock Collar sprite update

* add commit hash

---------

Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
2024-08-15 10:30:39 -04:00
PJBot
02eed07e21 Automatic changelog update 2024-08-15 12:35:49 +00:00
FATFSAAM2
b17f1c336b Figurine voicelines (#30865)
* initial commit

* tweaks to the new voice lines

capitalizations of I's and the Y on hos 3 voice lines
also changed the captain figuring voice line 2 to something completely different

* capitalized one letter

man i really suck at capitalization

* more capitalization

i never thought this pr would need so many changes

* changed one comma to a dot

* Update Resources/Locale/en-US/datasets/figurines.ftl

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

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
2024-08-15 22:34:41 +10:00
Plykiya
af01d8fbff Correct relay mover entity (#31040)
* Relay entity

* Server
2024-08-15 22:30:53 +10:00
Emisse
d752702092 meta update (#31037) 2024-08-15 02:33:35 -06:00
Emisse
caece1473c update lox (#31035)
* lox update

* real
2024-08-15 02:02:43 -06:00
Emisse
19e8c2eb3e fland update (#31034) 2024-08-15 02:02:35 -06:00
Emisse
54d42acec8 bagel update (#31033) 2024-08-15 02:02:20 -06:00
Brandon Hu
c1d41267e5 tweak(experiment): Reduce diamond stack from 30 to 1 (#31025) 2024-08-15 09:51:22 +03:00
Brandon Hu
b53ed58515 tweak(hospital): Reduce diamond stack from 30 to 1 (#31027) 2024-08-15 09:50:09 +03:00
lzk
bc5bc37346 Fix top bun desc (#31019)
FIX
2024-08-15 09:38:28 +03:00
Spessmann
0fd7b69daa Cog fixes (again) (#31030)
* more cogmap fixes

* updated cog a bunch
2024-08-14 21:39:24 -06:00
Emisse
1bde7f3874 packed update (#31024)
* packed update

* bartender spawn
2024-08-14 20:40:33 -06:00
PJBot
20334f9115 Automatic changelog update 2024-08-15 01:52:01 +00:00
PoorMansDreams
793eb87f31 Adds a Dinky Star Sticker for security officers (#29767)
* Sec-Star-TASTIC

* fix license

* now counts as trash

* Sticks to people + wearable

* no more sticky

* no more sticky

* mystery-space

* Update Loadout Yaml
2024-08-15 11:50:55 +10:00
PJBot
df82c0ac8e Automatic changelog update 2024-08-15 00:39:29 +00:00
Eris
e5d2738a26 Sedin (#27110)
* Sedin

* almost forgot this.

* what

* Did a few fixes, tried to readd popup

* Fix popup, ready for merge

* minor change

* Generalize seed removal/destruction system, add popups in general.

* fix

* fix (again)

* deadcode is no more

* Sedin buff (nerfing the recipe soon)

* godont

* webedit moment

ok listen I had to nuke my setup because my computer was being Dumb As Shit so let me do this ONE webedit

* CRIKEY

* Revert "CRIKEY"

This reverts commit 1c6959ffd9ba8bb1e97ccf2eb0b37b3ff4ec82ee.

I was not supposed to commit that >:/

* Alright, it should be good now but I havent tested it

* ready for merge/review

* prob -> probability

* Requested changes nodoc

* Review nodoc

* documento

* thumb up emoji
2024-08-15 10:38:21 +10:00
to4no_fix
b48ac8439d Localization of the slot (#31008)
localization of the slot
2024-08-14 20:06:32 -04:00
Brandon Hu
dc2c02cd6a fix(origin): Make medbay exit button functional (#31001) 2024-08-14 13:48:34 -06:00
Pieter-Jan Briers
6c008be126 Fix creator prefix for Cog (#30995)
Now uses the same one as Atlas.
2024-08-14 13:48:16 -06:00
PJBot
84f229a149 Automatic changelog update 2024-08-14 19:48:11 +00:00
themias
cd8bd908da Fix lizards being unable to eat custom burgers (#31005)
* Fix lizards being unable to eat custom burgers

* ensure tag comp
2024-08-14 13:47:03 -06:00
Velcroboy
890c0eeb99 Adds a random smokable spawner (#30870)
* Textures to make lamps mappable to East/West.

* Adds a random smokable spawner

* remove double derma

* Increase chance for just basic cig

* Update to enitity tables

* Tweak values

* Remove space

---------

Co-authored-by: Jeff <velcroboy333@hotmail.com>
2024-08-14 09:44:21 -04:00
DrSmugleaf
1fb03e9074 Fix popups not working properly with an eye offset (#30990) 2024-08-14 09:38:36 -04:00
PJBot
07451eb92b Automatic changelog update 2024-08-14 13:05:08 +00:00
Ed
e659eb992f Custom Taco & Kebabs - FoodSequence 2 (#30905)
* control layer ordering

* sprite update

* taco!

* taco naming gen

* fix separator naming

* some proto fixes

* default naming separation

* fix taco naming

* rat update

* hamsterburger

* organs taco

* remove from spawners

* fixes

* fix separators and lemons

* Update food-sequence.ftl

* randomization offset

* fix

* kebabs refactor

* fix

* skewer weapon

* Update food_single.yml

* Update migration.yml

* suppermatter

* hamlet centered

* hamsburger

* Update dead-0.png

* 20 -> 4 burger layers max

* Update burger.yml

* canReact: false

* capfruit update

* Update burger.yml

* Update burger.yml

* Update produce.yml

* some fixes

* Update Resources/Textures/Objects/Consumable/Food/taco.rsi/meta.json

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

* Update Content.Shared/Nutrition/Components/FoodSequenceStartPointComponent.cs

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

* Update Content.Shared/Nutrition/Components/FoodSequenceStartPointComponent.cs

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

* chicken

* Update food-sequence.ftl

* documentation

* fixes

* Update meat.yml

* Update meat.yml

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
2024-08-14 15:04:00 +02:00
Pieter-Jan Briers
8ac0246998 Enable worldgen by default in non-wizden config (#30996)
Enable worldgen by default

If somebody can explain to me WHY this isn't enabled by default, that would be epic. Otherwise we're merging this
2024-08-14 21:56:45 +10:00
DrSmugleaf
12b6cf5e5d Make ahelp.admin_prefix default false (#30994)
* Make ahelp.admin_prefix default false

* More disabling

Game defaults should match wizden as closely as possible.

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-08-14 13:22:10 +02:00
Pieter-Jan Briers
059e3ef72f Update submodule to v230.0.1 (#30973)
Fix replays due to vulnerability update
2024-08-14 13:18:41 +02:00
slarticodefast
8cb2cfc0af fix the admin verbs trying to kill players by filling their lungs with plasma (#30991)
fix the admin tools trying to kill players by filling their lungs with plasma
2024-08-14 12:56:38 +03:00
PJBot
600d5bfae0 Automatic changelog update 2024-08-14 06:27:00 +00:00
Alzore
1ded394ffb Add a saline syringe to the combat medical kit (#29954)
saline
2024-08-14 16:25:54 +10:00
PJBot
452be33229 Automatic changelog update 2024-08-14 05:22:09 +00:00
IProduceWidgets
07ec00ed05 refactor event schedulers to use explicit game rules (#29320)
* works, still has testing values, im sure I did stupid shit.

* shitvent crapfactor

* snap extra word out of existence

* shit I died of old

* remove useless inaccurate design comments

* Oopsie, handle requirement params in RandomRuleSystem too

* I'm a slash slinging hasher

* Address reviews, add admin alerts I forgor

* EntityMan saves the day

* address reviews 1

* eh, I actually don't care about the cargo gifts thing.

* started

* Do reviews

* you actually meant 1.2 lmao

* dependency inheritance is a fickle bitch

* I have no idea.

* Threads are for sheets not computers.

* fix traitor rule test

* fix round type tattling

* break things

* It worky

* Toolshed makes we want to drink depresso.

* Finished?

* remove debug values

* timings

* use defaults

* alphabetize

* bobby drop tables

* Float required fr fr

* continue

* more continence

* uno mas

* obsolution

* cleanup and documentations

* Yell at self

* use the right value defaults

* housekeeping
2024-08-14 15:21:01 +10:00
Spessmann
58a33b2593 Various cog fixes (#30987)
various cog fixes
2024-08-13 20:23:56 -06:00
Errant
9de922adba development build ghostrole lottery changes (#30980)
debug ghostrole lottery changes
2024-08-14 11:57:09 +10:00
NotSoDamn
381e0d6f93 No weather on concrete tiles (#30952)
* concrete weather

* miss
2024-08-13 15:39:25 -06:00
IgorAnt028
204f3e6ae3 Fix bug with rotating shelves (#30978)
* Main

* Update shelfs.yml

* Little bug fix
2024-08-13 15:38:28 -06:00
TheWaffleJesus
1e029efd2a cleaning up bounty descriptions (#30981)
fixed up dumb text
2024-08-13 15:37:28 -06:00
Emisse
a186b91631 cog update (#30982) 2024-08-13 15:37:08 -06:00
PJBot
0d5d9f92cd Automatic changelog update 2024-08-13 20:33:59 +00:00
PoorMansDreams
02521d59dd Throngler Plushie Sprite (#30969)
Throngonkler Plush sprite
2024-08-13 23:32:53 +03:00
PJBot
44db0bfb05 Automatic changelog update 2024-08-13 19:28:31 +00:00
Spessmann
208b1d23b8 Added Cog(map) and its own evac shuttle (#30840)
* Added new map Cog and its own evac shuttle

* removed cog from default.yml so all checks will pass

I added cog here because i thought it might need to be here for map choosing??????????? ill add it back if needed

* updated cog and fixed a bunch of stuff that was not visable in the image, also nerfed the singo so it no longer solos

* added renault spawner that was missing and changed some minor stuff

* moved a escape pod from med/sci hallway to northern maints

* added shutters to the brig and added vents and scrubbers where they were missing in the halls

* extended salv's docking port so they can dock

* Fixed according to cog review thread

* fix pools

* fix PostMapInitTest

* Update MapLoadBenchmark.cs

* added cap and hop spawn that i just forgot to add???????????????????

* fixed again

---------

Co-authored-by: Emisse <99158783+Emisse@users.noreply.github.com>
2024-08-13 13:27:24 -06:00
PJBot
14dfeab306 Automatic changelog update 2024-08-13 18:32:39 +00:00
Cojoke
5d6baea7d3 Make the Borgs Names Appear When they Make an Announcement (#30107)
* Make the Borgs Names Appear When they Make an Announcement

* bwomp

* re-order

* Use Name instead of metadata

* goiup
2024-08-13 11:31:33 -07:00
Brandon Hu
dba0ea76b4 tweak(Shuttle_honki): Remove DeviceQuantumSpinInverter from honki shuttle. (#30618) 2024-08-13 23:50:41 +10:00
PJBot
c729079408 Automatic changelog update 2024-08-13 13:37:52 +00:00
slarticodefast
6a462221ff Add keybinds for rotating and flipping objects (#30540)
* add keybinds for rotating and flipping objects

* no popup for verbs
2024-08-13 23:36:41 +10:00
pigeonpeas
7153e43946 Fixes the grey mantle (#30971)
add the thing i missed
2024-08-13 22:13:50 +10:00
Ed
245e111226 Merge pull request #411 from crystallpunk-14/ed-13-08-2024-license
Sublicense
2024-08-13 15:10:51 +03:00
Ed
fab706f0cb Update README.md 2024-08-13 15:06:19 +03:00
Ed
23b61850a8 Sublicense 2024-08-13 15:04:28 +03:00
PJBot
79315c2531 Automatic changelog update 2024-08-13 11:09:46 +00:00
Ubaser
135d52406a Add capfruit, a gatfruit mutation. (#30850)
* add base

* add fake suffix and flavor fix

* address some reviews

* parent it

* fix test fail

* add FoodSequence support and address small review
2024-08-13 13:08:40 +02:00
PJBot
0998645077 Automatic changelog update 2024-08-13 10:55:06 +00:00
Ed
cc180e54e6 Sliceable doAfter (#30824)
* init

* wtf git
2024-08-13 13:53:59 +03:00
PJBot
bf7c32737b Automatic changelog update 2024-08-13 09:21:19 +00:00
pigeonpeas
329a0a875b Grey mantle in winterdrobe (#29774)
* add the mantle

* fix a few things

* fix merge conflict stuff, remove from winterdrobe and lathe.

* Add mantle to loadouts (20 hour requirement)
2024-08-13 19:20:12 +10:00
PJBot
d49d78cebf Automatic changelog update 2024-08-12 23:26:37 +00:00
nikthechampiongr
dcc11873f9 Fix permission check for whitelists in player panel (#30948) 2024-08-12 18:25:31 -05:00
PJBot
ab1613315d Automatic changelog update 2024-08-12 23:22:00 +00:00
LeoSantich
c9366b818c Lines 88 and 89, updated 'narsie' and 'ratvar' to 'Nar'Sie' and Ratva… (#30954)
Lines 88 and 89, updated 'narsie' and 'ratvar' to 'Nar'Sie' and Ratvar, per issue #30719.
2024-08-13 09:20:53 +10:00
Cojoke
0c1cfe8f18 Fix Reagent Fire Stacks (#30766)
* Fix Reagent Fire Stacks

* Comments on code

* little nicer wording?
2024-08-12 10:48:39 -04:00
Token
d4c0155d66 Fix DoDrop to DropNextTo in container cases (#30911)
* Fix DoDrop to DropNextTo in container cases
DoDrop is too heavy to calculation. In any other case we should use alternatives, for example DropNextTo helper method

* codestyle change
2024-08-12 10:18:26 -04:00
Ed
6db3ccb342 Merge pull request #387 from crystallpunk-14/ed-05-08-2024-upstream
Ed 05 08 2024 upstream
2024-08-12 15:07:14 +03:00
Gagarinten-Noverdo
7491c6874d Вода теперь не в папке флоры, а в папке Floor. Вода теперь тушит. (#410) 2024-08-12 15:01:56 +03:00
Ed
5af5aaa651 fix 2024-08-12 14:52:40 +03:00
Ed
d9336781f1 Merge remote-tracking branch 'upstream/master' into ed-05-08-2024-upstream
# Conflicts:
#	Content.Shared/Inventory/InventorySystem.Equip.cs
#	Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs
2024-08-12 14:40:15 +03:00
Ed
ff8d294af5 Update PostMapInitTest.cs 2024-08-12 14:21:53 +03:00
Ed
47dc84e48e Update debug.toml 2024-08-12 13:56:27 +03:00
Chief-Engineer
691e37c0db Update space law for contraband upate (#30937)
* update space law

* missed a thing
2024-08-12 04:58:20 -05:00
Ubaser
c02aeebdf1 Add conducting gloves to the maintenance loot table (#30904)
* add

* lower weight
2024-08-12 03:06:55 -06:00
Plykiya
883dc998e6 First Review Pass Contraband Fixes (#30930)
* First pass contra fix

* bam

* More

* Security crates are now marked as security restricted

* Syndicate raid suit is contra

* damn I forgot gatfruit
2024-08-12 02:17:26 -06:00
Ubaser
7e17063b86 Removes variant cube migration (#30934)
add
2024-08-12 02:16:59 -06:00
PJBot
ca75d5a3e3 Automatic changelog update 2024-08-12 07:15:52 +00:00
Ubaser
c6209cc725 Make mantles loadout only. (#30929)
* remove cloaks and mantles from dresser

* remove mantles from lathe

* add role timer loadout thing

* add back cloak dresser

* fix test fail

* potentially fix test fail (?)
2024-08-12 01:14:45 -06:00
Ubaser
d220249902 Hotfix #30291 (#30932)
* add

* Revert "add"

This reverts commit 2b4ae87c4fbdd80d224ab19a655c9c547fcc43fd.

* add
2024-08-12 17:04:21 +10:00
PJBot
463a5299db Automatic changelog update 2024-08-12 06:22:03 +00:00
DieselMohawk
6759d6a614 Sec Vest & Helmet Resprite (Attempt 2) (#30291)
* Sec Vest & Helmet Resprite

* Made Helmet side sprites better

* Vest Alterations In Line With Feedback

* Feedback Based Alterations PT.2 (How did I miss that?)

* Alterations in line with Art Director Feedback
2024-08-12 16:20:56 +10:00
PJBot
9f550002d1 Automatic changelog update 2024-08-12 03:58:57 +00:00
Kara
485caa4553 Contraband marking & examining (#28688)
* System & loc strings

* pass over syndie contraband

* fixes

* grand theft pass

* contrabandexamine -> contraband

* examine text generation update

* all composition parents necessary

* bring back minor contra so it has a less confusing message

* minor

* weapon pass

* jumpsuit pass

* feet pass

* AUUUUUUUUUGHHHHHHHHHHHHHHHHHH

* head

* AUUUUGH

* ear

* belt

* back

* fix

* bro

* rename for more clarity

* do da review

* add cvar for contraband examine

---------

Co-authored-by: EmoGarbage404 <retron404@gmail.com>
Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
2024-08-11 23:57:49 -04:00
Mervill
9f180e4f62 Don't show the Meteor Swarm announcement to players in the lobby. (#30922) 2024-08-12 12:13:28 +10:00
Nemanja
a0a1421da4 give trash a reusable entity table (#30921) 2024-08-12 12:07:39 +10:00
PJBot
15812d2a08 Automatic changelog update 2024-08-12 01:50:16 +00:00
Alzore
bcd7085a3e Add diamond mining drills, buff regular mining drills (#30814)
* why do I have to change so much just to add drills

* diamon
2024-08-12 11:49:09 +10:00
Brandon Hu
dea9a637fb chore(grammar): correct some mistakes (#30916) 2024-08-12 08:25:45 +10:00
PJBot
86f4346c05 Automatic changelog update 2024-08-11 22:13:37 +00:00
Plykiya
c688da00a0 Add execution component to all energy sword weapons (#30909)
Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-12 08:12:31 +10:00
Zachary Yona
7aaac2c179 Minor sprite fix to inhand med hardsuits and HoS turtleneck jumpsuit (#30918)
* Fixes inhand hardsuit cross color from https://github.com/space-wizards/space-station-14/issues/26516#issue-2213917971

* Fixed HoS Turtle not covering all butt pixels

---------

Co-authored-by: Zachary Yona <magicalusf@gmail.com>
2024-08-12 07:45:28 +10:00
Brandon Hu
aa15a9a839 chrore(ageRequirement): fix grammar. atleast -> at least (#30914) 2024-08-11 22:28:07 +02:00
Brandon Hu
46f631fa11 tweak(AgeRequirement): Make the requiredAge more sensible (#30913) 2024-08-11 21:47:26 +03:00
Killerqu00
65521696c8 Remove rest of Geras files (#30912)
remove remaining geras stuff
2024-08-11 14:01:00 -04:00
Ed
8ecae1288f Merge branch 'master' into ed-05-08-2024-upstream 2024-08-11 18:13:17 +03:00
mhamster
e26e662023 Small json files fixes (#30907)
Small json fixes

Resources/Textures/Interface/Default/meta.json: trailing comma
Resources/Textures/Objects/Specific/Hydroponics/attribution.json: proper json
Resources/Textures/Tiles/Planet/Snow/meta.json: trailing commas
2024-08-11 23:26:45 +10:00
Tornado Tech
3f3ca359ce Fixed namespace 2024-08-11 23:23:55 +10:00
Tornado Tech
beec816357 Lamo 2024-08-11 23:13:06 +10:00
Ubaser
58fd50c369 fix a capitalisation mistake (#30908)
ad
2024-08-11 22:51:19 +10:00
PJBot
6ec6a28aeb Automatic changelog update 2024-08-11 10:27:43 +00:00
Moomoobeef
28efd76e82 adds a throngler plushie, and adds it to the grand lotto (#29978)
* adds a throngler plushie, and adds it to the grand lotto

* this was supposed to be in the initial commit but I apparently forgot to save so anyway here it is

* make throngler plushie more rare

how about I split the difference:
original throngler: 0.0001
other plushies: 0.01
thongler plushie: 0.0005

* removed inheritied soundhit
2024-08-11 20:26:37 +10:00
Brandon Hu
31c5d3555e fix(Commands): Improve Localization of commands. Standardize some behaviors. (#30362)
* I should be studying for school but that is sofucking boring, I will pass my class no matter, however getting an A might be a challenge. My gpa is important but is the tourture for 1 point of GPA worth it? The american government says yes but they are responsible for the majority of all genocides that have ever been conducted since the dawn of man

* ugh

* ugh
2024-08-11 19:46:57 +10:00
PJBot
e0f292921f Automatic changelog update 2024-08-11 09:24:20 +00:00
slarticodefast
bd5d415575 Make the sentient plant mutation non-copyable to other plantholders (#29133)
make the sentient plant mutation not propagate by clipping, using the seed extractor or cryoxadone
2024-08-11 19:23:14 +10:00
osjarw
456410239e Reorder priorities in MeleeCombatCompound (#30066)
Reordered MeleeCombatCompound to improve unpulling
2024-08-11 19:22:46 +10:00
PJBot
8c4e5c5848 Automatic changelog update 2024-08-11 09:20:06 +00:00
Token
ad71d8cb2f Pax now refresh instead of stacking pacified forever (#30778)
Pax refreshes instead of stacking pacified forever
2024-08-11 19:19:00 +10:00
Plykiya
b6d04f884c EntityTables for sec belts and webbing (#30900)
* EntityTables for sec belts and webbing

* update name

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-11 19:16:46 +10:00
PJBot
93c9d49f9b Automatic changelog update 2024-08-11 09:16:34 +00:00
lzk
f93fe05f4c Make RCD ui activateable only in hand (#30861) 2024-08-11 19:15:28 +10:00
blueDev2
b16e57ee94 Add MinHealth Construction Condition (#30892)
* Add Min Health Condition and associated Locale

* Add Comment
2024-08-11 19:14:35 +10:00
PJBot
aabaaf1fec Automatic changelog update 2024-08-11 09:05:48 +00:00
BombasterDS
67253a3fe1 Change suit sensors on other players (#29668)
* Suit sensors can be turned off on other players

* less doafter time + interaction (nostate) check

* code cleanup

* code cleanup 2
2024-08-11 19:04:42 +10:00
PJBot
c8f1c22ff2 Automatic changelog update 2024-08-11 06:22:41 +00:00
Plykiya
6aed90ed75 Rename cooking shelf, improve content whitelist (#30858)
Rename cooking shelf to kitchen shelf

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-11 16:21:34 +10:00
Mervill
d7b5ae061a Don't show Station Event announcements to players in the lobby. (#30886)
* Don't show Station Event announcements to players in the lobby.

* fix pr

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-08-11 16:09:33 +10:00
Plykiya
8d72e7d932 Make head of department lockers use EntityTables (#30854)
* EntityTables for head of department lockers

* I hope this works

* even smaller

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-11 13:12:26 +10:00
PJBot
0dc2d0acd0 Automatic changelog update 2024-08-11 03:07:02 +00:00
beck-thompson
ee922e37f6 Replaced some AddMarkups with AddMarkupOrThrow (#30632)
* First commit

* Silly me

* bruh
2024-08-11 13:06:33 +10:00
Scribbles0
220aff21eb Melee Executions (#30104)
* melee executions

* fix damage bug

* cleanup

* address reviews hopefully

* resistance bypass mechanic

* component changes

* self executions (not finished yet)

* self execs part two

* ok i fixed things (still not finished)

* finish everything

* review stuff

* nuke if (kind = special)

* more review stuffs

* Make suicide system much less hardcoded and make much more use of events

* Fix a dumb bug I introduced

* self execution popups

* Integration tests

* Why did they even take 0.5 blunt damage?

* More consistent integration tests

* Destructive equals true

* Allow it to dirty-dispose

* IS THIS WHAT YOU WANT?

* FRESH AND CLEAN

* modifier to multiplier

* don't jinx the integration tests

* no file-scoped namespace

* Move the rest of execution to shared, create SuicideGhostEvent

* handled

* Get rid of unused code and add a comment

* ghost before suicide

* stop cat suicides

* popup fix + small suicide change

* make it a bit better

---------

Co-authored-by: Plykiya <58439124+Plykiya@users.noreply.github.com>
2024-08-11 13:05:54 +10:00
Mervill
c25c5ec666 Remove uses of Component.Owner from AmbientSoundSystem (#30842)
Remove Component.Owner from AmbientSoundSystem
2024-08-11 12:39:37 +10:00
ShadowCommander
2f77d0d4f3 Add a component that inserts the held item when no interaction happens on the stored item (#29823)
* Insert when held item has no interaction with stored item

* Decouple inserting on failure

* Add component that stores the used entity when no interaction happened

* Add prediction
2024-08-11 12:29:44 +10:00
metalgearsloth
d7c3ba8409 Add some container checks (#30896)
Climbable container probably getting checked up the callstack so just check these ones.
2024-08-11 12:28:07 +10:00
metalgearsloth
9be61bfaa5 Make followed session-specific (#30770)
* Make followed session-specific

* misimport
2024-08-11 12:26:32 +10:00
github-actions[bot]
84217011a9 Update Credits (#30891)
Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>
2024-08-11 11:45:49 +10:00
Mervill
32e315c68f Refactor LightBehaviorSystem to remove obsolete code (#30890) 2024-08-11 11:45:37 +10:00
beck-thompson
09c8d4abd4 Torch no longer gives errors (#30884)
First commit
2024-08-10 20:05:35 -04:00
PJBot
f76fa0e5ae Automatic changelog update 2024-08-10 21:07:57 +00:00
Flareguy
179534f18d Vox Minor Clothing Update 2: Electric Boogaloo (#30838)
* more vox masks n stuff

* cluwne

* fix

* merc gas mask

* bunny tail height fix
2024-08-11 00:06:50 +03:00
PJBot
a99900fcd8 Automatic changelog update 2024-08-10 19:51:16 +00:00
Brandon Hu
63967bec88 tweak(Shuttle_honki): Replace clown spiders with honkbots (#30617) 2024-08-10 13:51:00 -06:00
Preston Smith
81371b90d1 Updated Core boxing ring beacon (#30800)
Updated boxing ring beacon to be correctly labeled
2024-08-10 13:50:08 -06:00
Brandon Hu
76d60140f6 add(meta): Add missing airvent to airlock (#30871) 2024-08-10 13:49:01 -06:00
Brandon Hu
d524d61b22 tweak(oasis): Lower the engineering borg spawn (#30874) 2024-08-10 13:48:47 -06:00
Brandon Hu
8afec58a0c chore(atlas): Remove EntityStorageComponent (#30876) 2024-08-10 13:48:19 -06:00
Brandon Hu
95034512c1 chore(marathon): Remove EntityStorageComponent (#30877) 2024-08-10 13:48:10 -06:00
Brandon Hu
6b5eb93fe7 chore(omega): Remove EntityStorageComponent (#30879) 2024-08-10 13:47:52 -06:00
Brandon Hu
6803021623 chore(meta): Remove EntityStorageComponent (#30883) 2024-08-10 13:47:41 -06:00
Brandon Hu
e569a78f17 chore(bagel): Remove EntityStorageComponent (#30881) 2024-08-10 13:47:28 -06:00
PJBot
cfd8ef49d9 Automatic changelog update 2024-08-10 19:32:38 +00:00
Ed
4d75cb54c8 Custom burgers - FoodSequence system (#30755)
* setup foodsequence

* name generation and max layers limit

* move to nutrition

* move code to serverside

* move to appearance data

* marked fields as required

* Update foodsequence.yml

* b

* burgeers!

* Update produce.yml

* Update meat.yml

* Update burger.yml

* fix duplicate naming

* Update Resources/Locale/en-US/nutrition/components/food-sequence.ftl

Co-authored-by: Hrosts <35345601+Hrosts@users.noreply.github.com>

* merge flavor profiles

* make food trash List<>

* merge trash

* Update FoodComponent.cs

* Update FoodComponent.cs

* organs and cannabis support

---------

Co-authored-by: Hrosts <35345601+Hrosts@users.noreply.github.com>
2024-08-10 22:31:32 +03:00
Brandon Hu
707daa1a5e tweak(snowy_labs): Remove non-valid holoparasite (#30875) 2024-08-10 21:53:56 +03:00
Kukutis96513
b6e10a0b7b Reach tweaks reattempt 2 (#30782)
* uhhh fix stuff maybe

* - added a fax machine
- made botany slightly bigger to accommodate a nutrimax vending machine
- added air alarms, scrubbers and a waste pipe network
2024-08-10 12:25:12 -06:00
PJBot
084812cb44 Automatic changelog update 2024-08-10 18:18:03 +00:00
lzk
9ba038d04f Standartize clothing recipes (#29315) 2024-08-11 04:16:56 +10:00
MerrytheManokit
249d3cd74d Remove meta voiceline from the passenger figure (#30868)
Update figurines.ftl
2024-08-11 02:37:33 +10:00
deathride58
7e44e75d74 #30851 followup - murderficates links (#30863)
kneecaps links while we're at it
2024-08-11 02:15:05 +10:00
lzk
6926033423 Fix some locales (#30860)
* Fix some locales

* forgor
2024-08-10 18:19:28 +03:00
Nemanja
fc1446e73a Cursed Mask (#29659)
* Cursed Mask

* extra expressions

* block ingestion

* mind returning

* okay fix the removal shit
2024-08-10 11:14:58 -04:00
PJBot
53058df8b9 Automatic changelog update 2024-08-10 15:01:27 +00:00
Ubaser
c812bd8ff4 Partially revert light tube sprites (#29091)
* add

* reduce contrast
2024-08-10 11:00:21 -04:00
Nemanja
fdbf2a229e Infer type for EntSelector (#30856)
Infer type for EntSelector.cs
2024-08-10 10:17:45 -04:00
PJBot
2b4acde2c8 Automatic changelog update 2024-08-10 12:56:26 +00:00
Unkn0wn_Gh0st
d340c5c844 ERT Chaplain Loadout fixer (#30855)
Chaplain fixy
2024-08-10 15:55:20 +03:00
PJBot
f642a01f96 Automatic changelog update 2024-08-10 12:53:00 +00:00
TakoDragon
9bc1fabdb3 Added suitskirt for psychologist (#30709)
* Added jumpskirt

* added locale

* Added spaces in code

* Updated the meta
2024-08-10 08:51:54 -04:00
PJBot
4e1feded66 Automatic changelog update 2024-08-10 12:49:01 +00:00
shamp
563190f4d8 Add Rat Kings wideswinging (#30808)
Update regalrat.yml
2024-08-10 15:47:55 +03:00
slarticodefast
0a4b220854 remove TerminatorRoleComponent (#30733) 2024-08-10 08:46:49 -04:00
Unkn0wn_Gh0st
bf2bd9a33e Change siren.ogg to gamma.ogg (#30836) 2024-08-10 15:31:28 +03:00
Ed
7e70859d2e Update README.md 2024-08-10 13:19:08 +03:00
Ed
76b89b8dc5 licenses (#405) 2024-08-10 13:15:19 +03:00
Nim
bf256b25e9 Two new spells of light (#404)
* light spell

* fix
2024-08-10 13:08:14 +03:00
deathride58
7f23634450 [S] hopefully prevents a repeat of 4 am on 8/10/2024 in the #server-leviathan-use channel (#30851)
cant test this because the documentation sucks ass but fuck it we ball
2024-08-10 19:18:13 +10:00
PJBot
45214c2063 Automatic changelog update 2024-08-10 03:31:49 +00:00
Alzore
4c678e4955 Bent pipes deal 8 thrown damage (#30634)
8 pipe throw damage
2024-08-09 23:30:43 -04:00
PJBot
46398647bf Automatic changelog update 2024-08-10 03:24:53 +00:00
Ko4ergaPunk
1fe46a7210 Changed chemistry airlock color (#30666)
* chem

* re-check

* re-check. again

* checks just die. last try

* re-check :blessrng:
2024-08-09 23:23:46 -04:00
PJBot
1745000e0d Automatic changelog update 2024-08-10 02:13:47 +00:00
Nemanja
437c861622 Entity Tables (EntitySpawnEntry replacement) (#30579)
* Entity table code

* entity table examples

* fix dat shit

* access

* tests tests tests

* sloth review
2024-08-09 22:12:40 -04:00
PJBot
b692ab1850 Automatic changelog update 2024-08-10 01:11:47 +00:00
lzk
45a2327ef5 Revert "Make hotplate and grill anchorable on table part 2" (#30830)
Revert "Make hotplate and grill anchorable on table part 2 (#29454)"

This reverts commit 268cab97f5.
2024-08-10 11:10:39 +10:00
DrSmugleaf
ee7644c631 Network WallMountComponent (#30837) 2024-08-10 11:08:56 +10:00
Tornado Tech
9f4932a8af Remove day cycle shit (#400) 2024-08-10 01:12:13 +03:00
Jaraten
32ce8f2164 directional window (#401)
* pu-pu-piri-pu

* fx remove
2024-08-10 01:10:22 +03:00
lzk
c80c26221b Crowbars fix (#30828)
* Crowbars fix

* oops
2024-08-09 14:19:04 -06:00
PJBot
218fef37c8 Automatic changelog update 2024-08-09 19:30:06 +00:00
Ubaser
ed3ec4dc5f Crowbar variants (#28988)
* add

* comma

* sprites

* yaml-ify

* add s

* lathe and engi belt

* yes

* fix maybe

* progress

* nearly there

* fix

* fix yaml linter having a stroke
2024-08-09 13:29:00 -06:00
PJBot
bd5b13e2f3 Automatic changelog update 2024-08-09 15:44:09 +00:00
Plykiya
1e824d704f Buckling an entity requires a do-after (#29621)
* Buckling an entity requires a do-after

* Works but feels like bad code?

* Cleanup

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-10 01:43:02 +10:00
Vasilis
873c314aec Add arm64 support to packager for windows and osx (#29723)
Wanted to package a server for the funny on my macbook and noticed it was not working cause of this... now it will workie :3
2024-08-09 23:01:13 +10:00
MureixloI
3f4e093eaf [Sprites/Fix] In hand for BlueSpace beaker & CMO's equipped cloak (#28719)
* [Fix] CMO's equipped cloak

* [Fix] CMO's equipped cloak

* [Sprites] In hands for bluespace beaker

* [Sprites] In hands for bluespace beaker

* [Sprites] In hands for bluespace beaker

* [Sprites] In hands for bluespace beaker
2024-08-09 22:02:26 +10:00
Plykiya
9f8ff021ea Fix for stripping doafters (#30821)
Apparently I need to check if they're holding an item

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-09 22:02:06 +10:00
Jaraten
f70e3051ed update update (#403) 2024-08-09 14:08:50 +03:00
PJBot
c3e96b3770 Automatic changelog update 2024-08-09 10:34:01 +00:00
Ubaser
c1797b769c Resprite the equipped gas canister suit slots (#30809)
* add

* red and generic

* add meta
2024-08-09 20:32:55 +10:00
Ed
e3e142592a add art (#402) 2024-08-09 11:03:39 +03:00
deltanedas
ed8bcb0a66 improve job special (#30753)
* cleanup of AddComponentSpecial

* add RemoveComponentSpecial

* require

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-08-09 17:43:57 +10:00
eoineoineoin
d0a2187a9b Improve Criminal Records Computer usability (#30292)
* Avoid destroying and recreating record list on every update message

* Add early-out on nullptr input

* Remove sussy null suppress

---------

Co-authored-by: Eoin Mcloughlin <helloworld@eoinrul.es>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-08-09 17:41:29 +10:00
metalgearsloth
0b2fa941a7 Revert "setgamepreset command rework" (#30807)
Revert "setgamepreset command rework (#30756)"

This reverts commit de86fdb373.
2024-08-09 17:39:45 +10:00
metalgearsloth
1649ed45bd Add access to gun components (#30688)
* Add access to gun components

Found from an rmc14 PR.

* Admin verbs proving why access needs to exist

* Someone is probably going to post this pr to le reddit and complain about self-merges.
2024-08-09 17:39:27 +10:00
PJBot
b8fc879cd7 Automatic changelog update 2024-08-09 07:07:43 +00:00
Ko4ergaPunk
55d400b1b9 New decals for mapping (#30374)
* new decals

* license

* re-check
2024-08-09 17:06:37 +10:00
PJBot
0b256a025a Automatic changelog update 2024-08-09 07:05:25 +00:00
metalgearsloth
ecf278a89b Make clickable 1% nicer (#29706)
* Make vox roundstart

I believe all the issues are fixed.

* Click detection bandaid

* Make clickable 1% nicer

Still bad. Still doesn't handle multi-viewports well.
2024-08-09 17:05:12 +10:00
Errant
de86fdb373 setgamepreset command rework (#30756)
* gameticker.gamepreset namespace

* setgamepreset now has a finite duration

* comments, cleanup
2024-08-09 17:04:19 +10:00
Ilya246
3c6df0e39f add ghost role raffle to sentient artifact (#30746)
arti raffle
2024-08-09 16:57:43 +10:00
lzk
268cab97f5 Make hotplate and grill anchorable on table part 2 (#29454)
* Make hotplate and grill anchorable on table part 2

* Update hotplate.yml
2024-08-09 16:28:45 +10:00
Brandon Hu
c46a4e057a Fix(CuffableSystem): Check if cuffs can be transferred(dropped) (#30465)
* ugh

* Revert "ugh"

This reverts commit 8b5b0e24bcaa6c5cc9229c17eb138cdb476f0c9e.

* Revert "Automatic changelog update"

This reverts commit 4f8b634f38.

* Revert "Revert "Automatic changelog update""

This reverts commit 9fc2a3307a06e5f7d19bbb8d29e45b9a1d1470c8.

* Revert "Revert "Revert "Automatic changelog update"""

This reverts commit 1ae733bf308d7285159dd1fafd7f17101d8f4ced.

* Revert "Revert "Automatic changelog update""

This reverts commit 9fc2a3307a06e5f7d19bbb8d29e45b9a1d1470c8.

* Revert ""

This reverts commit 1cc0953333cf3d4f2cad5fae2f5c562ae99563ae.

* Revert ""

This reverts commit edca368a8444fd0fd403333695668d1c1db3726a.

* Revert "Revert "ugh""

This reverts commit 3ac15dfb07c7ae43b797724f85b76193ecd7a0e1.

* Revert "Revert "Revert "ugh"""

This reverts commit b952a4556397c8abcc7c319d736fab7ae48073a2.

* Revert "Revert "Revert "Revert "ugh""""

This reverts commit ff1a151571dea653d78ac2bda75eb11209da8d96.

* Revert "ugh"

This reverts commit 8b5b0e24bcaa6c5cc9229c17eb138cdb476f0c9e.

* Update SharedCuffableSystem.cs
2024-08-09 16:26:04 +10:00
PJBot
efe36c3bf8 Automatic changelog update 2024-08-09 06:25:25 +00:00
Repo
31cd2a0927 Add admin logs to Rehydration comp. (#30597) 2024-08-09 16:24:19 +10:00
TsjipTsjip
736325a31f Log enrichment: Explosion damage to players (#29762)
* Log enrichment: Explosion damage to players

* Update Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs

Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com>

* Revert to total damage variant only, currently serialised as string.

* Make this its own log type.

---------

Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com>
2024-08-09 16:24:05 +10:00
Brandon Hu
e05df5d3e2 tweak(HealthAnalyzer): Stop showing poup when used on self or when cancelled (by twitter) (#30467)
* .
2024-08-09 16:19:58 +10:00
PJBot
619ba162ce Automatic changelog update 2024-08-09 06:18:57 +00:00
Stalen
60b63b58dd Head bandana ingestion blocking fix (#28910) 2024-08-09 16:17:51 +10:00
PJBot
05a21ab3ca Automatic changelog update 2024-08-09 06:17:28 +00:00
Simon
4c4cdb5b06 UI to edit silicon laws from admin verb (#28483)
* UI to edit silicon laws from admin verb

(peak shitcode)

* Improve UI

* Use Moderator admin flag

* Reviews
2024-08-09 16:16:22 +10:00
PJBot
8d96c993a2 Automatic changelog update 2024-08-09 06:15:13 +00:00
slarticodefast
e0d30aff4e Add tooltips to the agent ID job icons and improve status icon prototypes (#28575)
* add tooltips to agentid job icons

* forgot to stage this

* make StatusIconPrototype abstract

* minor visual improvements

* cleanup

* use currentculture to sort job names

* review
2024-08-09 16:14:07 +10:00
Thomas
eab0c34822 Create In-Guidebook Errors (#28942)
* Create in-guidebook errors

* Localize client-facing parser error

* Uncomment line

* Missed another localization string
2024-08-09 16:05:51 +10:00
Plykiya
e6d6416a19 Add minimum required trigger speed to mouse traps (#30593)
Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-09 16:00:40 +10:00
CaasGit
c091acd1f5 add(FaxComponent): Allows for setting defaults in the component. (#30484)
* add(FaxComponent): Allows for setting defaults in the component.

This wires up the Fax component to allow the default paper and office
paper to be set. The defaults are what they were before, and it's an
optional option. Now Fax machines can be set to print different paper
when using Print File or other cases where the default paper style is
used.

* Requested change to EntProtoId.

* Fix build failure.
2024-08-09 15:54:08 +10:00
Psychpsyo
b7ef5ada11 Add replacement words to Russian accent (#30554) 2024-08-09 15:49:38 +10:00
PJBot
1c5c155db1 Automatic changelog update 2024-08-09 05:49:07 +00:00
Repo
279e28768c Fix Panic bunker and Baby Jail being up to 14hrs incorrect. (#30581)
Fix a potential bypass for Panic bunker and baby Jail
2024-08-09 15:48:01 +10:00
metalgearsloth
bce17e6aec Fix decal megadiffs (#30732)
Just preserves the uids and doesn't care about trying to re-map the decals lower. We have a uint to work with anyway.
2024-08-09 15:47:23 +10:00
PJBot
b54a4206e8 Automatic changelog update 2024-08-09 05:46:56 +00:00
Ian
06a44a82b6 require the warden to notify admins on disconnect (#30745) 2024-08-09 15:45:51 +10:00
PJBot
a03d7c6c7f Automatic changelog update 2024-08-09 05:34:32 +00:00
nikthechampiongr
d1a60fafe7 Implement a playerpanel (#30238)
* Basic structure for the player panel ui

* Ensure basic functionality

Player panel now receives and displays basic info

* Make whitelistcommands accept user ids

* Make PlayerPanel use GUIDs where possible

* Add functionality to most playerpanel buttons

* Implement remaining playerpanel features

* Localize everything

* Finish up

* Put command arguments in quotes

I am not sure if it's even possible to have something like a space in
them considering they are guids and usernames but sure why not

* Make playerpanel a verb

* Add Logs button to player panel

* Change Notesbutton text and make whitelistbutton a confirmtion button

* Add freeze button that does not mute the player

* Add sharedconnections counter to playerpanel

* Make the playetime format clearer

* Allow for copying of the a player's username

* Do minor cleanup

* Rearrange buttons

* Fix unfreeze button not updating

* Fix wrong localisation text

* "Fix" the same role ban counting multiple times

The way rolebans are stored is horrible.
As such if you ban someone from a departmenrt or something
role bans are individually placed for every role.
The only way I found to distinguish them is the bantime.
This is horrible but I do not want to rewrite how all the bans are
stored right now.

* Add Delete and Rejuvenate buttons to player panel

By popular demand

* Marginally improve ui

* Add logs

* review update

* Fix verb

* Fix double notes

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-08-09 15:33:25 +10:00
Plykiya
b266c9b545 Create Generic DamageOnInteract/Attacked Comps/Systems (#30244)
* Everything but the submodule

* stuff I forgot

* heat

* missed lights

* behonky

* LocId

* I guess it was a skill issue?

* predicted audio

* It works with lights now

* Borg equality

* Gorilla gauntlet grants protection from anomaly returned damage when attacking it

* woops, there we go

* NONE

* Use DamageModifierSets, remove Behonker damage

* Reviews dealt with

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-09 15:32:41 +10:00
Leon Friedrich
8a4ef69e86 Add random seed options to tests (#30735)
* Add random seed options to tests

* Ensure profile randomization
2024-08-09 15:25:43 +10:00
slarticodefast
da0b8d4731 remove UntrackedMapBoundUserInterface (#30752) 2024-08-09 15:24:49 +10:00
PJBot
2ea17a492d Automatic changelog update 2024-08-09 04:53:31 +00:00
Nemanja
2528231ad1 offgrid mob friction (#29383)
* offgrid mob friction

* save the world...
2024-08-09 14:52:25 +10:00
beck-thompson
ad18c6e9a5 Secret stash refractor (#29396)
* First commit

* Will do this in another PR!

* maybe?

* Moved stuff to ToolOpenableSystem because its smarter and cooler
2024-08-09 09:51:58 +10:00
deltanedas
e4ff5780d5 full sticky prediction (#30230)
* move all sticky stuff to shared and cleanup/grammar fix

* update imports and ref

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-08-09 09:36:15 +10:00
PJBot
15ae0435c4 Automatic changelog update 2024-08-08 23:33:48 +00:00
themias
8ebe105458 Add butter slices (#30789) 2024-08-09 09:32:42 +10:00
Nemanja
1b1008ad85 Scrap (#30762)
* jnk sprites

* scrap pt 2

* metal inhands

* hell yeah brudda

* Update meta.json

* Update scrap.yml

---------

Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com>
2024-08-09 09:31:53 +10:00
PJBot
0423691d76 Automatic changelog update 2024-08-08 23:28:34 +00:00
SlamBamActionman
07174d0aaf Better DNA forensics & ReagentData (#26699)
* Added the ability for blood to track DNA using ReagentData; Forensic Scanner now accounts for solution DNA, non-DNA holders have "Unknown DNA"

* Removes touch DNA for puddles, adds DNA to vomit

* DNA now leaves traces in containers and those marked without don't show DNA on scan (except for puddles), gibbed parts have DNA

* Fix stupid metamorphic glass bug grrr

* Removed SpillableComponent since DnaSubstanceTraceComponent is used instead

* Removes data field from maps, adds DNA tracking for some missed items

* Give default value, fix missing values.

* Fixes recipe bug

* Review changes

* Make the Data list into a nullable type

* Revert map changes

* Move gibbed unknown DNA to forensicssystem
2024-08-09 09:27:27 +10:00
PJBot
c43fcdfa06 Automatic changelog update 2024-08-08 22:52:05 +00:00
lzk
25afc2653d Add RD labcoat in RD's dresser (#30671) 2024-08-08 18:50:56 -04:00
Emisse
3db51a7e36 train update (#30797)
train fix
2024-08-08 15:06:40 -06:00
dffdff2423
66f0cd4f43 Remove suspect figurine line (#30795) 2024-08-08 14:36:36 -04:00
saintmuntzer
703c8840c4 Fix for issue 30781 (#30788)
Fixed typos in name and description of syndicate recruit jumpsuit.
2024-08-08 18:35:01 +02:00
PJBot
6570515c8d Automatic changelog update 2024-08-08 13:18:57 +00:00
Ed
03745efc7f Thief beacons (try 2) (#29997)
content
2024-08-08 16:17:50 +03:00
PJBot
6d6c836095 Automatic changelog update 2024-08-08 11:40:52 +00:00
Plykiya
190ceda02e Add BreakOnDropItem, update do afters, remove unnecessary declarations (#30361)
* Add BreakOnDropItem, update do afters, remove unnecessary declarations

* bola

* Changed my mind about the nuke

* gennies too

* Make the comments more clear.

* Sorry for the trailing commas

* Revert "Sorry for the trailing commas"

This reverts commit e60fd9a30977393df3344948e6d5c0ce035723cd.

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-08 13:39:46 +02:00
Vigers Ray
bd51cf330b Teleport and delete buttons in objects tab. (#28914)
* Тыкнул и нету ЦК

* review

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-08-08 21:00:08 +10:00
PJBot
aea28b7a99 Automatic changelog update 2024-08-08 10:05:57 +00:00
Plykiya
1d5a3303e1 Buff EMP implant and grenade range (#30660)
Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-08 20:04:49 +10:00
strO0pwafel
35819e4ba5 more consistent CentComm naming (#29217)
* first-commit

* second commit

* i think i get git kinda maybe

* all of resources i think

* last easy changes

* oops

* Update Resources/Prototypes/Entities/Clothing/Head/hoods.yml

---------

Co-authored-by: stroopwafel <j.o.luijkx@student.tudelft.nl>
Co-authored-by: Plykiya <58439124+Plykiya@users.noreply.github.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-08-08 20:04:19 +10:00
IProduceWidgets
f24949a0b0 ai stays seated and pulled while cuffed. (#30397)
* ai stays seated while cuffed.

* Do the thing I thought was dumb.... whatever.

* less than
2024-08-08 19:53:07 +10:00
ShadowCommander
1df84515c7 Add EntityWorldTargetAction (#29819)
* Add EntityWorldTargetAction initial implementation

* Update obsolete methods

* Partially working EntityWorldTargetAction

* Fix entity selection

* Move and clean up AfterInteract

* Fix building new walls

* Readd no entity or coordinates error

* Consolidate action validation code

* Add summaries to component

---------

Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
2024-08-08 12:47:08 +03:00
Ed
97939f6e3e Merge remote-tracking branch 'upstream/master' into ed-05-08-2024-upstream
# Conflicts:
#	Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs
2024-08-08 12:29:20 +03:00
deltanedas
489efeb717 remove GenericAntag completely (#28218)
Co-authored-by: deltanedas <@deltanedas:kde.org>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-08-08 18:16:02 +10:00
PJBot
0057141039 Automatic changelog update 2024-08-08 03:09:34 +00:00
TakoDragon
929e6a2c00 Improved Spanish accent (#30551)
* Spanish and French comment

* Added the interrobang

* Make spanish accent use sting builder
2024-08-08 13:08:28 +10:00
Mervill
ab28e1a9a9 Remove obsolete occurrences of IComponent.Owner being assigned just before AddComponent (#30724) 2024-08-08 13:03:41 +10:00
metalgearsloth
23e4a9a004 Update submodule to 229.1.2 (#30764)
Tests should work decently again
2024-08-08 12:34:00 +10:00
PJBot
3a50880070 Automatic changelog update 2024-08-08 02:15:38 +00:00
Mervill
b8d10a3f5e Add verbose (client predicted!) examine text to Gas Miners (#30480)
* add verbose examine text to gas miners so their behaviour can be understood

* no need for these to be properties

* use an enum instead of two booleans for the miner state

* require the gas miner to be anchored in order to not be disabled

* xmldoc

* pr feedback

* file-scope namespace

* it's to late to hide my transgressions in a rebase

* turns out the normal examine distance is totally fine for this
2024-08-08 12:14:31 +10:00
ShadowCommander
9645f5528b Strip drag drop test (#30754)
* Add test for drag drop to open strip menu

* Make screencoords change based on deadzone
2024-08-08 12:12:01 +10:00
lzk
d86c886f61 Add more advertisements (#30653) 2024-08-07 19:11:05 -04:00
Mr. 27
d4ad2e473a Don't disable collision on dead mobs, fixes an issue with pulling (#30532)
Update MobStateSystem.Subscribers.cs
2024-08-07 18:59:04 -04:00
PJBot
8e3ddcc278 Automatic changelog update 2024-08-07 21:48:11 +00:00
IProduceWidgets
ff28feec14 butter is slippery (#29772)
* butter slippery

* rerun tests

* standardize
2024-08-07 15:47:03 -06:00
lzk
7754ea925d Fix warning cone rotation in inventory (#30672) 2024-08-07 20:13:24 +02:00
Ko4ergaPunk
3dd5d562da Rename mining walls as "mining walls" (#30729)
* fix

* re-check

* re-check vol.2 season battle pass episode

* да иди ты нахуй уже

* re-cheeeck
2024-08-07 09:37:12 -04:00
PJBot
6d0e915ebb Automatic changelog update 2024-08-07 09:27:48 +00:00
Errant
15aa75f092 Updated remaining ghostroles for species loadouts (#29746)
* LoadoutSystem change, deathsquad species loadout

* survival gear loadouts for disaster victims

* skrek

* centcom

* syndicate agent

* cburn

* ERT

* syndicates

* Is it ever gonna be enough?

* Take what you can

* Move startingGearEquippedEvent after Loadouts are spawned

* skreee

* even more formatting

* Unbreak auto internals activation

* undo AddAllRoleLoadouts change

* syndicate reinforcement survival loadouts

* re-enable vox ghostroles
2024-08-07 19:26:40 +10:00
PJBot
150e8f0721 Automatic changelog update 2024-08-07 09:20:17 +00:00
ShadowCommander
6a5cc883ce Dragdrop fold rollerbed (#30002)
* Deploy foldable

* Add NetworkedComponent and access to the component

* Add handled to afterinteract

* Use drop target location instead of setcoordinates

* Put back in hand after failed deploy

This prevents dropping the bed when clicking while inside a locker.

* Created BaseDeployFoldable for folding chairs, body bags, and rollerbeds

* Add dragdrop to fold rollerbed to hand
2024-08-07 19:19:10 +10:00
Emisse
d86438c22b martathon update (#30731)
martathon
2024-08-07 02:49:28 -06:00
Emisse
d10fa77372 bagel update (#30730)
big gas miners
2024-08-07 02:49:17 -06:00
PJBot
c61384122b Automatic changelog update 2024-08-07 07:43:07 +00:00
Lye
7dc6a56969 Space Dragons can't be flashed (#30658)
* added flash immunity component to Space Dragon

* Changed use of component to use of whitelist
2024-08-07 09:42:00 +02:00
Ed
d4001598d4 workbench ui clean up (#399)
* workbench ui clean fix

* stack localization
2024-08-07 09:32:27 +03:00
Nemanja
ce97225c2d Replace IClickAlert with events (#30728)
* Replace IAlertClick with events

* whoop

* eek!
2024-08-07 15:15:35 +10:00
Brandon Hu
2dabf33d46 fix(FieldGenerators): Add missing connection check (#30468)
.
2024-08-06 23:43:21 -04:00
PJBot
9903d36c7d Automatic changelog update 2024-08-07 03:42:48 +00:00
Errant
6ccc10e240 Vox Medical Mask sprite fix (#30702)
vox medical mask
2024-08-06 23:41:40 -04:00
NakataRin
5247878060 Fix Mind Transfer and Role Assignment Logic for Antagonists (#30711)
* AntagSelectionSystemFix

* readded the stuff that shouldn've been deleted in the first place

* re-arrange

* undo indentation

---------

Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
2024-08-06 23:38:47 -04:00
Emisse
05630bd4ba bagel update (#30727)
* bagel update

* remove invalid
2024-08-06 19:32:31 -06:00
Leon Friedrich
b4dfeec696 Add more storage admin logs & fix some interaction validation (#30725) 2024-08-07 10:15:41 +10:00
metalgearsloth
e5fe6f7824 Add mapping load acts (#30722)
Smug doesn't seem to have time to fix mapping state and I don't want to dump more time into it so.
2024-08-07 10:04:44 +10:00
Emisse
bd36ff93d2 train update (#30721)
* train update

* train
2024-08-06 17:50:39 -06:00
Emisse
1caddfa110 packed update (#30716) 2024-08-06 17:29:36 -06:00
Emisse
bb80d44331 marathon update (#30714)
* marathon update

* marathon update
2024-08-06 17:29:29 -06:00
Brandon Hu
048da47f32 fix(emergency_meta): Lock the doors on evac shuttle (#29255)
* fix(emergency_meta): Lock the doors on evac shuttle
2024-08-06 15:09:24 -06:00
Ed
78e140b4ae Revert "SpawnSpell tweak (#393)" (#398)
This reverts commit dc853b6662.
2024-08-06 20:25:29 +03:00
PJBot
de98531a5d Automatic changelog update 2024-08-06 13:13:20 +00:00
Flareguy
37e36bfa7c Vox head displacement map (#30699)
* head displacement for vox

* fixe
2024-08-06 16:12:14 +03:00
Tornado Tech
1b0bf2ad18 Added base workbench window (#372)
* Added base workbench window

* Fixed serialization errors

* Created base UI layout

* Updated ui

* Updated UI after crafting
2024-08-06 14:25:59 +03:00
Plykiya
a87efd7d01 Replace xform.WorldPosition with xformSystem method for Setting/Getting (#30029)
* GET WORLD POSITON, ROTATION

* Missing parentheses

* ui system depndency

* the issue

* Unused

* Let the function do the transform

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-06 21:02:01 +10:00
PJBot
7b3ff00129 Automatic changelog update 2024-08-06 11:01:21 +00:00
slarticodefast
7117b925a5 Add whitelist to cryostorage (#30574) 2024-08-06 21:00:15 +10:00
PJBot
4426ce123a Automatic changelog update 2024-08-06 10:59:39 +00:00
Nemanja
53719bded3 better examine text for radiation collectors (#30606)
* better examine text for radiation collectors

* this?

* penis
2024-08-06 20:59:37 +10:00
Errant
9522dbc655 Vox nukies and ninjafix (#29783)
* Nukie species gear

* antagselectionsystem autointernals

* nukie vox

* skrek

* Remove duplicate code

* skrek

* EVA tank loadouts, vox ninja update

* fix prototype duplication

* skrek

* fix summary

* fixed empty startinggear error

* enable vox ninjas

* Comments

* removed comment because startinggearprototype should not be used on antagselection aaaanyway

* squish that if

* Update Content.Shared/Clothing/LoadoutSystem.cs

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-08-06 20:58:28 +10:00
PJBot
26ad898105 Automatic changelog update 2024-08-06 10:52:42 +00:00
Ed
fb994e8d21 Add comment into jetpack system, to prevent future grid jetpack PRs (#30682)
Update SharedJetpackSystem.cs
2024-08-06 20:51:59 +10:00
Nemanja
e477a49cc6 Make biofab distinct from biogen (#30696)
* Make biofab distinct from biogen

* biocube fabricator
2024-08-06 20:51:33 +10:00
Brandon Hu
a8c74ca4e1 add(emergency_omega): Add defib to medical treatment area (#30693) 2024-08-05 21:49:23 -06:00
Brandon Hu
c948bd08c6 add(reach): Add a space cleaner dispenser to flands janitoral department (#30697) 2024-08-05 21:49:05 -06:00
Brandon Hu
f8c5a514bc add(fland): Add a space cleaner dispenser to flands janitoral department (#30695) 2024-08-05 21:48:41 -06:00
DrSmugleaf
edd17072ae Fix admin logs cache caching rounds forever if multiple game servers are ran on the same db (#30687) 2024-08-05 16:13:27 -07:00
Brandon Hu
9bb9e80ede tweak(SolutionTransfer): Raise default max transfer amount. (#29969)
* fix(jugs): Make max and minimum transfer amounts make sense

* fix name

* Better

* Implement bounds checks

* large beaker too

* More logical solution

* Bleh

* remove

* Doesn't make sense yet

WAS MISTAKEN AAAAAAAAAAAAAAD@ @!
2024-08-06 00:59:51 +02:00
Ed
dc853b6662 SpawnSpell tweak (#393)
* some refactor

* Update flame_creation.yml
2024-08-06 00:42:53 +03:00
Ed
56affe2f8b Merge branch 'master' into ed-17-07-2024-dungenagain 2024-08-05 23:08:01 +03:00
Ed
37e0ffa173 Workshop update (#392)
* kreks spell icons

* vladimirs walls update

* add flora material
2024-08-05 22:46:22 +03:00
Ed
38c3bf2595 Merge branch 'master' into ed-05-08-2024-upstream 2024-08-05 21:08:20 +03:00
Ed
6ab9ca6bd0 yupiiii (#391) 2024-08-05 21:08:10 +03:00
Nim
934c1e44a6 second rebalance (#390)
Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
2024-08-05 21:00:40 +03:00
Nim
818eb30d2d spells (#389) 2024-08-05 20:59:40 +03:00
Tornado Tech
12d2af692f Update wave.swsl (#388) 2024-08-05 16:48:22 +03:00
Ed
f5c0efd3cd Update departments.yml 2024-08-05 16:06:10 +03:00
Ed
f2b2da41a6 fix again 2024-08-05 15:50:12 +03:00
Ed
f2eccdc3dd loadout update 2024-08-05 15:37:54 +03:00
Ed
dd580c68c9 Merge remote-tracking branch 'upstream/master' into ed-05-08-2024-upstream
# Conflicts:
#	.github/PULL_REQUEST_TEMPLATE.md
#	Resources/Prototypes/Accents/word_replacements.yml
2024-08-05 15:28:42 +03:00
Ed
1f4b7f68bf Playtest polishing (#383)
* fix predicting sharpening

* finish shadow step

* tweak ice dagger

* shadow grab test

* fireball spell

* ice shards spell

* battle royale crates

* loot tables

* remove expedition ship

* dev update

* br map update

* spells tweak

* tips update

* some fixes

* biome forest spawner

* give keys to orgs

* mobs and aura spawners

* expedition update: goal, new ship, new map

* fix doors layers

* add new map proto

* translate keys

* Update crates.yml

* Update PostMapInitTest.cs

* fix spawning on expedition ship

* Fuck you, Randy. Remove nails

* fix lock systems

* localize tips

* some tweaks

* update ship, remove shrooms.

* fixes

* Update StationSystem.cs

* Revert "Update StationSystem.cs"

This reverts commit f632241324.

* Update expedition_test.yml
2024-08-05 15:22:14 +03:00
metalgearsloth
f7d8b4e7d4 Fix verb sub (#30667)
* Fix verb sub

Mapping casualty, verbs are weird.

* also this
2024-08-05 19:24:56 +10:00
Mervill
c1eb319bda Refactor the hungry debug command to be in line with thirsty (#30591)
refactor the hungry debug command to be in line with thirsty

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-08-05 18:12:07 +10:00
PJBot
d45810bbdb Automatic changelog update 2024-08-05 08:08:08 +00:00
Nemanja
b3a64d4bcd Add slowdown to low temperatures (#29692)
* TemperatureSpeed component

* temperature slowdown prediction (done right)

* remove unnecessary changes

* that too

* get in line

* make it readonly

* auto pause
2024-08-05 18:07:02 +10:00
PJBot
50408da21a Automatic changelog update 2024-08-05 08:04:30 +00:00
SlamBamActionman
bf06d0e869 Change Thief Syndie & Chameleon kit contents, add Syndie codeword paper (#30446)
* Initial commit

* more like bYE

* Fix exception during test
2024-08-05 11:03:24 +03:00
metalgearsloth
8197382444 Fix random unused field warnings (#29948)
* Fix random unused field warnings

Not many but it counts.

* caught

* Field
2024-08-05 17:59:37 +10:00
PJBot
52e03d2de5 Automatic changelog update 2024-08-05 07:37:32 +00:00
Mervill
8771cc03db Debug command to set either your hunger or thirst to one of their respective threshold levels (#30563)
* Debug command to set either your hunger or thirst to one of their respective threshold levels

* code updates

* code + loc changes
2024-08-05 17:36:26 +10:00
PJBot
bc3d53ec0a Automatic changelog update 2024-08-05 06:10:42 +00:00
foboscheshir
1629e200cb Missing mime mask sprites for Vox (#30607)
mime-mask-vox
2024-08-05 09:09:35 +03:00
Brandon Hu
e2ca1a1957 add(Spawners): Add a honk bot spawner (#30616) 2024-08-05 09:08:05 +03:00
metalgearsloth
c9b69ef491 Update submodule to 229.1.0 (#30664) 2024-08-05 15:51:10 +10:00
voidnull000
a3cbbe745e Change the mimebot and killer tomato's descriptions (#30639)
fix those two descriptions
2024-08-05 08:49:51 +03:00
metalgearsloth
9a8620d9d8 De-duplicate loadout gear (#30341)
* De-duplicate loadout gear

Needs supporting code, probably with an interface with StartingGearPrototype which would also use it, but I regexed the yaml.

* Code updates

* Remaining yaml

* Fix automatic naming

* raiseEvent

* Don't code when tired kids
2024-08-05 00:42:25 -04:00
Errant
6264a557a6 Restore the longer traitor briefing message (#30404)
* Restore the long traitor briefing

* highlights
2024-08-05 00:41:34 -04:00
PJBot
71a8fd5eab Automatic changelog update 2024-08-05 04:26:56 +00:00
Ed
a95d2257c3 JobRequirement refactor (#30347)
* refactor JobRequirements

* add profile support

* fix

* Update quartermaster.yml

* sloth fixes

* inport 30208

* Update DepartmentPrototype.cs

* species restriction

* left tweak stick

* stringbuilder is cool!
2024-08-05 14:25:49 +10:00
Plykiya
01e1624567 Move PaperSystem to Shared (#30592)
* Fix paper system late localization

* IS THIS WHAT YOU WANT

* well I guess this is happening now

* fix the BUI

* did that even do anything before?

* again with the escapeformatting...

* Dirtying

* Move dirty to function

* Rename PaperSystem to PaperVisualizerSystem

* Fix namespace

* how many namespace changes must I suffer through

* SetContent is for Setting Content

* minor shuffling

* review

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-08-05 14:23:23 +10:00
Simon
0ef5f668e2 Fix warnings in ShuttleSystem.GridFill.cs (#30253)
* Fix warnings in ShuttleSystem.GridFill.cs

* remove the thingie

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-08-05 14:09:42 +10:00
PJBot
68dbf6449c Automatic changelog update 2024-08-05 03:27:34 +00:00
Cojoke
a79cbf383c Give Mice Combat Mode and a 0 Damage Attack (#30487) 2024-08-05 13:26:28 +10:00
PJBot
7e285ee55c Automatic changelog update 2024-08-05 03:18:59 +00:00
Killerqu00
6503cb7833 Sleeper agents event no longer occurs when evacuation is called (#30646)
no more evac sleeper agents
2024-08-05 13:17:53 +10:00
Plykiya
c5b887fb4e Gets rid of all leftover usages of IEyeManager.CurrentMap (#30022)
* Gets rid of all leftover usages of CurrentMap

* args works

* reverty

* skill issue

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-05 13:16:47 +10:00
slarticodefast
d0884a3074 Improve communications console countdown formatting (#30495)
* improve communications console countdown formatting

* tostring
2024-08-05 13:16:19 +10:00
lzk
82d7636619 Fix some descriptions (#30644) 2024-08-05 13:15:52 +10:00
PJBot
118ea97652 Automatic changelog update 2024-08-05 03:15:13 +00:00
beck-thompson
490de1de4e Explosion resistance is now predicted! (#30654)
* First commit

* Added Network and access
2024-08-05 13:15:07 +10:00
DrSmugleaf
84a9253b10 Fix falsely showing damage color effect to entities that aren't allowed to be attacked (#30661) 2024-08-05 13:14:01 +10:00
Plykiya
d87d9c4676 Fix GhostRoleRadio icons (#30650)
Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-04 21:24:40 -04:00
Brandon Hu
c04f71728e tweak(grammar): Correct multiple instances of incorrect "its" usage (#30640) 2024-08-04 13:09:01 -06:00
Brandon Hu
b306130fbc tweak(grammar): Correct multiple instances of incorrect "it's" usage (#30641) 2024-08-04 13:08:13 -06:00
OrangeMoronage9622
8e43bb1b07 fml to chatsan (#30651) 2024-08-04 13:07:34 -06:00
JustCone
c237ef2843 Fland Atmos Gas Miners Grille Removal (#30615)
Removes grilles from atmos gas miners on Fland
2024-08-04 12:40:41 -06:00
JustCone
548f9e31fb Box Atmos Miners Grille Removal (#30610)
Removes Grilles from around atmos miners
2024-08-04 12:40:25 -06:00
Ubaser
17cececde0 Update Core (#30633)
add
2024-08-04 12:39:18 -06:00
JustCone
f1b8dcbfba Cluster Christmas Removal (#30635)
No Fun Allowed
2024-08-04 12:38:45 -06:00
JustCone
21d0326e13 Meta Robotics Console (#30652)
Adds robotics console to Meta
2024-08-04 12:38:26 -06:00
PJBot
65d5539248 Automatic changelog update 2024-08-04 10:15:04 +00:00
Mervill
f15cb8b77d Give rotten meat Trash and Recyclable tag (#30594) 2024-08-04 20:13:57 +10:00
Jaraten
9a07a9a052 stone upd (#384)
* stone upd

* fix ooopsiee

* fix tile
2024-08-04 12:28:31 +03:00
Skilets
1633895321 Fix Hud (#385)
Co-authored-by: SkiletsKir <113713080+Skilets@users.noreply.github.com>
2024-08-04 11:49:17 +03:00
Tornado Tech
0cd3a3c2d9 Added personal signature system (#382)
* Added personal signature system

* FUCKING RUSSIAN LANUAGE

A HATE LATTERS, HATEEEEEEEEEEEEEEEEEEEEEEE!

* Added locale and sound
2024-08-04 11:48:09 +03:00
slarticodefast
bb2981400c replace all uses of TryGetContainingContainer with non-obsolete overload (#30583)
* replace all uses of TryGetContainerContainer with non-obsolete overload

* rerun
2024-08-04 15:38:53 +10:00
Mervill
206495fd6d Remove unused variable from ThownInsertContainerSystem (#30611) 2024-08-04 15:36:25 +10:00
metalgearsloth
c3c5a6ba22 Fix verbs in mapping state (#30631) 2024-08-04 15:35:02 +10:00
Brandon Hu
f8bbc31903 fix(Solutions): Implement missing bounds check (#30239) 2024-08-04 00:13:09 -04:00
PJBot
4fc8790452 Automatic changelog update 2024-08-04 03:36:02 +00:00
DrSmugleaf
94a1b67c67 Don't show toggle internals verb if no breath tools are found while off (#30622)
* Don't show toggle internals verb if no breath tools are found

* only if not working
2024-08-04 13:34:55 +10:00
DrSmugleaf
8865d4a782 Make pointing cooldown a cvar (#30623)
* Make pointing cooldown a cvar

* Remove empty line
2024-08-04 13:34:33 +10:00
PJBot
2aa7226266 Automatic changelog update 2024-08-04 03:33:18 +00:00
Brandon Hu
968cd9dac6 fix(ChargerSystem): Don't show battery level twice. (#30625) 2024-08-04 13:32:55 +10:00
Ian
0d506ccffc added relevant guides to help menus (#30462)
* added relevant guides to help menus

* fixed ALL missing help-menu links

* missed one
2024-08-04 13:32:10 +10:00
DrSmugleaf
54d5bd266c Add mapping editor (#23427)
* Add mapping editor (#757)

* Remove mapping actions, never again

* Cleanup actions system

* Jarvis, remove all references to CM14

* Fix InventoryUIController crashing when an InventoryGui is not found

* Rename mapping1 to mapping

* Clean up context calls

* Add doc comments

* Add delegate for hiding decals in the mapping screen

* Jarvis mission failed

* a

* Add test

* Fix not flushing save stream in mapping manager

* change

* Fix verbs

* fixes

* localise

---------

Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-08-04 13:31:45 +10:00
github-actions[bot]
6e8f8d706a Update Credits (#30620)
Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>
2024-08-03 21:01:41 -04:00
voidnull000
b55c37ec3e Fix a typo in the raid suit's description (#30612)
fix typo (for real this time i swear)
2024-08-03 23:20:00 +02:00
IProduceWidgets
936dc94a8c Docking ports now block air only in the direction their port is. (#30195)
speedholes.
2024-08-03 13:36:19 -04:00
Brandon Hu
a937f05929 fix(GhostRoleAntagSpawner): Create a new mind before transfering (#30354)
* IN MY WALLS! IN MY WALLS THEY ARE IN MY WALLS HELP HEELP HEKP PA DADOEAMD EODM AOM D *DIES* IM NOT DEAD ADADADDADIAMSDIM

* RUN I SAID RUN! THEY TOOK MY UKELELE

Co-authored-by: EmoGarbage404 <retron404@gmail.com>

* Many have fallen

* It's all emo's fault

---------

Co-authored-by: EmoGarbage404 <retron404@gmail.com>
2024-08-03 10:09:41 -04:00
PJBot
77eb70ea8a Automatic changelog update 2024-08-03 14:08:26 +00:00
Joel Zimmerman
aad2441f30 Made the dank pizza actually dank (#30430)
* made the dank pizza actually dank

* made the dank pizza also contain some nutritional value
2024-08-03 10:07:20 -04:00
PJBot
a15fb56381 Automatic changelog update 2024-08-03 14:07:11 +00:00
lzk
a9693322be SpeakOnUseSystem and phrases for figurines (#30455)
* commit 1

* 2

* add more text

* apply review

* apply review 2

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

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
2024-08-03 10:06:04 -04:00
RiceMar1244
b432dc6125 Shotgun spread refactor (#27773)
* Moves spread data to new component

* Refactors shotgun spread code

* Makes shotgun cartridges and projectiles use new component

* Attempts to fix nullable build error

* Fixes hitscan weapons that I broke :(

* Saviour commit?

---------

Co-authored-by: EmoGarbage404 <retron404@gmail.com>
2024-08-03 09:26:32 -04:00
GrandApothecary19
72cbed696f More sprites for tavern (#357)
* - add sprites

* - fixo for .ftl

* - fix because no whiskey in Eberron((

* - fixo
2024-08-03 15:51:29 +03:00
to4no_fix
64273cb914 Display the administrator's title in ahelp and ahelp relay (#30075)
* Adding the admin prefix to the ahelp

* Updating the admin prefix

* The second update of the admin prefix

* Configuration correction
2024-08-02 22:59:20 -05:00
OrangeMoronage9622
893180038f Add Ngl to chatsan (#30589)
* Ngl added fr this time

* Update speech-chatsan.ftl

Not gonna lie sounds more realistic than not going to lie
2024-08-03 04:59:50 +03:00
IProduceWidgets
7c7dfb48bb Greyscale Winter Coats and Hoods, some reorganizing of rsi. (#30314)
* reorganize coats, greyscale coats and hoods

* fix inhands

* make rsi validator happy

* even mo' hap'

* comma schmomma

* oops theres a recipe

* armor credits

* painflicts
2024-08-02 19:24:54 -04:00
PJBot
b72818cb12 Automatic changelog update 2024-08-02 23:06:27 +00:00
Alzore
0689e16939 Recolor winter boots (#30350)
* Recolor winter boots

* fix inhands

* better colors

* FOOT
2024-08-02 19:05:18 -04:00
JustCone
4a1b654928 Toxins Guidebook spelling mistakes (#30585)
Corrects spelling mistakes and changes dosages to doses
2024-08-02 18:22:51 -04:00
JustCone
be681aac11 Packed Singulo Substation & Chemistry Windoors (#30572)
* Rearrange Engineering Wires

* Replaces chemistry windoors with correct access
2024-08-02 14:12:07 -06:00
JustCone
7efc557bb4 Marathon had extra pipes under windows (#30584)
Deletes extra pipes
2024-08-02 14:10:05 -06:00
PJBot
32d1ea41bf Automatic changelog update 2024-08-02 18:59:06 +00:00
Repo
a2f27f956a Fix Bwoink Player sorting again.... (#30580)
Keep players that have had messages in the round at the top
2024-08-02 13:58:00 -05:00
PJBot
858f954c2f Automatic changelog update 2024-08-02 17:13:14 +00:00
slarticodefast
577f30fb13 Add haloperidol, potassium iodide (#27454)
* add haloperidol, potassium iodide

* review fixes

* review and tuning

* shader review

* use timespan and AutoPausedField
2024-08-03 03:12:08 +10:00
PJBot
ea6be02ea3 Automatic changelog update 2024-08-02 15:50:14 +00:00
Mervill
0f33025d34 Debug command to make you thirsty (#30562)
* debug command to make you thirsty

* pr feedback
2024-08-03 01:49:08 +10:00
Gagarinten-Noverdo
c620d66dab Biome spawner fix. Again (#381)
* Biome spawner update. Round seed system

* Format fix

* Round seed M I T

* Error to Warning

* Test fix #2

* Test fix #3

* VV

* Del out of body

* Fix of flood
2024-08-02 18:46:00 +03:00
DrSmugleaf
1fd934d5dc Use EntityQuery for footstep modifier and map grid in SharedMoverController (#30575) 2024-08-02 10:08:46 -04:00
Ed
599c599a4e Some shit (#380)
* telegraphy effects

* shadow step spell backend

* add component spell

* predict sharpening

* Update ring.yml

* locale sync

* Update entities.ftl

* icon shadow step
2024-08-02 13:51:54 +03:00
PJBot
f153f134fc Automatic changelog update 2024-08-02 10:03:23 +00:00
slarticodefast
2a29fea51d make the digital audio workstation rotatable (#30571)
make the DAW rotatable
2024-08-02 20:02:16 +10:00
Mervill
af75258601 Remove obsolete code from Food, Drink and Respirator systems. (#30560)
* Remove obsolete code from Food, Drink and Respirator systems

* remove obsolete comment
2024-08-02 17:30:45 +10:00
Emisse
ac4086f984 apc touchup sprite (#30460)
* apc touchup

* darker borders ofjdsa fjoias
2024-08-02 17:19:25 +10:00
Tayrtahn
ed4ae1628c Add "fill levels" to spear in-hand/wielded/equipped sprites (#28865)
* Add fill level sprite support for equipped items

* Add fill sprites for held/equipped spears
2024-08-02 17:18:59 +10:00
Aiden
029771d672 'Spawn Here' Verb Forces Mind Transfer (#30568)
Update AdminVerbSystem.cs
2024-08-02 17:14:21 +10:00
IProduceWidgets
36038a1707 Allow ai to understand if its handcuffed. (#30402)
* allow ai to understand if its handcuffed.

* rerun tests they worky on local

* Contained here in, a string of expletives about flaky tests.

* on retrospect, default true is probably smorter.

* do reviews

* I forgor xml

* more xml
2024-08-02 17:07:46 +10:00
PJBot
8d23d36e88 Automatic changelog update 2024-08-02 07:06:18 +00:00
ShadowCommander
1579c8a424 Deploy foldable (#30000)
* Deploy foldable

* Add NetworkedComponent and access to the component

* Add handled to afterinteract

* Use drop target location instead of setcoordinates

* Put back in hand after failed deploy

This prevents dropping the bed when clicking while inside a locker.

* Created BaseDeployFoldable for folding chairs, body bags, and rollerbeds
2024-08-02 17:05:12 +10:00
osjarw
ccdbdee929 Add DoAfter handling to InteractWithOperator (#30319)
* commit used for the recording, mostly copypaste

* Remove recording artifacts, now contains content

* use switch expression instead of switch statement
Also `ExpectedDoAfter` to fail when doafter isn't raised
2024-08-02 16:17:11 +10:00
slarticodefast
448f3804e6 Remove duplicate cartridge rsi (#30498)
remove duplicate cartridge rsi
2024-08-02 16:14:50 +10:00
PJBot
533c968928 Automatic changelog update 2024-08-02 06:06:44 +00:00
Plykiya
85bcc74bdf Add percentage reading to chargers (#28500)
* Add percentage reading to chargers

* using args.pushgroup

* change message

* Update Content.Server/Power/EntitySystems/ChargerSystem.cs

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2024-08-02 16:05:38 +10:00
Velcroboy
378d1e5c71 Adjusts the Bingus steal target to include any of the named cats (#30504)
* Adjusts the Bingus steal target to include any of the named cats

* Change name of target group

---------

Co-authored-by: Velcroboy <velcroboy333@hotmail.com>
2024-08-02 16:02:56 +10:00
Vasilis
c36b94316e Add PR guidelines checkbox to PR template (#30283)
* Add PR guidelines checkbox to PR template

* Update PULL_REQUEST_TEMPLATE.md
2024-08-02 16:02:24 +10:00
PJBot
bfd47ed69a Automatic changelog update 2024-08-02 05:58:56 +00:00
Benny (TropicalHibi)
d13bcd1faa WRU and FS autocompletion (#30508)
modified:   Resources/Locale/en-US/speech/speech-chatsan.ftl
modified:   Resources/Prototypes/Accents/word_replacements.yml
2024-08-02 15:57:50 +10:00
Brandon Hu
5dd3e9b514 tweak(BurialSystem): Don't cancel doafter on keyup (#30507) 2024-08-02 15:57:18 +10:00
deltanedas
b4607acea0 add flash bounty for revs (#29248)
* add flash bounty

* escape

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
2024-08-02 15:48:04 +10:00
PJBot
fa5995ddab Automatic changelog update 2024-08-02 05:46:05 +00:00
slarticodefast
2c31e03ee3 Fix borgs being unable to state laws or open other UIs without modules (#30299)
borg law 2 state laws all day
2024-08-02 15:44:59 +10:00
PJBot
0b253e94eb Automatic changelog update 2024-08-02 05:44:47 +00:00
WarMechanic
67fc8af814 Adjusts the lethality of meteor explosions (#29199)
* make changes

* int oops
2024-08-02 15:43:40 +10:00
Kaga-404
745d90ad73 Revolver fixes for caseless ammo and wielding (#30117)
* handled check

* splits Handled and TryResetDelay checks
2024-08-02 15:43:11 +10:00
Mervill
4179821ffa Show the player a server message when they get booted from the arrivals shuttle (#30519)
show the player a server message when they get booted from the arrivals shuttle
2024-08-02 15:42:02 +10:00
NakataRin
c99cdf5c59 susbox locale fix (#30564) 2024-08-02 15:39:00 +10:00
PJBot
a2e945f272 Automatic changelog update 2024-08-02 01:34:26 +00:00
metalgearsloth
20cb65f2e6 Fix double interaction popups (#30452)
The issue is it plays the "you hug xyz" to everyone in range as well as the target client so they overlap. IDK if anything else is broken by this but this control flow is pretty bad so I wouldn't be surprised.
2024-08-02 11:33:20 +10:00
Plykiya
e6b67540e4 Update MoverController.cs to not use Component.Owner (#29965)
* Update MoverController.cs

* Update a bunch of movement code to use Entity<T>

* Last errors

* wow, there were more errors

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-08-02 11:32:32 +10:00
Brandon Hu
dceb562be7 add(TransferAmountWindow): QoL stuff (#30464)
* ugh
2024-08-02 11:20:36 +10:00
Ed
31c9485e3b Magic rings (#378)
* flame creation spell backend

* flame creation visuals

* flame creation spell visual

* tweak

* some tweaks

* ice dagger spell

* move spells to rings

* Update ring.yml

* fix server crash
2024-08-02 00:48:36 +03:00
PJBot
1fcbaa132f Automatic changelog update 2024-08-01 20:02:13 +00:00
marbow
b8fc703bf0 Detective's closet now has a hand labeler (#30501)
handlabeler
2024-08-01 14:01:05 -06:00
PJBot
ce3169ed17 Automatic changelog update 2024-08-01 20:00:53 +00:00
IProduceWidgets
dc5f951f0e Remove cubes from nukie outpost. (#30547)
I expect you to die!
2024-08-01 14:00:30 -06:00
lzk
2422a606d5 Update Marathon (#30534)
* Update Marathon

restocks
beacons
emitters

* Update 2

perma fixes
2024-08-01 14:00:16 -06:00
NakataRin
71ea1516ed added paramedic to train (#30556) 2024-08-01 13:59:43 -06:00
SlamBamActionman
7da2fb1218 Change from using obsolete method in ExplosionOverlay (#30550)
* Change from obsolete method in ExplosionOverlay

* Send AppearanceSystem through constructor
2024-08-01 23:17:15 +10:00
PJBot
ff76214247 Automatic changelog update 2024-08-01 10:27:39 +00:00
Ko4ergaPunk
27f3bea46d Cutter machine & steel mozaic item tiles (#30431)
* no texture machine

* done

* delete wip

* bug fixes

* oops

* CutterMachineCircuitboard
Glass: 500
2024-08-01 13:26:32 +03:00
Ed
56da23925f Magic Items (#370)
* attuning to items start

* verbs attuning, forgot oldest attuning

* Update CP14SharedMagicAttuningSystem.cs

* some fixes

* add deattuning

* fix popups on owner

* file restructurization

* MORE FOLDER RENAMING GODD

* magic via holding yea!

* fix deattuning

* vfx bugfixs

* wearing magic items

* Cure wounds spell

* refactor spells

* more redo

* finish refactor
2024-08-01 11:52:27 +03:00
Nim
19365c0314 Statue of Gob (#376)
* statue of Gob

* fix
2024-08-01 09:15:44 +03:00
Nemanja
2c26be606f Add support for printing reagents in lathes (#30476)
* Add support for reagents in lathes

* missing locale
2024-08-01 14:15:05 +10:00
Mervill
4b7325098a Remove obsolete code from LightningSystem (#30546) 2024-07-31 20:27:17 -07:00
PJBot
ab05228479 Automatic changelog update 2024-08-01 03:01:00 +00:00
Plykiya
1d79b924a6 Adds hand labelers to the ChemDrobe, LawDrobe, and PTech (#29958)
* Adds hand labelers to the chemdrobe and ptech

* LawDrobe too

* Update cart.yml

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-07-31 19:59:54 -07:00
PJBot
b5524bfcd1 Automatic changelog update 2024-08-01 02:59:22 +00:00
PixeltheAertistContrib
ad0aba5108 Change Social Anxiety ----> Stutter (#29898)
* Change Social Anxiety ----> Stutter

* Update traits.ftl

---------

Co-authored-by: Plykiya <58439124+Plykiya@users.noreply.github.com>
2024-07-31 19:58:16 -07:00
PJBot
34450dc908 Automatic changelog update 2024-08-01 02:56:09 +00:00
Plykiya
1d2b7131ab Meteors now leave behind a bit of ore (#30419)
* Meteors that leave behind asteroid ore

* bigger offset

* Bit more generic

* Better defaults

* hrm?

* I HATE CUSTOM SERIALIZERS

* More comments

* renamed a variable

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-07-31 19:55:02 -07:00
Mervill
1ef4f26a44 Remove obsolete code from VomitSystem (#30544) 2024-07-31 19:30:14 -07:00
Plykiya
ba1610012a Update RespiratorSystem.cs to not use Component.Owner (#30426)
Update RespiratorSystem.cs

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-07-31 19:29:02 -07:00
themias
52b32fa7fa Fix hypo pen clicking (#30535) 2024-07-31 14:07:12 -07:00
themias
7a99c962a3 Fix exploding pen clicking (#30533) 2024-07-31 21:50:16 +03:00
PJBot
b5a6f85a75 Automatic changelog update 2024-07-31 17:58:47 +00:00
themias
2575e44ec3 Add pen clicking sound (#30531)
* Add pen clicking sound

* switch to OnUse and reduce distance a little
2024-07-31 20:57:41 +03:00
IgorAnt028
5eb6425c42 A small addition to the documentation (#30506)
* Completion of documentation

Comments have been added to ConditionalSpawnerComponent and RandomSpawnerComponent

* Appear fix

Some fixes with word "appear"
2024-07-31 19:09:51 +03:00
PJBot
282292fdae Automatic changelog update 2024-07-31 16:09:27 +00:00
slarticodefast
3cecfd6e4c Skeletons no longer have fingerprints (#30530)
skeletons no longer have fingerprints
2024-07-31 19:08:20 +03:00
PJBot
d09ed501f0 Automatic changelog update 2024-07-31 15:15:27 +00:00
metalgearsloth
0432f21110 Add sprite exporting (#29874)
* Redo of code

* Dump IDs on lobby exports
2024-07-31 11:14:19 -04:00
Brandon Hu
5bc9c04a02 add(SharedDoAfterSystem): Add a IsRunning "Utility" function (#30512) 2024-07-31 21:32:19 +10:00
metalgearsloth
778bfe3355 Add test for anchored prototypes (#30526)
Nothing fails at least but at some point will let us remove some hacky engine code.
2024-07-31 21:31:41 +10:00
PJBot
f264da89c4 Automatic changelog update 2024-07-31 01:59:37 +00:00
Repo
b784edb758 Fix aHelp menu sorting (#30518)
* Keep Pinned status through player status changes.

* Fix filtering to be admin optimal.
2024-07-31 11:58:31 +10:00
PJBot
61e87cb43a Automatic changelog update 2024-07-31 00:15:55 +00:00
slarticodefast
0ceb99df1c Fix singularity overlay mouse position correction when zooming (#30509)
fix singularity overlay mouse position correction when zooming
2024-07-31 10:14:49 +10:00
Pieter-Jan Briers
524dad566b Rename CLF3 to "chlorine trifluoride" (#30510)
Other chemicals like sodium hydroxide are written in full too, why wouldn't ClF3 be.
2024-07-31 10:14:23 +10:00
Dvir
50fcde25dd Fix Viper yaml (#30513)
Update pistols.yml
2024-07-30 16:08:54 -07:00
TemporalOroboros
bea8c1a0b8 Relocate TemperatureProtectionComponent (#30437)
Moves it from the atmos module to the temperature module where it is actually used.
2024-07-30 12:31:55 -07:00
PJBot
7fefcbc6fb Automatic changelog update 2024-07-30 14:42:21 +00:00
Ed
23b97bd8b5 Added diamond into upgraded ore processor (#30499)
Update lathe.yml
2024-07-30 10:41:15 -04:00
Roudenn
f501bd500c Grappling gun's ReelRate can be changed (#30493)
* Grappling gun ReelRate can be changed

Moves ReelRate constant from SharedGrapplingGunSystem to GrapplingGunComponent, so it's Reel speed can be changed.

* Add coment + remove redudant
2024-07-31 00:26:18 +10:00
PJBot
0fddee233d Automatic changelog update 2024-07-30 14:08:41 +00:00
Cojoke
ea136838c0 Another QSI Fix About Things Being in Places they Should not (#30241)
* Another QSI Fix About Things Being in Places they Should not

* cleanup
2024-07-30 10:07:35 -04:00
chavonadelal
9e1e9b8c34 Localization of the Salvage Expedition Console (#30339)
* Localization of the Salvage Expedition Console

* Localization of the Salvage Expedition Console 2
2024-07-30 10:05:19 -04:00
lzk
1a672c9c6d add more logic gates prototypes for mapping (#30385)
* Add more logic gates types for mapping

* i forgor

* me stupid

* nuh
2024-07-30 09:55:21 -04:00
PJBot
5607c35744 Automatic changelog update 2024-07-30 13:54:51 +00:00
Cojoke
247222beab Remove Walking out of Containers while You can't Walk (#30391)
* Require Standing to Exit Containers

* whoops, forgot a not

* You can't walk out if cuffed

* GUAH(requested stuff)

* bwomp(tiny cleanup)
2024-07-30 09:53:44 -04:00
PJBot
04b7b80210 Automatic changelog update 2024-07-30 11:30:38 +00:00
chavonadelal
0b95de1efc Adding an AccessReader to a particle accelerator with engineering access (#30394)
Adding an AccessReader to a particle accelerator
2024-07-30 14:29:32 +03:00
PJBot
89b8ec713a Automatic changelog update 2024-07-30 08:36:37 +00:00
themias
648f94f4bb Fix fingerprint transfer on weapon attack (#30257)
* Fix fingerprint transfer on weapon attack

* Switch to just not raising the event

* one more
2024-07-30 18:35:30 +10:00
DrSmugleaf
517598f72d Fix climbing not ending when any contacts still exist (#30488) 2024-07-30 18:31:59 +10:00
PJBot
fda852d2e0 Automatic changelog update 2024-07-30 08:29:40 +00:00
Repo
a72445c419 aHelp fixes and improvements (#28639)
* Clear search criteria on loading aHelp window

* Pinning technology.

* Relay to aHelp window and discord if a user disconnect/reconnect

* Fix pinning localization

* Log disconnect, reconnects, bans to relay and admin in aHelp

* Drop to 5min to hold active conversations

* Update Content.Server/Administration/Systems/BwoinkSystem.cs

Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com>

* discord text styling if diconnect,reconnect,banned message.

* Pin icons instead of text

* Better Icons

* space

* Move button generation in to its own XAML

* List entry control

* Fix spaces

* Remove from active conversations on banned

* Discord if else block cleanup

* Better pin icons

* Move icons to stylesheet styleclass

* Better field order.

* PR review fixes

* fixes

---------

Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
2024-07-30 18:28:32 +10:00
TurboTracker
2fc9a65da6 Update migration to remove maint detective armor (#30486)
Jensen migration
2024-07-29 23:41:52 -07:00
Brandon Hu
9a46c24854 tweak(SyndicateReinforcements): Replace combat knife with throwing knife (#30466)
* .

* Update Resources/Prototypes/Catalog/Fills/Items/misc.yml

Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>

* ugh

Co-authored-by:James Quiaoit <jquiaoit@ucsc.edu>

* ugh

Co-authored-by: James Quiaoit <jquiaoit@ucsc.edu>

---------

Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
Co-authored-by: James Quiaoit <jquiaoit@ucsc.edu>
2024-07-30 09:22:33 +03:00
PJBot
7eec380d99 Automatic changelog update 2024-07-30 04:42:27 +00:00
Alzore
517a62a6dd Recolor the defibrillator (#29964)
* tide-ification

* metajoson
2024-07-30 00:41:21 -04:00
Flareguy
0266ac029d Security biosuit sprite fix (#30151)
sec biosuit fix
2024-07-30 00:40:39 -04:00
Nemanja
c4c4a934af fix lathe screen animations (#30479) 2024-07-30 14:03:46 +10:00
PJBot
fc65acc7a0 Automatic changelog update 2024-07-30 03:05:23 +00:00
themias
92210ff51b Fix ACC wire light not appearing (#30453) 2024-07-29 23:04:17 -04:00
PJBot
16ec6443f6 Automatic changelog update 2024-07-30 02:49:34 +00:00
Cojoke
ad81c30dcb Lets Hamlet Dance (#30478)
Allow Ghost Roles that have NPC to turn on Combat Mode
2024-07-29 19:48:28 -07:00
osjarw
669cc55ba4 Allow the creation of recursive HTNs (#30138)
Allow recursive HTNs
2024-07-30 12:18:53 +10:00
PJBot
cc3d6441be Automatic changelog update 2024-07-30 02:18:33 +00:00
Plykiya
031eb9606c Make magic mirror do-after longer, add popups to notify the target (#30366)
* Make magic mirror do-after longer, add popups to notify the target

* Turns out I do have access to the user

* More detailed popups

* Helmets protecc

---------

Co-authored-by: plykiya <plykiya@protonmail.com>
2024-07-30 12:17:27 +10:00
PJBot
3104215ced Automatic changelog update 2024-07-30 02:15:17 +00:00
nikthechampiongr
2f42e1d18e Vanquish inaccurate docs (#30449) 2024-07-30 12:14:34 +10:00
TheKittehJesus
18370c3d1c Four cooking recipes changed to use liquid egg instead of whole egg, and 5u of milk added to cake batter (#30262)
* Changed four cooking recipes to use egg fluid instead of whole eggs, see issue #30123

* Added 5u of milk to cake batter due to it becoming a subset of brownie recipe and occurring in beaker
2024-07-29 22:14:11 -04:00
PJBot
ca8ee5fb77 Automatic changelog update 2024-07-30 01:50:11 +00:00
themias
85e36266fa Add envelopes (#30298)
* Add envelopes

* oops

* Remove unused loc string

* comments and fixes
2024-07-29 21:49:05 -04:00
PJBot
b16de9bb35 Automatic changelog update 2024-07-30 01:23:23 +00:00
slarticodefast
502ceb69de Fix puppy ian steal target (#30474)
fix puppy ian steal target
2024-07-29 18:22:16 -07:00
PJBot
0bbb099a38 Automatic changelog update 2024-07-30 01:06:33 +00:00
Ian
c5f332ea9f added a defib to the courser (#30471) 2024-07-29 19:05:26 -06:00
Mervill
83a56cc74a Objects that only have a basic description won't have extra line in examine text (#30472)
objects that only have a basic description won't have extra line in examine text
2024-07-29 17:57:46 -07:00
Emisse
fcaf04d72b bagel update (#30461) 2024-07-29 12:45:42 -06:00
PJBot
961514dd28 Automatic changelog update 2024-07-29 15:18:01 +00:00
TurboTracker
8595f59b83 Bring kobold ghostrole chance inline with monkey (#30450) 2024-07-29 18:16:54 +03:00
PJBot
16cbd1a36b Automatic changelog update 2024-07-29 12:53:46 +00:00
Moomoobeef
7761f86b99 added wearable lizard plushie (#30400) 2024-07-29 15:52:39 +03:00
end
6114a88628 N2O color change (#30383) 2024-07-29 15:37:31 +03:00
PJBot
7244b9b912 Automatic changelog update 2024-07-29 09:58:10 +00:00
Alzore
3db33d944f Increase the contents of the emergency oxygen and fire lockers (#29230)
* 201 asphyxiation damage

* A bit less oxygen
2024-07-29 02:57:04 -07:00
thecopbennet
cf7e680770 turns "nosmoking sign" into "no smoking sign" (#30444)
add a space

Co-authored-by: thecopbennet <bennetgood@gmail.com>
2024-07-29 02:31:05 -06:00
PJBot
a9fc5a4b94 Automatic changelog update 2024-07-29 08:30:33 +00:00
Maishul
0e4bc4cefc Candles Crate (#29736)
* First attempt at adding crate

* Changed loose candles to candle boxes

* Corrected minor typo

* Removed a change to the development preset

* Buffed crate to 2 of each box

* Hopefully removes strange not-change in the repo

* Updated description to be clearer.

---------

Co-authored-by: elee-github <elee2020@yahoo.com>
2024-07-29 02:29:27 -06:00
Mervill
ec19f9f4a9 Make the powered examine text fully client predicted (#30441)
* Make the powered examine text fully client predicted

* switch to using the Entity<T> API for the examine event
2024-07-29 00:28:17 -07:00
PJBot
c586561a9e Automatic changelog update 2024-07-29 06:50:35 +00:00
JIPDawg
0dd22c31d1 Change unused OpenScoreboardWindow to ToggleRoundEndSummaryWindow and bind it to F9 (#30438)
Change unused OpenScoreboardWindow to ToggleRoundEndSummaryWindow
2024-07-28 23:49:28 -07:00
Mervill
14a8e94f1e Fix punctuation in anchored / unanchored examine text (#30439)
fix punctuation in anchored / unachored  examine text
2024-07-29 08:35:50 +02:00
lzk
c4ef5e4205 Update marathon (#30442) 2024-07-29 00:31:05 -06:00
PJBot
4a13212bc3 Automatic changelog update 2024-07-29 06:20:54 +00:00
lzk
da5918c951 FINALLY FIX APPROVER IN CARGO INVOICE!!! (#29690)
* OH DEAR LORD I WASTED 2 HOURS ON THIS STUPID SHIT!!!

* change some logic
2024-07-28 23:19:43 -07:00
lzk
46c8cecbbc Update Marathon (Engineering revamp and small stuff) (#30429)
* Update Marathon

* Update 2

* Update 3

* Update 5

* Update 5 (actually this time)
2024-07-28 13:53:23 -06:00
Ed
aae4be5e7c Merge branch 'master' into ed-17-07-2024-dungenagain 2024-07-20 17:12:04 +03:00
Ed
0a82d35c0f Merge branch 'ed-17-07-2024-dungenagain' of https://github.com/crystallpunk-14/crystall-punk-14 into ed-17-07-2024-dungenagain 2024-07-17 23:48:34 +03:00
Ed
f94d3d9689 Update xandria.yml 2024-07-17 23:48:15 +03:00
Ed
8c41c9c565 Merge branch 'master' into ed-17-07-2024-dungenagain 2024-07-17 23:46:15 +03:00
Ed
d3955a4400 bruh 2024-07-17 23:21:00 +03:00
Ed
66495b368d Update CP14RoomSpawnerComponent.cs 2024-07-17 23:01:42 +03:00
Ed
8df640b693 move ignore tiles to roomProto 2024-07-17 21:29:24 +03:00
Ed
7511c8177b add spawner probability 2024-07-17 21:27:15 +03:00
Ed
f774c62792 remove nesting 2024-07-17 21:20:52 +03:00
Ed
4049907b7f Update CP14RoomSpawnerSystem.cs 2024-07-17 18:32:43 +03:00
Ed
ef460cb59c mask tile filtering 2024-07-17 18:30:28 +03:00
Ed
5ede8bb0a6 add xandria atlas 2024-07-17 18:11:08 +03:00
Ed
7c0c8c5b33 polishing, bugfixing 2024-07-17 17:38:00 +03:00
Ed
951e553926 room spawner 2024-07-17 16:33:28 +03:00
Ed
0a42834dbe move biomespawners 2024-07-17 16:32:58 +03:00
2784 changed files with 384435 additions and 494360 deletions

6
.vscode/tasks.json vendored
View File

@@ -10,7 +10,7 @@
"args": [
"build",
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
"/consoleloggerparameters:NoSummary" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
"/consoleloggerparameters:'ForceNoAlign;NoSummary'" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
],
"group": {
"kind": "build",
@@ -29,9 +29,9 @@
"build",
"${workspaceFolder}/Content.YAMLLinter/Content.YAMLLinter.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
"/consoleloggerparameters:'ForceNoAlign;NoSummary'"
],
"problemMatcher": "$msCompile"
}
]
}
}

View File

@@ -46,7 +46,7 @@ public class MapLoadBenchmark
PoolManager.Shutdown();
}
public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry", "Oasis" };
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Cog" };
[ParamsSource(nameof(MapsSource))]
public string Map;

View File

@@ -38,7 +38,7 @@ namespace Content.Client.Access.UI
SendMessage(new AgentIDCardJobChangedMessage(newJob));
}
public void OnJobIconChanged(ProtoId<StatusIconPrototype> newJobIconId)
public void OnJobIconChanged(ProtoId<JobIconPrototype> newJobIconId)
{
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
}
@@ -55,7 +55,7 @@ namespace Content.Client.Access.UI
_window.SetCurrentName(cast.CurrentName);
_window.SetCurrentJob(cast.CurrentJob);
_window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId);
_window.SetAllowedIcons(cast.CurrentJobIconId);
}
}
}

View File

@@ -6,12 +6,9 @@
<LineEdit Name="NameLineEdit" />
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
<LineEdit Name="JobLineEdit" />
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
<Control HorizontalExpand="True" MinSize="50 0"/>
<GridContainer Name="IconGrid" Columns="10">
<!-- Job icon buttons are generated in the code -->
</GridContainer>
</BoxContainer>
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
<GridContainer Name="IconGrid" Columns="10">
<!-- Job icon buttons are generated in the code -->
</GridContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -8,6 +8,7 @@ using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Numerics;
using System.Linq;
namespace Content.Client.Access.UI
{
@@ -23,7 +24,7 @@ namespace Content.Client.Access.UI
public event Action<string>? OnNameChanged;
public event Action<string>? OnJobChanged;
public event Action<ProtoId<StatusIconPrototype>>? OnJobIconChanged;
public event Action<ProtoId<JobIconPrototype>>? OnJobIconChanged;
public AgentIDCardWindow()
{
@@ -38,17 +39,16 @@ namespace Content.Client.Access.UI
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
}
public void SetAllowedIcons(HashSet<ProtoId<StatusIconPrototype>> icons, string currentJobIconId)
public void SetAllowedIcons(string currentJobIconId)
{
IconGrid.DisposeAllChildren();
var jobIconGroup = new ButtonGroup();
var jobIconButtonGroup = new ButtonGroup();
var i = 0;
foreach (var jobIconId in icons)
var icons = _prototypeManager.EnumeratePrototypes<JobIconPrototype>().Where(icon => icon.AllowSelection).ToList();
icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture));
foreach (var jobIcon in icons)
{
if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
continue;
String styleBase = StyleBase.ButtonOpenBoth;
var modulo = i % JobIconColumnCount;
if (modulo == 0)
@@ -62,8 +62,9 @@ namespace Content.Client.Access.UI
Access = AccessLevel.Public,
StyleClasses = { styleBase },
MaxSize = new Vector2(42, 28),
Group = jobIconGroup,
Pressed = i == 0,
Group = jobIconButtonGroup,
Pressed = currentJobIconId == jobIcon.ID,
ToolTip = jobIcon.LocalizedJobName
};
// Generate buttons textures
@@ -78,9 +79,6 @@ namespace Content.Client.Access.UI
jobIconButton.OnPressed += _ => OnJobIconChanged?.Invoke(jobIcon.ID);
IconGrid.AddChild(jobIconButton);
if (jobIconId.Equals(currentJobIconId))
jobIconButton.Pressed = true;
i++;
}
}

View File

@@ -48,6 +48,7 @@ namespace Content.Client.Actions
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentHandleState>(OnEntityWorldTargetHandleState);
}
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
@@ -76,6 +77,18 @@ namespace Content.Client.Actions
BaseHandleState<WorldTargetActionComponent>(uid, component, state);
}
private void OnEntityWorldTargetHandleState(EntityUid uid,
EntityWorldTargetActionComponent component,
ref ComponentHandleState args)
{
if (args.Current is not EntityWorldTargetActionComponentState state)
return;
component.Whitelist = state.Whitelist;
component.CanTargetSelf = state.CanTargetSelf;
BaseHandleState<EntityWorldTargetActionComponent>(uid, component, state);
}
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
{
// TODO ACTIONS use auto comp states
@@ -293,7 +306,7 @@ namespace Content.Client.Actions
continue;
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
var actionId = Spawn(null);
var actionId = Spawn();
AddComp(actionId, action);
AddActionDirect(user, actionId);

View File

@@ -7,67 +7,66 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Content.Client.Administration
namespace Content.Client.Administration;
internal sealed class AdminNameOverlay : Overlay
{
internal sealed class AdminNameOverlay : Overlay
private readonly AdminSystem _system;
private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager;
private readonly EntityLookupSystem _entityLookup;
private readonly Font _font;
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup)
{
private readonly AdminSystem _system;
private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager;
private readonly EntityLookupSystem _entityLookup;
private readonly Font _font;
_system = system;
_entityManager = entityManager;
_eyeManager = eyeManager;
_entityLookup = entityLookup;
ZIndex = 200;
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup)
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
protected override void Draw(in OverlayDrawArgs args)
{
var viewport = args.WorldAABB;
foreach (var playerInfo in _system.PlayerList)
{
_system = system;
_entityManager = entityManager;
_eyeManager = eyeManager;
_entityLookup = entityLookup;
ZIndex = 200;
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
var entity = _entityManager.GetEntity(playerInfo.NetEntity);
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
protected override void Draw(in OverlayDrawArgs args)
{
var viewport = args.WorldAABB;
foreach (var playerInfo in _system.PlayerList)
// Otherwise the entity can not exist yet
if (entity == null || !_entityManager.EntityExists(entity))
{
var entity = _entityManager.GetEntity(playerInfo.NetEntity);
// Otherwise the entity can not exist yet
if (entity == null || !_entityManager.EntityExists(entity))
{
continue;
}
// if not on the same map, continue
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != _eyeManager.CurrentMap)
{
continue;
}
var aabb = _entityLookup.GetWorldAABB(entity.Value);
// if not on screen, continue
if (!aabb.Intersects(in viewport))
{
continue;
}
var lineoffset = new Vector2(0f, 11f);
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
if (playerInfo.Antag)
{
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", Color.OrangeRed);
}
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, playerInfo.Connected ? Color.Yellow : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, playerInfo.Connected ? Color.Aquamarine : Color.White);
continue;
}
// if not on the same map, continue
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
{
continue;
}
var aabb = _entityLookup.GetWorldAABB(entity.Value);
// if not on screen, continue
if (!aabb.Intersects(in viewport))
{
continue;
}
var lineoffset = new Vector2(0f, 11f);
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
if (playerInfo.Antag)
{
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", Color.OrangeRed);
}
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, playerInfo.Connected ? Color.Yellow : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, playerInfo.Connected ? Color.Aquamarine : Color.White);
}
}
}

View File

@@ -88,26 +88,51 @@ namespace Content.Client.Administration.UI.Bwoink
var ach = AHelpHelper.EnsurePanel(a.SessionId);
var bch = AHelpHelper.EnsurePanel(b.SessionId);
// First, sort by unread. Any chat with unread messages appears first. We just sort based on unread
// status, not number of unread messages, so that more recent unread messages take priority.
// Pinned players first
if (a.IsPinned != b.IsPinned)
return a.IsPinned ? -1 : 1;
// First, sort by unread. Any chat with unread messages appears first.
var aUnread = ach.Unread > 0;
var bUnread = bch.Unread > 0;
if (aUnread != bUnread)
return aUnread ? -1 : 1;
// Sort by recent messages during the current round.
var aRecent = a.ActiveThisRound && ach.LastMessage != DateTime.MinValue;
var bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue;
if (aRecent != bRecent)
return aRecent ? -1 : 1;
// Next, sort by connection status. Any disconnected players are grouped towards the end.
if (a.Connected != b.Connected)
return a.Connected ? -1 : 1;
// Next, group by whether or not the players have participated in this round.
// The ahelp window shows all players that have connected since server restart, this groups them all towards the bottom.
if (a.ActiveThisRound != b.ActiveThisRound)
return a.ActiveThisRound ? -1 : 1;
// Sort connected players by New Player status, then by Antag status
if (a.Connected && b.Connected)
{
var aNewPlayer = a.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
var bNewPlayer = b.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
if (aNewPlayer != bNewPlayer)
return aNewPlayer ? -1 : 1;
if (a.Antag != b.Antag)
return a.Antag ? -1 : 1;
}
// Sort disconnected players by participation in the round
if (!a.Connected && !b.Connected)
{
if (a.ActiveThisRound != b.ActiveThisRound)
return a.ActiveThisRound ? -1 : 1;
}
// Finally, sort by the most recent message.
return bch.LastMessage.CompareTo(ach.LastMessage);
};
Bans.OnPressed += _ =>
{
if (_currentPlayer is not null)
@@ -253,7 +278,20 @@ namespace Content.Client.Administration.UI.Bwoink
public void PopulateList()
{
// Maintain existing pin statuses
var pinnedPlayers = ChannelSelector.PlayerInfo.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
ChannelSelector.PopulateList();
// Restore pin statuses
foreach (var player in ChannelSelector.PlayerInfo)
{
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
{
player.IsPinned = pinnedPlayer.IsPinned;
}
}
UpdateButtons();
}
}

View File

@@ -30,7 +30,11 @@ namespace Content.Client.Administration.UI.Bwoink
}
};
OnOpen += () => Bwoink.PopulateList();
OnOpen += () =>
{
Bwoink.ChannelSelector.StopFiltering();
Bwoink.PopulateList();
};
}
}
}

View File

@@ -4,154 +4,166 @@ using Content.Client.UserInterface.Controls;
using Content.Client.Verbs.UI;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls
namespace Content.Client.Administration.UI.CustomControls;
[GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
{
[GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
private readonly AdminSystem _adminSystem;
private readonly IEntityManager _entManager;
private readonly IUserInterfaceManager _uiManager;
private PlayerInfo? _selectedPlayer;
private List<PlayerInfo> _playerList = new();
private List<PlayerInfo> _sortedPlayerList = new();
public Comparison<PlayerInfo>? Comparison;
public Func<PlayerInfo, string, string>? OverrideText;
public PlayerListControl()
{
private readonly AdminSystem _adminSystem;
private List<PlayerInfo> _playerList = new();
private readonly List<PlayerInfo> _sortedPlayerList = new();
public event Action<PlayerInfo?>? OnSelectionChanged;
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
public Func<PlayerInfo, string, string>? OverrideText;
public Comparison<PlayerInfo>? Comparison;
private IEntityManager _entManager;
private IUserInterfaceManager _uiManager;
private PlayerInfo? _selectedPlayer;
public PlayerListControl()
{
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
}
private void PlayerListNoItemSelected()
{
_selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
return;
if (selectedPlayer == _selectedPlayer)
return;
if (args.Event.Function != EngineKeyFunctions.UIClick)
return;
OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
public void StopFiltering()
{
FilterLineEdit.Text = string.Empty;
}
private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}
if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
players ??= _adminSystem.PlayerList;
_playerList = players.ToList();
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null;
FilterList();
}
private string GetText(PlayerInfo info)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
button.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
{
new Label
{
ClipText = true,
Text = GetText(info)
}
}
});
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40) };
}
public record PlayerListData(PlayerInfo Info) : ListData;
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
public event Action<PlayerInfo?>? OnSelectionChanged;
private void PlayerListNoItemSelected()
{
_selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (selectedPlayer == _selectedPlayer)
return;
if (args.Event.Function != EngineKeyFunctions.UIClick)
return;
OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
public void StopFiltering()
{
FilterLineEdit.Text = string.Empty;
}
private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}
if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
// Maintain existing pin statuses
var pinnedPlayers = _playerList.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
players ??= _adminSystem.PlayerList;
_playerList = players.ToList();
// Restore pin statuses
foreach (var player in _playerList)
{
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
{
player.IsPinned = pinnedPlayer.IsPinned;
}
}
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null;
FilterList();
}
private string GetText(PlayerInfo info)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
var entry = new PlayerListEntry();
entry.Setup(info, OverrideText);
entry.OnPinStatusChanged += _ =>
{
FilterList();
};
button.AddChild(entry);
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
}
public record PlayerListData(PlayerInfo Info) : ListData;

View File

@@ -0,0 +1,6 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal" HorizontalExpand="true">
<Label Name="PlayerEntryLabel" Text="" ClipText="True" HorizontalExpand="True" />
<TextureButton Name="PlayerEntryPinButton"
HorizontalAlignment="Right" />
</BoxContainer>

View File

@@ -0,0 +1,58 @@
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls;
[GenerateTypedNameReferences]
public sealed partial class PlayerListEntry : BoxContainer
{
public PlayerListEntry()
{
RobustXamlLoader.Load(this);
}
public event Action<PlayerInfo>? OnPinStatusChanged;
public void Setup(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
{
Update(info, overrideText);
PlayerEntryPinButton.OnPressed += HandlePinButtonPressed(info);
}
private Action<BaseButton.ButtonEventArgs> HandlePinButtonPressed(PlayerInfo info)
{
return args =>
{
info.IsPinned = !info.IsPinned;
UpdatePinButtonTexture(info.IsPinned);
OnPinStatusChanged?.Invoke(info);
};
}
private void Update(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
{
PlayerEntryLabel.Text = overrideText?.Invoke(info, $"{info.CharacterName} ({info.Username})") ??
$"{info.CharacterName} ({info.Username})";
UpdatePinButtonTexture(info.IsPinned);
}
private void UpdatePinButtonTexture(bool isPinned)
{
if (isPinned)
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonUnpinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonPinned);
}
else
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonPinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonUnpinned);
}
}
}

View File

@@ -0,0 +1,36 @@
<ui:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc ban-panel-title}" MinSize="300 300">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Name="PlayerName"/>
<Button Name="UsernameCopyButton" Text="{Loc player-panel-copy-username}"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Name="Whitelisted"/>
<controls:ConfirmButton Name="WhitelistToggle" Text="{Loc 'player-panel-false'}" Visible="False"></controls:ConfirmButton>
</BoxContainer>
<Label Name="Playtime"/>
<Label Name="Notes"/>
<Label Name="Bans"/>
<Label Name="RoleBans"/>
<Label Name="SharedConnections"/>
<BoxContainer Align="Center">
<GridContainer Rows="5">
<Button Name="NotesButton" Text="{Loc player-panel-show-notes}" SetWidth="136" Disabled="True"/>
<Button Name="AhelpButton" Text="{Loc player-panel-help}" Disabled="True"/>
<Button Name="FreezeButton" Text = "{Loc player-panel-freeze}" Disabled="True"/>
<controls:ConfirmButton Name="KickButton" Text="{Loc player-panel-kick}" Disabled="True"/>
<controls:ConfirmButton Name="DeleteButton" Text="{Loc player-panel-delete}" Disabled="True"/>
<Button Name="ShowBansButton" Text="{Loc player-panel-show-bans}" SetWidth="136" Disabled="True"/>
<Button Name="LogsButton" Text="{Loc player-panel-logs}" Disabled="True"/>
<Button Name="FreezeAndMuteToggleButton" Text="{Loc player-panel-freeze-and-mute}" Disabled="True"/>
<Button Name="BanButton" Text="{Loc player-panel-ban}" Disabled="True"/>
<controls:ConfirmButton Name="RejuvenateButton" Text="{Loc player-panel-rejuvenate}" Disabled="True"/>
</GridContainer>
</BoxContainer>
</BoxContainer>
</ui:FancyWindow>

View File

@@ -0,0 +1,132 @@
using Content.Client.Administration.Managers;
using Content.Client.UserInterface.Controls;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Network;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.PlayerPanel;
[GenerateTypedNameReferences]
public sealed partial class PlayerPanel : FancyWindow
{
private readonly IClientAdminManager _adminManager;
public event Action<string>? OnUsernameCopy;
public event Action<NetUserId?>? OnOpenNotes;
public event Action<NetUserId?>? OnOpenBans;
public event Action<NetUserId?>? OnAhelp;
public event Action<string?>? OnKick;
public event Action<NetUserId?>? OnOpenBanPanel;
public event Action<NetUserId?, bool>? OnWhitelistToggle;
public event Action? OnFreezeAndMuteToggle;
public event Action? OnFreeze;
public event Action? OnLogs;
public event Action? OnDelete;
public event Action? OnRejuvenate;
public NetUserId? TargetPlayer;
public string? TargetUsername;
private bool _isWhitelisted;
public PlayerPanel(IClientAdminManager adminManager)
{
RobustXamlLoader.Load(this);
_adminManager = adminManager;
UsernameCopyButton.OnPressed += _ => OnUsernameCopy?.Invoke(PlayerName.Text ?? "");
BanButton.OnPressed += _ => OnOpenBanPanel?.Invoke(TargetPlayer);
KickButton.OnPressed += _ => OnKick?.Invoke(TargetUsername);
NotesButton.OnPressed += _ => OnOpenNotes?.Invoke(TargetPlayer);
ShowBansButton.OnPressed += _ => OnOpenBans?.Invoke(TargetPlayer);
AhelpButton.OnPressed += _ => OnAhelp?.Invoke(TargetPlayer);
WhitelistToggle.OnPressed += _ =>
{
OnWhitelistToggle?.Invoke(TargetPlayer, _isWhitelisted);
SetWhitelisted(!_isWhitelisted);
};
FreezeButton.OnPressed += _ => OnFreeze?.Invoke();
FreezeAndMuteToggleButton.OnPressed += _ => OnFreezeAndMuteToggle?.Invoke();
LogsButton.OnPressed += _ => OnLogs?.Invoke();
DeleteButton.OnPressed += _ => OnDelete?.Invoke();
RejuvenateButton.OnPressed += _ => OnRejuvenate?.Invoke();
}
public void SetUsername(string player)
{
Title = Loc.GetString("player-panel-title", ("player", player));
PlayerName.Text = Loc.GetString("player-panel-username", ("player", player));
}
public void SetWhitelisted(bool? whitelisted)
{
if (whitelisted == null)
{
Whitelisted.Text = null;
WhitelistToggle.Visible = false;
}
else
{
Whitelisted.Text = Loc.GetString("player-panel-whitelisted");
WhitelistToggle.Text = whitelisted.Value ? Loc.GetString("player-panel-true") : Loc.GetString("player-panel-false");
WhitelistToggle.Visible = true;
_isWhitelisted = whitelisted.Value;
}
}
public void SetBans(int? totalBans, int? totalRoleBans)
{
// If one value exists then so should the other.
DebugTools.Assert(totalBans.HasValue && totalRoleBans.HasValue || totalBans == null && totalRoleBans == null);
Bans.Text = totalBans != null ? Loc.GetString("player-panel-bans", ("totalBans", totalBans)) : null;
RoleBans.Text = totalRoleBans != null ? Loc.GetString("player-panel-rolebans", ("totalRoleBans", totalRoleBans)) : null;
}
public void SetNotes(int? totalNotes)
{
Notes.Text = totalNotes != null ? Loc.GetString("player-panel-notes", ("totalNotes", totalNotes)) : null;
}
public void SetSharedConnections(int sharedConnections)
{
SharedConnections.Text = Loc.GetString("player-panel-shared-connections", ("sharedConnections", sharedConnections));
}
public void SetPlaytime(TimeSpan playtime)
{
Playtime.Text = Loc.GetString("player-panel-playtime",
("days", playtime.Days),
("hours", playtime.Hours % 24),
("minutes", playtime.Minutes % (24 * 60)));
}
public void SetFrozen(bool canFreeze, bool frozen)
{
FreezeAndMuteToggleButton.Disabled = !canFreeze;
FreezeButton.Disabled = !canFreeze || frozen;
FreezeAndMuteToggleButton.Text = Loc.GetString(!frozen ? "player-panel-freeze-and-mute" : "player-panel-unfreeze");
}
public void SetAhelp(bool canAhelp)
{
AhelpButton.Disabled = !canAhelp;
}
public void SetButtons()
{
BanButton.Disabled = !_adminManager.CanCommand("banpanel");
KickButton.Disabled = !_adminManager.CanCommand("kick");
NotesButton.Disabled = !_adminManager.CanCommand("adminnotes");
ShowBansButton.Disabled = !_adminManager.CanCommand("banlist");
WhitelistToggle.Disabled =
!(_adminManager.CanCommand("whitelistadd") && _adminManager.CanCommand("whitelistremove"));
LogsButton.Disabled = !_adminManager.CanCommand("adminlogs");
RejuvenateButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
DeleteButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
}
}

View File

@@ -0,0 +1,72 @@
using Content.Client.Administration.Managers;
using Content.Client.Eui;
using Content.Shared.Administration;
using Content.Shared.Eui;
using JetBrains.Annotations;
using Robust.Client.Console;
using Robust.Client.UserInterface;
namespace Content.Client.Administration.UI.PlayerPanel;
[UsedImplicitly]
public sealed class PlayerPanelEui : BaseEui
{
[Dependency] private readonly IClientConsoleHost _console = default!;
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IClipboardManager _clipboard = default!;
private PlayerPanel PlayerPanel { get; }
public PlayerPanelEui()
{
PlayerPanel = new PlayerPanel(_admin);
PlayerPanel.OnUsernameCopy += username => _clipboard.SetText(username);
PlayerPanel.OnOpenNotes += id => _console.ExecuteCommand($"adminnotes \"{id}\"");
// Kick command does not support GUIDs
PlayerPanel.OnKick += username => _console.ExecuteCommand($"kick \"{username}\"");
PlayerPanel.OnOpenBanPanel += id => _console.ExecuteCommand($"banpanel \"{id}\"");
PlayerPanel.OnOpenBans += id => _console.ExecuteCommand($"banlist \"{id}\"");
PlayerPanel.OnAhelp += id => _console.ExecuteCommand($"openahelp \"{id}\"");
PlayerPanel.OnWhitelistToggle += (id, whitelisted) =>
{
_console.ExecuteCommand(whitelisted ? $"whitelistremove \"{id}\"" : $"whitelistadd \"{id}\"");
};
PlayerPanel.OnFreezeAndMuteToggle += () => SendMessage(new PlayerPanelFreezeMessage(true));
PlayerPanel.OnFreeze += () => SendMessage(new PlayerPanelFreezeMessage());
PlayerPanel.OnLogs += () => SendMessage(new PlayerPanelLogsMessage());
PlayerPanel.OnRejuvenate += () => SendMessage(new PlayerPanelRejuvenationMessage());
PlayerPanel.OnDelete+= () => SendMessage(new PlayerPanelDeleteMessage());
PlayerPanel.OnClose += () => SendMessage(new CloseEuiMessage());
}
public override void Opened()
{
PlayerPanel.OpenCentered();
}
public override void Closed()
{
PlayerPanel.Close();
}
public override void HandleState(EuiStateBase state)
{
if (state is not PlayerPanelEuiState s)
return;
PlayerPanel.TargetPlayer = s.Guid;
PlayerPanel.TargetUsername = s.Username;
PlayerPanel.SetUsername(s.Username);
PlayerPanel.SetPlaytime(s.Playtime);
PlayerPanel.SetBans(s.TotalBans, s.TotalRoleBans);
PlayerPanel.SetNotes(s.TotalNotes);
PlayerPanel.SetWhitelisted(s.Whitelisted);
PlayerPanel.SetSharedConnections(s.SharedConnections);
PlayerPanel.SetFrozen(s.CanFreeze, s.Frozen);
PlayerPanel.SetAhelp(s.CanAhelp);
PlayerPanel.SetButtons();
}
}

View File

@@ -1,19 +1,21 @@
using Content.Client.Administration.Managers;
using Content.Client.Station;
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map.Components;
using Robust.Shared.Timing;
namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
[GenerateTypedNameReferences]
public sealed partial class ObjectsTab : Control
{
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IClientConsoleHost _console = default!;
private readonly Color _altColor = Color.FromHex("#292B38");
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
@@ -49,10 +51,20 @@ public sealed partial class ObjectsTab : Control
RefreshListButton.OnPressed += _ => RefreshObjectList();
var defaultSelection = ObjectsTabSelection.Grids;
ObjectTypeOptions.SelectId((int) defaultSelection);
ObjectTypeOptions.SelectId((int)defaultSelection);
RefreshObjectList(defaultSelection);
}
private void TeleportTo(NetEntity nent)
{
_console.ExecuteCommand($"tpto {nent}");
}
private void Delete(NetEntity nent)
{
_console.ExecuteCommand($"delete {nent}");
}
public void RefreshObjectList()
{
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
@@ -116,9 +128,9 @@ public sealed partial class ObjectsTab : Control
if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
return;
var entry = new ObjectsTabEntry(info.Name,
info.Entity,
new StyleBoxFlat { BackgroundColor = backgroundColor });
var entry = new ObjectsTabEntry(_admin, info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
entry.OnTeleport += TeleportTo;
entry.OnDelete += Delete;
button.ToolTip = $"{info.Name}, {info.Entity}";
button.AddChild(entry);

View File

@@ -5,13 +5,25 @@
HorizontalExpand="True"
SeparationOverride="4">
<Label Name="NameLabel"
SizeFlagsStretchRatio="3"
SizeFlagsStretchRatio="5"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Label Name="EIDLabel"
SizeFlagsStretchRatio="5"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Button Name="TeleportButton"
Text="{Loc object-tab-entity-teleport}"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Button Name="DeleteButton"
Text="{Loc object-tab-entity-delete}"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"
ClipText="True"/>
</BoxContainer>
</PanelContainer>

View File

@@ -1,4 +1,5 @@
using Robust.Client.AutoGenerated;
using Content.Client.Administration.Managers;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
@@ -10,12 +11,30 @@ public sealed partial class ObjectsTabEntry : PanelContainer
{
public NetEntity AssocEntity;
public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox)
public Action<NetEntity>? OnTeleport;
public Action<NetEntity>? OnDelete;
private readonly Dictionary<Button, ConfirmationData> _confirmations = new();
public ObjectsTabEntry(IClientAdminManager manager, string name, NetEntity nent, StyleBox styleBox)
{
RobustXamlLoader.Load(this);
AssocEntity = nent;
EIDLabel.Text = nent.ToString();
NameLabel.Text = name;
BackgroundColorPanel.PanelOverride = styleBox;
TeleportButton.Disabled = !manager.CanCommand("tpto");
DeleteButton.Disabled = !manager.CanCommand("delete");
TeleportButton.OnPressed += _ => OnTeleport?.Invoke(nent);
DeleteButton.OnPressed += _ =>
{
if (!AdminUIHelpers.TryConfirm(DeleteButton, _confirmations))
{
return;
}
OnDelete?.Invoke(nent);
};
}
}

View File

@@ -5,17 +5,23 @@
HorizontalExpand="True"
SeparationOverride="4">
<Label Name="ObjectNameLabel"
SizeFlagsStretchRatio="3"
SizeFlagsStretchRatio="5"
HorizontalExpand="True"
ClipText="True"
Text="{Loc object-tab-object-name}"
MouseFilter="Pass"/>
<cc:VSeparator/>
<Label Name="EntityIDLabel"
SizeFlagsStretchRatio="3"
SizeFlagsStretchRatio="5"
HorizontalExpand="True"
ClipText="True"
Text="{Loc object-tab-entity-id}"
MouseFilter="Pass"/>
<Label Name="EntityTeleportLabel"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"/>
<Label Name="EntityDeleteLabel"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"/>
</BoxContainer>
</Control>

View File

@@ -10,220 +10,219 @@ using Robust.Client.UserInterface.XAML;
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Administration.UI.Tabs.PlayerTab
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
[GenerateTypedNameReferences]
public sealed partial class PlayerTab : Control
{
[GenerateTypedNameReferences]
public sealed partial class PlayerTab : Control
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPlayerManager _playerMan = default!;
private const string ArrowUp = "↑";
private const string ArrowDown = "↓";
private readonly Color _altColor = Color.FromHex("#292B38");
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
private readonly AdminSystem _adminSystem;
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
private Header _headerClicked = Header.Username;
private bool _ascending = true;
private bool _showDisconnected;
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
public PlayerTab()
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPlayerManager _playerMan = default!;
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
private const string ArrowUp = "↑";
private const string ArrowDown = "↓";
private readonly Color _altColor = Color.FromHex("#292B38");
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
private readonly AdminSystem _adminSystem;
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
_adminSystem = _entManager.System<AdminSystem>();
_adminSystem.PlayerListChanged += RefreshPlayerList;
_adminSystem.OverlayEnabled += OverlayEnabled;
_adminSystem.OverlayDisabled += OverlayDisabled;
private Header _headerClicked = Header.Username;
private bool _ascending = true;
private bool _showDisconnected;
OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
ListHeader.OnHeaderClicked += HeaderClicked;
public PlayerTab()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
SearchList.SearchBar = SearchLineEdit;
SearchList.GenerateItem += GenerateButton;
SearchList.DataFilterCondition += DataFilterCondition;
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
_adminSystem = _entManager.System<AdminSystem>();
_adminSystem.PlayerListChanged += RefreshPlayerList;
_adminSystem.OverlayEnabled += OverlayEnabled;
_adminSystem.OverlayDisabled += OverlayDisabled;
RefreshPlayerList(_adminSystem.PlayerList);
OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
ListHeader.OnHeaderClicked += HeaderClicked;
SearchList.SearchBar = SearchLineEdit;
SearchList.GenerateItem += GenerateButton;
SearchList.DataFilterCondition += DataFilterCondition;
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
RefreshPlayerList(_adminSystem.PlayerList);
}
#region Antag Overlay
private void OverlayEnabled()
{
OverlayButton.Pressed = true;
}
private void OverlayDisabled()
{
OverlayButton.Pressed = false;
}
private void OverlayButtonPressed(ButtonEventArgs args)
{
if (args.Button.Pressed)
{
_adminSystem.AdminOverlayOn();
}
else
{
_adminSystem.AdminOverlayOff();
}
}
#endregion
private void ShowDisconnectedPressed(ButtonEventArgs args)
{
_showDisconnected = args.Button.Pressed;
RefreshPlayerList(_players);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_adminSystem.PlayerListChanged -= RefreshPlayerList;
_adminSystem.OverlayEnabled -= OverlayEnabled;
_adminSystem.OverlayDisabled -= OverlayDisabled;
OverlayButton.OnPressed -= OverlayButtonPressed;
ListHeader.OnHeaderClicked -= HeaderClicked;
}
}
#region ListContainer
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
{
_players = players;
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
sortedPlayers.Sort(Compare);
UpdateHeaderSymbols();
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
.ToList());
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var player})
return;
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
button.AddChild(entry);
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
}
/// <summary>
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
/// If all characters are lowercase, the comparison ignores case.
/// If there is an uppercase character, the comparison is case sensitive.
/// </summary>
/// <param name="filter"></param>
/// <param name="listData"></param>
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
private bool DataFilterCondition(string filter, ListData listData)
{
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
return false;
if (!_showDisconnected && !info.Connected)
return false;
if (IsAllLower(filter))
{
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
return false;
}
else
{
if (!playerString.Contains(filter))
return false;
}
return true;
}
private bool IsAllLower(string input)
{
foreach (var c in input)
{
if (char.IsLetter(c) && !char.IsLower(c))
return false;
}
return true;
}
#endregion
#region Header
private void UpdateHeaderSymbols()
{
ListHeader.ResetHeaderText();
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
}
private int Compare(PlayerInfo x, PlayerInfo y)
{
if (!_ascending)
{
(x, y) = (y, x);
}
return _headerClicked switch
{
Header.Username => Compare(x.Username, y.Username),
Header.Character => Compare(x.CharacterName, y.CharacterName),
Header.Job => Compare(x.StartingJob, y.StartingJob),
Header.Antagonist => x.Antag.CompareTo(y.Antag),
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
_ => 1
};
}
private int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
private void HeaderClicked(Header header)
{
if (_headerClicked == header)
{
_ascending = !_ascending;
}
else
{
_headerClicked = header;
_ascending = true;
}
RefreshPlayerList(_adminSystem.PlayerList);
}
#endregion
}
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
#region Antag Overlay
private void OverlayEnabled()
{
OverlayButton.Pressed = true;
}
private void OverlayDisabled()
{
OverlayButton.Pressed = false;
}
private void OverlayButtonPressed(ButtonEventArgs args)
{
if (args.Button.Pressed)
{
_adminSystem.AdminOverlayOn();
}
else
{
_adminSystem.AdminOverlayOff();
}
}
#endregion
private void ShowDisconnectedPressed(ButtonEventArgs args)
{
_showDisconnected = args.Button.Pressed;
RefreshPlayerList(_players);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_adminSystem.PlayerListChanged -= RefreshPlayerList;
_adminSystem.OverlayEnabled -= OverlayEnabled;
_adminSystem.OverlayDisabled -= OverlayDisabled;
OverlayButton.OnPressed -= OverlayButtonPressed;
ListHeader.OnHeaderClicked -= HeaderClicked;
}
}
#region ListContainer
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
{
_players = players;
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
sortedPlayers.Sort(Compare);
UpdateHeaderSymbols();
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
.ToList());
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var player})
return;
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
button.AddChild(entry);
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
}
/// <summary>
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
/// If all characters are lowercase, the comparison ignores case.
/// If there is an uppercase character, the comparison is case sensitive.
/// </summary>
/// <param name="filter"></param>
/// <param name="listData"></param>
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
private bool DataFilterCondition(string filter, ListData listData)
{
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
return false;
if (!_showDisconnected && !info.Connected)
return false;
if (IsAllLower(filter))
{
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
return false;
}
else
{
if (!playerString.Contains(filter))
return false;
}
return true;
}
private bool IsAllLower(string input)
{
foreach (var c in input)
{
if (char.IsLetter(c) && !char.IsLower(c))
return false;
}
return true;
}
#endregion
#region Header
private void UpdateHeaderSymbols()
{
ListHeader.ResetHeaderText();
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
}
private int Compare(PlayerInfo x, PlayerInfo y)
{
if (!_ascending)
{
(x, y) = (y, x);
}
return _headerClicked switch
{
Header.Username => Compare(x.Username, y.Username),
Header.Character => Compare(x.CharacterName, y.CharacterName),
Header.Job => Compare(x.StartingJob, y.StartingJob),
Header.Antagonist => x.Antag.CompareTo(y.Antag),
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
_ => 1
};
}
private int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
private void HeaderClicked(Header header)
{
if (_headerClicked == header)
{
_ascending = !_ascending;
}
else
{
_headerClicked = header;
_ascending = true;
}
RefreshPlayerList(_adminSystem.PlayerList);
}
#endregion
}
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;

View File

@@ -93,6 +93,6 @@ public sealed class ClientAlertsSystem : AlertsSystem
public void AlertClicked(ProtoId<AlertPrototype> alertType)
{
RaiseNetworkEvent(new ClickAlertEvent(alertType));
RaisePredictiveEvent(new ClickAlertEvent(alertType));
}
}

View File

@@ -31,7 +31,7 @@ public sealed partial class AnomalyScannerMenu : FancyWindow
msg.PushNewline();
var time = NextPulseTime.Value - _timing.CurTime;
var timestring = $"{time.Minutes:00}:{time.Seconds:00}";
msg.AddMarkup(Loc.GetString("anomaly-scanner-pulse-timer", ("time", timestring)));
msg.AddMarkupOrThrow(Loc.GetString("anomaly-scanner-pulse-timer", ("time", timestring)));
}
TextDisplay.SetMarkup(msg.ToMarkup());

View File

@@ -0,0 +1,10 @@
using Content.Shared.Atmos.EntitySystems;
using JetBrains.Annotations;
namespace Content.Client.Atmos.EntitySystems;
[UsedImplicitly]
public sealed class GasMinerSystem : SharedGasMinerSystem
{
}

View File

@@ -16,6 +16,8 @@ namespace Content.Client.Atmos.UI
[GenerateTypedNameReferences]
public sealed partial class GasAnalyzerWindow : DefaultWindow
{
private NetEntity _currentEntity = NetEntity.Invalid;
public GasAnalyzerWindow()
{
RobustXamlLoader.Load(this);
@@ -55,6 +57,13 @@ namespace Content.Client.Atmos.UI
// Device Tab
if (msg.NodeGasMixes.Length > 1)
{
if (_currentEntity != msg.DeviceUid)
{
// when we get new device data switch to the device tab
CTabContainer.CurrentTab = 0;
_currentEntity = msg.DeviceUid;
}
CTabContainer.SetTabVisible(0, true);
CTabContainer.SetTabTitle(0, Loc.GetString("gas-analyzer-window-tab-title-capitalized", ("title", msg.DeviceName)));
// Set up Grid
@@ -143,6 +152,7 @@ namespace Content.Client.Atmos.UI
CTabContainer.SetTabVisible(0, false);
CTabContainer.CurrentTab = 1;
minSize = new Vector2(CEnvironmentMix.DesiredSize.X + 40, MinSize.Y);
_currentEntity = NetEntity.Invalid;
}
MinSize = minSize;

View File

@@ -57,7 +57,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
/// </summary>
private int MaxSingleSound => (int) (_maxAmbientCount / (16.0f / 6.0f));
private readonly Dictionary<AmbientSoundComponent, (EntityUid? Stream, SoundSpecifier Sound, string Path)> _playingSounds = new();
private readonly Dictionary<Entity<AmbientSoundComponent>, (EntityUid? Stream, SoundSpecifier Sound, string Path)> _playingSounds = new();
private readonly Dictionary<string, int> _playingCount = new();
public bool OverlayEnabled
@@ -107,7 +107,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
private void OnShutdown(EntityUid uid, AmbientSoundComponent component, ComponentShutdown args)
{
if (!_playingSounds.Remove(component, out var sound))
if (!_playingSounds.Remove((uid, component), out var sound))
return;
_audio.Stop(sound.Stream);
@@ -120,13 +120,13 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
{
_ambienceVolume = SharedAudioSystem.GainToVolume(value);
foreach (var (comp, values) in _playingSounds)
foreach (var (ent, values) in _playingSounds)
{
if (values.Stream == null)
continue;
var stream = values.Stream;
_audio.SetVolume(stream, _params.Volume + comp.Volume + _ambienceVolume);
_audio.SetVolume(stream, _params.Volume + ent.Comp.Volume + _ambienceVolume);
}
}
private void SetCooldown(float value) => _cooldown = value;
@@ -165,7 +165,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
if (_gameTiming.CurTime < _targetTime)
return;
_targetTime = _gameTiming.CurTime+TimeSpan.FromSeconds(_cooldown);
_targetTime = _gameTiming.CurTime + TimeSpan.FromSeconds(_cooldown);
var player = _playerManager.LocalEntity;
if (!EntityManager.TryGetComponent(player, out TransformComponent? xform))
@@ -190,7 +190,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
private readonly struct QueryState
{
public readonly Dictionary<string, List<(float Importance, AmbientSoundComponent)>> SourceDict = new();
public readonly Dictionary<string, List<(float Importance, Entity<AmbientSoundComponent>)>> SourceDict = new();
public readonly Vector2 MapPos;
public readonly TransformComponent Player;
public readonly SharedTransformSystem TransformSystem;
@@ -224,11 +224,11 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
if (ambientComp.Sound is SoundPathSpecifier path)
key = path.Path.ToString();
else
key = ((SoundCollectionSpecifier) ambientComp.Sound).Collection ?? string.Empty;
key = ((SoundCollectionSpecifier)ambientComp.Sound).Collection ?? string.Empty;
// Prioritize far away & loud sounds.
var importance = range * (ambientComp.Volume + 32);
state.SourceDict.GetOrNew(key).Add((importance, ambientComp));
state.SourceDict.GetOrNew(key).Add((importance, (value.Uid, ambientComp)));
return true;
}
@@ -242,16 +242,18 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
var mapPos = _xformSystem.GetMapCoordinates(playerXform);
// Remove out-of-range ambiences
foreach (var (comp, sound) in _playingSounds)
foreach (var (ent, sound) in _playingSounds)
{
var entity = comp.Owner;
//var entity = comp.Owner;
var owner = ent.Owner;
var comp = ent.Comp;
if (comp.Enabled &&
// Don't keep playing sounds that have changed since.
sound.Sound == comp.Sound &&
query.TryGetComponent(entity, out var xform) &&
query.TryGetComponent(owner, out var xform) &&
xform.MapID == playerXform.MapID &&
!metaQuery.GetComponent(entity).EntityPaused)
!metaQuery.GetComponent(owner).EntityPaused)
{
// TODO: This is just trydistance for coordinates.
var distance = (xform.ParentUid == playerXform.ParentUid)
@@ -263,7 +265,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
}
_audio.Stop(sound.Stream);
_playingSounds.Remove(comp);
_playingSounds.Remove(ent);
_playingCount[sound.Path] -= 1;
if (_playingCount[sound.Path] == 0)
_playingCount.Remove(sound.Path);
@@ -278,7 +280,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
_treeSys.QueryAabb(ref state, Callback, mapPos.MapId, worldAabb);
// Add in range ambiences
foreach (var (key, sources) in state.SourceDict)
foreach (var (key, sourceList) in state.SourceDict)
{
if (_playingSounds.Count >= _maxAmbientCount)
break;
@@ -286,13 +288,14 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
if (_playingCount.TryGetValue(key, out var playingCount) && playingCount >= MaxSingleSound)
continue;
sources.Sort(static (a, b) => b.Importance.CompareTo(a.Importance));
sourceList.Sort(static (a, b) => b.Importance.CompareTo(a.Importance));
foreach (var (_, comp) in sources)
foreach (var (_, sourceEntity) in sourceList)
{
var uid = comp.Owner;
var uid = sourceEntity.Owner;
var comp = sourceEntity.Comp;
if (_playingSounds.ContainsKey(comp) ||
if (_playingSounds.ContainsKey(sourceEntity) ||
metaQuery.GetComponent(uid).EntityPaused)
continue;
@@ -303,7 +306,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
.WithMaxDistance(comp.Range);
var stream = _audio.PlayEntity(comp.Sound, Filter.Local(), uid, false, audioParams);
_playingSounds[comp] = (stream.Value.Entity, comp.Sound, key);
_playingSounds[sourceEntity] = (stream.Value.Entity, comp.Sound, key);
playingCount++;
if (_playingSounds.Count >= _maxAmbientCount)

View File

@@ -5,71 +5,70 @@ using Content.Shared.Chat;
using Robust.Client.Console;
using Robust.Shared.Utility;
namespace Content.Client.Chat.Managers
namespace Content.Client.Chat.Managers;
internal sealed class ChatManager : IChatManager
{
internal sealed class ChatManager : IChatManager
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IClientAdminManager _adminMgr = default!;
[Dependency] private readonly IEntitySystemManager _systems = default!;
private ISawmill _sawmill = default!;
public void Initialize()
{
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IClientAdminManager _adminMgr = default!;
[Dependency] private readonly IEntitySystemManager _systems = default!;
_sawmill = Logger.GetSawmill("chat");
_sawmill.Level = LogLevel.Info;
}
private ISawmill _sawmill = default!;
public void Initialize()
public void SendMessage(string text, ChatSelectChannel channel)
{
var str = text.ToString();
switch (channel)
{
_sawmill = Logger.GetSawmill("chat");
_sawmill.Level = LogLevel.Info;
}
case ChatSelectChannel.Console:
// run locally
_consoleHost.ExecuteCommand(text);
break;
public void SendMessage(string text, ChatSelectChannel channel)
{
var str = text.ToString();
switch (channel)
{
case ChatSelectChannel.Console:
// run locally
_consoleHost.ExecuteCommand(text);
break;
case ChatSelectChannel.LOOC:
_consoleHost.ExecuteCommand($"looc \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.LOOC:
_consoleHost.ExecuteCommand($"looc \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.OOC:
_consoleHost.ExecuteCommand($"ooc \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.OOC:
_consoleHost.ExecuteCommand($"ooc \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.Admin:
_consoleHost.ExecuteCommand($"asay \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.Admin:
_consoleHost.ExecuteCommand($"asay \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.Emotes:
_consoleHost.ExecuteCommand($"me \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.Emotes:
_consoleHost.ExecuteCommand($"me \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.Dead:
if (_systems.GetEntitySystemOrNull<GhostSystem>() is {IsGhost: true})
goto case ChatSelectChannel.Local;
case ChatSelectChannel.Dead:
if (_systems.GetEntitySystemOrNull<GhostSystem>() is {IsGhost: true})
goto case ChatSelectChannel.Local;
if (_adminMgr.HasFlag(AdminFlags.Admin))
_consoleHost.ExecuteCommand($"dsay \"{CommandParsing.Escape(str)}\"");
else
_sawmill.Warning("Tried to speak on deadchat without being ghost or admin.");
break;
if (_adminMgr.HasFlag(AdminFlags.Admin))
_consoleHost.ExecuteCommand($"dsay \"{CommandParsing.Escape(str)}\"");
else
_sawmill.Warning("Tried to speak on deadchat without being ghost or admin.");
break;
// TODO sepearate radio and say into separate commands.
case ChatSelectChannel.Radio:
case ChatSelectChannel.Local:
_consoleHost.ExecuteCommand($"say \"{CommandParsing.Escape(str)}\"");
break;
// TODO sepearate radio and say into separate commands.
case ChatSelectChannel.Radio:
case ChatSelectChannel.Local:
_consoleHost.ExecuteCommand($"say \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.Whisper:
_consoleHost.ExecuteCommand($"whisper \"{CommandParsing.Escape(str)}\"");
break;
case ChatSelectChannel.Whisper:
_consoleHost.ExecuteCommand($"whisper \"{CommandParsing.Escape(str)}\"");
break;
default:
throw new ArgumentOutOfRangeException(nameof(channel), channel, null);
}
default:
throw new ArgumentOutOfRangeException(nameof(channel), channel, null);
}
}
}

View File

@@ -16,6 +16,7 @@ namespace Content.Client.Chat.UI
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] protected readonly IConfigurationManager ConfigManager = default!;
private readonly SharedTransformSystem _transformSystem;
public enum SpeechType : byte
{
@@ -83,6 +84,7 @@ namespace Content.Client.Chat.UI
{
IoCManager.InjectDependencies(this);
_senderEntity = senderEntity;
_transformSystem = _entityManager.System<SharedTransformSystem>();
// Use text clipping so new messages don't overlap old ones being pushed up.
RectClipContent = true;
@@ -140,7 +142,7 @@ namespace Content.Client.Chat.UI
}
var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -EntityVerticalOffset;
var worldPos = xform.WorldPosition + offset;
var worldPos = _transformSystem.GetWorldPosition(xform) + offset;
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
var screenPos = lowerCenter - new Vector2(ContentSize.X / 2, ContentSize.Y + _verticalOffsetAchieved);

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -9,11 +10,15 @@ namespace Content.Client.Chemistry.UI
[UsedImplicitly]
public sealed class TransferAmountBoundUserInterface : BoundUserInterface
{
private IEntityManager _entManager;
private EntityUid _owner;
[ViewVariables]
private TransferAmountWindow? _window;
public TransferAmountBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_owner = owner;
_entManager = IoCManager.Resolve<IEntityManager>();
}
protected override void Open()
@@ -21,6 +26,9 @@ namespace Content.Client.Chemistry.UI
base.Open();
_window = this.CreateWindow<TransferAmountWindow>();
if (_entManager.TryGetComponent<SolutionTransferComponent>(_owner, out var comp))
_window.SetBounds(comp.MinimumTransferAmount.Int(), comp.MaximumTransferAmount.Int());
_window.ApplyButton.OnPressed += _ =>
{
if (int.TryParse(_window.AmountLineEdit.Text, out var i))

View File

@@ -6,6 +6,10 @@
<BoxContainer Orientation="Horizontal">
<LineEdit Name="AmountLineEdit" Access="Public" HorizontalExpand="True" PlaceHolder="{Loc 'ui-transfer-amount-line-edit-placeholder'}"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Name="MinimumAmount" Access="Public" HorizontalExpand="True" />
<Label Name="MaximumAmount" Access="Public" />
</BoxContainer>
<Button Name="ApplyButton" Access="Public" Text="{Loc 'ui-transfer-amount-apply'}"/>
</BoxContainer>
</DefaultWindow>

View File

@@ -8,9 +8,29 @@ namespace Content.Client.Chemistry.UI
[GenerateTypedNameReferences]
public sealed partial class TransferAmountWindow : DefaultWindow
{
private int _max = Int32.MaxValue;
private int _min = 1;
public TransferAmountWindow()
{
RobustXamlLoader.Load(this);
AmountLineEdit.OnTextChanged += OnValueChanged;
}
public void SetBounds(int min, int max)
{
_min = min;
_max = max;
MinimumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-min", ("amount", _min));
MaximumAmount.Text = Loc.GetString("comp-solution-transfer-set-amount-max", ("amount", _max));
}
private void OnValueChanged(LineEdit.LineEditEventArgs args)
{
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount > _max || amount < _min)
ApplyButton.Disabled = true;
else
ApplyButton.Disabled = false;
}
}
}

View File

@@ -2,6 +2,8 @@ using Content.Client.Items.Systems;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Hands;
using Content.Shared.Item;
using Content.Shared.Rounding;
@@ -20,6 +22,7 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
base.Initialize();
SubscribeLocalEvent<SolutionContainerVisualsComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SolutionContainerVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals);
SubscribeLocalEvent<SolutionContainerVisualsComponent, GetEquipmentVisualsEvent>(OnGetClothingVisuals);
}
private void OnMapInit(EntityUid uid, SolutionContainerVisualsComponent component, MapInitEvent args)
@@ -174,4 +177,41 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
args.Layers.Add((key, layer));
}
}
private void OnGetClothingVisuals(Entity<SolutionContainerVisualsComponent> ent, ref GetEquipmentVisualsEvent args)
{
if (ent.Comp.EquippedFillBaseName == null)
return;
if (!TryComp<AppearanceComponent>(ent, out var appearance))
return;
if (!TryComp<ClothingComponent>(ent, out var clothing))
return;
if (!AppearanceSystem.TryGetData<float>(ent, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
return;
var closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, ent.Comp.EquippedMaxFillLevels + 1);
if (closestFillSprite > 0)
{
var layer = new PrototypeLayerData();
var equippedPrefix = clothing.EquippedPrefix == null ? $"equipped-{args.Slot}" : $" {clothing.EquippedPrefix}-equipped-{args.Slot}";
var key = equippedPrefix + ent.Comp.EquippedFillBaseName + closestFillSprite;
// Make sure the sprite state is valid so we don't show a big red error message
// This saves us from having to make fill level sprites for every possible slot the item could be in (including pockets).
if (!TryComp<SpriteComponent>(ent, out var sprite) || sprite.BaseRSI == null || !sprite.BaseRSI.TryGetState(key, out _))
return;
layer.State = key;
if (ent.Comp.ChangeColor && AppearanceSystem.TryGetData<Color>(ent, SolutionContainerVisuals.Color, out var color, appearance))
layer.Color = color;
args.Layers.Add((key, layer));
}
}
}

View File

@@ -1,148 +1,17 @@
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Utility;
using Robust.Shared.Graphics;
using static Robust.Client.GameObjects.SpriteComponent;
using Direction = Robust.Shared.Maths.Direction;
namespace Content.Client.Clickable;
namespace Content.Client.Clickable
[RegisterComponent]
public sealed partial class ClickableComponent : Component
{
[RegisterComponent]
public sealed partial class ClickableComponent : Component
[DataField] public DirBoundData? Bounds;
[DataDefinition]
public sealed partial class DirBoundData
{
[Dependency] private readonly IClickMapManager _clickMapManager = default!;
[DataField("bounds")] public DirBoundData? Bounds;
/// <summary>
/// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding
/// boxes (see <see cref="Bounds"/>). If that fails, attempts to use automatically generated click maps.
/// </summary>
/// <param name="worldPos">The world position that was clicked.</param>
/// <param name="drawDepth">
/// The draw depth for the sprite that captured the click.
/// </param>
/// <returns>True if the click worked, false otherwise.</returns>
public bool CheckClick(SpriteComponent sprite, TransformComponent transform, EntityQuery<TransformComponent> xformQuery, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom)
{
if (!sprite.Visible)
{
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
drawDepth = sprite.DrawDepth;
renderOrder = sprite.RenderOrder;
var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery);
var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation);
bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix);
// This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive();
Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
// First we get `localPos`, the clicked location in the sprite-coordinate frame.
var entityXform = Matrix3Helpers.CreateInverseTransform(spritePos, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix);
// Check explicitly defined click-able bounds
if (CheckDirBound(sprite, relativeRotation, localPos))
return true;
// Next check each individual sprite layer using automatically computed click maps.
foreach (var spriteLayer in sprite.AllLayers)
{
// TODO: Move this to a system and also use SpriteSystem.IsVisible instead.
if (!spriteLayer.Visible || spriteLayer is not Layer layer || layer.CopyToShaderParameters != null)
{
continue;
}
// Check the layer's texture, if it has one
if (layer.Texture != null)
{
// Convert to image coordinates
var imagePos = (Vector2i) (localPos * EyeManager.PixelsPerMeter * new Vector2(1, -1) + layer.Texture.Size / 2f);
if (_clickMapManager.IsOccluding(layer.Texture, imagePos))
return true;
}
// Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next
if (layer.ActualRsi is not { } rsi || !rsi.TryGetState(layer.State, out var rsiState))
continue;
var dir = Layer.GetDirection(rsiState.RsiDirections, relativeRotation);
// convert to layer-local coordinates
layer.GetLayerDrawMatrix(dir, out var matrix);
Matrix3x2.Invert(matrix, out var inverseMatrix);
var layerLocal = Vector2.Transform(localPos, inverseMatrix);
// Convert to image coordinates
var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f);
// Next, to get the right click map we need the "direction" of this layer that is actually being used to draw the sprite on the screen.
// This **can** differ from the dir defined before, but can also just be the same.
if (sprite.EnableDirectionOverride)
dir = sprite.DirectionOverride.Convert(rsiState.RsiDirections);
dir = dir.OffsetRsiDir(layer.DirOffset);
if (_clickMapManager.IsOccluding(layer.ActualRsi!, layer.State, dir, layer.AnimationFrame, layerImagePos))
return true;
}
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
public bool CheckDirBound(SpriteComponent sprite, Angle relativeRotation, Vector2 localPos)
{
if (Bounds == null)
return false;
// These explicit bounds only work for either 1 or 4 directional sprites.
// This would be the orientation of a 4-directional sprite.
var direction = relativeRotation.GetCardinalDir();
var modLocalPos = sprite.NoRotation
? localPos
: direction.ToAngle().RotateVec(localPos);
// First, check the bounding box that is valid for all orientations
if (Bounds.All.Contains(modLocalPos))
return true;
// Next, get and check the appropriate bounding box for the current sprite orientation
var boundsForDir = (sprite.EnableDirectionOverride ? sprite.DirectionOverride : direction) switch
{
Direction.East => Bounds.East,
Direction.North => Bounds.North,
Direction.South => Bounds.South,
Direction.West => Bounds.West,
_ => throw new InvalidOperationException()
};
return boundsForDir.Contains(modLocalPos);
}
[DataDefinition]
public sealed partial class DirBoundData
{
[DataField("all")] public Box2 All;
[DataField("north")] public Box2 North;
[DataField("south")] public Box2 South;
[DataField("east")] public Box2 East;
[DataField("west")] public Box2 West;
}
[DataField] public Box2 All;
[DataField] public Box2 North;
[DataField] public Box2 South;
[DataField] public Box2 East;
[DataField] public Box2 West;
}
}

View File

@@ -0,0 +1,168 @@
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Utility;
using Robust.Shared.Graphics;
namespace Content.Client.Clickable;
/// <summary>
/// Handles click detection for sprites.
/// </summary>
public sealed class ClickableSystem : EntitySystem
{
[Dependency] private readonly IClickMapManager _clickMapManager = default!;
[Dependency] private readonly SharedTransformSystem _transforms = default!;
[Dependency] private readonly SpriteSystem _sprites = default!;
private EntityQuery<ClickableComponent> _clickableQuery;
private EntityQuery<TransformComponent> _xformQuery;
public override void Initialize()
{
base.Initialize();
_clickableQuery = GetEntityQuery<ClickableComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
}
/// <summary>
/// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding
/// boxes (see <see cref="Bounds"/>). If that fails, attempts to use automatically generated click maps.
/// </summary>
/// <param name="worldPos">The world position that was clicked.</param>
/// <param name="drawDepth">
/// The draw depth for the sprite that captured the click.
/// </param>
/// <returns>True if the click worked, false otherwise.</returns>
public bool CheckClick(Entity<ClickableComponent?, SpriteComponent, TransformComponent?> entity, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom)
{
if (!_clickableQuery.Resolve(entity.Owner, ref entity.Comp1, false))
{
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
if (!_xformQuery.Resolve(entity.Owner, ref entity.Comp3))
{
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
var sprite = entity.Comp2;
var transform = entity.Comp3;
if (!sprite.Visible)
{
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
drawDepth = sprite.DrawDepth;
renderOrder = sprite.RenderOrder;
var (spritePos, spriteRot) = _transforms.GetWorldPositionRotation(transform);
var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation);
bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom;
Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix);
// This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive();
var cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero;
// First we get `localPos`, the clicked location in the sprite-coordinate frame.
var entityXform = Matrix3Helpers.CreateInverseTransform(spritePos, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping);
var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix);
// Check explicitly defined click-able bounds
if (CheckDirBound((entity.Owner, entity.Comp1, entity.Comp2), relativeRotation, localPos))
return true;
// Next check each individual sprite layer using automatically computed click maps.
foreach (var spriteLayer in sprite.AllLayers)
{
if (spriteLayer is not SpriteComponent.Layer layer || !_sprites.IsVisible(layer))
{
continue;
}
// Check the layer's texture, if it has one
if (layer.Texture != null)
{
// Convert to image coordinates
var imagePos = (Vector2i) (localPos * EyeManager.PixelsPerMeter * new Vector2(1, -1) + layer.Texture.Size / 2f);
if (_clickMapManager.IsOccluding(layer.Texture, imagePos))
return true;
}
// Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next
if (layer.ActualRsi is not { } rsi || !rsi.TryGetState(layer.State, out var rsiState))
continue;
var dir = SpriteComponent.Layer.GetDirection(rsiState.RsiDirections, relativeRotation);
// convert to layer-local coordinates
layer.GetLayerDrawMatrix(dir, out var matrix);
Matrix3x2.Invert(matrix, out var inverseMatrix);
var layerLocal = Vector2.Transform(localPos, inverseMatrix);
// Convert to image coordinates
var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f);
// Next, to get the right click map we need the "direction" of this layer that is actually being used to draw the sprite on the screen.
// This **can** differ from the dir defined before, but can also just be the same.
if (sprite.EnableDirectionOverride)
dir = sprite.DirectionOverride.Convert(rsiState.RsiDirections);
dir = dir.OffsetRsiDir(layer.DirOffset);
if (_clickMapManager.IsOccluding(layer.ActualRsi!, layer.State, dir, layer.AnimationFrame, layerImagePos))
return true;
}
drawDepth = default;
renderOrder = default;
bottom = default;
return false;
}
public bool CheckDirBound(Entity<ClickableComponent, SpriteComponent> entity, Angle relativeRotation, Vector2 localPos)
{
var clickable = entity.Comp1;
var sprite = entity.Comp2;
if (clickable.Bounds == null)
return false;
// These explicit bounds only work for either 1 or 4 directional sprites.
// This would be the orientation of a 4-directional sprite.
var direction = relativeRotation.GetCardinalDir();
var modLocalPos = sprite.NoRotation
? localPos
: direction.ToAngle().RotateVec(localPos);
// First, check the bounding box that is valid for all orientations
if (clickable.Bounds.All.Contains(modLocalPos))
return true;
// Next, get and check the appropriate bounding box for the current sprite orientation
var boundsForDir = (sprite.EnableDirectionOverride ? sprite.DirectionOverride : direction) switch
{
Direction.East => clickable.Bounds.East,
Direction.North => clickable.Bounds.North,
Direction.South => clickable.Bounds.South,
Direction.West => clickable.Bounds.West,
_ => throw new InvalidOperationException()
};
return boundsForDir.Contains(modLocalPos);
}
}

View File

@@ -0,0 +1,6 @@
using Content.Shared.Clothing;
namespace Content.Client.Clothing.Systems;
/// <inheritdoc/>
public sealed class CursedMaskSystem : SharedCursedMaskSystem;

View File

@@ -1,3 +1,4 @@
using Content.Client.Actions;
using Content.Client.Actions;
using Content.Client.Mapping;
using Content.Shared.Administration;
@@ -61,27 +62,3 @@ public sealed class LoadActionsCommand : LocalizedCommands
}
}
}
[AnyCommand]
public sealed class LoadMappingActionsCommand : LocalizedCommands
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
public const string CommandName = "loadmapacts";
public override string Command => CommandName;
public override string Help => LocalizationManager.GetString($"cmd-{Command}-help", ("command", Command));
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
try
{
_entitySystemManager.GetEntitySystem<MappingSystem>().LoadMappingActions();
}
catch
{
shell.WriteError(LocalizationManager.GetString($"cmd-{Command}-error"));
}
}
}

View File

@@ -30,7 +30,7 @@ public sealed class HideMechanismsCommand : LocalizedCommands
sprite.ContainerOccluded = false;
var tempParent = uid;
while (containerSys.TryGetContainingContainer(tempParent, out var container))
while (containerSys.TryGetContainingContainer((tempParent, null, null), out var container))
{
if (!container.ShowContents)
{

View File

@@ -1,6 +1,9 @@
using Content.Client.Actions;
using Content.Client.Mapping;
using Content.Client.Markers;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.State;
using Robust.Shared.Console;
namespace Content.Client.Commands;
@@ -10,6 +13,7 @@ internal sealed class MappingClientSideSetupCommand : LocalizedCommands
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
[Dependency] private readonly ILightManager _lightManager = default!;
[Dependency] private readonly IStateManager _stateManager = default!;
public override string Command => "mappingclientsidesetup";
@@ -21,8 +25,8 @@ internal sealed class MappingClientSideSetupCommand : LocalizedCommands
{
_entitySystemManager.GetEntitySystem<MarkerSystem>().MarkersVisible = true;
_lightManager.Enabled = false;
shell.ExecuteCommand(ShowSubFloorForever.CommandName);
shell.ExecuteCommand(LoadMappingActionsCommand.CommandName);
shell.ExecuteCommand("showsubfloorforever");
_entitySystemManager.GetEntitySystem<ActionsSystem>().LoadActionAssignments("/mapping_actions.yml", false);
}
}
}

View File

@@ -130,7 +130,7 @@ namespace Content.Client.Communications.UI
EmergencyShuttleButton.Text = Loc.GetString("comms-console-menu-recall-shuttle");
var infoText = Loc.GetString($"comms-console-menu-time-remaining",
("time", diff.TotalSeconds.ToString(CultureInfo.CurrentCulture)));
("time", diff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
CountdownLabel.SetMessage(infoText);
}
}

View File

@@ -2,6 +2,7 @@ using System.Numerics;
using System.Threading;
using Content.Client.CombatMode;
using Content.Client.Gameplay;
using Content.Client.Mapping;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Timer = Robust.Shared.Timing.Timer;
@@ -16,7 +17,7 @@ namespace Content.Client.ContextMenu.UI
/// <remarks>
/// This largely involves setting up timers to open and close sub-menus when hovering over other menu elements.
/// </remarks>
public sealed class ContextMenuUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CombatModeSystem>
public sealed class ContextMenuUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CombatModeSystem>, IOnStateEntered<MappingState>, IOnStateExited<MappingState>
{
public static readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
@@ -42,18 +43,51 @@ namespace Content.Client.ContextMenu.UI
public Action<ContextMenuElement>? OnSubMenuOpened;
public Action<ContextMenuElement, GUIBoundKeyEventArgs>? OnContextKeyEvent;
private bool _setup;
public void OnStateEntered(GameplayState state)
{
Setup();
}
public void OnStateExited(GameplayState state)
{
Shutdown();
}
public void OnStateEntered(MappingState state)
{
Setup();
}
public void OnStateExited(MappingState state)
{
Shutdown();
}
public void Setup()
{
if (_setup)
return;
_setup = true;
RootMenu = new(this, null);
RootMenu.OnPopupHide += Close;
Menus.Push(RootMenu);
}
public void OnStateExited(GameplayState state)
public void Shutdown()
{
if (!_setup)
return;
_setup = false;
Close();
RootMenu.OnPopupHide -= Close;
RootMenu.Dispose();
RootMenu = default!;
}
/// <summary>

View File

@@ -51,7 +51,7 @@ public sealed class CrewManifestSection : BoxContainer
title.SetMessage(entry.JobTitle);
if (prototypeManager.TryIndex<StatusIconPrototype>(entry.JobIcon, out var jobIcon))
if (prototypeManager.TryIndex<JobIconPrototype>(entry.JobIcon, out var jobIcon))
{
var icon = new TextureRect()
{

View File

@@ -7,10 +7,12 @@ using Content.Shared.Security;
using Content.Shared.StationRecords;
using Robust.Client.AutoGenerated;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using System.Linq;
namespace Content.Client.CriminalRecords;
@@ -36,7 +38,6 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
public Action<SecurityStatus, string>? OnDialogConfirmed;
private uint _maxLength;
private bool _isPopulating;
private bool _access;
private uint? _selectedKey;
private CriminalRecord? _selectedRecord;
@@ -74,7 +75,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
RecordListing.OnItemSelected += args =>
{
if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not uint cast)
if (RecordListing[args.ItemIndex].Metadata is not uint cast)
return;
OnKeySelected?.Invoke(cast);
@@ -82,8 +83,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
RecordListing.OnItemDeselected += _ =>
{
if (!_isPopulating)
OnKeySelected?.Invoke(null);
OnKeySelected?.Invoke(null);
};
FilterType.OnItemSelected += eventArgs =>
@@ -133,13 +133,8 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
FilterType.SelectId((int)_currentFilterType);
// set up the records listing panel
RecordListing.Clear();
var hasRecords = state.RecordListing != null && state.RecordListing.Count > 0;
NoRecords.Visible = !hasRecords;
if (hasRecords)
PopulateRecordListing(state.RecordListing!);
NoRecords.Visible = state.RecordListing == null || state.RecordListing.Count == 0;
PopulateRecordListing(state.RecordListing);
// set up the selected person's record
var selected = _selectedKey != null;
@@ -167,19 +162,59 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
}
}
private void PopulateRecordListing(Dictionary<uint, string> listing)
private void PopulateRecordListing(Dictionary<uint, string>? listing)
{
_isPopulating = true;
foreach (var (key, name) in listing)
if (listing == null)
{
var item = RecordListing.AddItem(name);
item.Metadata = key;
item.Selected = key == _selectedKey;
RecordListing.Clear();
return;
}
_isPopulating = false;
RecordListing.SortItemsByText();
var entries = listing.ToList();
entries.Sort((a, b) => string.Compare(a.Value, b.Value, StringComparison.Ordinal));
// `entries` now contains the definitive list of items which should be in
// our list of records and is in the order we want to present those items.
// Walk through the existing items in RecordListing and in the updated listing
// in parallel to synchronize the items in RecordListing with `entries`.
int i = RecordListing.Count - 1;
int j = entries.Count - 1;
while(i >= 0 && j >= 0)
{
var strcmp = string.Compare(RecordListing[i].Text, entries[j].Value, StringComparison.Ordinal);
if (strcmp == 0)
{
// This item exists in both RecordListing and `entries`. Nothing to do.
i--;
j--;
}
else if (strcmp > 0)
{
// Item exists in RecordListing, but not in `entries`. Remove it.
RecordListing.RemoveAt(i);
i--;
}
else if (strcmp < 0)
{
// A new entry which doesn't exist in RecordListing. Create it.
RecordListing.Insert(i + 1, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key});
j--;
}
}
// Any remaining items in RecordListing don't exist in `entries`, so remove them
while (i >= 0)
{
RecordListing.RemoveAt(i);
i--;
}
// And finally, any remaining items in `entries`, don't exist in RecordListing. Create them.
while (j >= 0)
{
RecordListing.Insert(0, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key});
j--;
}
}
private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord)
@@ -211,10 +246,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
private void FilterListingOfRecords(string text = "")
{
if (!_isPopulating)
{
OnFiltersChanged?.Invoke(_currentFilterType, text);
}
OnFiltersChanged?.Invoke(_currentFilterType, text);
}
private void SetStatus(SecurityStatus status)

View File

@@ -4,6 +4,7 @@ using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Client.Decals.Overlays;
@@ -16,7 +17,7 @@ public sealed class DecalPlacementOverlay : Overlay
private readonly SharedTransformSystem _transform;
private readonly SpriteSystem _sprite;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities;
public DecalPlacementOverlay(DecalPlacementSystem placement, SharedTransformSystem transform, SpriteSystem sprite)
{
@@ -24,6 +25,7 @@ public sealed class DecalPlacementOverlay : Overlay
_placement = placement;
_transform = transform;
_sprite = sprite;
ZIndex = 1000;
}
protected override void Draw(in OverlayDrawArgs args)
@@ -55,7 +57,7 @@ public sealed class DecalPlacementOverlay : Overlay
if (snap)
{
localPos = (Vector2) localPos.Floored() + grid.TileSizeHalfVector;
localPos = localPos.Floored() + grid.TileSizeHalfVector;
}
// Nothing uses snap cardinals so probably don't need preview?

View File

@@ -0,0 +1,80 @@
using Content.Shared.Drowsiness;
using Content.Shared.StatusEffect;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Drowsiness;
public sealed class DrowsinessOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true;
private readonly ShaderInstance _drowsinessShader;
public float CurrentPower = 0.0f;
private const float PowerDivisor = 250.0f;
private const float Intensity = 0.2f; // for adjusting the visual scale
private float _visualScale = 0; // between 0 and 1
public DrowsinessOverlay()
{
IoCManager.InjectDependencies(this);
_drowsinessShader = _prototypeManager.Index<ShaderPrototype>("Drowsiness").InstanceUnique();
}
protected override void FrameUpdate(FrameEventArgs args)
{
var playerEntity = _playerManager.LocalEntity;
if (playerEntity == null)
return;
if (!_entityManager.HasComponent<DrowsinessComponent>(playerEntity)
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
return;
var statusSys = _sysMan.GetEntitySystem<StatusEffectsSystem>();
if (!statusSys.TryGetTime(playerEntity.Value, SharedDrowsinessSystem.DrowsinessKey, out var time, status))
return;
var curTime = _timing.CurTime;
var timeLeft = (float)(time.Value.Item2 - curTime).TotalSeconds;
CurrentPower += 8f * (0.5f * timeLeft - CurrentPower) * args.DeltaSeconds / (timeLeft + 1);
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
return false;
if (args.Viewport.Eye != eyeComp.Eye)
return false;
_visualScale = Math.Clamp(CurrentPower / PowerDivisor, 0.0f, 1.0f);
return _visualScale > 0;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
var handle = args.WorldHandle;
_drowsinessShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_drowsinessShader.SetParameter("Strength", _visualScale * Intensity);
handle.UseShader(_drowsinessShader);
handle.DrawRect(args.WorldBounds, Color.White);
handle.UseShader(null);
}
}

View File

@@ -0,0 +1,53 @@
using Content.Shared.Drowsiness;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
namespace Content.Client.Drowsiness;
public sealed class DrowsinessSystem : SharedDrowsinessSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private DrowsinessOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DrowsinessComponent, ComponentInit>(OnDrowsinessInit);
SubscribeLocalEvent<DrowsinessComponent, ComponentShutdown>(OnDrowsinessShutdown);
SubscribeLocalEvent<DrowsinessComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<DrowsinessComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
_overlay = new();
}
private void OnPlayerAttached(EntityUid uid, DrowsinessComponent component, LocalPlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, DrowsinessComponent component, LocalPlayerDetachedEvent args)
{
_overlay.CurrentPower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnDrowsinessInit(EntityUid uid, DrowsinessComponent component, ComponentInit args)
{
if (_player.LocalEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnDrowsinessShutdown(EntityUid uid, DrowsinessComponent component, ComponentShutdown args)
{
if (_player.LocalEntity == uid)
{
_overlay.CurrentPower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
}
}

View File

@@ -16,15 +16,19 @@ public sealed class ExplosionOverlay : Overlay
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly SharedTransformSystem _transformSystem;
private SharedAppearanceSystem _appearance;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
private ShaderInstance _shader;
public ExplosionOverlay()
public ExplosionOverlay(SharedAppearanceSystem appearanceSystem)
{
IoCManager.InjectDependencies(this);
_shader = _proto.Index<ShaderPrototype>("unshaded").Instance();
_transformSystem = _entMan.System<SharedTransformSystem>();
_appearance = appearanceSystem;
}
protected override void Draw(in OverlayDrawArgs args)
@@ -33,15 +37,14 @@ public sealed class ExplosionOverlay : Overlay
drawHandle.UseShader(_shader);
var xforms = _entMan.GetEntityQuery<TransformComponent>();
var query = _entMan
.EntityQuery<ExplosionVisualsComponent, ExplosionVisualsTexturesComponent, AppearanceComponent>(true);
var query = _entMan.EntityQueryEnumerator<ExplosionVisualsComponent, ExplosionVisualsTexturesComponent>();
foreach (var (visuals, textures, appearance) in query)
while (query.MoveNext(out var uid, out var visuals, out var textures))
{
if (visuals.Epicenter.MapId != args.MapId)
continue;
if (!appearance.TryGetData(ExplosionAppearanceData.Progress, out int index))
if (!_appearance.TryGetData(uid, ExplosionAppearanceData.Progress, out int index))
continue;
index = Math.Min(index, visuals.Intensity.Count - 1);
@@ -67,7 +70,7 @@ public sealed class ExplosionOverlay : Overlay
continue;
var xform = xforms.GetComponent(gridId);
var (_, _, worldMatrix, invWorldMatrix) = xform.GetWorldPositionRotationMatrixWithInv(xforms);
var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(xform, xforms);
gridBounds = invWorldMatrix.TransformBox(worldBounds).Enlarged(grid.TileSize * 2);
drawHandle.SetTransform(worldMatrix);

View File

@@ -20,6 +20,7 @@ public sealed class ExplosionOverlaySystem : EntitySystem
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly SharedPointLightSystem _lights = default!;
[Dependency] private readonly IMapManager _mapMan = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
public override void Initialize()
{
@@ -28,7 +29,7 @@ public sealed class ExplosionOverlaySystem : EntitySystem
SubscribeLocalEvent<ExplosionVisualsComponent, ComponentInit>(OnExplosionInit);
SubscribeLocalEvent<ExplosionVisualsComponent, ComponentRemove>(OnCompRemove);
SubscribeLocalEvent<ExplosionVisualsComponent, ComponentHandleState>(OnExplosionHandleState);
_overlayMan.AddOverlay(new ExplosionOverlay());
_overlayMan.AddOverlay(new ExplosionOverlay(_appearance));
}
private void OnExplosionHandleState(EntityUid uid, ExplosionVisualsComponent component, ref ComponentHandleState args)

View File

@@ -0,0 +1,8 @@
using Content.Shared.Explosion.EntitySystems;
namespace Content.Client.Explosion.EntitySystems;
public sealed class ExplosionSystem : SharedExplosionSystem
{
}

View File

@@ -14,6 +14,7 @@ public sealed class PuddleOverlay : Overlay
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
private readonly PuddleDebugOverlaySystem _debugOverlaySystem;
private readonly SharedTransformSystem _transformSystem;
private readonly Color _heavyPuddle = new(0, 255, 255, 50);
private readonly Color _mediumPuddle = new(0, 150, 255, 50);
@@ -29,6 +30,7 @@ public sealed class PuddleOverlay : Overlay
_debugOverlaySystem = _entitySystemManager.GetEntitySystem<PuddleDebugOverlaySystem>();
var cache = IoCManager.Resolve<IResourceCache>();
_font = new VectorFont(cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 8);
_transformSystem = _entityManager.System<SharedTransformSystem>();
}
protected override void Draw(in OverlayDrawArgs args)
@@ -56,7 +58,7 @@ public sealed class PuddleOverlay : Overlay
continue;
var gridXform = xformQuery.GetComponent(gridId);
var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(xformQuery);
var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform, xformQuery);
gridBounds = invWorldMatrix.TransformBox(args.WorldBounds).Enlarged(mapGrid.TileSize * 2);
drawHandle.SetTransform(worldMatrix);
@@ -89,7 +91,7 @@ public sealed class PuddleOverlay : Overlay
continue;
var gridXform = xformQuery.GetComponent(gridId);
var (_, _, matrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(xformQuery);
var (_, _, matrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform, xformQuery);
var gridBounds = invMatrix.TransformBox(args.WorldBounds).Enlarged(mapGrid.TileSize * 2);
foreach (var debugOverlayData in _debugOverlaySystem.GetData(gridId))

View File

@@ -54,10 +54,16 @@ namespace Content.Client.Forensics
}
text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
foreach (var dna in msg.DNAs)
foreach (var dna in msg.TouchDNAs)
{
text.AppendLine(dna);
}
foreach (var dna in msg.SolutionDNAs)
{
if (msg.TouchDNAs.Contains(dna))
continue;
text.AppendLine(dna);
}
text.AppendLine();
text.AppendLine(Loc.GetString("forensic-scanner-interface-residues"));
foreach (var residue in msg.Residues)

View File

@@ -2,6 +2,7 @@ using System.Linq;
using System.Numerics;
using Content.Client.Clickable;
using Content.Client.UserInterface;
using Content.Client.Viewport;
using Content.Shared.Input;
using Robust.Client.ComponentTrees;
using Robust.Client.GameObjects;
@@ -13,11 +14,13 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Console;
using Robust.Shared.Graphics;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using YamlDotNet.Serialization.TypeInspectors;
namespace Content.Client.Gameplay
{
@@ -98,7 +101,15 @@ namespace Content.Client.Gameplay
public EntityUid? GetClickedEntity(MapCoordinates coordinates)
{
var first = GetClickableEntities(coordinates).FirstOrDefault();
return GetClickedEntity(coordinates, _eyeManager.CurrentEye);
}
public EntityUid? GetClickedEntity(MapCoordinates coordinates, IEye? eye)
{
if (eye == null)
return null;
var first = GetClickableEntities(coordinates, eye).FirstOrDefault();
return first.IsValid() ? first : null;
}
@@ -110,6 +121,20 @@ namespace Content.Client.Gameplay
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates)
{
return GetClickableEntities(coordinates, _eyeManager.CurrentEye);
}
public IEnumerable<EntityUid> GetClickableEntities(MapCoordinates coordinates, IEye? eye)
{
/*
* TODO:
* 1. Stuff like MeleeWeaponSystem need an easy way to hook into viewport specific entities / entities under mouse
* 2. Cleanup the mess around InteractionOutlineSystem + below the keybind click detection.
*/
if (eye == null)
return Array.Empty<EntityUid>();
// Find all the entities intersecting our click
var spriteTree = _entityManager.EntitySysManager.GetEntitySystem<SpriteTreeSystem>();
var entities = spriteTree.QueryAabb(coordinates.MapId, Box2.CenteredAround(coordinates.Position, new Vector2(1, 1)));
@@ -117,15 +142,12 @@ namespace Content.Client.Gameplay
// Check the entities against whether or not we can click them
var foundEntities = new List<(EntityUid, int, uint, float)>(entities.Count);
var clickQuery = _entityManager.GetEntityQuery<ClickableComponent>();
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
// TODO: Smelly
var eye = _eyeManager.CurrentEye;
var clickables = _entityManager.System<ClickableSystem>();
foreach (var entity in entities)
{
if (clickQuery.TryGetComponent(entity.Uid, out var component) &&
component.CheckClick(entity.Component, entity.Transform, xformQuery, coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom))
clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom))
{
foundEntities.Add((entity.Uid, drawDepthClicked, renderOrder, bottom));
}
@@ -188,7 +210,15 @@ namespace Content.Client.Gameplay
if (args.Viewport is IViewportControl vp && kArgs.PointerLocation.IsValid)
{
var mousePosWorld = vp.PixelToMap(kArgs.PointerLocation.Position);
entityToClick = GetClickedEntity(mousePosWorld);
if (vp is ScalingViewport svp)
{
entityToClick = GetClickedEntity(mousePosWorld, svp.Eye);
}
else
{
entityToClick = GetClickedEntity(mousePosWorld);
}
coordinates = _mapManager.TryFindGridAt(mousePosWorld, out _, out var grid) ?
grid.MapToGrid(mousePosWorld) :

View File

@@ -22,7 +22,7 @@ public sealed class GhostRoleRadioBoundUserInterface : BoundUserInterface
_ghostRoleRadioMenu.SendGhostRoleRadioMessageAction += SendGhostRoleRadioMessage;
}
public void SendGhostRoleRadioMessage(ProtoId<GhostRolePrototype> protoId)
private void SendGhostRoleRadioMessage(ProtoId<GhostRolePrototype> protoId)
{
SendMessage(new GhostRoleRadioMessage(protoId));
}

View File

@@ -1,7 +1,6 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Ghost.Roles;
using Content.Shared.Ghost.Roles.Components;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
@@ -33,7 +32,7 @@ public sealed partial class GhostRoleRadioMenu : RadialMenu
private void RefreshUI()
{
// The main control that will contain all of the clickable options
// The main control that will contain all the clickable options
var main = FindControl<RadialContainer>("Main");
// The purpose of this radial UI is for ghost role radios that allow you to select
@@ -70,7 +69,7 @@ public sealed partial class GhostRoleRadioMenu : RadialMenu
if (_prototypeManager.TryIndex(ghostRoleProto.IconPrototype, out var iconProto))
entProtoView.SetPrototype(iconProto);
else
entProtoView.SetPrototype(comp.Prototype);
entProtoView.SetPrototype(ghostRoleProto.EntityPrototype);
button.AddChild(entProtoView);
main.AddChild(button);

View File

@@ -1,4 +1,5 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Orientation="Horizontal"
HorizontalAlignment="Stretch"
HorizontalExpand="True"
@@ -26,5 +27,18 @@
VerticalAlignment="Center"
Access="Public"
Visible="False"/>
<!-- CP14 random reactions begin -->
<BoxContainer Name="RandomVariations"
Orientation="Vertical"
VerticalExpand="True"
VerticalAlignment="Center"
HorizontalAlignment="Left">
<controls:SplitBar MinHeight="10">
</controls:SplitBar>
<Label Name="RandomVariationsLabel" Text="{Loc 'cp14-guidebook-random-variations-title'}" Visible="False"/>
<controls:SplitBar MinHeight="10">
</controls:SplitBar>
</BoxContainer>
<!-- CP14 random reactions end -->
</BoxContainer>
</BoxContainer>

View File

@@ -1,5 +1,6 @@
using System.Linq;
using Content.Client.Message;
using Content.Client.UserInterface.Controls; // CP14 random reactions
using Content.Client.UserInterface.ControlExtensions;
using Content.Shared.Atmos.Prototypes;
using Content.Shared.Chemistry.Components;
@@ -37,13 +38,36 @@ public sealed partial class GuideReagentReaction : BoxContainer, ISearchableCont
var reactantsLabel = ReactantsLabel;
SetReagents(prototype.Reactants, ref reactantsLabel, protoMan);
var productLabel = ProductsLabel;
var products = new Dictionary<string, FixedPoint2>(prototype.Products);
var products = new Dictionary<string, FixedPoint2>(prototype._products); // CP14 random reactions
foreach (var (reagent, reactantProto) in prototype.Reactants)
{
if (reactantProto.Catalyst)
products.Add(reagent, reactantProto.Amount);
}
SetReagents(products, ref productLabel, protoMan);
// CP14 random reagents begin
foreach (var randomVariation in prototype.Cp14RandomProducts)
{
// If there aren't any variations, this label will be not visible
RandomVariationsLabel.Visible = true;
var randomProductLabel = new RichTextLabel {
HorizontalAlignment=HAlignment.Left,
VerticalAlignment=VAlignment.Center,
};
var randomProducts = new Dictionary<string, FixedPoint2>(randomVariation);
RandomVariations.AddChild(randomProductLabel);
RandomVariations.AddChild(new SplitBar
{
MinHeight = 10,
});
foreach (var (reagent, reactantProto) in prototype.Reactants)
{
if (reactantProto.Catalyst)
randomProducts.Add(reagent, reactantProto.Amount);
}
SetReagents(randomProducts, ref randomProductLabel, protoMan);
}
// CP14 random reagents end
var mixingCategories = new List<MixingCategoryPrototype>();
if (prototype.MixingCategories != null)

View File

@@ -0,0 +1,41 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Margin="5 5 5 5"
MinHeight="200">
<PanelContainer HorizontalExpand="True">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BorderThickness="2" BorderColor="White" />
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Vertical">
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BorderThickness="0 0 0 1" BackgroundColor="DarkRed" BorderColor="Black" />
</PanelContainer.PanelOverride>
<Label Margin="5" StyleClasses="bold" Text="{Loc 'guidebook-parser-error'}" />
</PanelContainer>
<OutputPanel Margin="5" MinHeight="75" VerticalExpand="True" Name="Original">
<OutputPanel.StyleBoxOverride>
<gfx:StyleBoxFlat BorderThickness="0 0 0 1" BorderColor="Gray"
ContentMarginLeftOverride="3" ContentMarginRightOverride="3"
ContentMarginBottomOverride="3" ContentMarginTopOverride="3" />
</OutputPanel.StyleBoxOverride>
</OutputPanel>
<Collapsible Margin="5" MinHeight="75" VerticalExpand="True">
<CollapsibleHeading Title="{Loc 'guidebook-error-message' }" />
<CollapsibleBody VerticalExpand="True">
<OutputPanel Name="Error" VerticalExpand="True" MinHeight="100">
<OutputPanel.StyleBoxOverride>
<gfx:StyleBoxFlat
ContentMarginLeftOverride="3" ContentMarginRightOverride="3"
ContentMarginBottomOverride="3" ContentMarginTopOverride="3" />
</OutputPanel.StyleBoxOverride>
</OutputPanel>
</CollapsibleBody>
</Collapsible>
</BoxContainer>
</PanelContainer>
</BoxContainer>

View File

@@ -0,0 +1,23 @@
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Guidebook.Controls;
[UsedImplicitly] [GenerateTypedNameReferences]
public sealed partial class GuidebookError : BoxContainer
{
public GuidebookError()
{
RobustXamlLoader.Load(this);
}
public GuidebookError(string original, string? error) : this()
{
Original.AddText(original);
if (error is not null)
Error.AddText(error);
}
}

View File

@@ -4,12 +4,10 @@ using Content.Client.UserInterface.ControlExtensions;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Controls.FancyTree;
using Content.Client.UserInterface.Systems.Info;
using Content.Shared.CCVar;
using Content.Shared.Guidebook;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Prototypes;
@@ -18,15 +16,18 @@ namespace Content.Client.Guidebook.Controls;
[GenerateTypedNameReferences]
public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
{
[Dependency] private readonly IResourceManager _resourceManager = default!;
[Dependency] private readonly DocumentParsingManager _parsingMan = default!;
[Dependency] private readonly IResourceManager _resourceManager = default!;
private Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry> _entries = new();
private readonly ISawmill _sawmill;
public GuidebookWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sawmill = Logger.GetSawmill("Guidebook");
Tree.OnSelectedItemChanged += OnSelectionChanged;
@@ -36,6 +37,20 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
};
}
public void HandleClick(string link)
{
if (!_entries.TryGetValue(link, out var entry))
return;
if (Tree.TryGetIndexFromMetadata(entry, out var index))
{
Tree.ExpandParentEntries(index.Value);
Tree.SetSelectedIndex(index);
}
else
ShowGuide(entry);
}
private void OnSelectionChanged(TreeItem? item)
{
if (item != null && item.Metadata is GuideEntry entry)
@@ -71,8 +86,9 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
if (!_parsingMan.TryAddMarkup(EntryContainer, file.ReadToEnd()))
{
EntryContainer.AddChild(new Label() { Text = "ERROR: Failed to parse document." });
Logger.Error($"Failed to parse contents of guide document {entry.Id}.");
// The guidebook will automatically display the in-guidebook error if it fails
_sawmill.Error($"Failed to parse contents of guide document {entry.Id}.");
}
}
@@ -124,8 +140,10 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
entry.Children = sortedChildren;
}
entries.ExceptWith(entry.Children);
}
rootEntries = entries.ToList();
}
@@ -135,22 +153,26 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
.ThenBy(rootEntry => Loc.GetString(rootEntry.Name));
}
private void RepopulateTree(List<ProtoId<GuideEntryPrototype>>? roots = null, ProtoId<GuideEntryPrototype>? forcedRoot = null)
private void RepopulateTree(List<ProtoId<GuideEntryPrototype>>? roots = null,
ProtoId<GuideEntryPrototype>? forcedRoot = null)
{
Tree.Clear();
HashSet<ProtoId<GuideEntryPrototype>> addedEntries = new();
TreeItem? parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries);
var parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries);
foreach (var entry in GetSortedEntries(roots))
{
if (!entry.CrystallPunkAllowed) continue; //CrystallPunk guidebook filter
AddEntry(entry.Id, parent, addedEntries);
}
Tree.SetAllExpanded(true);
}
private TreeItem? AddEntry(ProtoId<GuideEntryPrototype> id, TreeItem? parent, HashSet<ProtoId<GuideEntryPrototype>> addedEntries)
private TreeItem? AddEntry(ProtoId<GuideEntryPrototype> id,
TreeItem? parent,
HashSet<ProtoId<GuideEntryPrototype>> addedEntries)
{
if (!_entries.TryGetValue(id, out var entry))
return null;
@@ -180,22 +202,6 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
return item;
}
public void HandleClick(string link)
{
if (!_entries.TryGetValue(link, out var entry))
return;
if (Tree.TryGetIndexFromMetadata(entry, out var index))
{
Tree.ExpandParentEntries(index.Value);
Tree.SetSelectedIndex(index);
}
else
{
ShowGuide(entry);
}
}
private void HandleFilter()
{
var emptySearch = SearchBar.Text.Trim().Length == 0;
@@ -209,6 +215,5 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
element.SetHiddenState(true, SearchBar.Text.Trim());
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Linq;
using Content.Client.Guidebook.Controls;
using Content.Client.Guidebook.Richtext;
using Content.Shared.Guidebook;
using Pidgin;
@@ -7,6 +8,7 @@ using Robust.Shared.ContentPack;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Sandboxing;
using Robust.Shared.Utility;
using static Pidgin.Parser;
namespace Content.Client.Guidebook;
@@ -22,8 +24,10 @@ public sealed partial class DocumentParsingManager
[Dependency] private readonly ISandboxHelper _sandboxHelper = default!;
private readonly Dictionary<string, Parser<char, Control>> _tagControlParsers = new();
private Parser<char, Control> _tagParser = default!;
private Parser<char, Control> _controlParser = default!;
private ISawmill _sawmill = default!;
private Parser<char, Control> _tagParser = default!;
public Parser<char, IEnumerable<Control>> ControlParser = default!;
public void Initialize()
@@ -32,7 +36,8 @@ public sealed partial class DocumentParsingManager
.Assert(_tagControlParsers.ContainsKey, tag => $"unknown tag: {tag}")
.Bind(tag => _tagControlParsers[tag]);
_controlParser = OneOf(_tagParser, TryHeaderControl, ListControlParser, TextControlParser).Before(SkipWhitespaces);
_controlParser = OneOf(_tagParser, TryHeaderControl, ListControlParser, TextControlParser)
.Before(SkipWhitespaces);
foreach (var typ in _reflectionManager.GetAllChildren<IDocumentTag>())
{
@@ -40,6 +45,8 @@ public sealed partial class DocumentParsingManager
}
ControlParser = SkipWhitespaces.Then(_controlParser.Many());
_sawmill = Logger.GetSawmill("Guidebook");
}
public bool TryAddMarkup(Control control, ProtoId<GuideEntryPrototype> entryId, bool log = true)
@@ -68,37 +75,57 @@ public sealed partial class DocumentParsingManager
}
catch (Exception e)
{
if (log)
Logger.Error($"Encountered error while generating markup controls: {e}");
_sawmill.Error($"Encountered error while generating markup controls: {e}");
control.AddChild(new GuidebookError(text, e.ToStringBetter()));
return false;
}
return true;
}
private Parser<char, Control> CreateTagControlParser(string tagId, Type tagType, ISandboxHelper sandbox) => Map(
(args, controls) =>
{
var tag = (IDocumentTag) sandbox.CreateInstance(tagType);
if (!tag.TryParseTag(args, out var control))
{
Logger.Error($"Failed to parse {tagId} args");
return new Control();
}
private Parser<char, Control> CreateTagControlParser(string tagId, Type tagType, ISandboxHelper sandbox)
{
return Map(
(args, controls) =>
{
try
{
var tag = (IDocumentTag) sandbox.CreateInstance(tagType);
if (!tag.TryParseTag(args, out var control))
{
_sawmill.Error($"Failed to parse {tagId} args");
return new GuidebookError(args.ToString() ?? tagId, $"Failed to parse {tagId} args");
}
foreach (var child in controls)
{
control.AddChild(child);
}
return control;
},
ParseTagArgs(tagId),
TagContentParser(tagId)).Labelled($"{tagId} control");
foreach (var child in controls)
{
control.AddChild(child);
}
return control;
}
catch (Exception e)
{
var output = args.Aggregate(string.Empty,
(current, pair) => current + $"{pair.Key}=\"{pair.Value}\" ");
_sawmill.Error($"Tag: {tagId} \n Arguments: {output}/>");
return new GuidebookError($"Tag: {tagId}\nArguments: {output}", e.ToString());
}
},
ParseTagArgs(tagId),
TagContentParser(tagId))
.Labelled($"{tagId} control");
}
// Parse a bunch of controls until we encounter a matching closing tag.
private Parser<char, IEnumerable<Control>> TagContentParser(string tag) =>
OneOf(
Try(ImmediateTagEnd).ThenReturn(Enumerable.Empty<Control>()),
TagEnd.Then(_controlParser.Until(TryTagTerminator(tag)).Labelled($"{tag} children"))
);
private Parser<char, IEnumerable<Control>> TagContentParser(string tag)
{
return OneOf(
Try(ImmediateTagEnd).ThenReturn(Enumerable.Empty<Control>()),
TagEnd.Then(_controlParser.Until(TryTagTerminator(tag)).Labelled($"{tag} children"))
);
}
}

View File

@@ -1,4 +1,5 @@
using System.Linq;
using Content.Client.Guidebook.Controls;
using Pidgin;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -14,92 +15,142 @@ public sealed partial class DocumentParsingManager
{
private const string ListBullet = " ";
#region Text Parsing
#region Basic Text Parsing
// Try look for an escaped character. If found, skip the escaping slash and return the character.
private static readonly Parser<char, char> TryEscapedChar = Try(Char('\\').Then(OneOf(
Try(Char('<')),
Try(Char('>')),
Try(Char('\\')),
Try(Char('-')),
Try(Char('=')),
Try(Char('"')),
Try(Char(' ')),
Try(Char('n')).ThenReturn('\n'),
Try(Char('t')).ThenReturn('\t')
)));
// Parser that consumes a - and then just parses normal rich text with some prefix text (a bullet point).
private static readonly Parser<char, char> TryEscapedChar = Try(Char('\\')
.Then(OneOf(
Try(Char('<')),
Try(Char('>')),
Try(Char('\\')),
Try(Char('-')),
Try(Char('=')),
Try(Char('"')),
Try(Char(' ')),
Try(Char('n')).ThenReturn('\n'),
Try(Char('t')).ThenReturn('\t')
)));
private static readonly Parser<char, Unit> SkipNewline = Whitespace.SkipUntil(Char('\n'));
private static readonly Parser<char, char> TrySingleNewlineToSpace = Try(SkipNewline).Then(SkipWhitespaces).ThenReturn(' ');
private static readonly Parser<char, char> TrySingleNewlineToSpace =
Try(SkipNewline).Then(SkipWhitespaces).ThenReturn(' ');
private static readonly Parser<char, char> TextChar = OneOf(
TryEscapedChar, // consume any backslashed being used to escape text
TrySingleNewlineToSpace, // turn single newlines into spaces
Any // just return the character.
);
);
// like TextChar, but not skipping whitespace around newlines
private static readonly Parser<char, char> QuotedTextChar = OneOf(TryEscapedChar, Any);
private static readonly Parser<char, string> QuotedText =
Char('"').Then(QuotedTextChar.Until(Try(Char('"'))).Select(string.Concat)).Labelled("quoted text");
private static readonly Parser<char, Unit> TryStartList =
Try(SkipNewline.Then(SkipWhitespaces).Then(Char('-'))).Then(SkipWhitespaces);
private static readonly Parser<char, Unit> TryStartTag = Try(Char('<')).Then(SkipWhitespaces);
private static readonly Parser<char, Unit> TryStartParagraph =
Try(SkipNewline.Then(SkipNewline)).Then(SkipWhitespaces);
private static readonly Parser<char, Unit> TryLookTextEnd =
Lookahead(OneOf(TryStartTag, TryStartList, TryStartParagraph, Try(Whitespace.SkipUntil(End))));
private static readonly Parser<char, string> TextParser =
TextChar.AtLeastOnceUntil(TryLookTextEnd).Select(string.Concat);
private static readonly Parser<char, Control> TextControlParser = Try(Map<char, string, Control>(text =>
{
var rt = new RichTextLabel
{
HorizontalExpand = true,
Margin = new Thickness(0, 0, 0, 15.0f)
};
var msg = new FormattedMessage();
// THANK YOU RICHTEXT VERY COOL
// (text doesn't default to white).
msg.PushColor(Color.White);
// If the parsing fails, don't throw an error and instead make an inline error message
string? error;
if (!msg.TryAddMarkup(text, out error))
{
Logger.GetSawmill("Guidebook").Error("Failed to parse RichText in Guidebook");
return new GuidebookError(text, error);
}
msg.Pop();
rt.SetMessage(msg);
return rt;
},
TextParser)
.Cast<Control>())
.Labelled("richtext");
private static readonly Parser<char, Control> HeaderControlParser = Try(Char('#'))
.Then(SkipWhitespaces.Then(Map(text => new Label
{
Text = text,
StyleClasses = { "LabelHeadingBigger" }
},
AnyCharExcept('\n').AtLeastOnceString())
.Cast<Control>()))
.Labelled("header");
private static readonly Parser<char, Control> SubHeaderControlParser = Try(String("##"))
.Then(SkipWhitespaces.Then(Map(text => new Label
{
Text = text,
StyleClasses = { "LabelHeading" }
},
AnyCharExcept('\n').AtLeastOnceString())
.Cast<Control>()))
.Labelled("subheader");
private static readonly Parser<char, Control> TryHeaderControl = OneOf(SubHeaderControlParser, HeaderControlParser);
private static readonly Parser<char, Control> ListControlParser = Try(Char('-'))
.Then(SkipWhitespaces)
.Then(Map(
control => new BoxContainer
{
Children = { new Label { Text = ListBullet, VerticalAlignment = VAlignment.Top }, control },
Orientation = LayoutOrientation.Horizontal
},
TextControlParser)
.Cast<Control>())
.Labelled("list");
#region Text Parsing
#region Basic Text Parsing
// Try look for an escaped character. If found, skip the escaping slash and return the character.
// like TextChar, but not skipping whitespace around newlines
// Quoted text
private static readonly Parser<char, string> QuotedText = Char('"').Then(QuotedTextChar.Until(Try(Char('"'))).Select(string.Concat)).Labelled("quoted text");
#endregion
#region rich text-end markers
private static readonly Parser<char, Unit> TryStartList = Try(SkipNewline.Then(SkipWhitespaces).Then(Char('-'))).Then(SkipWhitespaces);
private static readonly Parser<char, Unit> TryStartTag = Try(Char('<')).Then(SkipWhitespaces);
private static readonly Parser<char, Unit> TryStartParagraph = Try(SkipNewline.Then(SkipNewline)).Then(SkipWhitespaces);
private static readonly Parser<char, Unit> TryLookTextEnd = Lookahead(OneOf(TryStartTag, TryStartList, TryStartParagraph, Try(Whitespace.SkipUntil(End))));
#endregion
// parses text characters until it hits a text-end
private static readonly Parser<char, string> TextParser = TextChar.AtLeastOnceUntil(TryLookTextEnd).Select(string.Concat);
private static readonly Parser<char, Control> TextControlParser = Try(Map(text =>
{
var rt = new RichTextLabel()
{
HorizontalExpand = true,
Margin = new Thickness(0, 0, 0, 15.0f),
};
var msg = new FormattedMessage();
// THANK YOU RICHTEXT VERY COOL
// (text doesn't default to white).
msg.PushColor(Color.White);
msg.AddMarkup(text);
msg.Pop();
rt.SetMessage(msg);
return rt;
}, TextParser).Cast<Control>()).Labelled("richtext");
#endregion
#region Headers
private static readonly Parser<char, Control> HeaderControlParser = Try(Char('#')).Then(SkipWhitespaces.Then(Map(text => new Label()
{
Text = text,
StyleClasses = { "LabelHeadingBigger" }
}, AnyCharExcept('\n').AtLeastOnceString()).Cast<Control>())).Labelled("header");
private static readonly Parser<char, Control> SubHeaderControlParser = Try(String("##")).Then(SkipWhitespaces.Then(Map(text => new Label()
{
Text = text,
StyleClasses = { "LabelHeading" }
}, AnyCharExcept('\n').AtLeastOnceString()).Cast<Control>())).Labelled("subheader");
private static readonly Parser<char, Control> TryHeaderControl = OneOf(SubHeaderControlParser, HeaderControlParser);
#endregion
// Parser that consumes a - and then just parses normal rich text with some prefix text (a bullet point).
private static readonly Parser<char, Control> ListControlParser = Try(Char('-')).Then(SkipWhitespaces).Then(Map(
control => new BoxContainer()
{
Children = { new Label() { Text = ListBullet, VerticalAlignment = VAlignment.Top, }, control },
Orientation = LayoutOrientation.Horizontal,
}, TextControlParser).Cast<Control>()).Labelled("list");
#region Tag Parsing
// closing brackets for tags
private static readonly Parser<char, Unit> TagEnd = Char('>').Then(SkipWhitespaces);
private static readonly Parser<char, Unit> ImmediateTagEnd = String("/>").Then(SkipWhitespaces);
@@ -107,20 +158,24 @@ public sealed partial class DocumentParsingManager
private static readonly Parser<char, Unit> TryLookTagEnd = Lookahead(OneOf(Try(TagEnd), Try(ImmediateTagEnd)));
//parse tag argument key. any normal text character up until we hit a "="
private static readonly Parser<char, string> TagArgKey = LetterOrDigit.Until(Char('=')).Select(string.Concat).Labelled("tag argument key");
private static readonly Parser<char, string> TagArgKey =
LetterOrDigit.Until(Char('=')).Select(string.Concat).Labelled("tag argument key");
// parser for a singular tag argument. Note that each TryQuoteOrChar will consume a whole quoted block before the Until() looks for whitespace
private static readonly Parser<char, (string, string)> TagArgParser = Map((key, value) => (key, value), TagArgKey, QuotedText).Before(SkipWhitespaces);
private static readonly Parser<char, (string, string)> TagArgParser =
Map((key, value) => (key, value), TagArgKey, QuotedText).Before(SkipWhitespaces);
// parser for all tag arguments
private static readonly Parser<char, IEnumerable<(string, string)>> TagArgsParser = TagArgParser.Until(TryLookTagEnd);
private static readonly Parser<char, IEnumerable<(string, string)>> TagArgsParser =
TagArgParser.Until(TryLookTagEnd);
// parser for an opening tag.
private static readonly Parser<char, string> TryOpeningTag =
Try(Char('<'))
.Then(SkipWhitespaces)
.Then(TextChar.Until(OneOf(Whitespace.SkipAtLeastOnce(), TryLookTagEnd)))
.Select(string.Concat).Labelled($"opening tag");
.Then(SkipWhitespaces)
.Then(TextChar.Until(OneOf(Whitespace.SkipAtLeastOnce(), TryLookTagEnd)))
.Select(string.Concat)
.Labelled("opening tag");
private static Parser<char, Dictionary<string, string>> ParseTagArgs(string tag)
{
@@ -138,5 +193,6 @@ public sealed partial class DocumentParsingManager
.Then(TagEnd)
.Labelled($"closing {tag} tag");
}
#endregion
}

View File

@@ -283,7 +283,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer
for (var i = 0; i < PointsUsed; i++)
{
SlotSelector.AddItem($"Slot {i + 1}", i);
SlotSelector.AddItem(Loc.GetString("marking-slot", ("number", $"{i + 1}")), i);
if (i == _slot)
{

View File

@@ -16,6 +16,8 @@ namespace Content.Client.IconSmoothing
[UsedImplicitly]
public sealed partial class IconSmoothSystem : EntitySystem
{
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
private readonly Queue<EntityUid> _dirtyEntities = new();
private readonly Queue<EntityUid> _anchorChangedEntities = new();
@@ -46,7 +48,7 @@ namespace Content.Client.IconSmoothing
if (xform.Anchored)
{
component.LastPosition = TryComp<MapGridComponent>(xform.GridUid, out var grid)
? (xform.GridUid.Value, grid.TileIndicesFor(xform.Coordinates))
? (xform.GridUid.Value, _mapSystem.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates))
: (null, new Vector2i(0, 0));
DirtyNeighbours(uid, component);
@@ -151,9 +153,12 @@ namespace Content.Client.IconSmoothing
Vector2i pos;
EntityUid entityUid;
if (transform.Anchored && TryComp<MapGridComponent>(transform.GridUid, out var grid))
{
pos = grid.CoordinatesToTile(transform.Coordinates);
entityUid = transform.GridUid.Value;
pos = _mapSystem.CoordinatesToTile(transform.GridUid.Value, grid, transform.Coordinates);
}
else
{
@@ -164,21 +169,22 @@ namespace Content.Client.IconSmoothing
if (!TryComp(gridId, out grid))
return;
entityUid = gridId;
pos = oldPos;
}
// Yes, we updates ALL smoothing entities surrounding us even if they would never smooth with us.
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(1, 0)));
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(-1, 0)));
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(0, 1)));
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(0, -1)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(1, 0)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(-1, 0)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(0, 1)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(0, -1)));
if (comp.Mode is IconSmoothingMode.Corners or IconSmoothingMode.NoSprite or IconSmoothingMode.Diagonal)
{
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(1, 1)));
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(-1, -1)));
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(-1, 1)));
DirtyEntities(grid.GetAnchoredEntitiesEnumerator(pos + new Vector2i(1, -1)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(1, 1)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(-1, -1)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(-1, 1)));
DirtyEntities(_mapSystem.GetAnchoredEntitiesEnumerator(entityUid, grid, pos + new Vector2i(1, -1)));
}
}
@@ -206,7 +212,7 @@ namespace Content.Client.IconSmoothing
IconSmoothComponent? smooth = null)
{
TransformComponent? xform;
MapGridComponent? grid = null;
Entity<MapGridComponent>? gridEntity = null;
// The generation check prevents updating an entity multiple times per tick.
// As it stands now, it's totally possible for something to get queued twice.
@@ -223,17 +229,20 @@ namespace Content.Client.IconSmoothing
{
var directions = DirectionFlag.None;
if (TryComp(xform.GridUid, out grid))
if (TryComp(xform.GridUid, out MapGridComponent? grid))
{
var pos = grid.TileIndicesFor(xform.Coordinates);
var gridUid = xform.GridUid.Value;
var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates);
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.North)), smoothQuery))
gridEntity = (gridUid, grid);
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery))
directions |= DirectionFlag.North;
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.South)), smoothQuery))
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery))
directions |= DirectionFlag.South;
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.East)), smoothQuery))
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery))
directions |= DirectionFlag.East;
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.West)), smoothQuery))
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery))
directions |= DirectionFlag.West;
}
@@ -257,7 +266,11 @@ namespace Content.Client.IconSmoothing
if (xform.Anchored)
{
if (!TryComp(xform.GridUid, out grid))
if (TryComp(xform.GridUid, out MapGridComponent? grid))
{
gridEntity = (xform.GridUid.Value, grid);
}
else
{
Log.Error($"Failed to calculate IconSmoothComponent sprite in {uid} because grid {xform.GridUid} was missing.");
return;
@@ -267,28 +280,31 @@ namespace Content.Client.IconSmoothing
switch (smooth.Mode)
{
case IconSmoothingMode.Corners:
CalculateNewSpriteCorners(grid, smooth, spriteEnt, xform, smoothQuery);
CalculateNewSpriteCorners(gridEntity, smooth, spriteEnt, xform, smoothQuery);
break;
case IconSmoothingMode.CardinalFlags:
CalculateNewSpriteCardinal(grid, smooth, spriteEnt, xform, smoothQuery);
CalculateNewSpriteCardinal(gridEntity, smooth, spriteEnt, xform, smoothQuery);
break;
case IconSmoothingMode.Diagonal:
CalculateNewSpriteDiagonal(grid, smooth, spriteEnt, xform, smoothQuery);
CalculateNewSpriteDiagonal(gridEntity, smooth, spriteEnt, xform, smoothQuery);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void CalculateNewSpriteDiagonal(MapGridComponent? grid, IconSmoothComponent smooth,
private void CalculateNewSpriteDiagonal(Entity<MapGridComponent>? gridEntity, IconSmoothComponent smooth,
Entity<SpriteComponent> sprite, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
{
if (grid == null)
if (gridEntity == null)
{
sprite.Comp.LayerSetState(0, $"{smooth.StateBase}0");
return;
}
var gridUid = gridEntity.Value.Owner;
var grid = gridEntity.Value.Comp;
var neighbors = new Vector2[]
{
new(1, 0),
@@ -296,14 +312,14 @@ namespace Content.Client.IconSmoothing
new(0, -1),
};
var pos = grid.TileIndicesFor(xform.Coordinates);
var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates);
var rotation = xform.LocalRotation;
var matching = true;
for (var i = 0; i < neighbors.Length; i++)
{
var neighbor = (Vector2i) rotation.RotateVec(neighbors[i]);
matching = matching && MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos + neighbor), smoothQuery);
var neighbor = (Vector2i)rotation.RotateVec(neighbors[i]);
matching = matching && MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos + neighbor), smoothQuery);
}
if (matching)
@@ -316,27 +332,30 @@ namespace Content.Client.IconSmoothing
}
}
private void CalculateNewSpriteCardinal(MapGridComponent? grid, IconSmoothComponent smooth, Entity<SpriteComponent> sprite, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
private void CalculateNewSpriteCardinal(Entity<MapGridComponent>? gridEntity, IconSmoothComponent smooth, Entity<SpriteComponent> sprite, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
{
var dirs = CardinalConnectDirs.None;
if (grid == null)
if (gridEntity == null)
{
sprite.Comp.LayerSetState(0, $"{smooth.StateBase}{(int) dirs}");
sprite.Comp.LayerSetState(0, $"{smooth.StateBase}{(int)dirs}");
return;
}
var pos = grid.TileIndicesFor(xform.Coordinates);
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.North)), smoothQuery))
var gridUid = gridEntity.Value.Owner;
var grid = gridEntity.Value.Comp;
var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates);
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery))
dirs |= CardinalConnectDirs.North;
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.South)), smoothQuery))
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery))
dirs |= CardinalConnectDirs.South;
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.East)), smoothQuery))
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery))
dirs |= CardinalConnectDirs.East;
if (MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.West)), smoothQuery))
if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery))
dirs |= CardinalConnectDirs.West;
sprite.Comp.LayerSetState(0, $"{smooth.StateBase}{(int) dirs}");
sprite.Comp.LayerSetState(0, $"{smooth.StateBase}{(int)dirs}");
var directions = DirectionFlag.None;
@@ -367,11 +386,11 @@ namespace Content.Client.IconSmoothing
return false;
}
private void CalculateNewSpriteCorners(MapGridComponent? grid, IconSmoothComponent smooth, Entity<SpriteComponent> spriteEnt, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
private void CalculateNewSpriteCorners(Entity<MapGridComponent>? gridEntity, IconSmoothComponent smooth, Entity<SpriteComponent> spriteEnt, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
{
var (cornerNE, cornerNW, cornerSW, cornerSE) = grid == null
var (cornerNE, cornerNW, cornerSW, cornerSE) = gridEntity == null
? (CornerFill.None, CornerFill.None, CornerFill.None, CornerFill.None)
: CalculateCornerFill(grid, smooth, xform, smoothQuery);
: CalculateCornerFill(gridEntity.Value, smooth, xform, smoothQuery);
// TODO figure out a better way to set multiple sprite layers.
// This will currently re-calculate the sprite bounding box 4 times.
@@ -379,10 +398,10 @@ namespace Content.Client.IconSmoothing
// At the very least each event currently only queues a sprite for updating.
// Oh god sprite component is a mess.
var sprite = spriteEnt.Comp;
sprite.LayerSetState(CornerLayers.NE, $"{smooth.StateBase}{(int) cornerNE}");
sprite.LayerSetState(CornerLayers.SE, $"{smooth.StateBase}{(int) cornerSE}");
sprite.LayerSetState(CornerLayers.SW, $"{smooth.StateBase}{(int) cornerSW}");
sprite.LayerSetState(CornerLayers.NW, $"{smooth.StateBase}{(int) cornerNW}");
sprite.LayerSetState(CornerLayers.NE, $"{smooth.StateBase}{(int)cornerNE}");
sprite.LayerSetState(CornerLayers.SE, $"{smooth.StateBase}{(int)cornerSE}");
sprite.LayerSetState(CornerLayers.SW, $"{smooth.StateBase}{(int)cornerSW}");
sprite.LayerSetState(CornerLayers.NW, $"{smooth.StateBase}{(int)cornerNW}");
var directions = DirectionFlag.None;
@@ -401,17 +420,20 @@ namespace Content.Client.IconSmoothing
CalculateEdge(spriteEnt, directions, sprite);
}
private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(MapGridComponent grid, IconSmoothComponent smooth, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(Entity<MapGridComponent> gridEntity, IconSmoothComponent smooth, TransformComponent xform, EntityQuery<IconSmoothComponent> smoothQuery)
{
var pos = grid.TileIndicesFor(xform.Coordinates);
var n = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.North)), smoothQuery);
var ne = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.NorthEast)), smoothQuery);
var e = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.East)), smoothQuery);
var se = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.SouthEast)), smoothQuery);
var s = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.South)), smoothQuery);
var sw = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.SouthWest)), smoothQuery);
var w = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.West)), smoothQuery);
var nw = MatchingEntity(smooth, grid.GetAnchoredEntitiesEnumerator(pos.Offset(Direction.NorthWest)), smoothQuery);
var gridUid = gridEntity.Owner;
var grid = gridEntity.Comp;
var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates);
var n = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery);
var ne = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthEast)), smoothQuery);
var e = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery);
var se = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthEast)), smoothQuery);
var s = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery);
var sw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthWest)), smoothQuery);
var w = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery);
var nw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthWest)), smoothQuery);
// ReSharper disable InconsistentNaming
var cornerNE = CornerFill.None;

View File

@@ -74,6 +74,9 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.OpenBackpack);
human.AddFunction(ContentKeyFunctions.OpenBelt);
human.AddFunction(ContentKeyFunctions.MouseMiddle);
human.AddFunction(ContentKeyFunctions.RotateObjectClockwise);
human.AddFunction(ContentKeyFunctions.RotateObjectCounterclockwise);
human.AddFunction(ContentKeyFunctions.FlipObject);
human.AddFunction(ContentKeyFunctions.ArcadeUp);
human.AddFunction(ContentKeyFunctions.ArcadeDown);
human.AddFunction(ContentKeyFunctions.ArcadeLeft);

View File

@@ -206,7 +206,7 @@ namespace Content.Client.Instruments.UI
var container = _entManager.System<SharedContainerSystem>();
// If we're a handheld instrument, we might be in a container. Get it just in case.
container.TryGetContainingContainer(Entity, out var conMan);
container.TryGetContainingContainer((Entity, null, null), out var conMan);
// If the instrument is handheld and we're not holding it, we return.
if (instrument.Handheld && (conMan == null || conMan.Owner != localEntity))

View File

@@ -42,6 +42,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
// how often to recheck possible targets (prevents calling expensive
// check logic each update)
@@ -89,7 +90,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
/// </summary>
private bool _isReplaying;
private float _deadzone;
public float Deadzone;
private DragState _state = DragState.NotDragging;
@@ -121,7 +122,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
private void SetDeadZone(float deadZone)
{
_deadzone = deadZone;
Deadzone = deadZone;
}
public override void Shutdown()
@@ -211,7 +212,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
_draggedEntity = entity;
_state = DragState.MouseDown;
_mouseDownScreenPos = _inputManager.MouseScreenPosition;
_mouseDownScreenPos = args.ScreenCoordinates;
_mouseDownTime = 0;
// don't want anything else to process the click,
@@ -239,8 +240,13 @@ public sealed class DragDropSystem : SharedDragDropSystem
if (TryComp<SpriteComponent>(_draggedEntity, out var draggedSprite))
{
var screenPos = _inputManager.MouseScreenPosition;
// No _draggedEntity in null window (Happens in tests)
if (!screenPos.IsValid)
return;
// pop up drag shadow under mouse
var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
var mousePos = _eyeManager.PixelToMap(screenPos);
_dragShadow = EntityManager.SpawnEntity("dragshadow", mousePos);
var dragSprite = Comp<SpriteComponent>(_dragShadow.Value);
dragSprite.CopyFrom(draggedSprite);
@@ -517,6 +523,9 @@ public sealed class DragDropSystem : SharedDragDropSystem
if (dropEv2.Handled)
return dropEv2.CanDrop;
if (dropEv.Handled && dropEv.CanDrop)
return true;
return null;
}
@@ -530,7 +539,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
case DragState.MouseDown:
{
var screenPos = _inputManager.MouseScreenPosition;
if ((_mouseDownScreenPos!.Value.Position - screenPos.Position).Length() > _deadzone)
if ((_mouseDownScreenPos!.Value.Position - screenPos.Position).Length() > Deadzone)
{
StartDrag();
}
@@ -551,7 +560,7 @@ public sealed class DragDropSystem : SharedDragDropSystem
if (Exists(_dragShadow))
{
var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
Transform(_dragShadow.Value).WorldPosition = mousePos.Position;
_transformSystem.SetWorldPosition(_dragShadow.Value, mousePos.Position);
}
}
}

View File

@@ -4,23 +4,23 @@ using Content.Client.Chat.Managers;
using Content.Client.Clickable;
using Content.Client.DebugMon;
using Content.Client.Eui;
using Content.Client.Fullscreen;
using Content.Client.GhostKick;
using Content.Client.Guidebook;
using Content.Client.Launcher;
using Content.Client.Mapping;
using Content.Client.Parallax.Managers;
using Content.Client.Players.PlayTimeTracking;
using Content.Client.Replay;
using Content.Client.Screenshot;
using Content.Client.Fullscreen;
using Content.Client.Stylesheets;
using Content.Client.Viewport;
using Content.Client.Voting;
using Content.Shared.Administration.Logs;
using Content.Client.Guidebook;
using Content.Client.Lobby;
using Content.Client.Replay;
using Content.Shared.Administration.Managers;
using Content.Shared.Players.PlayTimeTracking;
namespace Content.Client.IoC
{
internal static class ClientContentIoC
@@ -49,6 +49,7 @@ namespace Content.Client.IoC
collection.Register<DocumentParsingManager>();
collection.Register<ContentReplayPlaybackManager, ContentReplayPlaybackManager>();
collection.Register<ISharedPlaytimeManager, JobRequirementsManager>();
collection.Register<MappingManager>();
collection.Register<DebugMonitorManager>();
}
}

View File

@@ -43,7 +43,7 @@ public sealed class ItemSystem : SharedItemSystem
public override void VisualsChanged(EntityUid uid)
{
// if the item is in a container, it might be equipped to hands or inventory slots --> update visuals.
if (Container.TryGetContainingContainer(uid, out var container))
if (Container.TryGetContainingContainer((uid, null, null), out var container))
RaiseLocalEvent(container.Owner, new VisualsChangedEvent(GetNetEntity(uid), container.ID));
}

View File

@@ -2,9 +2,11 @@ using System.Linq;
using System.Numerics;
using Content.Client.CrewManifest;
using Content.Client.GameTicking.Managers;
using Content.Client.Lobby;
using Content.Client.UserInterface.Controls;
using Content.Client.Players.PlayTimeTracking;
using Content.Shared.CCVar;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.StatusIcon;
using Robust.Client.Console;
@@ -26,6 +28,7 @@ namespace Content.Client.LateJoin
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
[Dependency] private readonly JobRequirementsManager _jobRequirements = default!;
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
public event Action<(NetEntity, string)> SelectedId;
@@ -254,7 +257,7 @@ namespace Content.Client.LateJoin
jobButton.OnPressed += _ => SelectedId.Invoke((id, jobButton.JobId));
if (!_jobRequirements.IsAllowed(prototype, out var reason))
if (!_jobRequirements.IsAllowed(prototype, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
{
jobButton.Disabled = true;

View File

@@ -22,21 +22,29 @@ public sealed class LatheSystem : SharedLatheSystem
if (args.Sprite == null)
return;
// Lathe specific stuff
if (_appearance.TryGetData<bool>(uid, LatheVisuals.IsRunning, out var isRunning, args.Component))
{
if (args.Sprite.LayerMapTryGet(LatheVisualLayers.IsRunning, out var runningLayer) &&
component.RunningState != null &&
component.IdleState != null)
{
var state = isRunning ? component.RunningState : component.IdleState;
args.Sprite.LayerSetState(runningLayer, state);
}
}
if (_appearance.TryGetData<bool>(uid, PowerDeviceVisuals.Powered, out var powered, args.Component) &&
args.Sprite.LayerMapTryGet(PowerDeviceVisualLayers.Powered, out var powerLayer))
{
args.Sprite.LayerSetVisible(powerLayer, powered);
}
// Lathe specific stuff
if (_appearance.TryGetData<bool>(uid, LatheVisuals.IsRunning, out var isRunning, args.Component) &&
args.Sprite.LayerMapTryGet(LatheVisualLayers.IsRunning, out var runningLayer) &&
component.RunningState != null &&
component.IdleState != null)
{
var state = isRunning ? component.RunningState : component.IdleState;
args.Sprite.LayerSetAnimationTime(runningLayer, 0f);
args.Sprite.LayerSetState(runningLayer, state);
if (component.UnlitIdleState != null &&
component.UnlitRunningState != null)
{
var state = isRunning ? component.UnlitRunningState : component.UnlitIdleState;
args.Sprite.LayerSetState(powerLayer, state);
}
}
}

View File

@@ -100,11 +100,9 @@
Margin="5 0 0 0"
Text="{Loc 'lathe-menu-fabricating-message'}">
</Label>
<EntityPrototypeView
Name="FabricatingEntityProto"
HorizontalAlignment="Left"
Margin="100 0 0 0"
/>
<BoxContainer Name="FabricatingDisplayContainer"
HorizontalAlignment="Left"
Margin="100 0 0 0"/>
<Label
Name="NameLabel"
RectClipContent="True"

View File

@@ -1,18 +1,15 @@
using System.Buffers;
using System.Linq;
using System.Text;
using Content.Client.Materials;
using Content.Shared.Lathe;
using Content.Shared.Lathe.Prototypes;
using Content.Shared.Materials;
using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.ResourceManagement;
using Robust.Client.Graphics;
using Robust.Shared.Prototypes;
namespace Content.Client.Lathe.UI;
@@ -76,6 +73,16 @@ public sealed partial class LatheMenu : DefaultWindow
MaterialsList.SetOwner(Entity);
}
protected override void Opened()
{
base.Opened();
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComp))
{
AmountLineEdit.SetText(latheComp.DefaultProductionAmount.ToString());
}
}
/// <summary>
/// Populates the list of all the recipes
/// </summary>
@@ -92,7 +99,7 @@ public sealed partial class LatheMenu : DefaultWindow
if (SearchBar.Text.Trim().Length != 0)
{
if (proto.Name.ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
if (_lathe.GetRecipeName(recipe).ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
recipesToShow.Add(proto);
}
else
@@ -104,19 +111,15 @@ public sealed partial class LatheMenu : DefaultWindow
if (!int.TryParse(AmountLineEdit.Text, out var quantity) || quantity <= 0)
quantity = 1;
var sortedRecipesToShow = recipesToShow.OrderBy(p => p.Name);
var sortedRecipesToShow = recipesToShow.OrderBy(_lathe.GetRecipeName);
RecipeList.Children.Clear();
_entityManager.TryGetComponent(Entity, out LatheComponent? lathe);
foreach (var prototype in sortedRecipesToShow)
{
EntityPrototype? recipeProto = null;
if (_prototypeManager.TryIndex(prototype.Result, out EntityPrototype? entityProto))
recipeProto = entityProto;
var canProduce = _lathe.CanProduce(Entity, prototype, quantity, component: lathe);
var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, recipeProto);
var control = new RecipeControl(_lathe, prototype, () => GenerateTooltipText(prototype), canProduce, GetRecipeDisplayControl(prototype));
control.OnButtonPressed += s =>
{
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
@@ -132,9 +135,9 @@ public sealed partial class LatheMenu : DefaultWindow
StringBuilder sb = new();
var multiplier = _entityManager.GetComponent<LatheComponent>(Entity).MaterialUseMultiplier;
foreach (var (id, amount) in prototype.RequiredMaterials)
foreach (var (id, amount) in prototype.Materials)
{
if (!_prototypeManager.TryIndex<MaterialPrototype>(id, out var proto))
if (!_prototypeManager.TryIndex(id, out var proto))
continue;
var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier);
@@ -163,8 +166,9 @@ public sealed partial class LatheMenu : DefaultWindow
sb.AppendLine(tooltipText);
}
if (!string.IsNullOrWhiteSpace(prototype.Description))
sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", prototype.Description)));
var desc = _lathe.GetRecipeDescription(prototype);
if (!string.IsNullOrWhiteSpace(desc))
sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", desc)));
// Remove last newline
if (sb.Length > 0)
@@ -222,13 +226,10 @@ public sealed partial class LatheMenu : DefaultWindow
var queuedRecipeBox = new BoxContainer();
queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
var queuedRecipeProto = new EntityPrototypeView();
queuedRecipeBox.AddChild(queuedRecipeProto);
if (_prototypeManager.TryIndex(recipe.Result, out EntityPrototype? entityProto) && entityProto != null)
queuedRecipeProto.SetPrototype(entityProto);
queuedRecipeBox.AddChild(GetRecipeDisplayControl(recipe));
var queuedRecipeLabel = new Label();
queuedRecipeLabel.Text = $"{idx}. {recipe.Name}";
queuedRecipeLabel.Text = $"{idx}. {_lathe.GetRecipeName(recipe)}";
queuedRecipeBox.AddChild(queuedRecipeLabel);
QueueList.AddChild(queuedRecipeBox);
idx++;
@@ -241,10 +242,29 @@ public sealed partial class LatheMenu : DefaultWindow
if (recipe == null)
return;
if (_prototypeManager.TryIndex(recipe.Result, out EntityPrototype? entityProto) && entityProto != null)
FabricatingEntityProto.SetPrototype(entityProto);
FabricatingDisplayContainer.Children.Clear();
FabricatingDisplayContainer.AddChild(GetRecipeDisplayControl(recipe));
NameLabel.Text = $"{recipe.Name}";
NameLabel.Text = _lathe.GetRecipeName(recipe);
}
public Control GetRecipeDisplayControl(LatheRecipePrototype recipe)
{
if (recipe.Icon != null)
{
var textRect = new TextureRect();
textRect.Texture = _spriteSystem.Frame0(recipe.Icon);
return textRect;
}
if (recipe.Result is { } result)
{
var entProtoView = new EntityPrototypeView();
entProtoView.SetPrototype(result);
return entProtoView;
}
return new Control();
}
private void OnItemSelected(OptionButton.ItemSelectedEventArgs obj)

View File

@@ -5,8 +5,8 @@
Margin="0"
StyleClasses="ButtonSquare">
<BoxContainer Orientation="Horizontal">
<EntityPrototypeView
Name="RecipePrototype"
<BoxContainer
Name="RecipeDisplayContainer"
Margin="0 0 4 0"
HorizontalAlignment="Center"
VerticalAlignment="Center"

View File

@@ -1,10 +1,7 @@
using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Lathe.UI;
@@ -14,13 +11,12 @@ public sealed partial class RecipeControl : Control
public Action<string>? OnButtonPressed;
public Func<string> TooltipTextSupplier;
public RecipeControl(LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, EntityPrototype? entityPrototype = null)
public RecipeControl(LatheSystem latheSystem, LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, Control displayControl)
{
RobustXamlLoader.Load(this);
RecipeName.Text = recipe.Name;
if (entityPrototype != null)
RecipePrototype.SetPrototype(entityPrototype);
RecipeName.Text = latheSystem.GetRecipeName(recipe);
RecipeDisplayContainer.AddChild(displayControl);
Button.Disabled = !canProduce;
TooltipTextSupplier = tooltipTextSupplier;
Button.TooltipSupplier = SupplyTooltip;

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using Content.Shared.Light.Components;
using JetBrains.Annotations;
using Robust.Client.Animations;
@@ -68,7 +68,7 @@ namespace Content.Client.Light.Components
if (MinDuration > 0)
{
MaxTime = (float) _random.NextDouble() * (MaxDuration - MinDuration) + MinDuration;
MaxTime = (float)_random.NextDouble() * (MaxDuration - MinDuration) + MinDuration;
}
else
{
@@ -192,11 +192,11 @@ namespace Content.Client.Light.Components
{
if (interpolateValue < 0.5f)
{
ApplyInterpolation(StartValue, EndValue, interpolateValue*2);
ApplyInterpolation(StartValue, EndValue, interpolateValue * 2);
}
else
{
ApplyInterpolation(EndValue, StartValue, (interpolateValue-0.5f)*2);
ApplyInterpolation(EndValue, StartValue, (interpolateValue - 0.5f) * 2);
}
}
else
@@ -238,9 +238,9 @@ namespace Content.Client.Light.Components
public override void OnInitialize()
{
_randomValue1 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble());
_randomValue2 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble());
_randomValue3 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble());
_randomValue1 = (float)InterpolateLinear(StartValue, EndValue, (float)_random.NextDouble());
_randomValue2 = (float)InterpolateLinear(StartValue, EndValue, (float)_random.NextDouble());
_randomValue3 = (float)InterpolateLinear(StartValue, EndValue, (float)_random.NextDouble());
}
public override void OnStart()
@@ -258,7 +258,7 @@ namespace Content.Client.Light.Components
}
_randomValue3 = _randomValue4;
_randomValue4 = (float) InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble());
_randomValue4 = (float)InterpolateLinear(StartValue, EndValue, (float) _random.NextDouble());
}
public override (int KeyFrameIndex, float FramePlayingTime) AdvancePlayback(
@@ -362,7 +362,7 @@ namespace Content.Client.Light.Components
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private const string KeyPrefix = nameof(LightBehaviourComponent);
public const string KeyPrefix = nameof(LightBehaviourComponent);
public sealed class AnimationContainer
{
@@ -387,7 +387,7 @@ namespace Content.Client.Light.Components
public readonly List<AnimationContainer> Animations = new();
[ViewVariables(VVAccess.ReadOnly)]
private Dictionary<string, object> _originalPropertyValues = new();
public Dictionary<string, object> OriginalPropertyValues = new();
void ISerializationHooks.AfterDeserialization()
{
@@ -397,155 +397,12 @@ namespace Content.Client.Light.Components
{
var animation = new Animation()
{
AnimationTracks = {behaviour}
AnimationTracks = { behaviour }
};
Animations.Add(new AnimationContainer(key, animation, behaviour));
key++;
}
}
/// <summary>
/// If we disable all the light behaviours we want to be able to revert the light to its original state.
/// </summary>
private void CopyLightSettings(EntityUid uid, string property)
{
if (_entMan.TryGetComponent(uid, out PointLightComponent? light))
{
var propertyValue = AnimationHelper.GetAnimatableProperty(light, property);
if (propertyValue != null)
{
_originalPropertyValues.Add(property, propertyValue);
}
}
else
{
Logger.Warning($"{_entMan.GetComponent<MetaDataComponent>(uid).EntityName} has a {nameof(LightBehaviourComponent)} but it has no {nameof(PointLightComponent)}! Check the prototype!");
}
}
/// <summary>
/// Start animating a light behaviour with the specified ID. If the specified ID is empty, it will start animating all light behaviour entries.
/// If specified light behaviours are already animating, calling this does nothing.
/// Multiple light behaviours can have the same ID.
/// </summary>
public void StartLightBehaviour(string id = "")
{
var uid = Owner;
if (!_entMan.TryGetComponent(uid, out AnimationPlayerComponent? animation))
{
return;
}
var animations = _entMan.System<AnimationPlayerSystem>();
foreach (var container in Animations)
{
if (container.LightBehaviour.ID == id || id == string.Empty)
{
if (!animations.HasRunningAnimation(uid, animation, KeyPrefix + container.Key))
{
CopyLightSettings(uid, container.LightBehaviour.Property);
container.LightBehaviour.UpdatePlaybackValues(container.Animation);
animations.Play(uid, animation, container.Animation, KeyPrefix + container.Key);
}
}
}
}
/// <summary>
/// If any light behaviour with the specified ID is animating, then stop it.
/// If no ID is specified then all light behaviours will be stopped.
/// Multiple light behaviours can have the same ID.
/// </summary>
/// <param name="id"></param>
/// <param name="removeBehaviour">Should the behaviour(s) also be removed permanently?</param>
/// <param name="resetToOriginalSettings">Should the light have its original settings applied?</param>
public void StopLightBehaviour(string id = "", bool removeBehaviour = false, bool resetToOriginalSettings = false)
{
var uid = Owner;
if (!_entMan.TryGetComponent(uid, out AnimationPlayerComponent? animation))
{
return;
}
var toRemove = new List<AnimationContainer>();
var animations = _entMan.System<AnimationPlayerSystem>();
foreach (var container in Animations)
{
if (container.LightBehaviour.ID == id || id == string.Empty)
{
if (animations.HasRunningAnimation(uid, animation, KeyPrefix + container.Key))
{
animations.Stop(uid, animation, KeyPrefix + container.Key);
}
if (removeBehaviour)
{
toRemove.Add(container);
}
}
}
foreach (var container in toRemove)
{
Animations.Remove(container);
}
if (resetToOriginalSettings && _entMan.TryGetComponent(uid, out PointLightComponent? light))
{
foreach (var (property, value) in _originalPropertyValues)
{
AnimationHelper.SetAnimatableProperty(light, property, value);
}
}
_originalPropertyValues.Clear();
}
/// <summary>
/// Checks if at least one behaviour is running.
/// </summary>
/// <returns>Whether at least one behaviour is running, false if none is.</returns>
public bool HasRunningBehaviours()
{
var uid = Owner;
if (!_entMan.TryGetComponent(uid, out AnimationPlayerComponent? animation))
{
return false;
}
var animations = _entMan.System<AnimationPlayerSystem>();
return Animations.Any(container => animations.HasRunningAnimation(uid, animation, KeyPrefix + container.Key));
}
/// <summary>
/// Add a new light behaviour to the component and start it immediately unless otherwise specified.
/// </summary>
public void AddNewLightBehaviour(LightBehaviourAnimationTrack behaviour, bool playImmediately = true)
{
var key = 0;
while (Animations.Any(x => x.Key == key))
{
key++;
}
var animation = new Animation()
{
AnimationTracks = {behaviour}
};
behaviour.Initialize(Owner, _random, _entMan);
var container = new AnimationContainer(key, animation, behaviour);
Animations.Add(container);
if (playImmediately)
{
StartLightBehaviour(behaviour.ID);
}
}
}
}

View File

@@ -11,6 +11,7 @@ public sealed class ExpendableLightSystem : VisualizerSystem<ExpendableLightComp
{
[Dependency] private readonly PointLightSystem _pointLightSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly LightBehaviorSystem _lightBehavior = default!;
public override void Initialize()
{
@@ -32,11 +33,11 @@ public sealed class ExpendableLightSystem : VisualizerSystem<ExpendableLightComp
if (AppearanceSystem.TryGetData<string>(uid, ExpendableLightVisuals.Behavior, out var lightBehaviourID, args.Component)
&& TryComp<LightBehaviourComponent>(uid, out var lightBehaviour))
{
lightBehaviour.StopLightBehaviour();
_lightBehavior.StopLightBehaviour((uid, lightBehaviour));
if (!string.IsNullOrEmpty(lightBehaviourID))
{
lightBehaviour.StartLightBehaviour(lightBehaviourID);
_lightBehavior.StartLightBehaviour((uid, lightBehaviour), lightBehaviourID);
}
else if (TryComp<PointLightComponent>(uid, out var light))
{

View File

@@ -1,7 +1,9 @@
using System.Linq;
using Content.Client.Light.Components;
using Robust.Client.GameObjects;
using Robust.Client.Animations;
using Robust.Shared.Random;
using Robust.Shared.Animations;
namespace Content.Client.Light.EntitySystems;
@@ -36,23 +38,163 @@ public sealed class LightBehaviorSystem : EntitySystem
}
}
private void OnLightStartup(EntityUid uid, LightBehaviourComponent component, ComponentStartup args)
private void OnLightStartup(Entity<LightBehaviourComponent> entity, ref ComponentStartup args)
{
// TODO: Do NOT ensure component here. And use eventbus events instead...
EnsureComp<AnimationPlayerComponent>(uid);
EnsureComp<AnimationPlayerComponent>(entity);
foreach (var container in component.Animations)
foreach (var container in entity.Comp.Animations)
{
container.LightBehaviour.Initialize(uid, _random, EntityManager);
container.LightBehaviour.Initialize(entity, _random, EntityManager);
}
// we need to initialize all behaviours before starting any
foreach (var container in component.Animations)
foreach (var container in entity.Comp.Animations)
{
if (container.LightBehaviour.Enabled)
{
component.StartLightBehaviour(container.LightBehaviour.ID);
StartLightBehaviour(entity, container.LightBehaviour.ID);
}
}
}
/// <summary>
/// If we disable all the light behaviours we want to be able to revert the light to its original state.
/// </summary>
private void CopyLightSettings(Entity<LightBehaviourComponent> entity, string property)
{
if (EntityManager.TryGetComponent(entity, out PointLightComponent? light))
{
var propertyValue = AnimationHelper.GetAnimatableProperty(light, property);
if (propertyValue != null)
{
entity.Comp.OriginalPropertyValues.Add(property, propertyValue);
}
}
else
{
Log.Warning($"{EntityManager.GetComponent<MetaDataComponent>(entity).EntityName} has a {nameof(LightBehaviourComponent)} but it has no {nameof(PointLightComponent)}! Check the prototype!");
}
}
/// <summary>
/// Start animating a light behaviour with the specified ID. If the specified ID is empty, it will start animating all light behaviour entries.
/// If specified light behaviours are already animating, calling this does nothing.
/// Multiple light behaviours can have the same ID.
/// </summary>
public void StartLightBehaviour(Entity<LightBehaviourComponent> entity, string id = "")
{
if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation))
{
return;
}
foreach (var container in entity.Comp.Animations)
{
if (container.LightBehaviour.ID == id || id == string.Empty)
{
if (!_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key))
{
CopyLightSettings(entity, container.LightBehaviour.Property);
container.LightBehaviour.UpdatePlaybackValues(container.Animation);
_player.Play(entity, container.Animation, LightBehaviourComponent.KeyPrefix + container.Key);
}
}
}
}
/// <summary>
/// If any light behaviour with the specified ID is animating, then stop it.
/// If no ID is specified then all light behaviours will be stopped.
/// Multiple light behaviours can have the same ID.
/// </summary>
/// <param name="id"></param>
/// <param name="removeBehaviour">Should the behaviour(s) also be removed permanently?</param>
/// <param name="resetToOriginalSettings">Should the light have its original settings applied?</param>
public void StopLightBehaviour(Entity<LightBehaviourComponent> entity, string id = "", bool removeBehaviour = false, bool resetToOriginalSettings = false)
{
if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation))
{
return;
}
var comp = entity.Comp;
var toRemove = new List<LightBehaviourComponent.AnimationContainer>();
foreach (var container in comp.Animations)
{
if (container.LightBehaviour.ID == id || id == string.Empty)
{
if (_player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key))
{
_player.Stop(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key);
}
if (removeBehaviour)
{
toRemove.Add(container);
}
}
}
foreach (var container in toRemove)
{
comp.Animations.Remove(container);
}
if (resetToOriginalSettings && EntityManager.TryGetComponent(entity, out PointLightComponent? light))
{
foreach (var (property, value) in comp.OriginalPropertyValues)
{
AnimationHelper.SetAnimatableProperty(light, property, value);
}
}
comp.OriginalPropertyValues.Clear();
}
/// <summary>
/// Checks if at least one behaviour is running.
/// </summary>
/// <returns>Whether at least one behaviour is running, false if none is.</returns>
public bool HasRunningBehaviours(Entity<LightBehaviourComponent> entity)
{
//var uid = Owner;
if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation))
{
return false;
}
return entity.Comp.Animations.Any(container => _player.HasRunningAnimation(entity, animation, LightBehaviourComponent.KeyPrefix + container.Key));
}
/// <summary>
/// Add a new light behaviour to the component and start it immediately unless otherwise specified.
/// </summary>
public void AddNewLightBehaviour(Entity<LightBehaviourComponent> entity, LightBehaviourAnimationTrack behaviour, bool playImmediately = true)
{
var key = 0;
var comp = entity.Comp;
while (comp.Animations.Any(x => x.Key == key))
{
key++;
}
var animation = new Animation()
{
AnimationTracks = { behaviour }
};
behaviour.Initialize(entity.Owner, _random, EntityManager);
var container = new LightBehaviourComponent.AnimationContainer(key, animation, behaviour);
comp.Animations.Add(container);
if (playImmediately)
{
StartLightBehaviour(entity, behaviour.ID);
}
}
}

View File

@@ -3,15 +3,15 @@ using Content.Client.Light.Components;
using Content.Shared.Light;
using Content.Shared.Light.Components;
using Content.Shared.Toggleable;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
using Content.Client.Light.EntitySystems;
namespace Content.Client.Light;
public sealed class HandheldLightSystem : SharedHandheldLightSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly LightBehaviorSystem _lightBehavior = default!;
public override void Initialize()
{
@@ -41,9 +41,9 @@ public sealed class HandheldLightSystem : SharedHandheldLightSystem
if (TryComp<LightBehaviourComponent>(uid, out var lightBehaviour))
{
// Reset any running behaviour to reset the animated properties back to the original value, to avoid conflicts between resets
if (lightBehaviour.HasRunningBehaviours())
if (_lightBehavior.HasRunningBehaviours((uid, lightBehaviour)))
{
lightBehaviour.StopLightBehaviour(resetToOriginalSettings: true);
_lightBehavior.StopLightBehaviour((uid, lightBehaviour), resetToOriginalSettings: true);
}
if (!enabled)
@@ -56,10 +56,10 @@ public sealed class HandheldLightSystem : SharedHandheldLightSystem
case HandheldLightPowerStates.FullPower:
break; // We just needed to reset all behaviours
case HandheldLightPowerStates.LowPower:
lightBehaviour.StartLightBehaviour(component.RadiatingBehaviourId);
_lightBehavior.StartLightBehaviour((uid, lightBehaviour), component.RadiatingBehaviourId);
break;
case HandheldLightPowerStates.Dying:
lightBehaviour.StartLightBehaviour(component.BlinkingBehaviourId);
_lightBehavior.StartLightBehaviour((uid, lightBehaviour), component.BlinkingBehaviourId);
break;
}
}

View File

@@ -43,6 +43,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
[UISystemDependency] private readonly GuidebookSystem _guide = default!;
[UISystemDependency] private readonly LoadoutSystem _loadouts = default!;
private CharacterSetupGui? _characterSetup;
private HumanoidProfileEditor? _profileEditor;
@@ -272,6 +273,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
_logManager,
_playerManager,
_prototypeManager,
_resourceCache,
_requirements,
_markings);
@@ -364,7 +366,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
continue;
_spawn.EquipStartingGear(uid, _prototypeManager.Index(loadoutProto.Equipment));
_spawn.EquipStartingGear(uid, loadoutProto);
}
}
}
@@ -387,36 +389,51 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
continue;
// TODO: Need some way to apply starting gear to an entity coz holy fucking shit dude.
var loadoutGear = _prototypeManager.Index(loadoutProto.Equipment);
// TODO: Need some way to apply starting gear to an entity and replace existing stuff coz holy fucking shit dude.
foreach (var slot in slots)
{
var itemType = loadoutGear.GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
// Try startinggear first
if (_prototypeManager.TryIndex(loadoutProto.StartingGear, out var loadoutGear))
{
EntityManager.DeleteEntity(unequippedItem.Value);
var itemType = ((IEquipmentLoadout) loadoutGear).GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntityManager.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
_inventory.TryEquip(dummy, item, slot.Name, true, true);
}
}
if (itemType != string.Empty)
else
{
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
_inventory.TryEquip(dummy, item, slot.Name, true, true);
var itemType = ((IEquipmentLoadout) loadoutProto).GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{
EntityManager.DeleteEntity(unequippedItem.Value);
}
if (itemType != string.Empty)
{
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
_inventory.TryEquip(dummy, item, slot.Name, true, true);
}
}
}
}
}
}
if (job.StartingGear == null)
if (!_prototypeManager.TryIndex(job.StartingGear, out var gear))
return;
var gear = _prototypeManager.Index<StartingGearPrototype>(job.StartingGear);
foreach (var slot in slots)
{
var itemType = gear.GetGear(slot.Name);
var itemType = ((IEquipmentLoadout) gear).GetGear(slot.Name);
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
{

View File

@@ -38,6 +38,8 @@
<Button Name="ResetButton" Disabled="True" Text="{Loc 'humanoid-profile-editor-reset-button'}"/>
<Button Name="ImportButton" Text="{Loc 'humanoid-profile-editor-import-button'}"/>
<Button Name="ExportButton" Text="{Loc 'humanoid-profile-editor-export-button'}"/>
<Button Name="ExportImageButton" Text="{Loc 'humanoid-profile-editor-export-image-button'}"/>
<Button Name="OpenImagesButton" Text="{Loc 'humanoid-profile-editor-open-image-button'}"/>
</BoxContainer>
</ui:HighlightedContainer>
</BoxContainer>

View File

@@ -6,6 +6,7 @@ using Content.Client.Lobby.UI.Loadouts;
using Content.Client.Lobby.UI.Roles;
using Content.Client.Message;
using Content.Client.Players.PlayTimeTracking;
using Content.Client.Sprite;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared._CP14.Humanoid;
@@ -28,6 +29,7 @@ using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -44,6 +46,7 @@ namespace Content.Client.Lobby.UI
private readonly IFileDialogManager _dialogManager;
private readonly IPlayerManager _playerManager;
private readonly IPrototypeManager _prototypeManager;
private readonly IResourceManager _resManager;
private readonly MarkingManager _markingManager;
private readonly JobRequirementsManager _requirements;
private readonly LobbyUIController _controller;
@@ -55,6 +58,7 @@ namespace Content.Client.Lobby.UI
private LoadoutWindow? _loadoutWindow;
private bool _exporting;
private bool _imaging;
/// <summary>
/// If we're attempting to save.
@@ -108,6 +112,7 @@ namespace Content.Client.Lobby.UI
ILogManager logManager,
IPlayerManager playerManager,
IPrototypeManager prototypeManager,
IResourceManager resManager,
JobRequirementsManager requirements,
MarkingManager markings)
{
@@ -120,6 +125,7 @@ namespace Content.Client.Lobby.UI
_prototypeManager = prototypeManager;
_markingManager = markings;
_preferencesManager = preferencesManager;
_resManager = resManager;
_requirements = requirements;
_controller = UserInterfaceManager.GetUIController<LobbyUIController>();
@@ -133,6 +139,16 @@ namespace Content.Client.Lobby.UI
ExportProfile();
};
ExportImageButton.OnPressed += args =>
{
ExportImage();
};
OpenImagesButton.OnPressed += args =>
{
_resManager.UserData.OpenOsWindow(ContentSpriteSystem.Exports);
};
ResetButton.OnPressed += args =>
{
SetProfile((HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter, _preferencesManager.Preferences?.SelectedCharacterIndex);
@@ -425,7 +441,6 @@ namespace Content.Client.Lobby.UI
SpeciesInfoButton.OnPressed += OnSpeciesInfoButtonPressed;
UpdateSpeciesGuidebookIcon();
ReloadPreview();
IsDirty = false;
}
@@ -635,7 +650,7 @@ namespace Content.Client.Lobby.UI
selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1);
var requirements = _entManager.System<SharedRoleSystem>().GetAntagRequirement(antag);
if (!_requirements.CheckRoleTime(requirements, out var reason))
if (!_requirements.CheckRoleRequirements(requirements, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
{
selector.LockRequirements(reason);
Profile = Profile?.WithAntagPreference(antag.ID, false);
@@ -698,11 +713,12 @@ namespace Content.Client.Lobby.UI
_entManager.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
if (Profile == null || !_prototypeManager.HasIndex<SpeciesPrototype>(Profile.Species))
if (Profile == null || !_prototypeManager.HasIndex(Profile.Species))
return;
PreviewDummy = _controller.LoadProfileEntity(Profile, JobOverride, ShowClothes.Pressed);
SpriteView.SetEntity(PreviewDummy);
_entManager.System<MetaDataSystem>().SetEntityName(PreviewDummy, Profile.Name);
}
/// <summary>
@@ -888,7 +904,7 @@ namespace Content.Client.Lobby.UI
icon.Texture = jobIcon.Icon.Frame0();
selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon, job.Guides);
if (!_requirements.IsAllowed(job, out var reason))
if (!_requirements.IsAllowed(job, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
{
selector.LockRequirements(reason);
}
@@ -1139,6 +1155,17 @@ namespace Content.Client.Lobby.UI
_loadoutWindow?.Dispose();
_loadoutWindow = null;
}
protected override void EnteredTree()
{
base.EnteredTree();
ReloadPreview();
}
protected override void ExitedTree()
{
base.ExitedTree();
_entManager.DeleteEntity(PreviewDummy);
PreviewDummy = EntityUid.Invalid;
}
@@ -1199,6 +1226,11 @@ namespace Content.Client.Lobby.UI
{
Profile = Profile?.WithName(newName);
SetDirty();
if (!IsDirty)
return;
_entManager.System<MetaDataSystem>().SetEntityName(PreviewDummy, newName);
}
private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
@@ -1543,6 +1575,19 @@ namespace Content.Client.Lobby.UI
UpdateNameEdit();
}
private async void ExportImage()
{
if (_imaging)
return;
var dir = SpriteView.OverrideDirection ?? Direction.South;
// I tried disabling the button but it looks sorta goofy as it only takes a frame or two to save
_imaging = true;
await _entManager.System<ContentSpriteSystem>().Export(PreviewDummy, dir, includeId: false);
_imaging = false;
}
private async void ImportProfile()
{
if (_exporting || CharacterSlot == null || Profile == null)

View File

@@ -0,0 +1,8 @@
<mapping:MappingActionsButton
xmlns="https://spacestation14.io"
xmlns:mapping="clr-namespace:Content.Client.Mapping"
StyleClasses="ButtonSquare" ToggleMode="True" SetSize="32 32" Margin="0 0 5 0"
TooltipDelay="0">
<TextureRect Name="Texture" Access="Public" Stretch="Scale" SetSize="16 16"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</mapping:MappingActionsButton>

View File

@@ -0,0 +1,15 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Mapping;
[GenerateTypedNameReferences]
public sealed partial class MappingActionsButton : Button
{
public MappingActionsButton()
{
RobustXamlLoader.Load(this);
}
}

View File

@@ -0,0 +1,4 @@
<mapping:MappingDoNotMeasure
xmlns="https://spacestation14.io"
xmlns:mapping="clr-namespace:Content.Client.Mapping">
</mapping:MappingDoNotMeasure>

View File

@@ -0,0 +1,21 @@
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Mapping;
[GenerateTypedNameReferences]
public sealed partial class MappingDoNotMeasure : Control
{
public MappingDoNotMeasure()
{
RobustXamlLoader.Load(this);
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
return Vector2.Zero;
}
}

View File

@@ -0,0 +1,69 @@
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Content.Shared.Mapping;
using Robust.Client.UserInterface;
using Robust.Shared.Network;
namespace Content.Client.Mapping;
public sealed class MappingManager : IPostInjectInit
{
[Dependency] private readonly IFileDialogManager _file = default!;
[Dependency] private readonly IClientNetManager _net = default!;
private Stream? _saveStream;
private MappingMapDataMessage? _mapData;
public void PostInject()
{
_net.RegisterNetMessage<MappingSaveMapMessage>();
_net.RegisterNetMessage<MappingSaveMapErrorMessage>(OnSaveError);
_net.RegisterNetMessage<MappingMapDataMessage>(OnMapData);
}
private void OnSaveError(MappingSaveMapErrorMessage message)
{
_saveStream?.DisposeAsync();
_saveStream = null;
}
private async void OnMapData(MappingMapDataMessage message)
{
if (_saveStream == null)
{
_mapData = message;
return;
}
await _saveStream.WriteAsync(Encoding.ASCII.GetBytes(message.Yml));
await _saveStream.DisposeAsync();
_saveStream = null;
_mapData = null;
}
public async Task SaveMap()
{
if (_saveStream != null)
await _saveStream.DisposeAsync();
var request = new MappingSaveMapMessage();
_net.ClientSendMessage(request);
var path = await _file.SaveFile();
if (path is not { fileStream: var stream })
return;
if (_mapData != null)
{
await stream.WriteAsync(Encoding.ASCII.GetBytes(_mapData.Yml));
_mapData = null;
await stream.FlushAsync();
await stream.DisposeAsync();
return;
}
_saveStream = stream;
}
}

View File

@@ -0,0 +1,84 @@
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using static Content.Client.Mapping.MappingState;
namespace Content.Client.Mapping;
public sealed class MappingOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _prototypes = default!;
// 1 off in case something else uses these colors since we use them to compare
private static readonly Color PickColor = new(1, 255, 0);
private static readonly Color DeleteColor = new(255, 1, 0);
private readonly Dictionary<EntityUid, Color> _oldColors = new();
private readonly MappingState _state;
private readonly ShaderInstance _shader;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public MappingOverlay(MappingState state)
{
IoCManager.InjectDependencies(this);
_state = state;
_shader = _prototypes.Index<ShaderPrototype>("unshaded").Instance();
}
protected override void Draw(in OverlayDrawArgs args)
{
foreach (var (id, color) in _oldColors)
{
if (!_entities.TryGetComponent(id, out SpriteComponent? sprite))
continue;
if (sprite.Color == DeleteColor || sprite.Color == PickColor)
sprite.Color = color;
}
_oldColors.Clear();
if (_player.LocalEntity == null)
return;
var handle = args.WorldHandle;
handle.UseShader(_shader);
switch (_state.State)
{
case CursorState.Pick:
{
if (_state.GetHoveredEntity() is { } entity &&
_entities.TryGetComponent(entity, out SpriteComponent? sprite))
{
_oldColors[entity] = sprite.Color;
sprite.Color = PickColor;
}
break;
}
case CursorState.Delete:
{
if (_state.GetHoveredEntity() is { } entity &&
_entities.TryGetComponent(entity, out SpriteComponent? sprite))
{
_oldColors[entity] = sprite.Color;
sprite.Color = DeleteColor;
}
break;
}
}
handle.UseShader(null);
}
}

View File

@@ -0,0 +1,39 @@
using Content.Shared.Decals;
using Content.Shared.Maps;
using Robust.Shared.Prototypes;
namespace Content.Client.Mapping;
/// <summary>
/// Used to represent a button's data in the mapping editor.
/// </summary>
public sealed class MappingPrototype
{
/// <summary>
/// The prototype instance, if any.
/// Can be one of <see cref="EntityPrototype"/>, <see cref="ContentTileDefinition"/> or <see cref="DecalPrototype"/>
/// If null, this is a top-level button (such as Entities, Tiles or Decals)
/// </summary>
public readonly IPrototype? Prototype;
/// <summary>
/// The text to display on the UI for this button.
/// </summary>
public readonly string Name;
/// <summary>
/// Which other prototypes (buttons) this one is nested inside of.
/// </summary>
public List<MappingPrototype>? Parents;
/// <summary>
/// Which other prototypes (buttons) are nested inside this one.
/// </summary>
public List<MappingPrototype>? Children;
public MappingPrototype(IPrototype? prototype, string name)
{
Prototype = prototype;
Name = name;
}
}

View File

@@ -0,0 +1,21 @@
<mapping:MappingPrototypeList
xmlns="https://spacestation14.io"
xmlns:mapping="clr-namespace:Content.Client.Mapping">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Button Name="CollapseAllButton" Access="Public" Text="-" SetSize="48 48"
StyleClasses="ButtonSquare" ToolTip="Collapse All" TooltipDelay="0" />
<LineEdit Name="SearchBar" SetHeight="48" HorizontalExpand="True" Access="Public" />
<Button Name="ClearSearchButton" Access="Public" Text="X" SetSize="48 48"
StyleClasses="ButtonSquare" />
</BoxContainer>
<ScrollContainer Name="ScrollContainer" Access="Public" VerticalExpand="True"
ReserveScrollbarSpace="True">
<BoxContainer Name="PrototypeList" Access="Public" Orientation="Vertical" />
<PrototypeListContainer Name="SearchList" Access="Public" Visible="False" />
</ScrollContainer>
<mapping:MappingDoNotMeasure Visible="False">
<mapping:MappingSpawnButton Name="MeasureButton" Access="Public" />
</mapping:MappingDoNotMeasure>
</BoxContainer>
</mapping:MappingPrototypeList>

View File

@@ -0,0 +1,170 @@
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Mapping;
[GenerateTypedNameReferences]
public sealed partial class MappingPrototypeList : Control
{
private (int start, int end) _lastIndices;
private readonly List<MappingPrototype> _prototypes = new();
private readonly List<Texture> _insertTextures = new();
private readonly List<MappingPrototype> _search = new();
public MappingSpawnButton? Selected;
public Action<IPrototype, List<Texture>>? GetPrototypeData;
public event Action<MappingSpawnButton, IPrototype?>? SelectionChanged;
public event Action<MappingSpawnButton, ButtonToggledEventArgs>? CollapseToggled;
public MappingPrototypeList()
{
RobustXamlLoader.Load(this);
MeasureButton.Measure(Vector2Helpers.Infinity);
ScrollContainer.OnScrolled += UpdateSearch;
OnResized += UpdateSearch;
}
public void UpdateVisible(List<MappingPrototype> prototypes)
{
_prototypes.Clear();
PrototypeList.DisposeAllChildren();
_prototypes.AddRange(prototypes);
Selected = null;
ScrollContainer.SetScrollValue(new Vector2(0, 0));
foreach (var prototype in _prototypes)
{
Insert(PrototypeList, prototype, true);
}
}
public MappingSpawnButton Insert(Container list, MappingPrototype mapping, bool includeChildren)
{
var prototype = mapping.Prototype;
_insertTextures.Clear();
if (prototype != null)
GetPrototypeData?.Invoke(prototype, _insertTextures);
var button = new MappingSpawnButton { Prototype = mapping };
button.Label.Text = mapping.Name;
if (_insertTextures.Count > 0)
{
button.Texture.Textures.AddRange(_insertTextures);
button.Texture.InvalidateMeasure();
}
else
{
button.Texture.Visible = false;
}
if (prototype != null && button.Prototype == Selected?.Prototype)
{
Selected = button;
button.Button.Pressed = true;
}
list.AddChild(button);
button.Button.OnToggled += _ => SelectionChanged?.Invoke(button, prototype);
if (includeChildren && mapping.Children?.Count > 0)
{
button.CollapseButton.Visible = true;
button.CollapseButton.OnToggled += args => CollapseToggled?.Invoke(button, args);
}
else
{
button.CollapseButtonWrapper.Visible = false;
button.CollapseButton.Visible = false;
}
return button;
}
public void Search(List<MappingPrototype> prototypes)
{
_search.Clear();
SearchList.DisposeAllChildren();
_lastIndices = (0, -1);
_search.AddRange(prototypes);
SearchList.TotalItemCount = _search.Count;
ScrollContainer.SetScrollValue(new Vector2(0, 0));
UpdateSearch();
}
/// <summary>
/// Constructs a virtual list where not all buttons exist at one time, since there may be thousands of them.
/// </summary>
private void UpdateSearch()
{
if (!SearchList.Visible)
return;
var height = MeasureButton.DesiredSize.Y + PrototypeListContainer.Separation;
var offset = Math.Max(-SearchList.Position.Y, 0);
var startIndex = (int) Math.Floor(offset / height);
SearchList.ItemOffset = startIndex;
var (prevStart, prevEnd) = _lastIndices;
var endIndex = startIndex - 1;
var spaceUsed = -height;
// calculate how far down we are scrolled
while (spaceUsed < SearchList.Parent!.Height)
{
spaceUsed += height;
endIndex += 1;
}
endIndex = Math.Min(endIndex, _search.Count - 1);
// nothing changed in terms of which buttons are visible now and before
if (endIndex == prevEnd && startIndex == prevStart)
return;
_lastIndices = (startIndex, endIndex);
// remove previously seen but now unseen buttons from the top
for (var i = prevStart; i < startIndex && i <= prevEnd; i++)
{
var control = SearchList.GetChild(0);
SearchList.RemoveChild(control);
}
// remove previously seen but now unseen buttons from the bottom
for (var i = prevEnd; i > endIndex && i >= prevStart; i--)
{
var control = SearchList.GetChild(SearchList.ChildCount - 1);
SearchList.RemoveChild(control);
}
// insert buttons that can now be seen, from the start
for (var i = Math.Min(prevStart - 1, endIndex); i >= startIndex; i--)
{
Insert(SearchList, _search[i], false).SetPositionInParent(0);
}
// insert buttons that can now be seen, from the end
for (var i = Math.Max(prevEnd + 1, startIndex); i <= endIndex; i++)
{
Insert(SearchList, _search[i], false);
}
}
}

View File

@@ -0,0 +1,85 @@
<mapping:MappingScreen
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Chat.Widgets"
xmlns:hotbar="clr-namespace:Content.Client.UserInterface.Systems.Hotbar.Widgets"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:mapping="clr-namespace:Content.Client.Mapping"
VerticalExpand="False"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
<controls:RecordedSplitContainer Name="ScreenContainer" HorizontalExpand="True"
VerticalExpand="True" SplitWidth="0"
StretchDirection="TopLeft">
<BoxContainer Orientation="Vertical" VerticalExpand="True" Name="SpawnContainer" MinWidth="200" SetWidth="600">
<mapping:MappingPrototypeList Name="Prototypes" Access="Public" VerticalExpand="True" />
<BoxContainer Name="DecalContainer" Access="Public" Orientation="Horizontal"
Visible="False">
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
<ColorSelectorSliders Name="DecalColorPicker" IsAlphaVisible="True" />
<Button Name="DecalPickerOpen" Text="{Loc decal-placer-window-palette}"
StyleClasses="ButtonSquare" />
</BoxContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
<CheckBox Name="DecalEnableAuto" Margin="0 0 0 10"
Text="{Loc decal-placer-window-enable-auto}" />
<CheckBox Name="DecalEnableSnap"
Text="{Loc decal-placer-window-enable-snap}" />
<CheckBox Name="DecalEnableCleanable"
Text="{Loc decal-placer-window-enable-cleanable}" />
<BoxContainer Name="DecalSpinBoxContainer" Orientation="Horizontal">
<Label Text="{Loc decal-placer-window-rotation}" Margin="0 0 0 1" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc decal-placer-window-zindex}" Margin="0 0 0 1" />
<SpinBox Name="DecalZIndexSpinBox" HorizontalExpand="True" />
</BoxContainer>
</BoxContainer>
</BoxContainer>
<BoxContainer Name="EntityContainer" Access="Public" Orientation="Horizontal"
Visible="False">
<Button Name="EntityReplaceButton" Access="Public" ToggleMode="True"
SetHeight="48"
StyleClasses="ButtonSquare" Text="{Loc 'mapping-replace'}" HorizontalExpand="True" />
<OptionButton Name="EntityPlacementMode" Access="Public"
SetHeight="48"
StyleClasses="ButtonSquare" TooltipDelay="0"
ToolTip="{Loc entity-spawn-window-override-menu-tooltip}"
HorizontalExpand="True" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Button Name="EraseEntityButton" Access="Public" HorizontalExpand="True"
SetHeight="48"
ToggleMode="True" Text="{Loc 'mapping-erase-entity'}" StyleClasses="ButtonSquare" />
<Button Name="EraseDecalButton" Access="Public" HorizontalExpand="True"
SetHeight="48"
ToggleMode="True" Text="{Loc 'mapping-erase-decal'}" StyleClasses="ButtonSquare" />
</BoxContainer>
<widgets:ChatBox Visible="False" />
</BoxContainer>
<LayoutContainer Name="ViewportContainer" HorizontalExpand="True" VerticalExpand="True">
<controls:MainViewport Name="MainViewport"/>
<hotbar:HotbarGui Name="Hotbar" />
<PanelContainer Name="Actions" VerticalExpand="True" HorizontalExpand="True"
MaxHeight="48">
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="#222222AA" />
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Horizontal" Margin="15 10">
<mapping:MappingActionsButton
Name="Add" Access="Public" Disabled="True" ToolTip="" Visible="False" />
<mapping:MappingActionsButton Name="Fill" Access="Public"
ToolTip="" Visible="False" />
<mapping:MappingActionsButton Name="Grab" Access="Public"
ToolTip="" Visible="False" />
<mapping:MappingActionsButton Name="Move" Access="Public"
ToolTip="" Visible="False" />
<mapping:MappingActionsButton Name="Pick" Access="Public"
ToolTip="Pick (Hold 5)" />
<mapping:MappingActionsButton Name="Delete" Access="Public"
ToolTip="Delete (Hold 6)" />
</BoxContainer>
</PanelContainer>
</LayoutContainer>
</controls:RecordedSplitContainer>
</mapping:MappingScreen>

View File

@@ -0,0 +1,197 @@
using System.Linq;
using System.Numerics;
using Content.Client.Decals;
using Content.Client.Decals.UI;
using Content.Client.UserInterface.Screens;
using Content.Client.UserInterface.Systems.Chat.Widgets;
using Content.Shared.Decals;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Mapping;
[GenerateTypedNameReferences]
public sealed partial class MappingScreen : InGameScreen
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
public DecalPlacementSystem DecalSystem = default!;
private PaletteColorPicker? _picker;
private ProtoId<DecalPrototype>? _id;
private Color _decalColor = Color.White;
private float _decalRotation;
private bool _decalSnap;
private int _decalZIndex;
private bool _decalCleanable;
private bool _decalAuto;
public override ChatBox ChatBox => GetWidget<ChatBox>()!;
public event Func<MappingSpawnButton, bool>? IsDecalVisible;
public MappingScreen()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
AutoscaleMaxResolution = new Vector2i(1080, 770);
SetAnchorPreset(ScreenContainer, LayoutPreset.Wide);
SetAnchorPreset(ViewportContainer, LayoutPreset.Wide);
SetAnchorPreset(SpawnContainer, LayoutPreset.Wide);
SetAnchorPreset(MainViewport, LayoutPreset.Wide);
SetAnchorAndMarginPreset(Hotbar, LayoutPreset.BottomWide, margin: 5);
SetAnchorAndMarginPreset(Actions, LayoutPreset.TopWide, margin: 5);
ScreenContainer.OnSplitResizeFinished += () =>
OnChatResized?.Invoke(new Vector2(ScreenContainer.SplitFraction, 0));
var rotationSpinBox = new FloatSpinBox(90.0f, 0)
{
HorizontalExpand = true
};
DecalSpinBoxContainer.AddChild(rotationSpinBox);
DecalColorPicker.OnColorChanged += OnDecalColorPicked;
DecalPickerOpen.OnPressed += OnDecalPickerOpenPressed;
rotationSpinBox.OnValueChanged += args =>
{
_decalRotation = args.Value;
UpdateDecal();
};
DecalEnableAuto.OnToggled += args =>
{
_decalAuto = args.Pressed;
if (_id is { } id)
SelectDecal(id);
};
DecalEnableSnap.OnToggled += args =>
{
_decalSnap = args.Pressed;
UpdateDecal();
};
DecalEnableCleanable.OnToggled += args =>
{
_decalCleanable = args.Pressed;
UpdateDecal();
};
DecalZIndexSpinBox.ValueChanged += args =>
{
_decalZIndex = args.Value;
UpdateDecal();
};
for (var i = 0; i < EntitySpawnWindow.InitOpts.Length; i++)
{
EntityPlacementMode.AddItem(EntitySpawnWindow.InitOpts[i], i);
}
Pick.Texture.TexturePath = "/Textures/Interface/eyedropper.svg.png";
Delete.Texture.TexturePath = "/Textures/Interface/eraser.svg.png";
}
private void OnDecalColorPicked(Color color)
{
_decalColor = color;
DecalColorPicker.Color = color;
UpdateDecal();
}
private void OnDecalPickerOpenPressed(ButtonEventArgs obj)
{
if (_picker == null)
{
_picker = new PaletteColorPicker();
_picker.OpenToLeft();
_picker.PaletteList.OnItemSelected += args =>
{
var color = ((Color?) args.ItemList.GetSelected().First().Metadata)!.Value;
OnDecalColorPicked(color);
};
return;
}
if (_picker.IsOpen)
_picker.Close();
else
_picker.Open();
}
private void UpdateDecal()
{
if (_id is not { } id)
return;
DecalSystem.UpdateDecalInfo(id, _decalColor, _decalRotation, _decalSnap, _decalZIndex, _decalCleanable);
}
public void SelectDecal(string decalId)
{
if (!_prototype.TryIndex<DecalPrototype>(decalId, out var decal))
return;
_id = decalId;
if (_decalAuto)
{
_decalColor = Color.White;
_decalCleanable = decal.DefaultCleanable;
_decalSnap = decal.DefaultSnap;
DecalColorPicker.Color = _decalColor;
DecalEnableCleanable.Pressed = _decalCleanable;
DecalEnableSnap.Pressed = _decalSnap;
}
UpdateDecal();
RefreshList();
}
private void RefreshList()
{
foreach (var control in Prototypes.Children)
{
if (control is not MappingSpawnButton button ||
button.Prototype?.Prototype is not DecalPrototype)
{
continue;
}
foreach (var child in button.Children)
{
if (child is not MappingSpawnButton { Prototype.Prototype: DecalPrototype } childButton)
{
continue;
}
childButton.Texture.Modulate = _decalColor;
childButton.Visible = IsDecalVisible?.Invoke(childButton) ?? true;
}
}
}
public override void SetChatSize(Vector2 size)
{
ScreenContainer.DesiredSplitCenter = size.X;
ScreenContainer.ResizeMode = SplitContainer.SplitResizeMode.RespectChildrenMinSize;
}
public void UnPressActionsExcept(Control except)
{
Add.Pressed = Add == except;
Fill.Pressed = Fill == except;
Grab.Pressed = Grab == except;
Move.Pressed = Move == except;
Pick.Pressed = Pick == except;
Delete.Pressed = Delete == except;
}
}

View File

@@ -0,0 +1,26 @@
<mapping:MappingSpawnButton
xmlns="https://spacestation14.io"
xmlns:mapping="clr-namespace:Content.Client.Mapping">
<BoxContainer Orientation="Vertical">
<Control>
<Button Name="Button" Access="Public" ToggleMode="True" StyleClasses="ButtonSquare" />
<BoxContainer Orientation="Horizontal">
<LayeredTextureRect Name="Texture" Access="Public" MinSize="48 48"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="KeepAspectCentered" CanShrink="True" />
<Control SetSize="48 48" Access="Public" Name="CollapseButtonWrapper">
<Button Name="CollapseButton" Access="Public" Text="▶"
ToggleMode="True" StyleClasses="ButtonSquare" SetSize="48 48" />
</Control>
<Label Name="Label" Access="Public"
VAlign="Center"
VerticalExpand="True"
MinHeight="48"
Margin="5 0"
HorizontalExpand="True" ClipText="True" />
</BoxContainer>
</Control>
<BoxContainer Name="ChildrenPrototypes" Access="Public" Orientation="Vertical"
Margin="24 0 0 0" />
</BoxContainer>
</mapping:MappingSpawnButton>

View File

@@ -0,0 +1,16 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Mapping;
[GenerateTypedNameReferences]
public sealed partial class MappingSpawnButton : Control
{
public MappingPrototype? Prototype;
public MappingSpawnButton()
{
RobustXamlLoader.Load(this);
}
}

View File

@@ -0,0 +1,936 @@
using System.Linq;
using System.Numerics;
using Content.Client.Administration.Managers;
using Content.Client.ContextMenu.UI;
using Content.Client.Decals;
using Content.Client.Gameplay;
using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.Gameplay;
using Content.Client.Verbs;
using Content.Shared.Administration;
using Content.Shared.Decals;
using Content.Shared.Input;
using Content.Shared.Maps;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Placement;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Enums;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Serialization.Markdown.Value;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using static System.StringComparison;
using static Robust.Client.UserInterface.Controls.BaseButton;
using static Robust.Client.UserInterface.Controls.LineEdit;
using static Robust.Client.UserInterface.Controls.OptionButton;
using static Robust.Shared.Input.Binding.PointerInputCmdHandler;
namespace Content.Client.Mapping;
public sealed class MappingState : GameplayStateBase
{
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntityNetworkManager _entityNetwork = default!;
[Dependency] private readonly IInputManager _input = default!;
[Dependency] private readonly ILogManager _log = default!;
[Dependency] private readonly IMapManager _mapMan = default!;
[Dependency] private readonly MappingManager _mapping = default!;
[Dependency] private readonly IOverlayManager _overlays = default!;
[Dependency] private readonly IPlacementManager _placement = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IResourceCache _resources = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private EntityMenuUIController _entityMenuController = default!;
private DecalPlacementSystem _decal = default!;
private SpriteSystem _sprite = default!;
private TransformSystem _transform = default!;
private VerbSystem _verbs = default!;
private readonly ISawmill _sawmill;
private readonly GameplayStateLoadController _loadController;
private bool _setup;
private readonly List<MappingPrototype> _allPrototypes = new();
private readonly Dictionary<IPrototype, MappingPrototype> _allPrototypesDict = new();
private readonly Dictionary<Type, Dictionary<string, MappingPrototype>> _idDict = new();
private readonly List<MappingPrototype> _prototypes = new();
private (TimeSpan At, MappingSpawnButton Button)? _lastClicked;
private Control? _scrollTo;
private bool _updatePlacement;
private bool _updateEraseDecal;
private MappingScreen Screen => (MappingScreen) UserInterfaceManager.ActiveScreen!;
private MainViewport Viewport => UserInterfaceManager.ActiveScreen!.GetWidget<MainViewport>()!;
public CursorState State { get; set; }
public MappingState()
{
IoCManager.InjectDependencies(this);
_sawmill = _log.GetSawmill("mapping");
_loadController = UserInterfaceManager.GetUIController<GameplayStateLoadController>();
}
protected override void Startup()
{
EnsureSetup();
base.Startup();
UserInterfaceManager.LoadScreen<MappingScreen>();
_loadController.LoadScreen();
var context = _input.Contexts.GetContext("common");
context.AddFunction(ContentKeyFunctions.MappingUnselect);
context.AddFunction(ContentKeyFunctions.SaveMap);
context.AddFunction(ContentKeyFunctions.MappingEnablePick);
context.AddFunction(ContentKeyFunctions.MappingEnableDelete);
context.AddFunction(ContentKeyFunctions.MappingPick);
context.AddFunction(ContentKeyFunctions.MappingRemoveDecal);
context.AddFunction(ContentKeyFunctions.MappingCancelEraseDecal);
context.AddFunction(ContentKeyFunctions.MappingOpenContextMenu);
Screen.DecalSystem = _decal;
Screen.Prototypes.SearchBar.OnTextChanged += OnSearch;
Screen.Prototypes.CollapseAllButton.OnPressed += OnCollapseAll;
Screen.Prototypes.ClearSearchButton.OnPressed += OnClearSearch;
Screen.Prototypes.GetPrototypeData += OnGetData;
Screen.Prototypes.SelectionChanged += OnSelected;
Screen.Prototypes.CollapseToggled += OnCollapseToggled;
Screen.Pick.OnPressed += OnPickPressed;
Screen.Delete.OnPressed += OnDeletePressed;
Screen.EntityReplaceButton.OnToggled += OnEntityReplacePressed;
Screen.EntityPlacementMode.OnItemSelected += OnEntityPlacementSelected;
Screen.EraseEntityButton.OnToggled += OnEraseEntityPressed;
Screen.EraseDecalButton.OnToggled += OnEraseDecalPressed;
_placement.PlacementChanged += OnPlacementChanged;
CommandBinds.Builder
.Bind(ContentKeyFunctions.MappingUnselect, new PointerInputCmdHandler(HandleMappingUnselect, outsidePrediction: true))
.Bind(ContentKeyFunctions.SaveMap, new PointerInputCmdHandler(HandleSaveMap, outsidePrediction: true))
.Bind(ContentKeyFunctions.MappingEnablePick, new PointerStateInputCmdHandler(HandleEnablePick, HandleDisablePick, outsidePrediction: true))
.Bind(ContentKeyFunctions.MappingEnableDelete, new PointerStateInputCmdHandler(HandleEnableDelete, HandleDisableDelete, outsidePrediction: true))
.Bind(ContentKeyFunctions.MappingPick, new PointerInputCmdHandler(HandlePick, outsidePrediction: true))
.Bind(ContentKeyFunctions.MappingRemoveDecal, new PointerInputCmdHandler(HandleEditorCancelPlace, outsidePrediction: true))
.Bind(ContentKeyFunctions.MappingCancelEraseDecal, new PointerInputCmdHandler(HandleCancelEraseDecal, outsidePrediction: true))
.Bind(ContentKeyFunctions.MappingOpenContextMenu, new PointerInputCmdHandler(HandleOpenContextMenu, outsidePrediction: true))
.Register<MappingState>();
_overlays.AddOverlay(new MappingOverlay(this));
_prototypeManager.PrototypesReloaded += OnPrototypesReloaded;
Screen.Prototypes.UpdateVisible(_prototypes);
}
private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj)
{
if (!obj.WasModified<EntityPrototype>() &&
!obj.WasModified<ContentTileDefinition>() &&
!obj.WasModified<DecalPrototype>())
{
return;
}
ReloadPrototypes();
}
private bool HandleOpenContextMenu(in PointerInputCmdArgs args)
{
Deselect();
var coords = args.Coordinates.ToMap(_entityManager, _transform);
if (_verbs.TryGetEntityMenuEntities(coords, out var entities))
_entityMenuController.OpenRootMenu(entities);
return true;
}
protected override void Shutdown()
{
CommandBinds.Unregister<MappingState>();
Screen.Prototypes.SearchBar.OnTextChanged -= OnSearch;
Screen.Prototypes.CollapseAllButton.OnPressed -= OnCollapseAll;
Screen.Prototypes.ClearSearchButton.OnPressed -= OnClearSearch;
Screen.Prototypes.GetPrototypeData -= OnGetData;
Screen.Prototypes.SelectionChanged -= OnSelected;
Screen.Prototypes.CollapseToggled -= OnCollapseToggled;
Screen.Pick.OnPressed -= OnPickPressed;
Screen.Delete.OnPressed -= OnDeletePressed;
Screen.EntityReplaceButton.OnToggled -= OnEntityReplacePressed;
Screen.EntityPlacementMode.OnItemSelected -= OnEntityPlacementSelected;
Screen.EraseEntityButton.OnToggled -= OnEraseEntityPressed;
Screen.EraseDecalButton.OnToggled -= OnEraseDecalPressed;
_placement.PlacementChanged -= OnPlacementChanged;
_prototypeManager.PrototypesReloaded -= OnPrototypesReloaded;
UserInterfaceManager.ClearWindows();
_loadController.UnloadScreen();
UserInterfaceManager.UnloadScreen();
var context = _input.Contexts.GetContext("common");
context.RemoveFunction(ContentKeyFunctions.MappingUnselect);
context.RemoveFunction(ContentKeyFunctions.SaveMap);
context.RemoveFunction(ContentKeyFunctions.MappingEnablePick);
context.RemoveFunction(ContentKeyFunctions.MappingEnableDelete);
context.RemoveFunction(ContentKeyFunctions.MappingPick);
context.RemoveFunction(ContentKeyFunctions.MappingRemoveDecal);
context.RemoveFunction(ContentKeyFunctions.MappingCancelEraseDecal);
context.RemoveFunction(ContentKeyFunctions.MappingOpenContextMenu);
_overlays.RemoveOverlay<MappingOverlay>();
base.Shutdown();
}
private void EnsureSetup()
{
if (_setup)
return;
_setup = true;
_entityMenuController = UserInterfaceManager.GetUIController<EntityMenuUIController>();
_decal = _entityManager.System<DecalPlacementSystem>();
_sprite = _entityManager.System<SpriteSystem>();
_transform = _entityManager.System<TransformSystem>();
_verbs = _entityManager.System<VerbSystem>();
ReloadPrototypes();
}
private void ReloadPrototypes()
{
var entities = new MappingPrototype(null, Loc.GetString("mapping-entities")) { Children = new List<MappingPrototype>() };
_prototypes.Add(entities);
var mappings = new Dictionary<string, MappingPrototype>();
foreach (var entity in _prototypeManager.EnumeratePrototypes<EntityPrototype>())
{
Register(entity, entity.ID, entities);
}
Sort(mappings, entities);
mappings.Clear();
var tiles = new MappingPrototype(null, Loc.GetString("mapping-tiles")) { Children = new List<MappingPrototype>() };
_prototypes.Add(tiles);
foreach (var tile in _prototypeManager.EnumeratePrototypes<ContentTileDefinition>())
{
Register(tile, tile.ID, tiles);
}
Sort(mappings, tiles);
mappings.Clear();
var decals = new MappingPrototype(null, Loc.GetString("mapping-decals")) { Children = new List<MappingPrototype>() };
_prototypes.Add(decals);
foreach (var decal in _prototypeManager.EnumeratePrototypes<DecalPrototype>())
{
Register(decal, decal.ID, decals);
}
Sort(mappings, decals);
mappings.Clear();
}
private void Sort(Dictionary<string, MappingPrototype> prototypes, MappingPrototype topLevel)
{
static int Compare(MappingPrototype a, MappingPrototype b)
{
return string.Compare(a.Name, b.Name, OrdinalIgnoreCase);
}
topLevel.Children ??= new List<MappingPrototype>();
foreach (var prototype in prototypes.Values)
{
if (prototype.Parents == null && prototype != topLevel)
{
prototype.Parents = new List<MappingPrototype> { topLevel };
topLevel.Children.Add(prototype);
}
prototype.Parents?.Sort(Compare);
prototype.Children?.Sort(Compare);
}
topLevel.Children.Sort(Compare);
}
private MappingPrototype? Register<T>(T? prototype, string id, MappingPrototype topLevel) where T : class, IPrototype, IInheritingPrototype
{
{
if (prototype == null &&
_prototypeManager.TryIndex(id, out prototype) &&
prototype is EntityPrototype entity)
{
if (entity.HideSpawnMenu || entity.Abstract)
prototype = null;
}
}
if (prototype == null)
{
if (!_prototypeManager.TryGetMapping(typeof(T), id, out var node))
{
_sawmill.Error($"No {nameof(T)} found with id {id}");
return null;
}
var ids = _idDict.GetOrNew(typeof(T));
if (ids.TryGetValue(id, out var mapping))
{
return mapping;
}
else
{
var name = node.TryGet("name", out ValueDataNode? nameNode)
? nameNode.Value
: id;
if (node.TryGet("suffix", out ValueDataNode? suffix))
name = $"{name} [{suffix.Value}]";
mapping = new MappingPrototype(prototype, name);
_allPrototypes.Add(mapping);
ids.Add(id, mapping);
if (node.TryGet("parent", out ValueDataNode? parentValue))
{
var parent = Register<T>(null, parentValue.Value, topLevel);
if (parent != null)
{
mapping.Parents ??= new List<MappingPrototype>();
mapping.Parents.Add(parent);
parent.Children ??= new List<MappingPrototype>();
parent.Children.Add(mapping);
}
}
else if (node.TryGet("parent", out SequenceDataNode? parentSequence))
{
foreach (var parentNode in parentSequence.Cast<ValueDataNode>())
{
var parent = Register<T>(null, parentNode.Value, topLevel);
if (parent != null)
{
mapping.Parents ??= new List<MappingPrototype>();
mapping.Parents.Add(parent);
parent.Children ??= new List<MappingPrototype>();
parent.Children.Add(mapping);
}
}
}
else
{
topLevel.Children ??= new List<MappingPrototype>();
topLevel.Children.Add(mapping);
mapping.Parents ??= new List<MappingPrototype>();
mapping.Parents.Add(topLevel);
}
return mapping;
}
}
else
{
var ids = _idDict.GetOrNew(typeof(T));
if (ids.TryGetValue(id, out var mapping))
{
return mapping;
}
else
{
var entity = prototype as EntityPrototype;
var name = entity?.Name ?? prototype.ID;
if (!string.IsNullOrWhiteSpace(entity?.EditorSuffix))
name = $"{name} [{entity.EditorSuffix}]";
mapping = new MappingPrototype(prototype, name);
_allPrototypes.Add(mapping);
_allPrototypesDict.Add(prototype, mapping);
ids.Add(prototype.ID, mapping);
}
if (prototype.Parents == null)
{
topLevel.Children ??= new List<MappingPrototype>();
topLevel.Children.Add(mapping);
mapping.Parents ??= new List<MappingPrototype>();
mapping.Parents.Add(topLevel);
return mapping;
}
foreach (var parentId in prototype.Parents)
{
var parent = Register<T>(null, parentId, topLevel);
if (parent != null)
{
mapping.Parents ??= new List<MappingPrototype>();
mapping.Parents.Add(parent);
parent.Children ??= new List<MappingPrototype>();
parent.Children.Add(mapping);
}
}
return mapping;
}
}
private void OnPlacementChanged(object? sender, EventArgs e)
{
_updatePlacement = true;
}
protected override void OnKeyBindStateChanged(ViewportBoundKeyEventArgs args)
{
if (args.Viewport == null)
base.OnKeyBindStateChanged(new ViewportBoundKeyEventArgs(args.KeyEventArgs, Viewport.Viewport));
else
base.OnKeyBindStateChanged(args);
}
private void OnSearch(LineEditEventArgs args)
{
if (string.IsNullOrEmpty(args.Text))
{
Screen.Prototypes.PrototypeList.Visible = true;
Screen.Prototypes.SearchList.Visible = false;
return;
}
var matches = new List<MappingPrototype>();
foreach (var prototype in _allPrototypes)
{
if (prototype.Name.Contains(args.Text, OrdinalIgnoreCase))
matches.Add(prototype);
}
matches.Sort(static (a, b) => string.Compare(a.Name, b.Name, OrdinalIgnoreCase));
Screen.Prototypes.PrototypeList.Visible = false;
Screen.Prototypes.SearchList.Visible = true;
Screen.Prototypes.Search(matches);
}
private void OnCollapseAll(ButtonEventArgs args)
{
foreach (var child in Screen.Prototypes.PrototypeList.Children)
{
if (child is not MappingSpawnButton button)
continue;
Collapse(button);
}
Screen.Prototypes.ScrollContainer.SetScrollValue(new Vector2(0, 0));
}
private void OnClearSearch(ButtonEventArgs obj)
{
Screen.Prototypes.SearchBar.Text = string.Empty;
OnSearch(new LineEditEventArgs(Screen.Prototypes.SearchBar, string.Empty));
}
private void OnGetData(IPrototype prototype, List<Texture> textures)
{
switch (prototype)
{
case EntityPrototype entity:
textures.AddRange(SpriteComponent.GetPrototypeTextures(entity, _resources).Select(t => t.Default));
break;
case DecalPrototype decal:
textures.Add(_sprite.Frame0(decal.Sprite));
break;
case ContentTileDefinition tile:
if (tile.Sprite?.ToString() is { } sprite)
textures.Add(_resources.GetResource<TextureResource>(sprite).Texture);
break;
}
}
private void OnSelected(MappingPrototype mapping)
{
if (mapping.Prototype == null)
return;
var chain = new Stack<MappingPrototype>();
chain.Push(mapping);
var parent = mapping.Parents?.FirstOrDefault();
while (parent != null)
{
chain.Push(parent);
parent = parent.Parents?.FirstOrDefault();
}
_lastClicked = null;
Control? last = null;
var children = Screen.Prototypes.PrototypeList.Children;
foreach (var prototype in chain)
{
foreach (var child in children)
{
if (child is MappingSpawnButton button &&
button.Prototype == prototype)
{
UnCollapse(button);
OnSelected(button, prototype.Prototype);
children = button.ChildrenPrototypes.Children;
last = child;
break;
}
}
}
if (last != null && Screen.Prototypes.PrototypeList.Visible)
_scrollTo = last;
}
private void OnSelected(MappingSpawnButton button, IPrototype? prototype)
{
var time = _timing.CurTime;
if (prototype is DecalPrototype)
Screen.SelectDecal(prototype.ID);
// Double-click functionality if it's collapsible.
if (_lastClicked is { } lastClicked &&
lastClicked.Button == button &&
lastClicked.At > time - TimeSpan.FromSeconds(0.333) &&
string.IsNullOrEmpty(Screen.Prototypes.SearchBar.Text) &&
button.CollapseButton.Visible)
{
button.CollapseButton.Pressed = !button.CollapseButton.Pressed;
ToggleCollapse(button);
button.Button.Pressed = true;
Screen.Prototypes.Selected = button;
_lastClicked = null;
return;
}
// Toggle if it's the same button (at least if we just unclicked it).
if (!button.Button.Pressed && button.Prototype?.Prototype != null && _lastClicked?.Button == button)
{
_lastClicked = null;
Deselect();
return;
}
_lastClicked = (time, button);
if (button.Prototype == null)
return;
if (Screen.Prototypes.Selected is { } oldButton &&
oldButton != button)
{
Deselect();
}
Screen.EntityContainer.Visible = false;
Screen.DecalContainer.Visible = false;
switch (prototype)
{
case EntityPrototype entity:
{
var placementId = Screen.EntityPlacementMode.SelectedId;
var placement = new PlacementInformation
{
PlacementOption = placementId > 0 ? EntitySpawnWindow.InitOpts[placementId] : entity.PlacementMode,
EntityType = entity.ID,
IsTile = false
};
Screen.EntityContainer.Visible = true;
_decal.SetActive(false);
_placement.BeginPlacing(placement);
break;
}
case DecalPrototype decal:
_placement.Clear();
_decal.SetActive(true);
_decal.UpdateDecalInfo(decal.ID, Color.White, 0, true, 0, false);
Screen.DecalContainer.Visible = true;
break;
case ContentTileDefinition tile:
{
var placement = new PlacementInformation
{
PlacementOption = "AlignTileAny",
TileType = tile.TileId,
IsTile = true
};
_decal.SetActive(false);
_placement.BeginPlacing(placement);
break;
}
default:
_placement.Clear();
break;
}
Screen.Prototypes.Selected = button;
button.Button.Pressed = true;
}
private void Deselect()
{
if (Screen.Prototypes.Selected is { } selected)
{
selected.Button.Pressed = false;
Screen.Prototypes.Selected = null;
if (selected.Prototype?.Prototype is DecalPrototype)
{
_decal.SetActive(false);
Screen.DecalContainer.Visible = false;
}
if (selected.Prototype?.Prototype is EntityPrototype)
{
_placement.Clear();
}
if (selected.Prototype?.Prototype is ContentTileDefinition)
{
_placement.Clear();
}
}
}
private void OnCollapseToggled(MappingSpawnButton button, ButtonToggledEventArgs args)
{
ToggleCollapse(button);
}
private void OnPickPressed(ButtonEventArgs args)
{
if (args.Button.Pressed)
EnablePick();
else
DisablePick();
}
private void OnDeletePressed(ButtonEventArgs obj)
{
if (obj.Button.Pressed)
EnableDelete();
else
DisableDelete();
}
private void OnEntityReplacePressed(ButtonToggledEventArgs args)
{
_placement.Replacement = args.Pressed;
}
private void OnEntityPlacementSelected(ItemSelectedEventArgs args)
{
Screen.EntityPlacementMode.SelectId(args.Id);
if (_placement.CurrentMode != null)
{
var placement = new PlacementInformation
{
PlacementOption = EntitySpawnWindow.InitOpts[args.Id],
EntityType = _placement.CurrentPermission!.EntityType,
TileType = _placement.CurrentPermission.TileType,
Range = 2,
IsTile = _placement.CurrentPermission.IsTile,
};
_placement.BeginPlacing(placement);
}
}
private void OnEraseEntityPressed(ButtonEventArgs args)
{
if (args.Button.Pressed == _placement.Eraser)
return;
if (args.Button.Pressed)
EnableEraser();
else
DisableEraser();
}
private void OnEraseDecalPressed(ButtonToggledEventArgs args)
{
_placement.Clear();
Deselect();
Screen.EraseEntityButton.Pressed = false;
_updatePlacement = true;
_updateEraseDecal = args.Pressed;
}
private void EnableEraser()
{
if (_placement.Eraser)
return;
_placement.Clear();
_placement.ToggleEraser();
Screen.EntityPlacementMode.Disabled = true;
Screen.EraseDecalButton.Pressed = false;
Deselect();
}
private void DisableEraser()
{
if (!_placement.Eraser)
return;
_placement.ToggleEraser();
Screen.EntityPlacementMode.Disabled = false;
}
private void EnablePick()
{
Screen.UnPressActionsExcept(Screen.Pick);
State = CursorState.Pick;
}
private void DisablePick()
{
Screen.Pick.Pressed = false;
State = CursorState.None;
}
private void EnableDelete()
{
Screen.UnPressActionsExcept(Screen.Delete);
State = CursorState.Delete;
EnableEraser();
}
private void DisableDelete()
{
Screen.Delete.Pressed = false;
State = CursorState.None;
DisableEraser();
}
private bool HandleMappingUnselect(in PointerInputCmdArgs args)
{
if (Screen.Prototypes.Selected is not { Prototype.Prototype: DecalPrototype })
return false;
Deselect();
return true;
}
private bool HandleSaveMap(in PointerInputCmdArgs args)
{
#if FULL_RELEASE
return false;
#endif
if (!_admin.IsAdmin(true) || !_admin.HasFlag(AdminFlags.Host))
return false;
SaveMap();
return true;
}
private bool HandleEnablePick(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
EnablePick();
return true;
}
private bool HandleDisablePick(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
DisablePick();
return true;
}
private bool HandleEnableDelete(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
EnableDelete();
return true;
}
private bool HandleDisableDelete(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
DisableDelete();
return true;
}
private bool HandlePick(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (State != CursorState.Pick)
return false;
MappingPrototype? button = null;
// Try and get tile under it
// TODO: Separate mode for decals.
if (!uid.IsValid())
{
var mapPos = _transform.ToMapCoordinates(coords);
if (_mapMan.TryFindGridAt(mapPos, out var gridUid, out var grid) &&
_entityManager.System<SharedMapSystem>().TryGetTileRef(gridUid, grid, coords, out var tileRef) &&
_allPrototypesDict.TryGetValue(tileRef.GetContentTileDefinition(), out button))
{
OnSelected(button);
return true;
}
}
if (button == null)
{
if (uid == EntityUid.Invalid ||
_entityManager.GetComponentOrNull<MetaDataComponent>(uid) is not { EntityPrototype: { } prototype } ||
!_allPrototypesDict.TryGetValue(prototype, out button))
{
// we always block other input handlers if pick mode is enabled
// this makes you not accidentally place something in space because you
// miss-clicked while holding down the pick hotkey
return true;
}
// Selected an entity
OnSelected(button);
// Match rotation
_placement.Direction = _entityManager.GetComponent<TransformComponent>(uid).LocalRotation.GetDir();
}
return true;
}
private bool HandleEditorCancelPlace(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
if (!Screen.EraseDecalButton.Pressed)
return false;
_entityNetwork.SendSystemNetworkMessage(new RequestDecalRemovalEvent(_entityManager.GetNetCoordinates(coords)));
return true;
}
private bool HandleCancelEraseDecal(in PointerInputCmdArgs args)
{
if (!Screen.EraseDecalButton.Pressed)
return false;
Screen.EraseDecalButton.Pressed = false;
return true;
}
private async void SaveMap()
{
await _mapping.SaveMap();
}
private void ToggleCollapse(MappingSpawnButton button)
{
if (button.CollapseButton.Pressed)
{
if (button.Prototype?.Children != null)
{
foreach (var child in button.Prototype.Children)
{
Screen.Prototypes.Insert(button.ChildrenPrototypes, child, true);
}
}
button.CollapseButton.Label.Text = "▼";
}
else
{
button.ChildrenPrototypes.DisposeAllChildren();
button.CollapseButton.Label.Text = "▶";
}
}
private void Collapse(MappingSpawnButton button)
{
if (!button.CollapseButton.Pressed)
return;
button.CollapseButton.Pressed = false;
ToggleCollapse(button);
}
private void UnCollapse(MappingSpawnButton button)
{
if (button.CollapseButton.Pressed)
return;
button.CollapseButton.Pressed = true;
ToggleCollapse(button);
}
public EntityUid? GetHoveredEntity()
{
if (UserInterfaceManager.CurrentlyHovered is not IViewportControl viewport ||
_input.MouseScreenPosition is not { IsValid: true } position)
{
return null;
}
var mapPos = viewport.PixelToMap(position.Position);
return GetClickedEntity(mapPos);
}
public override void FrameUpdate(FrameEventArgs e)
{
if (_updatePlacement)
{
_updatePlacement = false;
if (!_placement.IsActive && _decal.GetActiveDecal().Decal == null)
Deselect();
Screen.EraseEntityButton.Pressed = _placement.Eraser;
Screen.EraseDecalButton.Pressed = _updateEraseDecal;
Screen.EntityPlacementMode.Disabled = _placement.Eraser;
}
if (_scrollTo is not { } scrollTo)
return;
// this is not ideal but we wait until the control's height is computed to use
// its position to scroll to
if (scrollTo.Height > 0 && Screen.Prototypes.PrototypeList.Visible)
{
var y = scrollTo.GlobalPosition.Y - Screen.Prototypes.ScrollContainer.Height / 2 + scrollTo.Height;
var scroll = Screen.Prototypes.ScrollContainer;
scroll.SetScrollValue(scroll.GetScrollValue() + new Vector2(0, y));
_scrollTo = null;
}
}
// TODO this doesn't handle pressing down multiple state hotkeys at the moment
public enum CursorState
{
None,
Pick,
Delete
}
}

View File

@@ -13,7 +13,6 @@ public sealed partial class MappingSystem : EntitySystem
{
[Dependency] private readonly IPlacementManager _placementMan = default!;
[Dependency] private readonly ITileDefinitionManager _tileMan = default!;
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
/// <summary>
@@ -26,8 +25,6 @@ public sealed partial class MappingSystem : EntitySystem
/// </summary>
private readonly SpriteSpecifier _deleteIcon = new Texture(new ("Interface/VerbIcons/delete.svg.192dpi.png"));
public string DefaultMappingActions = "/mapping_actions.yml";
public override void Initialize()
{
base.Initialize();
@@ -36,11 +33,6 @@ public sealed partial class MappingSystem : EntitySystem
SubscribeLocalEvent<StartPlacementActionEvent>(OnStartPlacementAction);
}
public void LoadMappingActions()
{
_actionsSystem.LoadActionAssignments(DefaultMappingActions, false);
}
/// <summary>
/// This checks if the placement manager is currently active, and attempts to copy the placement information for
/// some entity or tile into an action. This is somewhat janky, but it seem to work well enough. Though I'd

View File

@@ -18,6 +18,7 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly InputSystem _inputSystem = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
public bool Enabled { get; set; }
@@ -62,11 +63,11 @@ public sealed class GridDraggingSystem : SharedGridDraggingSystem
if (_dragging == null) return;
if (_lastMousePosition != null && TryComp(_dragging.Value, out TransformComponent? xform) &&
TryComp<PhysicsComponent>(_dragging.Value, out var body) &&
TryComp<PhysicsComponent>(_dragging.Value, out _) &&
xform.MapID == _lastMousePosition.Value.MapId)
{
var tickTime = _gameTiming.TickPeriod;
var distance = _lastMousePosition.Value.Position - xform.WorldPosition;
var distance = _lastMousePosition.Value.Position - _transformSystem.GetWorldPosition(xform);
RaiseNetworkEvent(new GridDragVelocityRequest()
{
Grid = GetNetEntity(_dragging.Value),

View File

@@ -0,0 +1,27 @@
using Content.Shared.Conveyor;
using Content.Shared.Materials;
using Robust.Client.GameObjects;
namespace Content.Client.Materials;
public sealed class RecyclerVisualizerSystem : VisualizerSystem<RecyclerVisualsComponent>
{
protected override void OnAppearanceChange(EntityUid uid, RecyclerVisualsComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null || !args.Sprite.LayerMapTryGet(RecyclerVisualLayers.Main, out var layer))
return;
AppearanceSystem.TryGetData<ConveyorState>(uid, ConveyorVisuals.State, out var running);
AppearanceSystem.TryGetData<bool>(uid, RecyclerVisuals.Bloody, out var bloody);
AppearanceSystem.TryGetData<bool>(uid, RecyclerVisuals.Broken, out var broken);
var activityState = running == ConveyorState.Off ? 0 : 1;
if (broken) //breakage overrides activity
activityState = 2;
var bloodyKey = bloody ? component.BloodyKey : string.Empty;
var state = $"{component.BaseKey}{activityState}{bloodyKey}";
args.Sprite.LayerSetState(layer, state);
}
}

View File

@@ -0,0 +1,17 @@
namespace Content.Client.Materials;
[RegisterComponent]
public sealed partial class RecyclerVisualsComponent : Component
{
/// <summary>
/// Key appended to state string if bloody.
/// </summary>
[DataField]
public string BloodyKey = "bld";
/// <summary>
/// Base key for the visual state.
/// </summary>
[DataField]
public string BaseKey = "grinder-o";
}

View File

@@ -257,7 +257,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
mainContainer.AddChild(jobContainer);
// Job icon
if (_prototypeManager.TryIndex<StatusIconPrototype>(sensor.JobIcon, out var proto))
if (_prototypeManager.TryIndex<JobIconPrototype>(sensor.JobIcon, out var proto))
{
var jobIcon = new TextureRect()
{

View File

@@ -64,15 +64,15 @@ public sealed class JetpackSystem : SharedJetpackSystem
private void CreateParticles(EntityUid uid)
{
var uidXform = Transform(uid);
// Don't show particles unless the user is moving.
if (Container.TryGetContainingContainer(uid, out var container) &&
if (Container.TryGetContainingContainer((uid, uidXform, null), out var container) &&
TryComp<PhysicsComponent>(container.Owner, out var body) &&
body.LinearVelocity.LengthSquared() < 1f)
{
return;
}
var uidXform = Transform(uid);
var coordinates = uidXform.Coordinates;
var gridUid = _transform.GetGrid(coordinates);

View File

@@ -28,7 +28,7 @@ public sealed class SpriteMovementSystem : EntitySystem
return;
var oldMoving = (SharedMoverController.GetNormalizedMovement(args.OldMovement) & MoveButtons.AnyDirection) != MoveButtons.None;
var moving = (SharedMoverController.GetNormalizedMovement(args.Component.HeldMoveButtons) & MoveButtons.AnyDirection) != MoveButtons.None;
var moving = (SharedMoverController.GetNormalizedMovement(args.Entity.Comp.HeldMoveButtons) & MoveButtons.AnyDirection) != MoveButtons.None;
if (oldMoving == moving || !_spriteQuery.TryGetComponent(uid, out var sprite))
return;

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